某国产PLC固件安全分析研究

· 2026-08-27 08:00 · 2 阅读

原创 ic3blac4 2026-08-27 08:00 辽宁

诚招web、re、crypto、pwn、misc、合约方向的师傅,长期招新IOT+Car+工控+样本分析多个组招人有意向的师傅请联系邮箱 admin@chamd5.org(带上简历和想加入的小组)

1 前言

工业控制系统是国家关键基础设施的"神经系统",而PLC作为其重要组成部分,一旦被攻破,攻击者即可直接篡改工艺逻辑、操纵寄存器值,甚至对产线实施毁瘫式攻击。因此,在攻击发生之前对控制器固件本身开展安全研究,将风险点逐一识别并加固,具有极高的现实价值。

本文以某国产品牌"天行"系列工业PLC为研究对象,从设备介绍、固件获取、固件格式逆向、系统解包、组件识别、攻击面定位到核心运行时深度分析,完整阐述了固件分析的技术路线。

2 设备简介

2.1 产品定位

    T426 系列PLC是全栈自研大型可编程控制器,可广泛应用于大型高可靠自动化系统。

T426采用钣金外壳无风扇设计,提供极为丰富的网络资源,已成为各种大型自动化任务中稳定可靠的解决方案。

       T426系列控制器支持运动控制功能,含速度控制轴、定位轴和主从轴。T426 系列控制器支持冗余控制功能,冗余系统由两个独立的T426EH型PLC 组成。从固件内容看,设备集成了 PROFINET、EtherCAT、Modbus、EtherNet/IP、PROFIBUS DP、OPC UA、DDS 等全谱系工业协议栈,定位属于中高端可编程控制器。

2.2 硬件平台

        T426 系列PLC主控采用NXP Layerscape LS1043A(ARM64 四核 Cortex-A53,基于FSL LS1043ARDB 参考设计),属于网络通信级SoC,配套 eMMC 存储与多种引导源(NOR 双 bank、NAND、SPI、SD),存储布局采用 A/B 双槽位 + ext4 数据分区 overlayfs 的典型工业设计。系统分层架构如下:

图1
图1

2.3 软件平台与启动链路

        设备启动链路为:U-Boot → 内核 → systemd → 关键服务。内核采用5.10.35-rt39(PREEMPT_RT 实时补丁),2021年基线、2026-08 编译(GCC 9.4.0),串口 console(ttyS0,115200)无密码保护。

图2
图2

2.4 固件获取

        T426-XEP-10 的固件以标准 SWUpdate 升级包(.swu 后缀)形式通过厂商提供的固件下载渠道获取,即T426-XEP-10_2.00.05_631-2609.2.swu,大小约数百MB。SWUpdate包格式本身是开放协议,但厂商在标准格式之上叠加了自定义的十六进制文件头,导致 binwalk 等通用工具识别率低——这是工控设备固件分析中最常见的"第一道坎",也是本文从零逆向格式的直接原因。

3. 固件文件格式分析

3.1 包结构总览

下载后的文件如下所示

        .swu 文件本质上是一个"ASCII十六进制编码头 + cpio 归档"的组合体。每个条目的头部是一段110字符的ASCII十六进制串(即 55 字节的二进制头),cpio归档魔数070701也以文本形式出现在头部中,而非通常的二进制偏移处。整个包的结构如下:

图3
图3

原始固件如下图所示

3.2 条目边界定位

        binwalk对该格式识别率低的原因在于:标准cpio魔数被"文本化"了,且条目头部并非紧贴文件内容。我们的自研脚本 swu_probe.py 采用纯启发式扫描:在文件中搜索连续的 110字符ASCII十六进制串([0-9a-f]),以此定位条目边界,再向后回扫允许40字节间隙内的gzip/cpio魔数,确认条目内容起点:

import re, zlib
HEX_HEAD = re.compile(rb'(?:[0-9a-f]{110})'# 55 字节二进制头的文本形态
def probe(path):
data = open(path, 'rb').read()
for m in HEX_HEAD.finditer(data):
start = m.end()
# 在头部后 40 字节窗口内找 gzip/cpio 魔数
win = data[start:start + 40]
if win[:2] == b'x1fx8b'# gzip
print(f'entry @ {m.start():#x} -> gzip 压缩数据')
elif b'070701'in win: # cpio 文本魔数
print(f'entry @ {m.start():#x} -> cpio 归档')

解压后的文件如下所示

3.3 签名机制与信任链

        固件的完整性保护依赖SWUpdate的CMS(PKCS#7)签名机制:SWUpdate 以 "built for signed images" 模式编译,升级时必须提供可被公钥验证的sw-description.sig,验证通过后才允许进入安装流程。验证流程如下:

图4
图4

        对sw-description.sig做openssl解析后发现信任链存在两个问题:其一,签名证书主体为CN=target, O=SWUpdate,与SWUpdate官方文档生成自签名证书的示例命令openssl req -subj "/O=SWUpdate/CN=target" 完全一致——厂商沿用文档示例搭建了签名体系,未建立自有PKI身份;其二,证书无CA链、无吊销机制,固件真实性完全依赖该自签名私钥的保密性。

3.4 镜像文件解包

        rootfs.img与bootloader.img都是gzip多成员流(多个 x1fx8b 魔数首尾相连)。用 zlib逐段解压:

import zlib
def gunzip_multi(comp: bytes):
out, pos = b'', 0
while pos < len(comp):
d = zlib.decompressobj(16 + zlib.MAX_WBITS) # 16 = gzip 包装
chunk = comp[pos:]
out += d.decompress(chunk) + d.flush()
consumed = len(chunk) - len(d.unused_data) # 本段实际消费字节数
if consumed == 0: # 防尾部脏数据死循环
break
pos += consumed # 推进到下一成员流
return out

        解压得到1 GB 的ext4镜像和14.7 MB的U-Boot镜像。这里有一个重要坑点:解包 ext4 必须使用完整版7-Zip(7z.exe),精简版7zr/7za不支持ext4文件系统,会直接失败。此外,sdinstaller.img(SD 安装卡)内是FAT32分区,其中明文存放全部6个镜像(blfw / bootcfg / bootloader / flash.scr / partition.bin / rootfs.img),并附带md5sum.txt——解包后与出厂哈希逐一核对,全部一致,验证了提取流程的完整性。

        整个解包流水线可归纳为下图,后续章节对rootfs的所有分析都建立在这一产物之上:

图5
图5

最终解压出的 rootfs 目录如下所示

3.5 固件初步分析

解包出rootfs文件树(约 371 MB)后,第一步是建立设备画像。关键信息几乎全部可以通过"读文件"而非"逆向二进制"获得。

        其中最有意思的识别技巧是:内核版本不需要看任何二进制——/lib/modules/ 目录名直接就是版本号 5.10.35-rt39。而 /etc/build-release 泄露了构建服务器主机名与构建时间,这属于供应链侧的信息泄露点。

        存储布局上,设备采用eMMC A/B双槽位 + ext4 数据分区overlayfs的典型工业设计:系统层只读基座、用户数据层可写分离,配合SWUpdate的签名校验,构成了防御的安全基础。但内核配置中CONFIG_DEVMEM=y、CONFIG_DEBUG_FS_ALLOW_ALL=y、CONFIG_KALLSYMS=y全开,为提权后的攻击者打开了物理内存直读的大门——这些配置项都来自 /boot/config-5.10.35-rt39,静态分析即可确认。

分析实战

组件识别

组件识别(软件成分分析,SCA)是固件安全分析的核心产出之一。我们总结了一套"三步走"方法,并将其固化成了通用工具firmware_sca.py,可对任意已解包rootfs 或原始 .swu 一键完成"解包 → 扫描 → CVE 映射 → 配置审计":

  • 第一步:关键文件探测。/lib/modules、/etc/os-release、/etc/build-release 等文本文件直接给出内核、发行版、构建信息,零成本。

  • 第二步:二进制版本字符串。对 libc.so.6、libssl、busybox、systemd 等知名二进制做字符串扫描,匹配内嵌版本串。实操中需要针对不同组件适配正则——例如glibc的版本串是 GNU C Library release version 2.35,NetworkManager 的版本串是 NetworkManager (version 1.40.10)(括号形式,通用正则极易漏掉),SWUpdate 的版本串则带厂商标识。这些坑都是实测后逐个修复的。

  • 第三步:行为侧写。通过systemd单元(BaoSky.service、swupdate 服务)、init 脚本、Platform.ini 等配置文件,推断设备实际启用的服务面:SWUpdate/mongoose(Web 升级)、pn_dcpd(PROFINET DCP)、DDSAgent(数据代理)、TXRuntime(PLC 运行时)。这一步把"装了什么东西"升级为"什么东西在跑、听哪个端口",是衔接攻击面分析的关键。

工具实测中有一个典型的"误报 vs 真发现"案例:OpenSSL 检出 3 个版本(1.1.1f / 1.1.1t / 3.4.1),初判为字符串残留误报。经逐文件核实,确认为真发现——除了系统自带 OpenSSL 1.1.1t,BaoskyRuntime 还捆绑了 2020 年的私库 libcrypto.so.1.1(1.1.1f,比系统库更老),另有一个较新的3.4.1。同一设备上并存三个 OpenSSL 版本,意味着漏洞评估必须分别针对三个版本进行,也侧面反映了厂商依赖管理的混乱。

最终组件识别结果与风险概括如下:

攻击面分析

静态分析阶段定位攻击面,核心思路是"查配置、看权限、验信任链"。结合上述行为侧写结果,先梳理出设备完整的服务与端口映射:

图7
图7

攻击面整体视图如下所示:

图8
图8

若干关键证据链(静态分析):

  • 8080无认证Web升级:/usr/lib/swupdate/conf.d/10-mongoose-args 中 SWUPDATE_WEBSERVER_ARGS="-r /var/www/swupdate -p 8080" 未携带 -a 认证参数,Web 页面自带上传与 "Restart System" 按钮——未认证远程重启 DoS 的复现点。

  • 端口绑定差异:Platform.ini 显示 TCP 60000(BaoSky Service)仅绑定 127.0.0.1,而 60001(DDSAgent)面向网络;net.ipv4.ip_local_reserved_ports=60000,60001,... 进一步佐证。攻击面主要在 60001 与二层协议。

  • 防火墙形同虚设:rootfs内含 firewalld/iptables组件,但没有任何开机启用配置,系统默认暴露全部端口。

  • TXRuntime 以root运行(BaoSky.service,User=root),且Platform.ini 中DebugFlag=1 调试开关处于打开状态;PLC 运行时任一解析漏洞(ObjectModel.json 数据模型等)直接获得 root。

  • 签名信任链脆弱:sw-description.sig证书主体为CN=target, O=SWUpdate,与 SWUpdate 官方文档生成示例命令openssl req -subj "/O=SWUpdate/CN=target" 完全一致——厂商沿用文档示例搭建签名体系。固件内另有明文 PKCS#8 RSA-2048 TLS 私钥(server.key),对应证书已过期(2024-10-15)。

核心Runtime深度分析

        前文对rootfs的攻击面定位停留在"配置文件 + 进程名"层面,本章节下沉到PLC的心脏——运行时二进制本身。两个主力程序TXRuntime与Plc.Execution均开启了完整的现代防护(PIE、RELRO、栈金丝雀),但厂商在固件中残留了6个配套库的完整调试符号,TXRuntime 自身也带有指向调试文件的链接——对逆向者而言,这等于拿到半张源码地图,二进制分析的难度被大幅拉低。

        进程模型与模块架构。整个运行时按"主进程 + 卫星进程 + 共享内存"组织:TXRuntime 是唯一的主进程,Plc.Execution(IEC 61131 程序执行引擎)、DDSAgent(数据代理)、pn_dcpd(PROFINET DCP)、TXLogHandler(日志)、WatchDog(守护)等卫星进程围绕它各司其职,进程拓扑如下图所示。卫星进程通过 DDS 数据分发服务与共享内存与主进程协作——DDSAgent 暴露的 60001 端口正是主进程数据的对外出口;WatchDog 进程组承担进程守护,任何进程异常都会被自动拉起,属于工业设备的标配可靠性设计。

图9a 进程拓扑
图9a 进程拓扑

        TXRuntime 内部可拆出七个职责清晰的子系统,按"对外服务 → 业务核心 → 执行与数据"三层组织:对外服务层是所有网络入口(内嵌 Web 服务与网络通信框架),业务核心层承载对象管理、用户认证与许可校验,执行与数据层则是 PLC 控制程序的执行引擎与进程间通信底座,各模块依赖关系如下图所示:

图9b TXRuntime内部模块
图9b TXRuntime内部模块

        OMG 对象模型与消息协议。OMG 是运行时的"对象总线":设备对外暴露的一切能力,都由一份名为ObjectModel.json的 JSON 数据字典(155 KB,版本 11)统一定义——每个网络可见对象都有唯一id与类型化属性,整份字典就是设备网络接口的说明书。对对象的操作被归纳为九种原语(创建、删除、读取、写入、订阅、遍历等),所有网络请求最终都落到这些原语上;会话层则在握手时交换用户角色与硬件身份信息,并靠心跳消息保活。安全视角上,这意味着攻击者无需逆向协议,直接读字典即可枚举出全部可操作对象;而认证相关的消息(登录、会话建立等)本身也以对象形式定义,等于为协议逆向提供了一份完整地图。

        认证与信任体系。这是本次逆向最有价值的部分——三套相互独立的认证机制并存,分别守护 Web 管理面、工业协议面与固件完整性:

  1. Web 面:token会话。内嵌Web服务共11个API路由,其中6个在免认证白名单中(含设备型号查询、升级配置查询等),其余路由必须携带登录后签发的token。登录流程为:提交用户名密码 → UMAC 认证 → 签发token(绑定客户端 IP 与用户名,按超时时间自动失效)→ 后续请求携带token鉴权,完整流程如下图所示。安全视角上,白名单路由无需任何凭证即可查询设备型号与升级配置,构成一个低危但真实的信息泄露面;token 采用服务端会话表管理而非 JWT 之类无状态方案,也意味着会话状态可被集中清理。

图10a Web面认证流程
图10a Web面认证流程
  1. OMG 协议面(60001):挑战应答。接入60001端口需先通过经典挑战应答:服务端下发随机challenge,客户端用口令计算应答,服务端与存储的摘要逐字节比对,通过后建立会话(认证流程如下图所示)。两个值得注意的实现细节:其一,口令以 SHA-1 无盐摘要存储(字段长度恰好 20 字节),离线撞库成本极低——一旦可写的 /data 分区被读取,口令哈希直接泄露;其二,比对循环存在提前退出(非恒定时间),理论上存在时序侧信道。账号治理方面,内置admin账号受保护不可删除、不可改密,且首次登录强制要求设置密码——这意味着出厂设备在首次配置前存在空凭据窗口期,存在被抢先接管的风险。

图10b OMG协议面认证
图10b OMG协议面认证
  1. 签名与许可:CPU硬件绑定。TXRuntime本体附带ECDSA P-256签名文件,Plc.Execution内置CPU序列号验证逻辑,许可证与硬件一一绑定,且同一套代码同时支持国密SM2与ECDSA两种算法(校验链如下图所示)。这意味着即便攻击者伪造运行时代码或窃取工程文件,也无法在另一台设备上运行——这是本次逆向中少见的正面设计。但密钥管理存在两处可观测性风险:验证公钥以明文 hex 形式出现在日志中,且二进制内残留了构建服务器的源码路径(暴露了 Jenkins 工作目录结构),为供应链攻击提供了线索。

图10c 签名与许可
图10c 签名与许可

结合本节的二进制级证据,可将前文攻击面清单升级为下表:

6.总结

        本文以宝信软件T426工业PLC固件为对象,依次阐述了"设备介绍 → 固件获取 → 格式逆向(SWUpdate hex 头 + cpio + gzip 多成员流)→ 系统解包(ext4/FAT32)→ 设备画像 → 组件识别→ 攻击面定位 → 核心运行时深度逆向"的全链路。核心方法论沉淀为三件可复用资产:SWUpdate 解包脚本(应对厂商自定义头)、通用SCA工具 firmware_sca.py(组件 + CVE + 配置审计一键化)、运行时逆向证据链(路由表 / UMAC 认证模型 / 签名验证机制逐项落地)。 

        受限于静态分析,本文未在PLC设备验证相关漏洞的可利用性、UMAC 挑战应答 Web token会话的实际对抗强度;运行时逆向也止步于"认证与信任链"层面,RTBL/JIT 执行引擎、DDS 数据面等尚未深挖。后续可沿三个方向深化:对 OMG 协议面(AcquireAccess/Session/Subscribe)与 SWUpdate Web 面做模糊测试、对 RTBL/JIT 控制块执行链做解析器级分析、以及在获授权目标上动态验证认证绕过与提权链。固件分析是工控安全研究的基石,本文抛砖引玉,期待更多针对国产控制器固件的研究,提升国产控制器安全基线,补全整体国产化替代的拼图。

结束

招新小广告

ChaMd5 Venom 招收大佬入圈

新成立组IOT+工控+样本分析 长期招新

欢迎联系admin@chamd5.org

图片

跳转微信打开