Nicholas-Kloster/BARE

GitHub: Nicholas-Kloster/BARE

将 BERT 语义搜索编译为单一 Rust 二进制文件,在完全离线环境下将安全扫描结果映射到 Metasploit 漏洞利用模块。

Stars: 0 | Forks: 0

``` ____ ___ ____ ______ / __ )/ | / __ \/ ____/ / __ / /| | / /_/ / __/ / /_/ / ___ |/ _, _/ /___ /_____/_/ |_/_/ |_/_____/ Offline Semantic Exploit Mapping Single-binary BERT encoder for air-gapped vulnerability ranking. ``` # BARE [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/b3fe592489082852.svg)](https://github.com/Nicholas-Kloster/BARE/actions/workflows/ci.yml) **BARE**(Binary Anywhere Rust Encoder)通过语义搜索将安全扫描器的发现映射到 Metasploit 模块。它是一个自包含的 Rust 二进制文件,内部嵌入了 BERT 编码器和 3,900 多个漏洞利用程序语料库,并在编译时将其打包。 **无需 Python。无需 PyTorch。无需网络。只有一个二进制文件。** ## 问题背景 语义搜索通常需要庞大的技术栈:Python 解释器、`pip`、`torch`、`transformers` 以及向量数据库。在物理隔离的网络、敏感信息设施(SCIF)或受限的终端环境中,安装这种 5GB 以上的依赖通常是根本不可能的。 **BARE** 解决了这个问题,它将整个流水线——分词器、模型权重和语料库——编译成一个仅 101MB 的独立产物。 ## 工作原理 BARE 接收原始的扫描发现(通过适配器),并根据语义相关性对 Metasploit 模块进行排序。 ``` # 将 nuclei scan 输入,获取排名后的 exploit: nuclei -u https://target.com -j | python adapters/nuclei/nuclei_to_bare.py | bare ``` ### BARE 的独特之处 | 工具 | 离线 | 语义 | 单一二进制文件 | 特定于安全 | |---------------------|---------|----------|---------------|-------------------| | SearchSploit | 是 | 否 | 是 | 是 | | Metasploit `search` | 是 | 否 | 是 | 是 | | rust-bert | 否 | 是 | 否 (libtorch) | 否 | | **BARE** | **是** | **是** | **是** | **是** | 虽然 `rust-bert` 需要安装约 2GB 的 `libtorch`,但 BARE 使用 [Candle](https://github.com/huggingface/candle) 在 Rust 中原生运行推理。约 101MB 的大小包含了 BERT 模型权重(约 87MB)和预编码的 Metasploit 语料库。 其他工具只解决了问题的部分环节。BARE 是首个将这五种特性结合在单一发布产物中的工具: | 工具 | 离线 | 语义 | 单一二进制文件 | 特定于安全 | 完整工具 | |---------------------|---------|----------|---------------|-------------------|---------------| | EdgeBERT | 是 | 是 | 是 | 否 | 否 (库) | | rust-bert | 否 | 是 | 否 (libtorch) | 否 | 否 (库) | | SearchSploit | 是 | 否 | 是 | 是 | 是 | | Metasploit `search` | 是 | 否 | 是 | 是 | 部分 | | **BARE** | **是** | **是** | **是** | **是** | **是** | BARE 以约 101MB 的 Linux x86_64 二进制文件形式发布。对于一个 CLI 工具来说这体积有些大,但对于以下约束条件来说却很小:它是一个自包含的 BERT 编码器、分词器,外加 3,904 个预编码且零外部依赖的 Metasploit 模块。在安装 Python 加 torch 需要没人敢签字批准的权限提升的环境中,磁盘空间反而是更廉价的资源。 有关包含发布日期和架构细节的完整比较,请参阅 [PRIOR_ART.md](PRIOR_ART.md)。 ## 安装说明 从 Releases 页面下载最新的预构建二进制文件: ``` curl -LO https://github.com/Nicholas-Kloster/BARE/releases/latest/download/bare-linux-x86_64 curl -LO https://github.com/Nicholas-Kloster/BARE/releases/latest/download/bare-linux-x86_64.sha256 sha256sum -c bare-linux-x86_64.sha256 chmod +x bare-linux-x86_64 ``` 该二进制文件包含了一切。BERT 编码器、分词器以及 3,904 个预编码的 Metasploit exploit 和 auxiliary(辅助)模块描述。无需 Rust 工具链。 ## 快速开始 ### 1. 安装 从 [Releases](https://github.com/Nicholas-Kloster/BARE/releases) 页面下载最新的预构建二进制文件: ``` git clone https://github.com/Nicholas-Kloster/BARE cd BARE curl -L -o assets/model.safetensors \ https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2/resolve/main/model.safetensors cargo build --release ``` 由于体积过大,模型权重(`assets/model.safetensors`,约 87MB)已被 gitignore 忽略,必须在首次构建前获取。构建时会通过 `include_bytes!` 将它们嵌入到二进制文件中——一旦二进制文件构建完成,就不再需要网络访问。 尝试运行内置示例: ``` cat adapters/nuclei/examples/nuclei_sample.jsonl \ | python adapters/nuclei/nuclei_to_bare.py \ | ./bare-linux-x86_64 --top 3 ``` ### 3. 输出 BARE 会生成结构化的 JSON,将每个发现映射到最相关的 Metasploit 模块: ``` { "id": "CVE-2023-22527", "title": "Atlassian Confluence SSTI RCE", "matches": [ { "rank": 1, "module": "exploits/multi/http/atlassian_confluence_rce_cve_2023_22527", "score": 0.8322 }, { "rank": 2, "module": "exploits/multi/http/atlassian_confluence_rce_cve_2024_21683", "score": 0.7472 } ] } ``` 每个发现都会从嵌入的 Metasploit 语料库中生成一个语义最相似模块的排序列表。 ## 使用方法 ``` bare [OPTIONS] [INPUT_PATH] OPTIONS: --top Number of top matches per finding (default: 3, capped to corpus size) --encode Read text from stdin, print L2-normalized 384-dim vector to stdout (used by the parity check — see Parity Validation below) --version Print version banner and exit --help Print help and exit INPUT: INPUT_PATH may be a path to a findings.json file, or "-" / omitted to read stdin. ``` 状态消息和警告会写入 stderr。标准输出上只有输出的 JSON 文档,因此可以安全地将其通过管道传递给其他工具。 ## 意义所在 其他所有的 BERT 实现都需要运行时技术栈。Python 解释器、pip、torch、transformers、虚拟环境,以及安装所有这些内容的足够权限。BARE 则完全不需要这些。模型权重、分词器、语料库和搜索逻辑在构建时全部编译进了一个可执行文件中。 有一类环境从未能够部署过语义搜索。物理隔离的网络。端点安全工具。嵌入式系统。没有互联网的野外部署硬件。法律要求隔离的系统。这些环境一直只能使用关键词匹配,因为这是唯一能编译成可移植形式的方法。BARE 在不改变其环境约束的前提下,为它们带来了语义理解。 一个需要 Python 和 ChromaDB 的语义搜索工具只是一个实验室工具。一个作为单一二进制文件的语义搜索工具则是一个实战工具。这种区别正是其全部意义所在。 ## 为何选择 Rust Rust 编译器在机制上就是一个静态分析器,只有在证明程序内存安全后才生成二进制文件。其类型系统、所有权模型和借用检查器会在编译时验证程序不会出现悬垂指针、双重释放、释放后使用错误、数据竞争、空指针解引用或缓冲区溢出(在安全代码中)。这些检查是不可绕过的。在满足每一条规则之前,编译器会拒绝产生任何输出。 对于一个旨在运行在无法应对失败的环境中的工具来说,这提高了运行可靠性的下限。BARE 不会在遇到意外输入时发生段错误。它不会在压力下因竞争导致数据损坏。它不会使其所在的系统崩溃。 Rust 不能带给你的是正确的分词、正确的余弦相似度计算或正确的排名。这些必须另行努力验证。本仓库记录了赢得这些信任的验证步骤:Rust 输出的向量与 Python sentence-transformers 的输出匹配,差异在 f32/f64 的舍入误差范围内(约 1e-7 的差值)。 内存安全性加上一致性验证是其部署承诺。不是“只要编译通过,就能正常工作”。而更接近于:如果它编译通过了,那些失败模式就是你可以测试到的,而不是操作系统在凌晨 3 点的 SCIF 中给你带来的“惊喜”。 ## 工作原理 ``` build time runtime +--------------+ +--------------+ | corpus texts | | query text | +------+-------+ +------+-------+ | | v v +--------------+ +--------------+ | Python | | Rust encoder | | serializer | | (Candle) | +------+-------+ +------+-------+ | | v v +--------------+ +--------------+ | corpus.bin |----include------> query vector | +--------------+ bytes! +------+-------+ | v +--------------+ | cosine sim | | ranking | +--------------+ ``` 在构建时,一个 Python 脚本将文本语料库(目前为 3,904 个 Metasploit exploit 和 auxiliary 模块描述)编码为一个扁平的二进制文件。该文件通过 `include_bytes!` 直接编译到 Rust 二进制文件中。BERT 模型权重以相同的方式嵌入。 在运行时,BARE 从标准输入或文件读取扫描发现,使用嵌入的 BERT 模型对每个描述进行编码,并通过余弦相似度在固化于二进制中的语料库进行搜索。输出是结构化的 JSON。每个输入发现对应一个模块排序列表。 ## 适配器生态 BARE 是格式无关的。它使用通用的 `findings.json` 格式,只需通过简单的适配器脚本即可与任何扫描器配合使用。 | 来源 | 适配器状态 | 最适用场景 | |--------|----------------|----------| | **Nuclei** | ✅ 可用 | 已确认的漏洞和 CVE | | **Nmap** | ✅ 可用 | 服务指纹和版本字符串 | | **Shodan** | ✅ 可用 | 批量横幅数据和端口导出 | | **Trivy** | 🛠️ 计划中 | 容器和文件系统扫描 | 请参阅 [adapters/README.md](adapters/README.md) 来构建你自己的适配器。 ## 为何选择 Rust? 对于为受限环境设计的工具而言,操作可靠性是不可妥协的。 - **内存安全:** Rust 编译器保证了 BARE 在解析不受信任的扫描器输出时,不会遭受缓冲区溢出或释放后使用错误的影响。 - **一致性验证:** 我们强制要求 BARE 的 Rust 编码器与 Python `sentence-transformers` 参考实现之间实现元素级别的一致性,允许的误差范围在 `1e-7` 之内。 - **可靠性:** 如果它能编译,那么失败模式就是可预测的。在 SCIF 中不会发生“凌晨 3 点的段错误”。 编写新适配器:有关模式和约定,请参阅 [adapters/README.md](adapters/README.md)。 ### nuclei 将 nuclei JSONL 输出(`-j` 标志)转换为 `findings.json`。每个 nuclei 结果对应一个发现。 ``` nuclei -u https://target.com -j | python adapters/nuclei/nuclei_to_bare.py | bare --top 5 python adapters/nuclei/nuclei_to_bare.py scan.jsonl | bare ``` 该适配器从多个 nuclei 字段构建丰富的描述——名称、描述、CVE/CWE ID、匹配值、提取结果和标签——以最大化嵌入的可用信息量。仅包含 `info.name` 和 `info.description` 的发现同样有效;仅这两个字段就足以为大多数查询提供足够的信号。 | findings.json 字段 | 来源 | |---------------------|--------| | `id` | `template-id`,回退到经过 slugify 处理的 `info.name` | | `title` | `info.name` | | `description` | 名称 + 描述 + CVE/CWE ID + 匹配器 + 提取结果 + 标签 | | `target` | `matched-at`,回退到 `host` | | `severity` | `info.severity`(标准化为小写) | | `metadata` | 所有剩余的 nuclei 字段(分类、参考、请求/响应) | 已知限制:`info.severity` 的值为 `unknown` 时会被丢弃。提取结果最多 3 条,每条限 200 字符;完整结果存放在 `metadata` 中。请求/响应体分别限制在 1000/500 字节。 完整文档:[adapters/nuclei/README.md](adapters/nuclei/README.md) ### nmap 将 nmap XML 输出(`-oX`)转换为 `findings.json`。每个开放端口对应一个发现。不会输出关闭和被过滤的端口。 ``` nmap -sV -oX - target.com | python adapters/nmap/nmap_to_bare.py | bare --top 5 python adapters/nmap/nmap_to_bare.py scan.xml | bare ``` 运行 nmap 时请带上 `-sV`(版本检测)。否则,大多数服务元素将为空,BARE 的匹配结果将不够精确。描述由服务名称、产品、版本、操作系统类型和额外信息构建——如果 nmap 根本无法识别服务,描述将回退为 `"open {protocol} port {portid}"`。 | findings.json 字段 | 来源 | |---------------------|--------| | `id` | `"{protocol}_{portid}_{host_address}"` | | `title` | `service/@name` + `service/@product` | | `description` | 名称 + 产品 + 版本 + 操作系统类型 + 额外信息 | | `target` | `"{host_address}:{portid}/{protocol}"` | | `severity` | 不输出——nmap 不产生严重性数据 | | `metadata` | 该端口上 NSE 脚本的 id + output | 已知限制:nmap 报告的是正在运行的服务,而不是已确认的漏洞——因此分数通常低于 nuclei 的输出。NSE 脚本输出保留在 `metadata.scripts` 中,但不包含在描述中(添加它会掩盖核心服务信号)。 完整文档:[adapters/nmap/README.md](adapters/nmap/README.md) ### shodan 将 Shodan JSONL 批量导出(`shodan download`)转换为 `findings.json`。每个横幅记录对应一个发现。 ``` shodan download --limit 1000 results 'apache country:US' gunzip results.json.gz cat results.json | python adapters/shodan/shodan_to_bare.py | bare --top 5 ``` 该适配器不调用 Shodan API——数据收集由用户负责。描述由产品、版本、原始横幅数据、HTTP server 头、TLS CN、CPE 标识符、Shodan 标签和 CVE ID 组装而成。严重性是根据报告的所有 CVE 中最高的 CVSS 评分得出的。 | findings.json 字段 | 来源 | |---------------------|--------| | `id` | `"{ip}_{port}_{transport}"` | | `title` | `product`,回退到 `http.title` → `cpe23[0]` → `"port N/proto"` | | `description` | 产品 + 版本 + 横幅 + HTTP server + TLS CN + 标签 + CPE + CVE ID | | `target` | `"{ip}:{port}/{transport}"` | | `severity` | `vulns` 字典中的最高 CVSS (≥9.0 critical, ≥7.0 high, ≥4.0 medium, <4.0 low) | | `metadata` | org, hostnames, 位置, Shodan 扫描元数据, 完的 `vulns` 字典 | 描述中的 CVE 列表限制为按 CVSS 排名的前 10 个。完整的 `vulns` 字典始终位于 `metadata.vulns` 中。原始横幅截断限制为 500 字符。 已知限制:Shodan 数据不是实时的——横幅数据可能是几周前的。CVE 归因基于版本字符串匹配,而非主动利用;应将发现视为线索,而非已确认的漏洞。没有主机级别的聚合——一台主机上的多个开放端口将显示为独立的发现。 完整文档:[adapters/shodan/README.md](adapters/shodan/README.md) ## 模式验证 输入和输出格式在 `schemas/` 目录中都有机器可读的 JSON 模式: - `schemas/input.schema.json`——在 BARE 处理 `findings.json` 之前对其进行验证 - `schemas/output.schema.json`——验证 BARE 的排序输出 CI 流水线通过 `ajv-cli` 针对每个适配器的样本数据运行模式检查。如果你正在构建新的适配器,请在运行 BARE 之前针对 `input.schema.json` 进行验证。如果你正在构建 BARE 输出的消费者,请针对 `output.schema.json` 进行验证。 ## 语料库生成 固化在二进制文件中的语料库分两步生成。如果你想从全新的 Metasploit 快照重新构建它: ``` # Step 1:从 GitHub 获取所有 Metasploit module 描述 python fetch_modules.py # Step 2:将描述编码为 384 维向量并写入 corpus.bin python serialize.py # Step 3:使用嵌入的新 corpus 重建二进制文件 cargo build --release ``` `fetch_modules.py` 通过 GitHub Trees API 抓取 Metasploit 框架仓库,并并发下载每个 `.rb` 文件以提取模块名称和描述。未经身份验证的 GitHub API 每小时有 60 次请求的速率限制,抓取器在获取完整的模块树之前就会耗尽该限制。请在环境中设置 `GITHUB_TOKEN`,或配置好 `gh auth login`——抓取器会自动选择其中一种方式。 该抓取器目前主要针对 `modules/exploits/` 和 `modules/auxiliary/`。发布版的语料库中不包含 `post`、`payloads`、`encoders`、`nops` 和 `evasion` 模块。当前语料库包含 2,647 个 exploit 和 1,257 个 auxiliary 模块。 `serialize.py` 读取 `modules_full.json`(抓取器的输出),加载 `sentence-transformers/all-MiniLM-L6-v2`,对每个模块的 `name + " " + description` 进行编码,并将结果写成小端序二进制文件,其格式记录在 [FORMAT.md](FORMAT.md) 中。 此步骤需要安装了 `sentence-transformers` 的 Python。仅在你需要更新语料库时才需要。运行预构建二进制文件或使用仓库克隆中现有 `corpus.bin` 进行构建的最终用户完全不需要 Python。 ## 一致性验证 在信任该二进制文件之前,Rust 编码器必须生成与 Python 参考实现相匹配的向量。Rust 二进制文件通过 `bare --encode` 暴露了其原始编码器,它从 stdin 读取文本并将 L2 归一化的 384 维向量打印到 stdout——形状与 `tools/encode_baseline.py` 相同。 CI 流水线会将相同的查询通过这两个编码器运行,并断言它们的元素级别一致性: ``` QUERY="unauthenticated remote code execution in apache struts via OGNL injection" echo "$QUERY" | python tools/encode_baseline.py > python.vec echo "$QUERY" | bare --encode > rust.vec python tools/parity_check.py python.vec rust.vec --threshold 1e-5 ``` 该阈值在实践中更为严格(在大多数输入上约为 1e-7);1e-5 是考虑到边缘情况下 f32/f64 舍入问题的底线。如果 `parity_check.py` 发现任何元素级别的差值高于该阈值,它将打印出最差的 5 个偏差并以非零状态退出。一致性不匹配将导致 CI 构建失败。 ## 当前状态 单一二进制文件(Linux x86_64 上约 101MB)包含: - 嵌入式 BERT 编码器权重(sentence-transformers/all-MiniLM-L6-v2) - 嵌入式分词器 - 包含 3,904 个预编码 Metasploit 模块描述(2,647 个 exploit + 1,257 个 auxiliary)的嵌入式语料库 - 搜索逻辑和输出格式强制执行 从标准输入或文件读取 `findings.json`。根据 OUTPUT_FORMAT.md 发出结构化的排序输出。 Rust 输出向量与 Python sentence-transformers 输出相匹配,差异在 f32/f64 舍入误差范围内(约 1e-7 差值),通过 `bare --encode` 与 `tools/encode_baseline.py` 的对比作为一项硬性 CI 门控强制执行。区分度是语义上的,而非基于关键字。 ### 已知排名行为 对于以探测式语言编写的查询(诸如“unauthenticated”、“injection”、“traversal”等关键字),辅助扫描器模块的排名往往高于漏洞利用模块。扫描器描述的编写通常比漏洞利用描述更为明确,因此在结构上,它们的嵌入更接近探测式语言的查询。在大多数查询的前 5 个结果中,这两种类型都会出现。这是描述风格的偏移,而不是语义错误。 表层的修复方法是加入类别感知的排名。更深层的修复方法是在 Metasploit、Exploit-DB 和 nuclei 模板文本上微调编码器,而不是依赖于通用英语的 MiniLM 检查点。加入类别感知的排名将被优先发布,因为这不需要重新训练。 ### 语料库更新 语料库在编译时固化于二进制中。出于可重现性和可审计性的考虑,这是有意为之的。对于每一个字节都应通过哈希值固定下来的 SCIF 场景,这也是正确的做法。但这也给日常运维带来了摩擦,因为 Metasploit 会持续发布新模块,而 BARE 直到下一次重新构建时才能看到它们。 未来的版本可能会支持从磁盘加载外部 `corpus.bin` 的模式,并针对固化在二进制文件中的值进行哈希验证。这在保留“无 Python、无网络、无包管理器”保证的同时,允许操作员无需重新编译即可更换语料库。在可重现性是全部意义所在的环境中,编译时加载路径仍将是默认选项。 ## 后续计划 - 更多适配器(Trivy, Semgrep, aimap 等) - 针对扫描器与漏洞利用优先级的类别感知排名 - 用于非 SCIF 部署的外部哈希固定语料库模式 - 领域特定的编码器微调(Metasploit + Exploit-DB + nuclei 模板) - 完整语料库规模下的性能基准测试 ## 从源码构建 需要 Rust 1.70+。 前置条件:Rust 1.70+(stable 工具链),以及就位的 `assets/model.safetensors`(已被 gitignore,只需获取一次——见上文的快速开始)。 ``` cargo build --release ``` 生成的二进制文件位于 `target/release/bare`。它所需的一切(BERT 权重、分词器、语料库)都在编译时嵌入了。 要从更新的 Metasploit 模块集重新生成语料库: ``` python fetch_modules.py python serialize.py cargo build --release ``` 这需要在 Python 中安装 `sentence-transformers`。仅在你需要更新语料库时才需要。只想运行 BARE 的最终用户不需要任何 Python 依赖。 ## 许可证 在以下两种许可证下双重授权: - MIT 许可证 ([LICENSE-MIT](LICENSE-MIT)) - Apache 许可证,版本 2.0 ([LICENSE-APACHE](LICENSE-APACHE)) 根据你的选择而定。这是标准的 Rust 生态系统双重许可证。选择适合你场景的即可——选择 MIT 许可证以保持简单,或者如果你想要明确的专利授权,请选择 Apache-2.0。 嵌入的模型权重(`sentence-transformers/all-MiniLM-L6-v2`)采用 Apache-2.0 许可证。用于构建语料库的 Metasploit 模块描述采用 BSD-3-Clause 许可证(Rapid7)。两者均与此双重许可证兼容。
标签:BERT, Candle, GPT, NLP, PE 加载器, Rust, SearchSploit, 信息检索, 单文件工具, 反取证, 可视化界面, 向量搜索, 域名收集, 安全测试, 安全评估, 攻击性安全, 无Python依赖, 无PyTorch依赖, 漏洞管理, 离线安全工具, 离线部署, 网络安全, 网络流量审计, 自动化利用, 语义匹配, 语义搜索, 逆向工具, 隐私保护