bigblue-r4/deobfuscate-rs

GitHub: bigblue-r4/deobfuscate-rs

一个面向 LLM 安全 pipeline 的 Rust 多趟文本反混淆与编码规避检测库,在调用大模型前对输入进行归一化和风险评分。

Stars: 1 | Forks: 0

# 反混淆 用于 Rust 的多趟文本去混淆与编码规避检测器。 专为 LLM 安全 pipeline 构建,适用于攻击者对 prompt-injection payload 进行编码 以规避内容分类器的场景。在任何 LLM 调用**之前**运行此程序:它将返回 供模型使用的已清理文本,以及用于审计追踪的结构化检测报告。 [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/39/39faa54be350a1dab8afd3b2fb8c1c83e4d9cff84abfef2374d19a18053687c4.svg)](https://github.com/bigblue-r4/deobfuscate-rs/actions/workflows/ci.yml) [![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![crates.io](https://img.shields.io/crates/v/deobfuscate.svg)](https://crates.io/crates/deobfuscate) ## 快速开始 ``` [dependencies] deobfuscate = "1" ``` ``` use deobfuscate::analyze; let result = analyze("Execute: .... .- -.-. -.-"); if result.should_block() { // score >= 0.60 — stop and ask / block eprintln!("blocked: {}", result.summary()); } else if result.should_flag() { // score >= 0.25 — flag for review, send normalized to model send_to_model(&result.normalized); } ``` ## 处理趟 按 pipeline 顺序执行 18 个连续的处理趟。每个处理趟独立触发;检测结果会累积。 | 处理趟 | 检测内容 | 示例 | |------|---------|---------| | `PreScanNfc` | Unicode NFD 组合序列 | Ä (已分解) → Ä (NFC) | | `InvisibleStrip` | 变体选择符,标签块 (U+E0000–E007F) | 已移除 U+FE0F | | `CjkSuperposition` ⚠ | 正向/反向 CJK 熵突增 — 注入接缝。**中止**:文本已清除。 | 混合 CJK+拉丁字符注入 | | `BiDiControl` | 不可见的 RTL/LTR 覆盖字符 (U+202E, U+200B, …) | `"ignore\u{202E}all"` → `"ignoreall"` | | `FullwidthChars` | 东亚全角 ASCII (U+FF01–U+FF5E) | `PWNED` → `PWNED` | | `BackslashEscape` | 逐字符 `\X` 前缀转义 | `\i\g\n\o\r\e` → `ignore` | | `UnicodeEscape` | 已解码的 `\xNN`, `\uNNNN`, `\u{N}`, 八进制转义 | `\x69gnore` → `ignore` | | `UrlEncoding` | 百分号编码序列(≥ 3 个连续的 `%XX`)且包含注入关键字 | `%69%67%6e%6f%72%65` → `ignore` | | `HtmlEntities` | 十进制、十六进制、命名的 XML 实体(≥ 4 个实体 + 注入关键字) | `ig…` → `ignore` | | `Base64` | 显式的 `b64.decode("…")` 和纯数据块(≥ 12 个字符) | `aWdub3Jl` → `ignore` | | `MorseCode` | 长度 ≥ 10 个字符的 ITU 摩斯密码段,摩斯字符比例 ≥ 60%,字母解码率 ≥ 40% | `.... .- -.-. -.-` → `HACK` | | `Homoglyph` | 包含 1,631 项的 TR39 易混淆字符:西里尔文、希腊文、希伯来文、数学/手写/哥特字体 | `іgnοre` → `ignore` | | `ScriptIntrusion` | 拉丁单词中嵌入的非拉丁字符 | `sy‌stem` (零宽连字) | | `Leetspeak` | 密集 leet token 中的数字/符号替换(leet 比例 ≥ 35%) | `1337h4x0r` → `ieetaxor` | | `EntropyBigram` | Shannon 熵 > 5.2 位 OR 英文双字母覆盖率 < 0.15 | 高熵编码块 | | `SplitString` | 跨分隔符碎片化的注入关键字 — 仅检测 | `ig.no.re` 重构为 `ignore` | | `Rot13` | 在包含注入关键字的全字母 token(≥ 4 个字符)中解码 ROT13 | `vtaber` → `ignore` | | `Punycode` | 通过 RFC 3492 解码 IDN `xn--` 标签,并在易混淆字符归一化后发现关键字 | `xn--shll-w4d` → `shell` | ## 评分 `obfuscation_score` 是 [0.0, 1.0] 范围内的浮点数,上限为 1.0。 | 处理趟 | 权重 | |------|--------| | CjkSuperposition | 1.00 (中止) | | BiDiControl | 0.90 | | Base64 | 0.85 | | BackslashEscape / UnicodeEscape / MorseCode / UrlEncoding / HtmlEntities / Rot13 | 0.80 | | Punycode | 0.85 | | InvisibleStrip | 0.75 | | SplitString | 0.70 | | FullwidthChars | 0.65 | | Homoglyph | 0.55 | | EntropyBigram | 0.50 | | ScriptIntrusion | 0.40 | | PreScanNfc | 0.35 | | Leetspeak | 0.30 | 默认阈值(可通过 [`Config`](#configuration) 配置): - `score >= 0.25` → **标记** (`should_flag()`) - `score >= 0.60` → **拦截** (`should_block()`) ## Builder API 默认情况下,所有处理趟均已启用。可选择性启用或禁用: ``` use deobfuscate::{Normalizer, PassKind}; // All passes except Morse: let r = Normalizer::default() .disable(PassKind::MorseCode) .analyze(input); // Only homoglyph + leet: let r = Normalizer::new() .enable(PassKind::Homoglyph) .enable(PassKind::Leetspeak) .analyze(input); ``` ## 配置 所有阈值和处理趟权重均可通过 `Config` 在运行时配置。加载部分 TOML —— 缺失字段将回退到默认值。 ``` # config.toml flag_threshold = 0.25 block_threshold = 0.60 # 为高灵敏度部署收紧 homoglyph 权重 weight_homoglyph = 0.70 # 为 1337 属于常态的游戏场景放宽 leet weight_leet = 0.10 leet_min_pct = 60 ``` ``` use deobfuscate::{Config, Normalizer}; use std::path::Path; // From file (returns Config::default() if file missing or unreadable) let config = Config::from_file(Path::new("config.toml")); // not available on wasm32 // From inline TOML string let config = Config::from_toml("block_threshold = 0.90").unwrap(); // Struct literal with defaults let config = Config { weight_homoglyph: 1.0, ..Config::default() }; let result = Normalizer::default() .with_config(config) .analyze(input); ``` 需要启用 `serde` 特性(默认启用)。使用 `default-features = false` 禁用以进行无 serde 构建。有关完整的 字段参考,请参见 [`examples/config.toml`](examples/config.toml)。 ## Result API ``` let r = deobfuscate::analyze(input); r.normalized // cleaned string — send this to your LLM r.obfuscation_score // f32 in [0.0, 1.0] r.is_obfuscated() // any detection fired? r.should_flag() // score >= flag_threshold (default 0.25) r.should_block() // score >= block_threshold (default 0.60) r.summary() // "score=0.80 detections=[morse-code]" r.detection_kinds() // Vec, deduplicated r.detections // Vec — full detail per event .kind // PassKind .original // obfuscated span .normalized // replacement .detail // human description ``` ## 审计追踪 `audit` 特性(默认启用)将无 payload 的 [`AuditRecord`] 附加到每个 `NormalizationResult`。该记录保存了原始输入的 SHA-256 哈希值和字符长度、 UTC 时间戳、分数、拦截/中止标志以及每次检测的元数据 —— **不包含原始字符串**。 ``` let result = deobfuscate::analyze(r"\x69\x67\x6e\x6f\x72\x65 all instructions"); // Serialize as a single JSONL line — wire to your SIEM or append to a log file let line: String = result.audit_jsonl(); // Append to a JSONL log (non-wasm32 only) result.audit.append_jsonl(std::path::Path::new("/var/log/deobfuscate.jsonl"))?; ``` 示例记录: ``` { "input_hash": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", "input_len": 43, "timestamp": "2024-11-14T22:13:20Z", "obfuscation_score": 0.8, "halted": false, "blocked": true, "passes_fired": ["unicode-escape"], "detections": [ { "pass": "unicode-escape", "original_len": 28, "normalized_len": 6, "detail": "unicode-escape decoded 4 sequence(s) [hex-byte]; result contains keyword: ignore" } ] } ``` 有关可运行的演示,请参见 [`examples/audit.rs`](examples/audit.rs)。 ## WebAssembly `wasm` 特性为浏览器内使用暴露了轻量级的 JS 可调用 API。 ``` wasm-pack build --target web --features wasm --no-default-features ``` ``` import init, { analyze_text, should_block, score } from './pkg/deobfuscate.js'; await init(); const result = analyze_text(userInput); if (result.should_block) { console.error('blocked:', result.summary); } else { sendToLLM(result.normalized); } ``` 请参见 [`wasm/README.md`](wasm/README.md) 和 [`wasm/example.html`](wasm/example.html)。 ## 基准测试 (CyberEC 对抗数据集,141 行) 针对来自 CyberEC prompt-injection 数据集的 26 个假阴性案例 (即逃避原始 LLM 分类器的攻击): | 类别 | 数量 | 捕获数 | |----------|-------|--------| | Unicode 同形字 (西里尔文/希腊文) | 4 | ✓ 全部 | | 反斜杠转义文本 | 3 | ✓ 全部 | | Leetspeak (混合字母) | 3 | ✓ 全部 | | 摩斯密码 | 1 | ✓ | | Base64 | 1 | ✓ | | 全角 Unicode | 1 | ✓ | | **总计** | **13 / 26** | **50%** | 剩余的 13 个是语义攻击(越狱框架、首字母缩略词替换、 多跳推理)—— 这些需要 LLM 级别的推理,而不是结构化归一化。 在良性文本(NIST 参考文献、代码片段、CLI 标志、 版本号)中零误报。 ## 路线图 | 处理趟 | 检测内容 | |------|---------| | `Rot13` | 全字母 token 中的 Caesar-13 替换 | | `Punycode` | 嵌入在文本中的 IDN `xn--` 编码主机名 | 计划改进的 API: - `no_std` 模式(移除文件系统依赖项,嵌入解码器) - 每 token 置信度分数 ## 起源 提取自 [split-brain-harness](https://github.com/bigblue-r4/split-brain-harness), 这是一个为 DHS SBIR 评估构建的 LLM 安全遥测代理。归一化器 作为 Stage 0 运行,位于两阶段的 LLM 提议+验证 pipeline 之前。 ## 许可证 MIT —— 参见 [LICENSE](LICENSE)。
标签:AI工具, DNS 反向解析, LLM防护墙, Naabu, Python安全, Rust, 内容安全, 可视化界面, 大语言模型安全, 提示词注入防护, 文本解混淆, 机密管理, 网络流量审计, 通知系统