摘要:在本文中,我们将把IoT安全分析从“软件”推向“物理硬件”层面。我们将探讨当物理访问设备成为可能时,如何利用设备电路板(PCB)上为调试而遗留的硬件接口来发起攻击。本文将重点聚焦于最常见、最容易被利用的“后门”——UART(通用异步收发器)串行控制台。你将学习如何识别UART的引脚(GND, TX, RX),并利用一个USB-to-UART适配器和Python的库,编写一个自动化的**“Root Shell猎手”脚本。这个脚本能够自动扫描所有可用的串口和波特率(Baud Rates),并注入命令(如
pyserial,
id),以检测是否存在一个无需密码的root权限**控制台,从而实现对设备的完全接管。
uname -a
关键词:Python, IoT安全, 硬件Hacking, UART, , 串行端口, Root Shell, 逆向工程
pyserial
正文
⚠️ 终极警告:硬件操作与合法性
硬件风险:进行硬件接口测试需要你拆开设备。操作不当(如短路、接错VCC)可能导致设备永久性损坏。
环境:你只能在你拥有的设备上进行此类实验。
工具:你需要一个USB-to-UART适配器(如PL2303, FT232R, CH340),几根杜邦线,以及(可选的)一个万用表。
1. “后门”:为什么UART是头号目标?
开发者在设计IoT设备(如路由器、摄像头)时,为了方便在工厂生产和后期调试,几乎总会在电路板(PCB)上留下一组调试接口。在理想情况下,这些接口应在产品发布前被“禁用”(例如,通过软件关闭或物理移除)。
然而,在现实中,无数的设备都忘记了这一步。
UART(通用异步收发器)就是最常见的调试接口。它提供了对设备底层串行控制台(Serial Console)的直接访问。在一个典型的嵌入式Linux系统中,这个控制台很可能会在系统启动时打印出所有的引导日志(Bootlog),并且在启动完成后,直接提供一个权限的shell提示符(
root),而不需要任何密码。
#
对于攻击者来说,找到这个接口,就等于直接“走进”了设备的“指挥中心”。
2. 物理侦察:找到UART的“针脚”
拆开设备:找到主PCB。
寻找引脚:寻找一组(通常是3个或4个)排成一排的、未连接的金属引脚或焊盘。
识别引脚:你需要识别出至少三个:
GND(地线):使用万用表,找到与设备USB接口金属外壳或大型金属屏蔽罩导通的那个引脚。
VCC(电源):通常是3.3V或5V。我们通常不需要连接它!
TX(发送):数据的发送端。
RX(接收):数据的接收端。
(TX和RX的识别通常靠“猜”和“换”,或者使用逻辑分析仪)
连接:使用USB-to-UART适配器,将适配器的连接到设备的
GND,将
GND连接到
TX,
RX连接到
RX(交叉连接)。
TX
3. Python的“硬件控制台”:
pyserial
pyserial
当你的USB-to-UART适配器插入电脑后,操作系统会将其识别为一个串行端口(例如,Linux上的,Windows上的
/dev/ttyUSB0)。
COM3
库允许Python像一个终端程序(如
pyserial或
minicom)一样,向这个端口读写数据。
putty
环境准备:
Bash
pip install pyserial
4. “盲猜”的艺术:波特率(Baud Rate)扫描
要成功通信,双方的“语速”(波特率)必须一致。常见的波特率有,
9600,
19200,
38400,
57600等。我们不知道目标设备用的是哪一个,所以我们必须写一个脚本来自动尝试所有可能。
115200
5. 完整代码实现 (
uart_hunter.py)
uart_hunter.py
我们将编写一个脚本,它能自动发现串口,遍历所有常见波特率,并尝试发送
(回车)来“唤醒”控制台,如果收到了可读的响应(如一个提示符或
login:提示符),就认为找到了正确的波特率,并尝试执行
#命令。
id
Python
# uart_hunter.py
import serial
import serial.tools.list_ports # 用于自动发现串口
import time
import argparse
# 常见的波特率列表
COMMON_BAUD_RATES = [9600, 19200, 38400, 57600, 115200]
# 常见的Shell提示符(正则表达式)
PROMPT_REGEX = rb"($ |# |> |login:|username:)"
def check_for_shell(port, baud_rate):
"""
尝试在给定的串口和波特率下,寻找一个Shell。
"""
print(f"[*] 正在测试: {port} @ {baud_rate}bps...")
try:
# 1. 打开串口连接,设置一个短超时
ser = serial.Serial(port, baud_rate, timeout=2) # 2秒读超时
# 2. "唤醒" 控制台:发送一个回车
ser.write(b"
")
time.sleep(0.5)
# 3. 读取所有响应
response = ser.read(1024)
if response:
print(f" - 收到响应 (len={len(response)}): {repr(response)}")
# 4. 检查是否命中了已知的Shell提示符
if re.search(PROMPT_REGEX, response, re.IGNORECASE):
print(f"
[!!!] 发现潜在的Shell! @ {port} ({baud_rate}bps)")
print(f" - 提示符: {response.strip()}")
# 5. 尝试发送 'id' 命令
ser.write(b"id
")
time.sleep(1)
id_response = ser.read(1024)
print(f" - 'id' 命令响应: {id_response.decode('utf-8', 'ignore')}")
if "uid=0(root)" in id_response.decode('utf-8', 'ignore'):
print(" - [!!!] 高危: 确认是 ROOT SHELL!")
return True # 确认发现
ser.close()
except serial.SerialException as e:
# print(f" - 无法打开 {port}: {e}") # 权限问题或端口占用
pass
except Exception as e:
print(f" - 测试时出错: {e}")
return False
def main():
parser = argparse.ArgumentParser(description="自动化的UART Shell猎手。")
parser.add_argument("-p", "--port", help="指定要扫描的串口 (例如 /dev/ttyUSB0 或 COM3)。")
args = parser.parse_args()
# 1. 自动发现串口
if args.port:
ports_to_scan = [args.port]
else:
print("[*] 正在自动发现串口...")
ports_to_scan = [port.device for port in serial.tools.list_ports.comports()]
if not ports_to_scan:
print("[!] 未发现任何串口。请确保USB-to-UART适配器已连接。")
return
print(f"[+] 发现串口: {ports_to_scan}")
# 2. 遍历所有串口和所有波特率
found = False
for port in ports_to_scan:
for baud in COMMON_BAUD_RATES:
if check_for_shell(port, baud):
found = True
break # 找到一个后就不再测试这个端口的其他波特率
if found:
break # 找到一个后就停止
if not found:
print("
[*] 扫描完成,未在常见波特率下发现明显的Shell。")
if __name__ == "__main__":
import re
main()
6. 总结与下一步
我们成功地构建了一个自动化的硬件接口测试工具。通过将与一个简单的启发式引擎(遍历波特率、发送
pyserial
、匹配)相结合,我们的Python脚本现在能够跨越数字和物理的界限,自动地发现IoT设备上最致命的、被遗忘的UART root后门。
#
这揭示了一个核心的IoT安全原则:如果攻击者可以物理接触到你的设备,你的软件防御(无论多强)都可能被轻易绕过。
















暂无评论内容