2018年blackhat的议题,pdf地址:https://i.blackhat.com/briefings/asia/2018/asia-18-Marco-return-to-csu-a-new-method-to-bypass-the-64-bit-Linux-ASLR-wp.pdf
the attached code
了解ret2csu之前先了解一下attached code的概念。
先看一个最简单的程序:
1 | int main(int argc,const char *argv[]){ |
这个程序编译之后,查询可执行文件的函数符号:
1 | root@Ubuntu:/pwn/wiki/mediumRop/ret2csu# nm -a empty | grep " t\| T" |
发现除了main函数还有很多其它函数,这些函数是编译器附加到可执行文件中的,称之为attached code.
这些attached code在main函数之前执行,负责加载或者链接库文件. 这些函数的执行顺序如下:
find gadget from attached code
- Q: 能否从attached code找一些通用的gadget?
- A: yes
在attached code中存在__libc_csu_init()
函数: 存在如下的gadget
可以在执行完第二个gadget之后ret到第一个gadget, 这样即可控制很多关键寄存器的值, 这对x64下函数调用帮助很大.
x64函数调用中的前六个参数依次保存在 RDI, RSI, RDX, RCX, R8 和 R9
1 | callq *(%r15,%rbx,8) -> (%r15 + (%rbx * 8) ) |
但是需要注意的是此gadget不能控制rax
的值, 也就无法直接进行系统调用,因为系统调用号是存在rax
里面的.(而且也没有SYSCALL/SYSENTER/INT 0x80
)
但是可以直接调用write
函数等来泄露got表中函数的地址,然后计算出libc地址,前提是不开pie(
1 | write@plt(4, &GOT_TABLE[1], 8); |
如图:
利用libc中的ROP GetShell:
所以有如下几种利用场景:
- ret2csu泄露libc地址之后利用libc中的gadget getshell.
- ret2csu配合
pop rax; syscall;
等gadget直接GetShell. - 开启PIE的情况下,利用offset2lib进行ret2csu,或者直接利用libc中的gadget getshell.
offset2lib参考: http://cybersecurity.upv.es/attacks/offset2lib/offset2lib-presentation.pdf
简单来说就是泄露任意代码段地址即可推得所有共享库地址,因为共享库之间的offset是固定的.
practice
ret2csu
题目地址: https://ropemporium.com/challenge/ret2csu.html
main函数直接调用pwnme
函数:
且存在ret2win函数:
满足a1 == 0xDEADBEEFDEADBEEFLL && a2 == 0xCAFEBABECAFEBABELL && a3 == 0xD00DF00DD00DF00DLL
即可输出flag.
这里可以利用ret2csu修改部分寄存器的值,但是存在一个问题是,mov edi, r13d
, rdi作为第一个参数需要赋值为0xDEADBEEFDEADBEEFLL
但mov edi,r13d
会让rdi寄存器的高位为0. 无法拿到flag.
解决方法是在ret2csu之后再次使用rop,pop_rdi_ret
修改rdi寄存器的值.
即:
而成功执行到第二个retn
, 必须满足以下几个条件:
(1) jnz不调整,即rbp==rbx, 控制rbp = rbx+1即可
(2) 使call [r12+rbx*8]
调用不发生异常且不改变寄存器的值,需要找到一个指针指向可执行函数.
这里直接参考了writewp . 寻找的方法有两种思路:
- ghidra搜索:
.dynamic
section
这是动态链接过程中会使用到的一个section, 包含了一系列的数组元素,单个成员的结构如下:
1 | typedef struct { |
可以看到第二份字段可以为value或者指针(ptr), 本例中这个数组为如下所示:
一些字段的意义:
类型 | 说明 |
---|---|
NEEDED | 依赖的共享对象文件 |
INIT | 初始化代码地址,也就是.init的位置 |
FINT | 是结束代码地址,也就是.fini的位置 |
GUN_HASH | 动态链接哈希表地址 |
STRTAB | 动态链接字符串表的位置,表示.dynstr的位置 |
SYMTAB | 动态链接符号表的位置,表示.dynsym的位置; |
PLTGOT | 指向.got的位置 |
JMPREL | 表示重定位表,也就是.rel.plt |
REL\RELA | 表示动态链接重定位表的位置;RELENT/RELAENT表示动态重读位表入口数量 |
可以利用0x600E38
或者0x600E48
处的指针来完成解引用,运行fini或者init处的代码,这些代码片段在本例中都很多,且只影响rax寄存器的使用.
完整exp: 参考 https://meowmeowxw.gitlab.io/wargame/rop-emporium/7-ret2csu/
1 | from pwn import * |
level5
参考蒸米师傅的ROP教程, 源码如下:
level5.c
1 |
|
编译: gcc -fno-stack-protector -no-pie -o level5 level5.c
, 注意不开pie和canary.
思路是通过ret2csu调用write函数,泄露write/read函数地址,然后计算出libc地址之后再通过libc的ROP chain 来getshell.
1 | from pwn import * |