首页
社区
课程
招聘
[原创][新手向]ret2dl-resolve详解
发表于: 2018-5-28 00:38 29781

[原创][新手向]ret2dl-resolve详解

2018-5-28 00:38
29781

最近做RCTF,结果pwn一道没做出来(虽然精力全放在更擅长的reverse上了),然后复盘的时候发现RNote4有个关于ret2dl-resolve的利用,遂在网上查之,发现很多资料讲的不是很清楚,但是还是慢慢琢磨弄懂了。这个技巧貌似是一个挺基础的技巧,玩pwn一段时间了,发现自己还有这种知识遗漏。。。所以这篇文章新手向,大神可以绕道了。。。。

我们知道,无论是windows下还是linux下,程序想要调用其他动态链接库的函数,必须要在程序加载的时候动态链接,比方说,windows下,叫作IAT表,linux下,叫作GOT表。调用库函数时,会有个类似call [xxx] 或者 jmp [xxx]的指令,其中xxx是IAT表或者GOT表的地址。在这里因为是linux的pwn,我们主要讨论GOT表,以及在linux下更为常见的jmp [xxx].

首先一个hello world程序

其中,这个puts是调用的libc这个动态链接库导出的一个函数。编译它,看看puts是怎么被调用的。

跟一下,看看这个off_804A00C在第一次调用时是什么东西

可以发现,是0x80482e6这个地址,并不直接是libc的puts函数的地址。这是因为linux在程序加载时使用了延迟绑定(lazy load),只有等到这个函数被调用了,才去把这个函数在libc的地址放到GOT表中。接下来,会再push一个0,再push一个dword ptr [0x804a004],待会会说这两个参数是什么意思,最后跳到libc的_dl_runtime_resolve去执行。这个函数的目的,是根据2个参数获取到导出函数(这里是puts)的地址,然后放到相应的GOT表,并且调用它。而这个函数的地址也是从GOT表取并且jmp [xxx]过去的,但是这个函数不会延迟绑定,因为所有函数都是用它做的延迟绑定,如果把它也延迟绑定就会出现先有鸡还是先有蛋的问题了。

section,segment是什么东西不说了,不知道的话呢谷歌百度一下

包含了一些关于动态链接的关键信息,在这个hellopwn上它长这样,事实上这个section所有程序都差不多

这个section的用处就是他包含了很多动态链接所需的关键信息,我们现在只关心DT_STRTAB, DT_SYMTAB, DT_JMPREL这三项,这三个东西分别包含了指向.dynstr, .dynsym, .rel.plt这3个section的指针,可以readelf -S hellopwn看一下,会发现这三个section的地址跟在上图所示的地址是一样的。

一个字符串表,index为0的地方永远是0,然后后面是动态链接所需的字符串,0结尾,包括导入函数名,比方说这里很明显有个puts。到时候,相关数据结构引用一个字符串时,用的是相对这个section头的偏移,比方说,在这里,就是字符串相对0x804821C的偏移。

这个东西,是一个符号表(结构体数组),里面记录了各种符号的信息,每个结构体对应一个符号。我们这里只关心函数符号,比方说上面的puts。结构体定义如下

这里是重定位表(不过跟windows那个重定位表概念不同),也是一个结构体数组,每个项对应一个导入函数。结构体定义如下:

这个想要深入理解的话呢可以去看glibc/elf/dl-runtime.c的源码,这里我就不贴了,因为有一堆宏,看着让人晕,我就直接说下他做了哪些事情。

首先说第一个参数,[0x804a004]是一个link_map的指针,这个结构是干什么的,我们不关心,但是有一点要知道,它包含了.dynamic的指针,通过这个link_map_dl_runtime_resolve函数可以访问到.dynamic这个section

0x08049f14是.dynamic的指针,与前面图中一致;而第二个参数,是当前要调用的导入函数在.rel.plt中的偏移(不过64位的话就直接是index下标),比方说这里,puts就是0,__libc_start_main就是1*sizeof(Elf32_Rel)=8

如果阅读libc源码的话会发现实际顺序可能跟我上面所说的有一点偏差,不过意思都一样,我这样说会比较好理解。

那么,这个怎么去利用呢,有两种利用方式

这个只有在checksec时No RELRO可行,即.dynamic可写。因为ret2dl-resolve会从.dynamic里面拿.dynstr字符串表的指针,然后加上offset取得函数名并且在动态链接库中搜索这个函数名,然后调用。而假如说我们能够改写这个指针到一块我们能够操纵的内存空间,当resolve的时候,就能resolve成我们所指定的任意库函数。比方说,原本是一个free函数,我们就把原本是free字符串的那个偏移位置设为system字符串,第一次调用free("bin/sh")(因为只有第一次才会resolve),就等于调用了system("/bin/sh")

例题就是RCTF的RNote4,题目是一道堆溢出,NO RELRO而且NO PIE溢出到后面的指针可以实现任意地址写。

所以呢,可以先add两个note,然后编辑第一个note使得堆溢出到第二个note的指针,然后再修改第二个note,实现任意写。至于写什么,刚刚也说了,先写.dynamic指向字符串表的指针,使其指向一块可写内存,比如.bss,然后再写这块内存,使得相应偏移出刚好有个system\x00。exp如下

如果.dynamic不可写,那么以上方法就没用了,所以有第二种利用方法。要知道,前面的_dl_runtime_resolve在第二步时

.rel.plt + 第二个参数求出当前函数的重定位表项Elf32_Rel的指针,记作rel

这个时候,_dl_runtime_resolve并没有检查.rel.plt + 第二个参数后是否造成越界访问,所以我们能给一个很大的.rel.plt的offset(64位的话就是下标),然后使得加上去之后的地址指向我们所能操纵的一块内存空间,比方说.bss

然后第三步

rel->r_info >> 8作为.dynsym的下标,求出当前函数的符号表项Elf32_Sym的指针,记作sym

所以在我们所伪造的Elf32_Rel,需要放一个r_info字段,大概长这样就行0xXXXXXX07,其中XXXXXX是相对.dynsym表的下标,注意不是偏移,所以是偏移除以Elf32_Sym的大小,即除以0x10(32位下)。然后这里同样也没有进行越界访问的检查,所以可以用类似的方法,伪造出这个Elf32_Sym。至于为什么是07,因为这是一个导入函数,而导入函数一般都是07,所以写成07就好。

然后第四步

.dynstr + sym->st_name得出符号名字符串指针

同样类似,没有进行越界访问检查,所以这个字符串也能够伪造。

所以,最终的利用思路,大概是

构造ROP,跳转到resolve的PLT,push link_map的位置,就是上图所示的这个地方。此时,栈中必须要有已经伪造好的指向伪造的Elf32_Rel的偏移,然后是返回地址(system的话无所谓),再然后是参数(如果是system函数的话就要是指向"/bin/sh\x00"的指针)

最后来道经典例题,

明显的栈溢出,但是没给libc,ROPgadget也少,所以要用ret2dl-resolve。

利用思路如下:

第一次调用read函数,返回地址再溢出成read函数,这次参数给一个.bss的地址,里面放我们的payload,包括所有伪造的数据结构以及ROP。注意ROP要放在数据结构的前面,不然ROP调用时有可能污染我们伪造的数据结构,而且前面要预留一段空间给ROP所调用的函数用。调用完第二个read之后,ROP到leave; retn的地址,以便切栈切到在.bss中我们构造的下一个ROP链

第二次调用read函数,此时要sendROP链以及所有相关的伪造数据结构

至于offset这些东西要自己慢慢撸,反正我搞了挺久的。。。就在IDA里把地址copy出来然后慢慢算偏移就好了。。。

完整exp写的有点丑,放附件了。

PS: 其他一些大佬博客的exp我没有很看懂。。。不知道为啥要写那么长。。。我是弄懂了方法就按照自己的思路写的,不过也对就是了。。。

然后貌似有个自动得出ROP的工具叫作roputils,这样就不用自己搞这么一串ROP了。。。不过用工具前还是要先搞懂原理的不然就成脚本小子了嘛。。。

貌似也可行,而且64位下link_map+0x1c8 好像要置0,所以可能要自己伪造link_map。但是link_map结构有点复杂,网上也没有关于这种利用方式的资料,以后有空会再研究一下。。。

http://phrack.org/issues/58/4.html

http://pwn4.fun/2016/11/09/Return-to-dl-resolve/

http://showlinkroom.me/2017/04/09/ret2dl-resolve/

https://0x00sec.org/t/linux-internals-the-art-of-symbol-resolution/1488

https://github.com/firmianay/CTF-All-In-One/blob/master/doc/6.1.3_pwn_xdctf2015_pwn200.md


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (18)
雪    币: 5451
活跃值: (3844)
能力值: ( LV13,RANK:283 )
在线值:
发帖
回帖
粉丝
2
多谢分享
2018-6-2 14:45
0
雪    币: 118
活跃值: (118)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
3
牛逼
2018-6-10 11:39
0
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
写的很有条理
2018-6-10 16:50
0
雪    币: 328
活跃值: (39)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
楼主你好,我最近遇到一个问题,也是ret2dlresolve的,64位,.dynamic不可写,原因是_dl_fixup会读取.dynamic的versym表项的内容,它指向了.gun_version节,偏移是以rela.r_info的高32位来看的,本来这个高32为是用来指向.dynsym段的,但是这样会导致它指向一个没有映射的内存导致_dl_fixup执行失败,不知道有没有好的解决办法,.bss段在0x601000,其他的在0x401000,
2018-6-16 23:09
0
雪    币: 5676
活跃值: (1303)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
6
ninebianbian 楼主你好,我最近遇到一个问题,也是ret2dlresolve的,64位,.dynamic不可写,原因是_dl_fixup会读取.dynamic的versym表项的内容,它指向了.gun_version ...
你就控制这个偏移,让他指到.bss呗,64要把link_map也处理下
2018-6-16 23:56
0
雪    币: 328
活跃值: (39)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
holing 你就控制这个偏移,让他指到.bss呗,64要把link_map也处理下
emmmm,可能是我表述不是很清楚,要不然我把程序和目前的脚本给你,你看一下
2018-6-17 13:23
0
雪    币: 5676
活跃值: (1303)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
8
ninebianbian emmmm,可能是我表述不是很清楚,要不然我把程序和目前的脚本给你,你看一下
直接发这
2018-6-17 14:03
0
雪    币: 328
活跃值: (39)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
holing 直接发这
你说的对着呢,是我没有处理linkmap,处理了就好了
2018-6-17 17:41
0
雪    币: 3567
活跃值: (467)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
10
请问指到bss段的时候为什么要在bss基址的基础上再加上一段偏移地址呢
2018-6-26 12:10
0
雪    币: 5676
活跃值: (1303)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
11
VINKKe 请问指到bss段的时候为什么要在bss基址的基础上再加上一段偏移地址呢
给ROP留空间,不然栈不够用
2018-6-26 13:07
0
雪    币: 2
活跃值: (10)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
12
VINKKe 请问指到bss段的时候为什么要在bss基址的基础上再加上一段偏移地址呢
一般建议在bss段伪造reloc_offset的时候,都布局在至少bss+0x800的地方,有的时候由于dl_fixup会引用更低的地址,如果不这样做会报错
2018-7-22 20:30
0
雪    币: 1620
活跃值: (17)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
刚开始学pwn的菜鸡路过。。。ctf-wiki上似乎有讲过相关的知识
2018-8-6 21:07
0
雪    币: 456
活跃值: (76)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
导入符号信息,从第二个字节读,这是怎么想到的。。找了很久的资料。。感谢
2019-2-18 16:24
0
雪    币: 514
活跃值: (43)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
这篇写的真好,收获很大,感谢!
2019-4-4 11:54
0
雪    币: 284
活跃值: (230)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
写的很详细,谢谢
2019-5-6 17:13
0
雪    币: 117
活跃值: (892)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
17
写的很好
2020-8-21 16:57
0
雪    币: 12
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
有个问题,就是为什么要进行栈转移?什么条件下才可以用栈转移呢?
2023-7-31 23:03
0
雪    币: 3535
活跃值: (31016)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
感谢分享
2023-8-1 09:55
1
游客
登录 | 注册 方可回帖
返回
//