3. 哈希计算与威胁情报匹配

哈希计算与威胁情报匹配 学习卡片

任务目标

本案例针对一个待分析的恶意代码样本文件,完成以下闭环:计算其 MD5、SHA-1、SHA-256 哈希值,并基于哈希值在 VirusTotal 等威胁情报平台进行查询,获取该样本的威胁标签与家族信息,从而建立“样本文件 → 哈希指纹 → 情报标签”的快速判定链路。

环境与依赖

  • Python 3.8+,无需第三方库即可完成哈希计算(使用标准库 hashlib)。
  • 查询 VirusTotal 需要 requests 库(pip install requests),以及一个有效的 VirusTotal (免费注册即可获取,社区版限额 4 次/分钟)。

关键完整代码

以下代码实现两个核心功能:(1) 对指定文件计算三种哈希;(2) 调用 VirusTotal API 查询文件报告并提取威胁标签与家族信息。代码采用最小可运行实现,文件路径与 通过命令行参数传入。

import hashlib
import sys
import requests
import json

def calculate_hashes(file_path):
    """计算文件的 MD5、SHA-1、SHA-256 哈希值"""
    md5 = hashlib.md5()
    sha1 = hashlib.sha1()
    sha256 = hashlib.sha256()
    with open(file_path, 'rb') as f:
        for chunk in iter(lambda: f.read(4096), b''):
            md5.update(chunk)
            sha1.update(chunk)
            sha256.update(chunk)
    return {
        'md5': md5.hexdigest(),
        'sha1': sha1.hexdigest(),
        'sha256': sha256.hexdigest()
    }

def query_virustotal(, file_hash):
    """通过哈希值查询 VirusTotal 文件报告,返回威胁标签与家族信息"""
    url = f'https://www.virustotal.com/api/v3/files/{file_hash}'
    headers = {'x-': }
    try:
        resp = requests.get(url, headers=headers, timeout=15)
        if resp.status_code == 200:
            data = resp.json()
            attrs = data.get('data', {}).get('attributes', {})
            # 提取社区投票标签
            tags = attrs.get('tags', [])
            # 提取 AV 引擎检测结果中的家族名(取第一个检测到的家族)
            results = attrs.get('last_analysis_results', {})
            family = None
            for engine, result in results.items():
                if result.get('category') == 'malicious' and result.get('result'):
                    family = result['result']
                    break
            # 提取流行威胁分类标签(如 trojan、ransomware 等)
            popular_threat = attrs.get('popular_threat_category', {})
            threat_label = popular_threat.get('value')
            return {
                'tags': tags,
                'family': family,
                'threat_label': threat_label,
                'detection_ratio': f"{attrs.get('last_analysis_stats', {}).get('malicious', 0)}/{attrs.get('last_analysis_stats', {}).get('total', 0)}"
            }
        elif resp.status_code == 404:
            return {'error': 'VirusTotal 中未找到该哈希对应的样本记录'}
        else:
            return {'error': f'API 请求失败,HTTP 状态码: {resp.status_code}'}
    except requests.exceptions.RequestException as e:
        return {'error': f'网络请求异常: {e}'}

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print('用法: python hash_ioc.py <样本文件路径> <VirusTotal_>')
        sys.exit(1)
    file_path = sys.argv[1]
     = sys.argv[2]
    # 1. 计算哈希
    hashes = calculate_hashes(file_path)
    print('=== 文件哈希 ===')
    print(f"MD5    : {hashes['md5']}")
    print(f"SHA-1  : {hashes['sha1']}")
    print(f"SHA-256: {hashes['sha256']}")
    # 2. 查询情报(优先使用 SHA-256,识别率最高)
    print('\n=== VirusTotal 情报查询 ===')
    result = query_virustotal(, hashes['sha256'])
    if 'error' in result:
        print(f"查询失败: {result['error']}")
    else:
        print(f"检测率    : {result['detection_ratio']} 个引擎检出")
        print(f"威胁标签  : {result['threat_label'] if result['threat_label'] else '未分类'}")
        print(f"家族信息  : {result['family'] if result['family'] else '未识别到明确家族'}")
        print(f"社区标签  : {', '.join(result['tags']) if result['tags'] else '无'}")

运行方式

# 安装依赖(仅查询功能需要)
pip install requests

# 运行脚本,传入样本文件路径和 
python hash_ioc.py /path/to/sample.exe YOUR_VT_

验证方法与判定标准

  • 哈希计算验证:在 Linux/macOS 下可用系统命令交叉验证,例如 md5sum sample.exe、sha1sum sample.exe、sha256sum sample.exe,输出应与脚本结果完全一致。
  • 情报查询验证:若 有效且样本已被 VirusTotal 收录,脚本将输出检测率(如 “45/72 个引擎检出”)、威胁标签(如 “trojan”)、家族信息(如 “Win32.Emotet”)。若返回 404,说明该哈希在 VirusTotal 库中无记录,可换用已知恶意样本哈希(如 44d88612fea8a8f36de82e1278abb02f,EICAR 测试文件)验证查询链路是否通畅。
  • 通过条件:脚本能正确输出三种哈希且与系统命令结果一致;使用有效 查询时能返回 JSON 格式的威胁情报信息(无论是否命中),即视为本案例闭环通过。

常见问题

  • API 限额:VirusTotal 社区版 API 限速 4 次/分钟,若查询频繁会返回 429 状态码,需等待后重试。
  • 样本未收录:新样本或小众样本可能未被 VirusTotal 收录,此时可先上传样本获取分析报告,再使用返回的哈希进行查询。
  • 哈希碰撞风险:MD5 和 SHA-1 已被证明存在碰撞攻击,实际威胁情报匹配应以 SHA-256 为准,本代码默认使用 SHA-256 查询即为此原因。