2. 固件中硬编码凭据的自动化扫描

固件中硬编码凭据的自动化扫描 学习卡片

扫描目标与凭据类型

固件解包后,根文件系统通常位于 squashfs-root/ 目录。硬编码凭据常出现在以下位置:

  • /etc/passwd、/etc/shadow:系统账户与口令哈希
  • /etc/config/、/etc/*.conf:服务配置中的明文密码
  • /usr/bin/、/usr/sbin/:二进制中嵌入的 API Key、Token
  • /www/、/web/:Web 前端 JS 中的密钥
  • /etc/init.d/:启动脚本中的硬编码凭据

扫描脚本设计

脚本采用“文件遍历 → 正则匹配 → 上下文提取 → 风险分级”四阶段处理。核心匹配规则如下:

凭据类型正则模式风险等级
明文密码(password|passwd|pwd)\s*[=:]\s*["']?([^"'\s]{4,})高
API Key(api[_-]?key|apikey)\s*[=:]\s*["']?([A-Za-z0-9]{16,})高
Token(token|secret)\s*[=:]\s*["']?([A-Za-z0-9_\-]{12,})中
私钥文件-----BEGIN.*PRIVATE KEY-----严重
Shadow 哈希^[^:]+:\$[0-9]\$[^:]+:严重

完整扫描脚本

#!/usr/bin/env python3
import os, re, json, sys

RULES = [
    ("CRITICAL", "私钥文件", re.compile(rb"-----BEGIN.*PRIVATE KEY-----")),
    ("CRITICAL", "Shadow哈希", re.compile(rb"^[^:]+:\$[0-9]\$[^:]+:", re.M)),
    ("HIGH", "明文密码", re.compile(rb"(?:password|passwd|pwd)\s*[=:]\s*[\"']?([^\"'\s]{4,})", re.I)),
    ("HIGH", "API Key", re.compile(rb"(?:api[_-]?key|apikey)\s*[=:]\s*[\"']?([A-Za-z0-9]{16,})", re.I)),
    ("MEDIUM", "Token/Secret", re.compile(rb"(?:token|secret)\s*[=:]\s*[\"']?([A-Za-z0-9_\-]{12,})", re.I)),
]

SKIP_EXT = {".png", ".jpg", ".gif", ".ico", ".woff", ".ttf", ".so", ".ko"}
MAX_SIZE = 2 * 1024 * 1024  # 跳过大于2MB的文件

def scan_file(path):
    findings = []
    try:
        if os.path.getsize(path) > MAX_SIZE:
            return findings
        with open(path, "rb") as f:
            data = f.read()
    except (OSError, PermissionError):
        return findings

    for level, label, pattern in RULES:
        for m in pattern.finditer(data):
            # 提取匹配行上下文
            start = data.rfind(b"\n", 0, m.start()) + 1
            end = data.find(b"\n", m.end())
            if end == -1:
                end = len(data)
            line = data[start:end].decode("utf-8", errors="replace").strip()
            findings.append({
                "level": level,
                "type": label,
                "file": path,
                "line": line[:200],
                "offset": m.start()
            })
    return findings

def scan_root(root_dir):
    all_findings = []
    for dirpath, dirnames, filenames in os.walk(root_dir):
        for fn in filenames:
            ext = os.path.splitext(fn)[1].lower()
            if ext in SKIP_EXT:
                continue
            fp = os.path.join(dirpath, fn)
            all_findings.extend(scan_file(fp))
    return all_findings

def main():
    if len(sys.argv) < 2:
        print("用法: python3 scan_creds.py <squashfs-root目录>")
        sys.exit(1)
    root = sys.argv[1]
    findings = scan_root(root)

    # 按风险等级排序输出
    order = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2}
    findings.sort(key=lambda x: order.get(x["level"], 9))

    report = {"total": len(findings), "findings": findings}
    print(json.dumps(report, indent=2, ensure_ascii=False))

    # 摘要统计
    from collections import Counter
    c = Counter(f["level"] for f in findings)
    print(f"\n=== 风险摘要 ===", file=sys.stderr)
    for lv in ["CRITICAL", "HIGH", "MEDIUM"]:
        print(f"  {lv}: {c.get(lv, 0)} 条", file=sys.stderr)

if __name__ == "__main__":
    main()

运行与结果判读

对已解包的固件根目录执行:

python3 scan_creds.py ./squashfs-root/ > creds_report.json

输出 JSON 中每条记录包含风险等级、凭据类型、文件路径、匹配行内容和字节偏移。判读要点:

  • CRITICAL 级发现(私钥、shadow 哈希)应立即标记为阻断项,说明固件存在可直接利用的凭据泄露。
  • HIGH 级明文密码和 API Key 需人工确认是否为默认凭据或调试凭据,若是则同样属于高危。
  • 同一凭据在多个文件中重复出现时,应合并统计并追溯其被哪些服务引用。
  • 若扫描结果为空,需检查是否因文件权限、压缩格式或编码问题导致漏扫,可先用 grep -r "password" ./squashfs-root/ 交叉验证。
对二进制文件中的凭据,可先用 strings 提取可打印字符串再送入正则匹配,避免直接读取大文件导致内存问题。脚本中 MAX_SIZE 和 SKIP_EXT 可根据固件实际情况调整。
固件硬编码凭据扫描流程与风险分级 输入 squashfs-root/ 已解包固件根目录 文件遍历 os.walk 递归 跳过二进制/大文件 正则匹配 5类凭据规则 字节级扫描 输出 creds_report.json 风险清单 风险分级与典型发现 CRITICAL 严重 • 私钥文件 (BEGIN PRIVATE KEY) • /etc/shadow 口令哈希 • 可直接利用的凭据泄露 → 阻断项,立即修复 HIGH 高 • 明文密码 (password=) • API Key (api_key=) • 配置文件/脚本中嵌入 → 人工确认后修复 MEDIUM 中 • Token / Secret 字符串 • 调试用临时凭据 • 低权限服务凭据 → 评估影响范围 结果判读逻辑 CRITICAL > 0 固件不可发布 HIGH > 0 逐条确认是否为默认凭据 MEDIUM > 0 记录并跟踪修复 全部为0 交叉验证防漏扫

验证与交叉检查

脚本输出后,用以下命令交叉验证是否存在漏扫:

# 快速grep验证
grep -rn "password\|passwd\|api_key\|secret" ./squashfs-root/etc/ 2>/dev/null | head -20

# 检查shadow文件是否被正确识别
cat ./squashfs-root/etc/shadow 2>/dev/null | head -5

# 统计各类型发现数量
python3 -c "
import json
d = json.load(open('creds_report.json'))
from collections import Counter
print(Counter(f['type'] for f in d['findings']))
"
扫描结果中的匹配行可能包含误报(如代码注释中的示例密码、文档中的占位符)。建议对每条 HIGH 及以上发现人工确认其是否为真实可用凭据,再纳入最终风险清单。