栈溢出利用之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

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