Skip to content

Database Dump Cleaning — 泄露数据清洗方法论 ​

从非结构化或半结构化泄露数据中提取、分类、关联有效信息,建立源数据到业务实体的映射。

关键词 ​

数据清洗 泄露数据 格式分类 特征提取 锚点映射 置信度分级 数据去噪 前缀聚类 计数器归一化

输入信号 ​

信号立即动作命中样本失败样本
HTML/JSON/日志里混入卡密、订单号、账号、下载链接先抽字段路径和原始行号同一字段下值格式稳定,能聚出高频前缀每行都是模板文案、错误页或分页噪声
dump 中有商品名、SKU、订单 ID、CDK 同时出现建立 record -> entity -> order 三表同一 SKU 下 CDK 前缀、长度、字符集一致SKU 与 CDK 无稳定关系,只是后台操作日志
文件里只有一堆短码/长 token做长度、字符集、前缀、校验位聚类聚类结果能反推出库存批次或权益类型分布均匀且没有任何可复查锚点
邮件/短信/回调日志泄露用订单号、手机号后四位、邮箱域名做弱锚点订单状态、支付金额、发货字段能连成链只有通知模板,无订单实体字段
备份 SQL / CSV / XLSX先解析 schema,再抽高价值字段字段名指向 card/pass/secret/key/url/license只有测试表、空表或迁移结构

0. 流程全景 ​

原始数据 → 结构识别 → 有效载荷提取 → 特征聚类 → 锚点关联 → 去噪验证 → 结构化输出
  │           │            │            │          │          │           │
HTML/JSON  定位数据块   去除标记行   长度/字符集  样本→实体   假数据剔除   CSV/DB
纯文本/CSV  识别分隔符   去重        前缀聚类     格式→名称   不可用过滤   索引

1. 源数据结构识别 ​

1.0 路由矩阵 ​

输入形态第一刀第二刀证据字段
HTML 多段 body/table/textareaBeautifulSoup.get_text() 与表格列名双轨保留 DOM 路径、行号和邻近标题source_path, dom_hint, line_no
JSON API 响应递归遍历所有标量字段字段路径聚类,优先含 card/token/order/url 的路径json_path, value, siblings
SQL dump解析 CREATE TABLE 与 INSERT字段名评分,按表输出 CSVtable, column, row_id
混合日志正则提取 key-value、URL query、JSON 片段按 request id/order id/session id 归并ts, request_id, entity_id
无结构短码列表行分类 + 长度/字符集/前缀聚类找计数器、校验位、批次前缀cluster, pattern, sample

1.1 原始格式判定 ​

格式特征提取策略
HTML 堆叠多段 <body>、多个 <textarea>/<table>定位目标标签,提取 textContent
JSON 响应{...} 或 [...] 结构解析后遍历字段路径
纯文本列表每行一条记录按行处理,识别行类型
混合格式数字行+文本行+标记行交错逐行分类,建立行类型状态机

1.2 行类型分类 ​

  • 数据行:符合预期格式(长度/字符集/前缀)
  • 分隔符行:短数字(如 1)、空行
  • 标记行:含特定关键词(状态/标签)
  • Header行:文件开头几行的元数据
  • 冗余行:与其他行完全重复

1.3 数据块定位 ​

有效数据常被包裹在噪声中——错误页输出、标签行穿插、重复 header。策略:状态机扫描,定义进入/退出条件,仅提取数据块内的行。

python
#!/usr/bin/env python3
import argparse
import csv
import json
import re
from html.parser import HTMLParser
from pathlib import Path

TOKEN_RE = re.compile(r"(?i)(https?://\S+|[A-Z0-9][A-Z0-9._-]{5,80}|[\w.+-]+@[\w.-]+\.[a-z]{2,})")
NOISE_RE = re.compile(r"(?i)^(null|none|undefined|test|demo|asd+|123456|[-_=]{3,})$")

class TextSink(HTMLParser):
    def __init__(self):
        super().__init__()
        self.parts = []
    def handle_data(self, data):
        if data.strip():
            self.parts.append(data.strip())

def html_text(raw):
    sink = TextSink()
    sink.feed(raw)
    return "\n".join(sink.parts)

def flatten_json(obj, prefix="$"):
    if isinstance(obj, dict):
        for k, v in obj.items():
            yield from flatten_json(v, f"{prefix}.{k}")
    elif isinstance(obj, list):
        for i, v in enumerate(obj):
            yield from flatten_json(v, f"{prefix}[{i}]")
    else:
        yield prefix, "" if obj is None else str(obj)

def iter_candidates(path):
    raw = Path(path).read_text(encoding="utf-8", errors="ignore")
    rows = []
    try:
        obj = json.loads(raw)
        for jp, value in flatten_json(obj):
            rows.append((jp, value))
    except json.JSONDecodeError:
        text = html_text(raw) if "<html" in raw.lower() or "<body" in raw.lower() else raw
        for idx, line in enumerate(text.splitlines(), 1):
            rows.append((f"line:{idx}", line.strip()))
    for source, text in rows:
        for m in TOKEN_RE.finditer(text):
            value = m.group(1).strip(" ,;\"'")
            if not NOISE_RE.match(value):
                yield {"source": source, "value": value, "length": len(value)}

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("dump")
    ap.add_argument("-o", "--output", default="clean-candidates.csv")
    args = ap.parse_args()
    with open(args.output, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=["source", "value", "length"])
        writer.writeheader()
        writer.writerows(iter_candidates(args.dump))
    print(args.output)

if __name__ == "__main__":
    main()

成功样本:输出 CSV 里同一字段路径出现稳定 token/CDK/URL;失败样本:候选集中大多是模板词、CSS 类名、前端 bundle hash,需要回到源格式重新定界。

2. 特征维度 ​

2.1 基础维度 ​

维度用途
长度分布聚类第一特征,同格式往往等长
字符集[A-Z0-9] vs [a-f0-9] vs 含特殊字符
前缀(2-4 字符)最强区分特征
结构模式固定段+分隔符+可变段

2.2 前缀聚类 ​

先取 2 字符分组,高频分组再细化到 3 字符。对计数器变体做归一化合并。

python
#!/usr/bin/env python3
import argparse
import csv
import re
from collections import Counter, defaultdict

HEX_COUNTER = re.compile(r"^([A-Z]{2})[0-9A-F](../../../../ctf-website/techniques/24-database/[A-Z0-9._-]{3,})$", re.I)

def charset_of(v):
    if re.fullmatch(r"[0-9]+", v):
        return "digits"
    if re.fullmatch(r"[a-f0-9]+", v):
        return "hex-lower"
    if re.fullmatch(r"[A-F0-9]+", v):
        return "hex-upper"
    if re.fullmatch(r"[A-Z0-9]+", v):
        return "upper-num"
    if v.startswith("http"):
        return "url"
    return "mixed"

def normalize_prefix(v, n=3):
    up = v.upper()
    m = HEX_COUNTER.match(up)
    if m:
        return f"{m.group(1)}*:{len(v)}:{charset_of(v)}"
    return f"{up[:n]}:{len(v)}:{charset_of(v)}"

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("csv_file")
    ap.add_argument("--value-column", default="value")
    ap.add_argument("--top", type=int, default=30)
    args = ap.parse_args()
    buckets = defaultdict(list)
    with open(args.csv_file, newline="", encoding="utf-8") as f:
        for row in csv.DictReader(f):
            v = row[args.value_column].strip()
            if v:
                buckets[normalize_prefix(v)].append(v)
    ranked = sorted(buckets.items(), key=lambda kv: (-len(kv[1]), kv[0]))[:args.top]
    for key, values in ranked:
        lens = Counter(map(len, values))
        print(f"{key}\tcount={len(values)}\tlens={dict(lens)}\tsample={values[:3]}")

if __name__ == "__main__":
    main()

2.3 计数器归一化 ​

前缀第三位是十六进制计数器(如 TK0-TKF)的,合并为同一类型。识别模式:固定前缀 + [0-9A-F] + 固定结构段。

3. 可独立使用资产识别 ​

3.1 直接可用 ​

类型判定方式
URL/下载链接https?:// 正则匹配
邮箱+密码@ + ---- 分隔符结构
手机号+API国际区号 + 固定长度 + URL
联系方式特定前缀 + 可识别 ID

3.2 需上下文 ​

短激活码(6-12 字符)、UUID 格式码——不知道对应哪个产品/平台就无法使用。

3.3 假数据特征 ​

重复模式(123456、111111)、明显占位符(asd123、dasdasd)、不符合任何已知业务格式的行。

4. 锚点映射 ​

4.1 原理 ​

从有限的 ground truth 样本出发,通过格式一致性将映射关系扩展到同格式的全部数据。

样本:  { prefix_A → Entity_X }  (来源: 订单/API 等可信数据)
扩展:  所有 prefix_A 的记录 → Entity_X

4.2 锚点来源与可信度 ​

来源可信度获取难度
订单/交易记录最高需要 API 访问
商品描述直接匹配高需要商品目录
品牌名拼音推断中需要目录+拼音映射
格式结构推测低仅需泄露数据本身

4.3 扩展验证 ​

  • 同一前缀下的数据格式是否完全一致
  • 同一商品下不同锚点返回的前缀是否一致
  • 块大小与商品库存量是否在同一数量级
python
#!/usr/bin/env python3
import argparse
import csv
from collections import defaultdict

def load_map(path, key, value):
    out = {}
    with open(path, newline="", encoding="utf-8") as f:
        for row in csv.DictReader(f):
            out[row[key].strip().upper()] = row[value].strip()
    return out

def prefix(v):
    up = v.strip().upper()
    return up[:3] if len(up) >= 3 else up

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("candidates")
    ap.add_argument("anchors", help="CSV: prefix,entity")
    ap.add_argument("-o", "--output", default="classified.csv")
    args = ap.parse_args()
    anchors = load_map(args.anchors, "prefix", "entity")
    stats = defaultdict(int)
    with open(args.candidates, newline="", encoding="utf-8") as src, open(args.output, "w", newline="", encoding="utf-8") as dst:
        reader = csv.DictReader(src)
        fieldnames = reader.fieldnames + ["prefix", "entity", "confidence"]
        writer = csv.DictWriter(dst, fieldnames=fieldnames)
        writer.writeheader()
        for row in reader:
            p = prefix(row["value"])
            entity = anchors.get(p, "")
            row.update({"prefix": p, "entity": entity, "confidence": "confirmed" if entity else "format-only"})
            stats[(p, entity or "UNKNOWN")] += 1
            writer.writerow(row)
    for (p, entity), count in sorted(stats.items(), key=lambda x: -x[1])[:40]:
        print(f"{p}\t{entity}\t{count}")

if __name__ == "__main__":
    main()

锚点 CSV 最小格式:

csv
prefix,entity
VIP,monthly_vip
CDK,game_card
TK*,ticket_batch

5. 品牌/名称关联 ​

5.1 直接匹配 ​

前缀出现在商品名称的显著位置(如被 【】 包裹的品牌名)。

5.2 拼音首字母 ​

中文品牌名逐字取首字母,匹配前缀。需处理多音字和生僻字。

5.3 英文缩写 ​

游戏/平台英文名缩写匹配(PUBG→GU、Minecraft→MC)。

5.4 收敛策略 ​

无法匹配的前缀:数量<阈值归入"未分类";结构可识别标记格式类型;无特征标记"未知"。

6. 置信度分级 ​

级别标准
确认有订单/API 直接证据
高品牌名直接匹配 + 格式一致
中拼音/缩写匹配
低纯格式推断

7. 去噪 ​

  • 重复值聚类——异常高频的同一内容
  • 模式匹配——连续数字、键盘序列、占位符
  • 业务验证——有效凭证/账号格式校验

8. 输出规范 ​

每条记录应包含:原始行号、数据内容、分类标签、关联实体 ID、关联实体名称、置信度。输出格式:CSV(全量)、SQLite(查询)、HTML(浏览)。

python
#!/usr/bin/env python3
import argparse
import csv
import sqlite3

SCHEMA = """
CREATE TABLE IF NOT EXISTS records(
  id INTEGER PRIMARY KEY,
  source TEXT,
  value TEXT,
  length INTEGER,
  prefix TEXT,
  entity TEXT,
  confidence TEXT
);
CREATE INDEX IF NOT EXISTS idx_records_prefix ON records(prefix);
CREATE INDEX IF NOT EXISTS idx_records_entity ON records(entity);
"""

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("classified_csv")
    ap.add_argument("-o", "--output", default="classified.sqlite")
    args = ap.parse_args()
    db = sqlite3.connect(args.output)
    db.executescript(SCHEMA)
    with open(args.classified_csv, newline="", encoding="utf-8") as f:
        rows = list(csv.DictReader(f))
    db.executemany(
        "INSERT INTO records(source,value,length,prefix,entity,confidence) VALUES(:source,:value,:length,:prefix,:entity,:confidence)",
        rows,
    )
    db.commit()
    for row in db.execute("SELECT prefix, entity, confidence, COUNT(*) FROM records GROUP BY 1,2,3 ORDER BY 4 DESC LIMIT 30"):
        print(row)
    db.close()

if __name__ == "__main__":
    main()

8.1 支付/卡密字段字典 ​

整理 dump 时优先给字段打业务标签,后续才能直接接订单、发货和回调链。

标签字段/值信号输出列
order_idorder, trade, out_trade_no, 长数字订单号entity_id, order_hint
paymentamount, money, paid, status, transaction_idamount, pay_state
card_secretcard, kami, cdk, license, secret, 短码/长码value, secret_type
deliverydownload_url, delivered_at, result, kminfodelivery_hint
callbacknotify, sign, raw, xml, jsoncallback_seed
identityemail、手机号、uid、session、cookieowner_hint
python
# dump_business_tagger.py — 给泄露记录打业务标签
import re

RULES = {
    "order_id": re.compile(r"(out_trade_no|order[_-]?id|trade[_-]?no)|\b\d{10,20}\b", re.I),
    "payment": re.compile(r"(amount|money|total_fee|paid|pay_status|transaction_id)", re.I),
    "card_secret": re.compile(r"(card|kami|cdk|license|secret|key)|[A-Z0-9]{8,40}", re.I),
    "delivery": re.compile(r"(download_url|delivered_at|kminfo|result)", re.I),
    "callback": re.compile(r"(notify|sign_type|sign=|<xml>|callback|webhook)", re.I),
    "identity": re.compile(r"[\w.+-]+@[\w.-]+\.[a-z]{2,}|\b1[3-9]\d{9}\b|PHPSESSID|session", re.I),
}

def tag_record(text):
    return [name for name, rx in RULES.items() if rx.search(text)]

8.2 输出到下一跳 ​

输出文件用途
classified_records.csv全量字段、行号、标签、置信度
entity_index.sqlite按订单/商品/用户/卡密查询
callback_replay_seed.jsonl给签名/回调文档复放
card_inventory_candidates.csv给数字商品订单核对
noise_rules.json记录被剔除的模板、占位符和冲突锚点

成功样本:一个泄露值能从原始行号追到实体标签、订单或商品,再追到卡密/下载链接/回调样本。失败样本:只有格式相似但无锚点,或同一前缀被多个实体冲突占用。

Evidence ​

项记录内容
原始输入dump 文件 hash、大小、来源类型、解析脚本版本
结构识别HTML/JSON/SQL/log/plaintext 判定依据、字段路径或原始行号
聚类结果top 前缀、长度分布、字符集、计数器归一化规则
锚点证据订单/API/商品目录/字段名如何把 prefix 绑定到实体
成功样本classified CSV/SQLite 中可复查的实体映射、可用 URL/token/CDK/订单字段
失败样本噪声规则、无法归类前缀、冲突锚点、格式相似但实体不一致的样本
下一跳转 12-payment、04-config-exposure 或 SQLi 抽取链的文件路径

MCP 工具映射 ​

分析步骤MCP 工具说明
知识检索kb_router按 database dump、data cleaning、leak triage 搜索
文件哈希hash_file记录原始 dump、清洗后 CSV/SQLite 的完整性
模式搜索search_pattern扫描邮箱、手机号、卡密、token、订单号等模式
脚本执行run_ctf_tool调用清洗、去重、聚类、格式转换脚本
证据记录workspace_write_text保存字段字典、字段样例、置信度规则和输出索引

9. 关联技术 ​

  • [[00-overview]] — 数据库攻击全景
  • [[01-sqli-fundamentals]] — SQL 注入
  • [[04-config-exposure]] — 配置泄露

Apache-2.0 · 仅供授权环境下的学习与防御性研究使用 · 免责声明