3. 离线口令哈希识别与格式判定

离线口令哈希识别与格式判定 学习卡片

哈希样本识别任务说明

本案例对一批离线口令哈希样本进行算法类型与存储格式判定。判定依据来自哈希字符串的前缀标识、字段分隔符、编码字符集、固定长度四个可观察特征。识别结果分为三类:可审计的常见哈希族、需进一步确认的疑似格式、无法识别的异常样本。

识别规则与判定依据

哈希族典型格式特征字段结构可审计性
MD5 (raw)32 位十六进制,无前缀单一字段可审计(弱)
SHA-1 (raw)40 位十六进制,无前缀单一字段可审计(弱)
SHA-256 (raw)64 位十六进制,无前缀单一字段可审计
bcrypt$2a$/$2b$/$2y$ 前缀$2b$cost$salt+hash可审计
SHA-512 crypt$6$ 前缀$6$salt$hash可审计
SHA-256 crypt$5$ 前缀$5$salt$hash可审计
MD5 crypt$1$ 前缀$1$salt$hash可审计(弱)
NTLM32 位十六进制,无前缀单一字段需上下文确认
MySQL 4.1+41 位,* + 40 位十六进制*HASH可审计
MD5 与 NTLM 均为 32 位十六进制,仅凭字符串无法区分,需结合来源系统(如 Windows SAM 转储 vs Linux shadow)判定。本案例将其标记为"需上下文确认"。

识别流程与判定逻辑

输入:哈希样本 逐条读取字符串 含 $ 前缀? $1$ $5$ $6$ $2a$ 等 是 前缀匹配识别 $2b$ → bcrypt $6$ → SHA-512 crypt 否 长度 + 字符集判定 纯十六进制?长度? 32 位 hex MD5 或 NTLM → 需上下文 40 位 hex SHA-1 (raw) 64 位 hex SHA-256 (raw) * + 40 位 hex MySQL 4.1+ 不匹配 异常样本 标记为无法识别 识别结果分类 可审计常见哈希族 bcrypt / SHA-crypt / MySQL 需上下文确认 MD5 / NTLM (32位hex) 无法识别异常样本 格式不符 / 长度异常 输出审计清单 算法 / 格式 / 可审计性 判定顺序:先看 $ 前缀 → 再看长度与字符集 → 无匹配则标记异常 32 位 hex 需结合来源系统区分 MD5 与 NTLM

识别脚本实现

以下 Python 脚本对样本列表逐条执行上述判定逻辑,输出结构化审计清单。脚本不依赖第三方库,可直接运行。

import re

# 前缀 → 算法映射表
PREFIX_MAP = {
    "$2a$": "bcrypt", "$2b$": "bcrypt", "$2y$": "bcrypt",
    "$6$": "SHA-512 crypt", "$5$": "SHA-256 crypt",
    "$1$": "MD5 crypt",
}

def identify_hash(h):
    """识别单条哈希样本,返回 (算法, 格式, 可审计性)"""
    h = h.strip()
    if not h:
        return ("空样本", "无效", "异常")

    # 1. 前缀匹配
    for prefix, algo in PREFIX_MAP.items():
        if h.startswith(prefix):
            parts = h.split("$")
            if len(parts) >= 4:
                return (algo, f"{prefix}cost$salt$hash", "可审计")
            return (algo, "前缀匹配但字段不完整", "需确认")

    # 2. MySQL 格式:* + 40位hex
    if re.fullmatch(r"\*[0-9A-Fa-f]{40}", h):
        return ("MySQL 4.1+", "*HASH(40hex)", "可审计")

    # 3. 纯十六进制长度判定
    if re.fullmatch(r"[0-9A-Fa-f]+", h):
        n = len(h)
        if n == 32:
            return ("MD5 或 NTLM", "32位hex", "需上下文确认")
        if n == 40:
            return ("SHA-1 (raw)", "40位hex", "可审计(弱)")
        if n == 64:
            return ("SHA-256 (raw)", "64位hex", "可审计")
        return (f"未知hex({n}位)", f"{n}位hex", "异常")

    # 4. 无法识别
    return ("无法识别", "格式不符", "异常")

# 教学示例样本
samples = [
    "5f4dcc3b5aa765d61d8327deb882cf99",                          # 32位hex
    "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d",                  # 40位hex
    "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",  # 64位hex
    "$2b$12$LJ3m4ys3Lk0TSwHnbfOMiOXQ8Y5vK3z1Q2w4e5r6t7y8u9i0o",  # bcrypt
    "$6$rounds=5000$saltvalue$hashvaluehere",                     # SHA-512 crypt
    "*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9",                  # MySQL
    "not-a-valid-hash-string!!!",                                 # 异常
    "abc123",                                                     # 异常
]

print(f"{'样本(截断)':<38} {'算法':<18} {'格式':<22} {'可审计性'}")
print("-" * 100)
for s in samples:
    algo, fmt, audit = identify_hash(s)
    display = s[:34] + ".." if len(s) > 36 else s
    print(f"{display:<38} {algo:<18} {fmt:<22} {audit}")

预期输出(教学示例,非实测结果):

样本(截断)                              算法               格式                   可审计性
----------------------------------------------------------------------------------------------------
5f4dcc3b5aa765d61d8327deb882cf99        MD5 或 NTLM        32位hex                需上下文确认
aaf4c61ddcc5e8a2dabede0f3b482cd9aea..   SHA-1 (raw)        40位hex                可审计(弱)
9f86d081884c7d659a2feaa0c55ad015a3..   SHA-256 (raw)      64位hex                可审计
$2b$12$LJ3m4ys3Lk0TSwHnbfOMiOXQ8Y5..   bcrypt             $2b$cost$salt$hash     可审计
$6$rounds=5000$saltvalue$hashvalue..   SHA-512 crypt      $6$cost$salt$hash      可审计
*6BB4837EB74329105EE4568DDA7DC67ED..   MySQL 4.1+         *HASH(40hex)           可审计
not-a-valid-hash-string!!!              无法识别            格式不符                异常
abc123                                  未知hex(6位)        6位hex                 异常

异常样本处理与审计清单

识别为"异常"的样本需单独记录,不进入后续字典攻击或掩码攻击流程。审计清单应包含以下字段,便于后续案例直接引用:

字段说明示例值
sample_id样本序号007
raw_hash原始哈希字符串(截断显示)not-a-valid-hash...
algorithm识别出的算法名无法识别
format存储格式描述格式不符
auditable是否可审计异常
action建议操作排除,不参与后续攻击
若样本来源为 Windows 系统(如 SAM 转储),32 位十六进制应优先判定为 NTLM 而非 MD5;若来源为 Linux shadow 且无 $ 前缀,则更可能是 MD5(raw)。来源信息缺失时,必须在审计清单中标注"需上下文确认",不得强行归类。

验证与通过条件

  • 脚本对 8 条教学样本的输出与预期表一致,无未捕获异常。
  • 所有 $ 前缀样本均被正确映射到对应算法族。
  • 32 位十六进制样本被标记为"需上下文确认",而非直接判定为 MD5。
  • 异常样本(长度不符、含非法字符)被正确标记为"异常"并排除。
  • 审计清单包含 sample_id、raw_hash、algorithm、format、auditable、action 六个字段。