棧溢出利用之return to dl-resolve payload 構造原理(二)

0x00. payload構造思路

前面我們已經大致介紹了dl-resolve如何進行函數地址的解析。

主要步驟是:通過函數的function@plt,將reloc_arg參數壓棧。再跳轉到過程鏈接表的開始PLT[0],將link_map地址壓棧。然後調用_dl_runtime_resolve(link_map, reloc_arg)解析函數的地址,並寫回到函數的全局偏移表(.got.plt)中,最後返回到需要解析的函數中。在_dl_runtime_resolve中調用_dl_fixup函數,主要的操作是:通過參數reloc_arg確定重定位表中該函數的重定位表項;再通過該重定位表項的r_info字段,在動態鏈接符號表中確定該函數的符號表項,以及類型,並進行一些檢查。再由動態鏈接符號表項的st_name在動態鏈接字符串表中確定函數名稱。
payload構造:
如果我們僞造reloc_arg,使該函數的重定位表項位於我們可控制的位置;再僞造重定位表項(即r_offset和r_info),可使該函數的動態鏈接符號表表項位於我們可控制的位置;然後,僞造動態鏈接符號表表項(即st_name、st_value、st_size、st_info、st_other、st_shndx),主要是st_name的值,使該函數動態鏈接字符串表表項位於我們控制的位置;最後,僞造動態鏈接字符串表表項值爲我們想要解析的函數名,就可以了。

0x01. 32位應用的payload構造

下面以XMAN level4中的32位ELF爲例,介紹32位應用payload的構造過程,該ELF有個明顯的棧溢出漏洞。

首先,通過棧溢出向bss段寫入我們精心構造的payload,一般選擇bss段偏移爲0x400的位置(.bss+0x400)寫入。該payload包含解析函數的地址入口、返回值、需要解析函數的參數、僞造的重定位表項、動態鏈接符號表表項、動態鏈接字符串表表項、以及函數的參數值。然後,通過平衡堆棧,將程序的執行流交給解析函數。

32位應用 payload的主要構成如下:
payload1: 利用漏洞進行讀操作,將數據寫入bss區。

   ___________________________________________________________________________________________________________
   | 'A' * offset | read_plt    | p_p_p_ret |   0   | base_stage | 100  | p_ebp_ret | base_stage | leave_ret  |
   |--------------|-------------|-----------|-------|------------|------|-----------|------------|------------|
   |  溢出填充    |返回地址     |返回地址   | arg1  |arg2,寫地址 | arg3 |以寫入地址恢復ebp,構造假的棧幀,返回|
   |              |覆蓋爲read   |爲gadget平 |       |.bss+0x400  |      |                                     | 
   |              |的plt地址    |衡堆棧     |       |            |      |                                     |
   |--------------|-------------|-----------|-------|------------|------|-------------------------------------| 

payload2: 構造假的表項
32位應用,構造假的重定位表項、動態鏈接符號表項、動態鏈接字符串表項

___       ______________
 |       |   "BBBB"     |  棧頂地址
 |       |--------------|
 |       |    PLT[0]    |  PLT表基址,即調用_dl_runtime_resolve函數的入口
 |       |--------------|
 |       | reloc_offset |  重定位偏移,即相對於重定位表(.rel.plt)的偏移
 |       |--------------|
 |       |     ret      |  返回地址,一般隨便填充。
 |       |--------------|
 |       |     arg1     |  函數參數1,arg1 
 |       |--------------|
 |       |     arg2     |  函數參數2,arg2
 |       |--------------|  
 |       |     arg3     |  函數參數3,arg3  //不足3個參數,隨便填充其他值
 |       |--------------|
80Bytes  |   r_offset   |  假的重定位表項: r_offset ,即函數的got 
 |       |--------------|
 |       |   r_info     |  假的重定位表項: r_info
 |       |--------------|
 |       |   align      |  由於動態鏈接符號表字節對齊,因此需要對齊字節
 |       |--------------|
 |       |   st_name    |  假的動態鏈接符號表表項: st_name
 |       |--------------|
 |       |   st_value   |  假的動態鏈接符號表表項: st_value
 |       |--------------|
 |       |   st_size    |  假的動態鏈接符號表表項: st_size
 |       |--------------|
 |       | st_info....  |  假的動態鏈接符號表表項: st_info、st_other、st_shndx
 |       |--------------|
 |       |   "system"   |  假的動態鏈接字符串表表相: 字符串如:"system""write"等等    
 |       |--------------|   
 |       |'AAAA....'    |  填充字符: 'A',使長度達到80字節
---      |--------------|  
 |       |  "/bin/sh"   |  寫入的參數字符串:"/bin/sh"
20Bytes  |--------------|
 |       |  'AAAA....'  |  填充字符: 'A'
_|_      |______________|

0x02. 64位應用的payload構造

前面提到過64位應用和32位應用在解析函數地址的時候有一些區別:

1)_dl_runtime_resolve函數的參數reloc_arg的值的不同,32位應用是偏移值,64位應用是索引值;
2)在_dl_fixup中,在僞造sym之後,會造成解析過程中VERSYM取值超出範圍,造成segment fault,需要將link_map + 0x1c8地址上的值置0;
3)動態鏈接符號表表項中的成員順序有變化(即ELF64_Sym結構體成員順序有變化)。

在構造payload的時候需要注意以上3點不同,並且64位應用通過寄存器傳參。此外payload的長度不要太長,構造的payload過長,傳輸的過程會截斷,造成無法利用成功。

64位應用 payload的主要構成如下:
payload1: 泄漏link_map地址

    _________________________________________
   | 'A' * offset |com_gadget   | addr_vulfun| 
   |--------------|-------------|------------|
   |溢出填充      | x86_64 通用 |漏洞函數     | 
   |              |的gadget     |的地址       |            
   |--------------|-------------|------------|

payload2: 覆蓋link_map 地址 + 0x1c8的地方爲0,並讀入數據

      ________________________________________________________________________________
   | 'A' * offset |com_gadget    | com_gadget |  p_rbp_ret | base_stage | leave_ret  |
   |--------------|--------------|------------|------------|------------|------------|
   |  溢出填充    |將link_map地  |讀入數據寫入| 以寫入地址恢復rbp,構造假的棧幀,返回|
   |              |址偏移0x1c8的 |bss區偏移   |                                      |
   |              |地方置0       |0x400位置   |                                      |
   |--------------|--------------|------------|--------------------------------------| 

payload3: 構造假的表項,注意函數通過寄存器傳參

___        __________________
 |        |   "BBBBBBBB"     |  棧頂地址
 |        |------------------|
 |        |   pop_rdi_ret    |  函數的參數入rdi寄存器
 |        |------------------|
 |        |   addr_shell     |   "/bin/sh" 字符串的地址
 |        |------------------|
 |        |    PLT[0]        |  PLT表基址,即調用_dl_runtime_resolve函數的入口
 |        |------------------|
 |        |  reloc_index     |  重定位偏移,即相對於重定位表(.rel.plt)的索引
 |        |------------------|
 |        |  reloc_align     |  reloc_arg值爲重定位表的索引,需要對齊
 |        |------------------|
180Bytes  |   r_offset       |  假的重定位表項: r_offset ,即函數的got 
 |        |------------------|
 |        |   r_info         |  假的重定位表項: r_info
 |        |------------------|
 |        |   sym_align      |  由於動態鏈接符號表字節對齊,因此需要對齊字節
 |        |------------------|
 |        |st_name,st_info...|  假的動態鏈接符號表表項: st_name、st_info、st_other、st_shndx(32+8+8+16=64bits)
 |        |------------------|
 |        |   st_size        |  假的動態鏈接符號表表項: st_size(64bits)
 |        |------------------|
 |        |   st_value       |  假的動態鏈接符號表表項: st_value(64bits)
 |        |------------------|
 |        |   "system"       |  假的動態鏈接字符串表表相: 字符串如:"system""write"等等    
 |        |------------------|  
 |        |'AAAA....'        |  填充字符: 'A',使長度達到180字節
---       |------------------|  
 |        |  "/bin/sh"       |  寫入的參數字符串:"/bin/sh"
200Bytes  |------------------|
 |        |  'AAAA....'      |  填充字符: 'A'
_|_       |__________________|

參考文獻

1. 通過ELF動態裝載構造ROP鏈(Return-to-dl-resolve)
2. ROP之return to dl-resolve

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章