🐾 claw-stack
· Orange & Qiushi Wu CTF knowledge retrieval RAG

为CTF智能体构建三模态知识引擎

我们如何构建Librarian背后的CTF知识检索系统:用于精确payload的SQLite、用于方法论的ChromaDB,以及用于本地工具的JSON索引——通过单一网关统一,带有自动回退和跨类型增强。

当Librarian被问到tcache stashing时,它需要返回比基础Claude模型所知道的更有用的内容。模型对堆利用有基本了解——它能描述tcache是什么,解释stash unlink攻击的概念,描绘漏洞利用的大致形态。但它不知道你的队友上周使用的具体pwntools惯用法,也不知道能在特定libc版本(绑定到题目二进制文件的那个版本)中揭示free list状态的确切GDB命令。这个差距——通用知识与可操作的具体细节之间的鸿沟——就是知识引擎存在的意义。

为什么”直接问LLM”不够

基础模型在CTF场景下有两种失效模式。

第一种是时效性。CTF题目通常涉及近期CVE、更新的工具版本,或者只在过去一年的writeup中有记录的技术。有训练截止日期的模型不知道这些。第二种是精确性。知道GTFOBins记录了nmap权限提升技术,和手头有确切的--script=exec用法可以直接粘贴,完全是两回事。在有时间限制的比赛中,“智能体懂理论”和”智能体有确切命令”之间的差距,可能就是解出题目和陷入死路的差距。

还有一个上下文预算问题。Librarian(Claude Haiku)每道题只被调用一次,有固定的上下文窗口。你无法把整个HackTricks都塞进prompt里。你需要精准检索:针对这道特定题目最相关的三条内容,快速送达,格式让智能体能立即采取行动。

三模态架构

知识库将三种根本不同的检索方式分别存入三个独立存储。

A型——肌肉记忆(SQLite + FTS5)

A型用于你想复制粘贴的命令。数据库(ctf_knowledge.db)包含两张表。

binaries存储来自GTFOBins和LOLBAS的约2,739条结构化记录——每个二进制文件每种利用方式一行,按名称、平台(linux/windows)和功能(shell、sudo、suid、下载等)索引。这些来自GTFOBins的YAML文件和LOLBAS的JSON导出。schema刻意保持严格:nameplatformfunctioncodedescription。查询nmap + sudo会返回确切命令,而不是对nmap能做什么的描述。

tricks是一个SQLite FTS5全文搜索表,包含来自HackTricks和PayloadsAllTheThings的约4,155条记录。构建流水线遍历所有Markdown文件,使用正则表达式提取代码块,并将周围的标题作为上下文记录。PayloadsAllTheThings经过过滤:Intruder、Wordlists、Files和Images目录被跳过(这些资产转到C型),超过20行的代码块被丢弃——这是一个刻意的选择,让tricks保持复制粘贴即可用,而不是把表变成脚本存档。

FTS5查询通过AND连接搜索词(nmap AND sudo AND shell)。当FTS5失败时——遇到含有标点符号的查询时会这样——网关回退到对第一个词的LIKE搜索。

B型——大脑皮层(ChromaDB + BGE-M3)

B型用于方法论、概念和writeup。它使用ChromaDB搭配BAAI/bge-m3嵌入:1024维向量,归一化处理,在Apple Silicon上使用Metal(MPS)加速。

构建流水线(crawl_type_b.py)是一个网络爬虫,从Markdown配置文件中读取目标URL,每个站点最多爬取500页,将文本摄入methodology集合。页面按双换行符分块,每块最多1,500字符,少于100字符的块被丢弃。原始HTML在本地缓存,这样在更改嵌入模型后重建索引时不需要重新爬取。

目前已索引:0xdf的博客(靶机writeup、实践利用技术)和pwntools文档。ctf-wiki仓库已克隆到本地,可供摄入,但需要单独处理。有一个小问题:trafilatura——主要的提取库——会被docs.pwntools.com拦截,因此爬虫对该域名回退到urllib。

关于嵌入模型的说明:我们在构建过程中将模型从384维升级到BGE-M3(1024维)。ChromaDB不支持混合维度集合,因此升级需要删除并重建整个数据库。构建脚本会自动处理这一点,但这意味着每次嵌入模型升级都是一次完整重建。

C型——武器库(JSON索引)

C型用于本地文件:字典、web shell和权限提升脚本。它不是数据库,而是一个扁平的JSON索引(asset_index.json),将名称和标签映射到绝对文件路径。

索引内容:SecLists(密码列表、目录字典、用户名列表、fuzzing payload)、PayloadsAllTheThings的web shell(.php.jsp等),以及PEASS-ng预编译二进制文件(linpeas.shwinpeas.batwinPEASany.exe)。rockyou.txt字典已预解压可直接使用。每条记录带有类别、标签列表和绝对路径——所以当Operator需要运行ffuf -w <path>时,Librarian返回的是路径,而不是找路径的指令。

pwntools和ROPgadget等工具被显式排除。这些是Operator直接调用的环境工具,它们是D型——存在于执行环境中但不在此处索引。C型用于你传输或引用的文件,不是你运行的二进制文件。

LibrarianGateway

网关(librarian_gateway.py)是三种类型的统一接口。它的职责是路由查询并应用自动回退和增强逻辑。

A型查询:
  FTS5搜索binaries + tricks
  ├─ 命中  → 返回payload
  └─ 未命中 → 回退:运行B型语义搜索,标记为理论(无现成payload)

B型查询:
  ChromaDB语义搜索
  ├─ 命中  → 提取关键词(>4字符的词,取前3个)→ 运行A型查找
  │          返回理论 + 具体示例
  └─ 未命中 → 无结果

C型查询:
  JSON子字符串/标签过滤(名称、标签、类别)
  → 返回最多5个匹配项及绝对路径

A型→B型回退在实践中是最有用的路径。当Operator向Librarian请求一个SQLite数据库中不存在的精确命令时,网关不会直接返回空——它会说”没有找到payload,但这是方法论”,给Operator足够的理论来从头重建解题思路。

B型→A型增强则反向运作。语义搜索返回方法论结果后,网关从返回文本中提取关键词并运行FTS5查找,找到说明该理论的具体命令。这避免了智能体理解概念但需要猜测语法的情况。

关键词提取方法比较粗糙:取长于四个字符的词,选前三个,运行FTS5。这种方法足够有用,但会漏掉短但领域专用的术语,比如”ROP”、“XSS”、“SQL”,或像nc这样的二进制名称。这是系统中最需要改进的部分。

构建流水线

从头构建:

# A型:解析GTFOBins YAML + LOLBAS JSON + HackTricks MD + PayloadsAllTheThings MD
python3 TypeA/build_db.py

# B型:爬取配置的站点,使用BGE-M3嵌入,存入ChromaDB
python3 TypeB/crawl_type_b.py

# C型:遍历SecLists / PayloadsAllTheThings / PEASS-ng,生成JSON索引
python3 TypeC/build_asset_index.py

A型构建只需几秒。B型是最慢的:BGE-M3需要对每个块运行推理,以0.5秒礼貌延迟爬取500页的站点需要一段时间。原始HTML缓存意味着一旦爬取完成,从缓存重建向量索引比重新爬取快得多。

一个值得注意的依赖约束:Python 3.14因Pydantic兼容性问题会导致ChromaDB 1.5.1崩溃。该项目需要Python 3.10–3.13。

在BearcatCTF的表现

最明显的信号是类别积累效应。当Operator到达第八道密码学题时,Librarian已经积累了足够的上下文——来自之前的解题记录和其自身的数据源——其简报的质量比第一道题时明显更好。取证题呈现了相同的规律:binwalk和foremost出现在早期的Librarian响应中,到第三道题时,Operator已经从正确的工具入手,而不是在尝试过程中才发现它们。

C型在Web题中表现有效。当Operator需要上传反弹shell或fuzz目录时,Librarian返回的是绝对路径而不是找文件的指令。在有时限的场景下,这种减少摩擦的效果虽小但真实。

架构的薄弱点在pwn。B型对堆利用方法论的覆盖还算合理——0xdf的writeup覆盖得不错——但A型对pwntools具体调用的覆盖很薄。大多数GTFOBins条目是权限提升的,不是二进制利用的。Operator不得不从文档中重建pwntools样板代码,而不是从索引源检索。

我们会做哪些改变

改善B型对pwn的覆盖。 目前的数据源是0xdf博客和pwntools文档。CTF-wiki已克隆到本地但尚未摄入。加入它,以及针对知名pwn writeup存档的专项爬取,将改善理论到payload转化最重要的题目类别的覆盖。

修复关键词提取。 当前的启发式方法(>4字符的词,取前3个)是一个从未被替换的占位符。一个最小改进是在回退到长度启发式之前提取已知CTF关键词——CVE编号、二进制名称、技术名称。

添加D型集成提示。 当Librarian返回暗示特定工具调用(ROPgadget、pwntools、gdb-peda)的方法论结果时,它应该注明该工具并建议调用模式,即使不在索引中。目前B型理论结果和执行环境中的D型工具之间没有任何关联。

B型的缓存失效。 原始HTML缓存没有过期机制。0xdf的博客会发布新writeup;pwntools文档随新版本更新。目前的方法需要手动删除缓存文件来获取变更。TTL或内容哈希检查可以解决这个问题。

当前形态的引擎是功能性的,在BearcatCTF上取得了净正效果。它显然也是第一个版本。架构是正确的——在即时payload、方法论和本地资产之间的三路分割,与人类CTF选手实际使用不同参考资料的方式完全吻合。粗糙的地方在于每层的数据填充和检索质量,而不在于设计本身。

← 返回博客 Orange & Qiushi Wu