ret2resolve学习笔记

一是做个总结,二是做个备份。上篇文章感谢@大米指出的错误,格式化字符串漏洞还未销声匿迹!!!

0x01 背景知识

在学习之前先了解一下其中必须知道的背景知识,包括动态链接、延时绑定、Global Offset Table、rel_offset等。

动态链接

《 程序员的自我修养 》,力荐此书!!有更好的欢迎分享。

下图是 ELF 的装载过程。

这篇文章主要关注的是动态链接器中的延时绑定,这是动态链接器中的一部分。

没找到上两张图的出处,望告知。再看看 .interp 这个 section 中的数据:

延时绑定

延时绑定的原理大概就是这样,等到需要用到则个函数的时候才去绑定再使用,不用则不绑定,绑定完之后下次调用就直接进入函数,不需要再次绑定。一次绑定,终生收益。绑定的实现大概是这样的:

就是相当于执行函数 __dl_runtime_resolve(link_map, rel_offset) 。这个函数的作用:

1. 修改 read@.got.plt 的值使其指向 read() 函数。

2. ret 到 read() 函数。

3. 调整栈

在程序调用库函数时其实是进入了 .plt 的 section 中,每一种需要被重定位的函数都有它私有的 PLT ,反汇编一看:

修改了 read@.got.plt 使其指向 read function ,这样下次 call read 的时候就直接 jmp 到了 read() 函数了。

流程图如下(图出自《动态链接库中函数的地址确定--- PLT 和 GOT 》 ):

http://blog.chinaunix.net/uid-24774106-id-3349549.html

第一次调用:

第二次调用:

Global Offset Table

需要重定位的函数和变量都放在 .got(Global Offset Table) 中。 .got 分为两种: .got 放全局变量, .got.plt 放函数。.got.plt 前三项有点特别:

第一项指向 .dynamic 的地址。 .dynamic 的 section 是专门用于动态链接的。保存了动态链接器所需要的基本信息。依赖哪些共享对象,动态链接符号表的位置,动态链接重定位表的位置等等。是一个结构数组。

使用 readelf -d 查看 .dynamic 的 section 中的内容:

再用 readelf -S hello 找到 .dynamic 的地址,用 gdb 查看一下( Tag 和 Value 都是一一对应的):

查看 elf.h :

https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=elf/elf.h

的定义,发现是一个 tag 加上一个数值或者是指针:

第二项是 Module ID "Lib.so" 这个其实就是 __dl_runtime_resolve( link_map, rel_offset ) 中的 link_map ,它是一个将有引用到的 library 所串成的 linked list。

第三项是 __dl_runtime_resolve( link_map, rel_offset ) 这个函数的地址。

第四项就是 function@.got.plt ,就是上图中的 GOT[n] ,调用 __dl_runtime_resolve( link_map, rel_offset ) 后使其指向各自的函数。

其中第二项和第三项由动态链接器在装载共享模块的时候将他们初始化。对比一下上图中的反汇编 .plt的 section 中的代码就是先 push n ,然后把 .got.plt 第二项 push 进去,然后 jmp 第三项。完成 __dl_runtime_resolve( link_map, rel_offset )

rel_offset

观察一下上图中反汇编的代码,发现相邻的两个需要重定位的函数的 rel_offset 都是相差 8 。现在问题来了,现在手里面有一个可用的 library 的 list ,一个 rel_offset ,__dl_runtime_resolve() 是怎么知道要绑定哪个函数,修改的 .got.plt 的 section的位置又是哪里?

先了解 .dynamic 中的三个 d_tag (可以参考上图 d_tag 的定义),对应这三个节区:

rel.plt

先看看 rel.plt 节区,上面是变量,下面是函数,:

offset 就是需要修改的 .got.plt 的地址,在内存中查看一下,发现就只有两个数值对应上图的5个类型:

在看下它的定义:

这个结构也是 8 个字节。其实 rel_offset 即为需要重定位的函数在 rel.plt 节中的节偏移。r_info 这个结构是两个参数合在一起的。其中 Type 是 ( r_info && 0xff ),另外一个参数就是 ( r_info >> 8 )Sym.ValueSym.Name 是通过 dynsym 节区找到的。

dynsym

再看 .dynsym 这个节区的内容,照例查看它的内存:

发现还是看不懂,还是配合定义来看,这是一个 16 字节的结构,可以阅读参考资料_符号表节:

其实 .dynsym 里面的大概是这样的:struct Elf32_Sym a[n]; 其中 n 是 (r_info >> 8)

dynstr

这里的 st_name 存放的又是 .dynstr 中的节偏移,验证一下:

结构总结

其实就是一个个结构数组。一图解千言:

__dl_runtime_resolve(link_map, rel_offset) 是用汇编写的。调用了 __dl_fixup() ,参数由寄存器传递。那里用 reloc_arg 替代了 rel_offset ,可以通过 apt-get source libc6-dev 下载源码,打开 elf 文件夹下的 dl-runtime.c 即可:

阅读源码对我来说很困难,所以这里抄袭了 7o8v 大佬文章里面的图:

0x02 思路

多数动态装载器实现不去检查重定位表的边界!!!所以我们在高地址处伪造 data 就可以。而程序的可读写段在更高的地址,所以我们只要在那伪造 data 就可以了,一般在 .bss 的 section 伪造。

1、控制 EIP 写入伪造的数据

2、控制 EIP 到dl_resolve

  • 伪造 rel_off 和 link_map
  • 其实只要跳到 PLT[0] ,这样伪造 rel_off 即可

3、在栈上构造函数参数执行即可。

0x03 EXP(PWN2)

借用 7o8v 所说的 pwn2 进行示例,题目链接如下:

http://pan.baidu.com/s/1miT1kPM 密码:hwt3

感谢 7o8v 大佬对我的指导以及帮助,exp 如下:

前面两个 payload 是向 link_map+0xe4 这个地址写 NUll 。一般是 64 位的 ret2resolve 碰到的问题,没想到我人品好碰到了!!!原因是伪造的 symbol 的 index 过大,使得 vernum[ELFW(R_SYM) (reloc->r_info)] 读取越界。为了绕过这部分,roputils 选择的方法便是令 l->l_info[VERSYMIDX (DT_VERSYM)] == NULL 。查看这段代码:

只要将 l->l_info[VERSYMIDX (DT_VERSYM)] 的地址位改为 NULL 就可以跳过这个引发错误的地方。参考资料上说的是 64 位的位于 link_map+0x1c8 ,反汇编 pwn2 如下:

edi 此时保存的是 link_map 的地址,我机器上则是在 link_map+0xe4 ,没有验证是否可以应用到所有 32 位机器上。跳过这段代码主要是修改 _dl_lookup_symbol_x() 函数的参数,部分传参使用了寄存器,修改的参数大概是 const struct r_found_version *version ,版本符号( symbol versioning )。应该是关于 glibc 的兼容性。搞懂一个问题,又蹦出几个问题,故就此作罢,留个坑。可以参考链接:摧毁圣诞。

本人水平有限,如有错误或疑问,欢迎指正讨论。

参考:

  • 1.符号表节: https://docs.oracle.com/cd/E26926_01/html/E25910/chapter6-79797.html#scrolltoc
  • 2.7o8v-ret2resolve: http://www.reversing.win/2017/08/29/%E4%BA%8C%E6%A0%88%E6%BA%A2%E5%87%BA%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8-ret2resolve/
  • 3.ROP之return to dl-resolve: http://rk700.github.io/2015/08/09/return-to-dl-resolve/
  • 4.ELF如何摧毁圣诞--通过ELF动态装载机制进行漏洞利用: http://www.inforsec.org/wp/?p=389
  • 5.动态链接: https://ctf-wiki.github.io/ctf-wiki/executable/elf/program_linking.html#id3

原文发布于微信公众号 - 信安之路(xazlsec)

原文发表时间:2017-11-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏joycl

c#面试题汇总

下面的参考解答只是帮助大家理解,不用背,面试题、笔试题千变万化,不要梦想着把题覆盖了,下面的题是供大家查漏补缺用的,真正的把这些题搞懂了,才能“以不变应万变”。...

5361
来自专栏Hongten

java开发_UUID(Universally Unique Identifier,全局唯一标识符)和GUID(Globally Unique Identifier,全球唯一标识符)

GUID: 即Globally Unique Identifier(全球唯一标识符) 也称作 UUID(Universally Unique IDentifie...

1131
来自专栏零基础使用Django2.0.1打造在线教育网站

在线网站搭建(七):数据库字段的定义(上)

努力与运动兼备~~~有任何问题可以加我好友或者关注微信公众号,欢迎交流,我们一起进步!

1992
来自专栏hotqin888的专栏

用golang递归构建无限级树状目录json数据和数据库

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hotqin888/article/det...

3402
来自专栏比原链

剥开比原看代码13:比原是如何通过/list-balances显示帐户余额的?

Gitee地址:https://gitee.com/BytomBlockchain/bytom

831
来自专栏逆向技术

PE格式第六讲,导出表

                PE格式第六讲,导出表 请注意,下方字数比较多,其实结构挺简单,但是你如果把博客内容弄明白了,对你受益匪浅,千万不要看到字...

1966
来自专栏大内老A

我所理解的Remoting(3):创建CAO Service Factory使接口和实现相互分离

我们知道对于Remoting,有两种不同的Activation模式:Server Activation和Client Activation。他我在前面的系列文章...

1986
来自专栏Golang语言社区

Go基础系列:channel入门

channel用于goroutines之间的通信,让它们之间可以进行数据交换。像管道一样,一个goroutine_A向channel_A中放数据,另一个goro...

1464
来自专栏青玉伏案

iOS逆向工程之Hopper中的ARM指令

虽然前段时间ARM被日本软银收购了,但是科技是无国界的,所以呢ARM相关知识该学的学。现在看ARM指令集还是倍感亲切的,毕竟大学里开了ARM这门课,并且做了不少...

3407
来自专栏犀利豆的技术空间

徒手撸框架--实现 RPC 远程调用

微服务已经是每个互联网开发者必须掌握的一项技术。而 RPC 框架,是构成微服务最重要的组成部分之一。趁最近有时间。又看了看 dubbo 的源码。dubbo 为了...

1562

扫码关注云+社区

领取腾讯云代金券