baby_pwn
前言
今年信安竞赛的题,本以为是最简单的一道,结果发现做不来,最后查到这个就在ctf-wiki里,当时自己学wiki的时候这个高级ROP被跳过了。。恶补一下ret2dl-resolve的知识
程序逻辑
栈溢出,栈不可执行,没有泄露地址的函数
背景知识
_dl_runtime_resolve函数是重定位的核心函数,这个函数会在进程运行的时候动态修改引用的函数地址,达到重定位的效果。使用1
objdump ./pwn -d -j .plt
可以看到.plt这里会调用0x0804a008,在IDA里可以看到这里存放的是函数的plt表,函数表里存放got表指针,got表里存放的是函数执行的实际地址,在函数执行时才会放进去
ELF动态链接的关键section
函数执行时涉及到的Section包括.dynamic、.dynstr、.dynsym、.rel.plt。使用1
readelf -S ./pwn | grep 'dynamic'
可以查看其地址为0x08049f14,在IDA中看其内容,这个节中包含了动态链接的信息,需要关注的是DT_STRTAB, DT_SYMTAB, DT_JMPREL这三项,这三个东西分别包含了指向.dynstr, .dynsym, .rel.plt这3个section的指针
.rel.plt是重定位表,它的每一个成员都是一个结构体,其结构如下:
1 | typedef struct |
在函数执行过程中会先用.rel.plt得到函数重定位表项的指针Elf32_Rel,之后rel->r_info >> 8作为.dynsym的下标,求出当前函数的符号表项Elf32_Sym的指针
.dynsym是符号表数组,每一个表项是一个结构体,其结构如下:
1 | typedef struct |
这里的st_name比较重要,函数执行到最后会用.dynstr + sym->st_name得出符号名字符串指针,在动态链接库查找这个函数的地址,并且把地址赋值给*rel->r_offset,即GOT表,最终调用这个函数
.dynstr存放各种字符串,以’\x00’结尾,在使用过程中按照相对于其基地址的偏移寻址
小结
_dl_runtime_resolve函数执行过程:
用link_map访问.dynamic,取出.dynstr, .dynsym, .rel.plt的指针
.rel.plt+第二个参数(rel偏移)得到的结果作为rel表项结构体Elf32_Rel的指针,这里记作rel
rel->r_info >> 8作为.dynsym下标,求出当前函数符号表项Elf32_Sym的指针,记作sym
.dynstr + sym->st_name得出符号名字符串指针
在动态链接库查找这个函数的地址,并且把地址赋值给*rel->r_offset,即GOT表
调用这个函数
漏洞利用
wiki里从简单到复杂讲了很多种攻击方式,这里可以用的是最后一种,我们先使用栈迁移将ROP代码放到bss段,然后在bss上构造假的rel和sym,rel的r_info为fake_sym与.dynsym的距离,r_offset为read或者alarm,fake_sym的st_name为bss上的’system\x00’与.dynstr的距离,bss上写入’/bin/sh\x00’,传地址进去,这里需要注意的是要在bss_addr+0x800的地方写入数据,为了给函数执行腾出空间
exp.py
1 | #coding=utf-8 |
此外还可以用roputil直接得到填充数据
1 | from roputils import * |