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