PentestGPT HTB Enigma 资格认证实录:LLM 自主渗透测试循环的现场证据、失败归因与 Trace 驱动修复
【免费下载链接】PentestGPTAutomated Penetration Testing Agentic Framework Powered by Large Language Models项目地址: https://gitcode.com/GitHub_Trending/pe/PentestGPT
本文基于 PentestGPT 仓库内的一次真实资格认证(qualification)记录,完整还原其自主渗透测试循环在 HTB Enigma 靶机上的六轮现场运行结果、关键控制条件与失败归因,并结合pentestgpt_agent包源码(任务编译器、执行校验器、循环调度器)解释每一条"trace 驱动修复"背后的确定性实现,帮助读者理解"LLM 负责推理、确定性代码负责权威状态"这一设计在现场高强度运行中暴露的优势与边界。
认证结论:管线可用,但未达成取证
该记录的最终判定是Not qualified(未通过)。管线展示出了持久的、可审计的多步枚举能力,但两个受测模型都未能获得经验证的用户级(user-level)与 root 级证明(proof)。运行结束后没有任何候选值提交到 Hack The Box,报告中也不复现任何 proof 值,Enigma 实例在产物收集后被成功关停。
报告特别澄清了两点边界:
- 这是管线(pipeline)层面的结果,不是"靶机不可解"的声明;
- 最后一轮 Codex 运行把大部分预算花在了重复发现与枚举上,而没有收敛到一条具体的利用链(exploitation chain)。
该记录同时标注了后续部署决策的变更:两个角色(Supervisor / Executor)都改用全量 provider 工具与FULL_ACCESS,选定的安全边界是隔离运行时本身,而不是 PentestGPT 工具中介层;当前架构以 docs/architecture.md 为准。
认证范围与控制条件
这一节定义了整场认证的可信前提,也是复现任何一次现场认证时必须照抄的控制项:
- 目标:明确授权的 HTB Enigma 机器,仅通过远程攻击箱(remote attack box)和 HTB VPN 访问;
- Provider 组合:先是
claude-opus-4-8/xhigh,随后是gpt-5.5/xhigh; - 硬超时:每次现场调用 3,600 秒;
- 现场包身份:PentestGPT wheel 的 SHA-256 为
adde0569ac9c7073b4c46181c143120c4a245243090325b3df3c62244ed59ab0,UnifiedAgent wheel 的 SHA-256 为9f5f58980e88109cde567bf7d6d2aafdbe75a4b7f31fcfb782b4317294cb7371——通过固定包哈希保证现场运行的代码可复述; - 同一台活动机器分配贯穿所有诊断性重跑;没有任何一轮使用 HTB 的 submit 端点;
- 每个角色的完整输入、归一化事件、动作回执、输出、用量与状态数据库都在关停前拷贝到本地。这些数据包含敏感目标信息,未经脱敏不得发布。
这些控制与pentestgpt_agent包的设计语言一一对应:角色、任务、回执、观察(observation)、转移(transition)等术语在 pentestgpt_agent/CONTEXT.md 中有完整定义,例如 "Evidence" 被严格限定为"由一个合格动作回执捕获的精确目标输出",而 provider/工具传输错误被明确排除在证据之外。
六轮现场运行结果
| 运行 | 任务数 | 尝试数 | 剧集数 | 墙钟时间 | 报告成本 | 终止结果 |
|---|---|---|---|---|---|---|
| Claude Q5 | 6 | 6 | 12 | 646.8 s | $2.1776195 | Provider 网络安全安全拦截(safety block) |
| Codex Q4 | 3 | 4 | 8 | 573.4 s | 报告 $0 | 先前的精确证据尚不可复用 |
| Codex Q5 | 5 | 6 | 12 | 700.5 s | 报告 $0 | 非零退出码的命令回执被拒绝作为证据 |
| Codex Q6 | 13 | 13 | 26 | 2,680.0 s | 报告 $0 | 过大的精确证据引文被拒绝 |
| Codex Q7 | 8 | 11 | 23 | 1,720.5 s | 报告 $0 | 不受支持的长证据重写被拒绝 |
| Codex Q8 | 12 | 15 | 30 | 2,533.8 s | 报告 $0 | 同任务证据复述(paraphrase)中止了运行 |
两个必须澄清的读数:
$0 reported不能解读为免费执行。Codex 后端没有返回美元成本核算,这只是"未报告"。成本字段来自每轮output.json的cost_usd求和——在 pentestgpt_agent/src/pentestgpt_agent/trial.py 的_trial_summary()中,cost_usd是对全部剧集输出逐项累加得到的,缺省记 0。- Codex Q5 出现过一个未标注的 32 字符十六进制串。管线未能将其确认为任一所需 proof(缺少权限上下文),因此没有提交。其余所有列出的运行产生的"规范候选(canonical candidates)"数量为零。
Q4–Q8 的命名本身就是迭代的痕迹:每一轮 Codex 重跑都针对前一轮暴露的一个确定性校验失败做修复后再打。终止结果列里的四句话(不可复用、非零回执、过大引文、长证据重写、复述中止)对应下一节逐条展开的 trace 驱动修复。
有效部分:最强路径与记忆内核
现场循环在多轮运行中持续维持了:目标范围锁定、全新的角色剧集(fresh episodes)、只追加(append-only)的 trace、持久租约(durable leases)以及规范回执来源(canonical receipt provenance)。其最强路径完整走完了六步:
- 发现暴露的服务集合;
- 枚举并挂载只读 NFS 导出(export);
- 提取出一份 onboarding 文档和一个 webmail 立足点(foothold);
- 完成 webmail 与邮件协议面的认证;
- 执行了一次有边界的认证后命令执行测试;
- 把一次失败的 SSH 认证尝试记录为有效的负向证据。
第 6 步值得单独强调:它正是 pentestgpt_agent/CONTEXT.md 中不变量"已完成命令的回执在非零退出状态时仍然合格,因为负向结果也是发现(findings)"的现场兑现。
**记忆内核(Memory Kernel)**在长运行中没有依赖任何 provider 会话记忆存活下来。Q8 一路跑到 30 个全新 agent 剧集、状态修订(revision)第 30 版,其间任务、尝试、trace、观察的身份保持一致,直到最后的校验失败为止。这与循环实现的语义一致:在 pentestgpt_agent/src/pentestgpt_agent/loop.py 的PentestLoop.run()中,每一轮迭代要么恢复已有租约、要么消耗一个 Supervisor 决策,且决策计数(plan_committed转移数)达到max_decisions时以decision_limit失败收尾——预算是持久化的,重启不会让它失效。
失败部分:控制器收敛,而非记忆容量
报告把决定性弱点定位在controller convergence(控制器收敛)上,并给出了可验证的判据:Q8 从未选中过任何EXPLOIT任务。在获得立足点并完成一次有边界测试之后,Supervisor 继续创建发现/枚举类工作、反复回访已经吃透的 NFS 与 HTTP 面,最终耗尽时间而没有维护住一条具体的利用假设。报告明确定性:这是过度分解(over-decomposition),不是记忆容量失败。
这一点可以用源码里的任务类型系统佐证。TaskKind在 pentestgpt_agent/src/pentestgpt_agent/plan.py 中定义了六种:DISCOVER、ENUMERATE、TEST、EXPLOIT、VERIFY、RECOVER。compile_plan()对EXPLOIT任务施加了硬约束——它必须复制精确的同一 target、引用该 target 上最新的已完成 TEST 观察并依赖之(见 plan.py#L222-L243)。也就是说,EXPLOIT在架构上是"被 TEST 证据解锁的",而 Q8 的失败恰恰是 Supervisor 从未走到那一步:不是编译器拦住了利用,而是决策层没有推进到利用。
报告还列出三条次要限制:
- Provider 的
max_turns不约束原生命令/工具动作。有一个早期剧集在 Executor 任务工作预算只有 6 轮的情况下打出了 28 条命令回执。当前设计把这视为遥测(telemetry);如果"可强制的动作上限"成为产品需求,它必须由隔离运行时或 provider 接缝(seam)提供,而不能从轮次计数推断。对照 pentestgpt_agent/src/pentestgpt_agent/agents.py#L209-L216 中的_EXECUTOR_TURN_BUDGETS(DISCOVER 5 / ENUMERATE 6 / TEST 5 / EXPLOIT 9 / VERIFY 4 / RECOVER 6),以及Executor.execute()中provider_turn_budget = min(self.max_turns, 预算 + 1)的钳制逻辑:这套预算约束的是provider 交互轮次,外部命令副作用天然在其外。pentestgpt_agent/README.md 的"Current limits"一节也重复了同一结论:max_turns不是可移植的命令/动作限制,在 Codex 上尤其如此。 xhigh推理档在动作之间常花一到两分钟。更好的提示词无法弥补反复出现低信息量任务的问题。- Claude 运行在拿到 webmail 立足点后,被 provider 的实时网络安全保护(real-time cybersecurity safeguard)拦下了下一步命令执行。该报告指出,这是外部策略,管线内部无法绕过;要获得有效的 Claude 资格认证,需要正确的 provider 接入路径。
Trace 驱动的修复:每条规则都有确定性的落点
报告的方法论是:每个确定性失败都先降级为"保存 trace 的复放",再改代码。五条修复及其在源码中的对应实现如下:
| 现场失败(运行) | 修复规则 | 源码落点 |
|---|---|---|
| Q4:先前精确证据不可复用 | 精确的先前观察只能被同一任务复用 | _matching_prior_observation()按observation.task_id != task_id过滤,只接受同任务、按修订倒序的最新观察 |
| Q5:非零退出回执被拒为证据 | 已完成命令的非零退出状态可充当负向证据 | 不变量层(CONTEXT.md)+is_grounding_receipt不再以退出码排除回执 |
| Q6:过大精确引文被拒 | 超大回执保留精确的4,000 字符后缀,且只能提交progress | compile_execution()中exact_slice > 4,000时取exact_slice[-4_000:]并标记truncated=True;截断回执的DONE结果被降级为PROGRESS |
| Q7:不受支持的长证据重写被拒 | 富文本引文回退到单个有界精确回执,且只能提交progress | _last_bounded_grounding_receipt()取最后一个非空接地回执的 4,000 字符尾;evidence_fallback=True时DONE同样降级为PROGRESS |
| Q8:同任务复述中止运行 | 无动作的DONE提案若复述本任务先前的规范证据,则丢弃复述,只提交任务本地progress,转移中带evidence_unresolved=true;不产生观察,也不能复用其他任务的证据 | evidence_unresolved的判定条件与结果降级集中在 execution.py#L334-L354 |
上表中的判定逻辑都集中在 pentestgpt_agent/src/pentestgpt_agent/execution.py 的compile_execution()中。值得注意其匹配次序:先在当前 trace 的接地回执中做精确子串匹配(CRLF/LF 归一化是唯一体文本传输归一化),再做"有序精确行跨度扩展"与"唯一行包络"等受控放宽,最后才允许证据回退——整个链条保证了任何被拒绝的引文都会落到一条可解释、可复放的路径上,而不是"随机失败"。
最后一条修复的验证方式在报告中单独强调:它是在现场运行结束后、对真实的 Q8 终端 trace 做复放验证的。复放现在产生progress、无观察、无回执序号、evidence_unresolved=true。由于没有再发起新的现场运行,这条修复的状态是replay-qualified(复放合格)而非 HTB-live qualified(现场合格)——这种"合格等级"的区分本身是这套认证方法的严谨之处。
验证门槛:合入前必须通过的检查
报告记录的最终本地源码状态通过了以下验证:
121 passed, 1 skipped(pytest 套件);- Ruff lint 与格式化检查;
- 对
src的严格 mypy; - 锁文件(lockfile)校验;
- 源码分发与 wheel 构建;
- 前述 Q8 终端 trace 复放。
对应的可复现命令来自 pentestgpt_agent/README.md(须在pentestgpt_agent/目录下执行,避免仓库根目录的兼容副本遮蔽依赖):
uv sync --extra claude # 或 codex / all uv run python -m pytest -q uv run ruff check src tests uv run ruff format --check src tests uv run mypy src uv lock --check uv build其中"121 passed"指该包的测试套件——测试文件覆盖 pentestgpt_agent/tests/test_execution.py(执行校验与证据回退)、pentestgpt_agent/tests/test_plan.py(计划编译)、pentestgpt_agent/tests/test_loop.py(循环恢复与重试)等模块,与上述每条修复规则形成"现场失败 → trace 复放 → 回归测试"的闭环。
产物目录:如何审计一次认证
六个资格认证根目录为:
runs/htb/htb-enigma-claude-opus48-xhigh-q5-20260712/runs/htb/htb-enigma-codex-gpt55-xhigh-q4-20260712/runs/htb/htb-enigma-codex-gpt55-xhigh-q5-20260712/runs/htb/htb-enigma-codex-gpt55-xhigh-q6-20260712/runs/htb/htb-enigma-codex-gpt55-xhigh-q7-20260712/runs/htb/htb-enigma-codex-gpt55-xhigh-q8-20260712/
这些runs/路径属于部署环境中的运行产物目录(--runs-root缺省即runs),不在本仓库内提交。审计顺序是固定的:
- 从每个根目录的
evaluation.json和pipeline.stderr.log入手; - 完整 agent 日志位于
agent-data/runs/<run-id>/traces/<episode-id>/,其中input.json是精确的角色输入(含 prompt/schema 哈希与 provider 策略)、events.jsonl是按时间排序的归一化事件/动作日志、output.json是终端 provider 结果与用量记录; state.sqlite3是规范记忆镜像——权威状态在 SQLite,trace 只是诊断输入。
input.json/events.jsonl/output.json的三件套结构由 pentestgpt_agent/src/pentestgpt_agent/trace.py 的EpisodeRunner/TraceStore写入,循环恢复逻辑(loop.py 的_supervisor_plan()与_drive_lease())在重启时会先检查traces.exists(...)再决定是否重调 provider——这保证了"已存在的终端 trace 在重新调用 provider 之前先被编译并提交"的恢复语义。需要再次提醒:trace 包含敏感目标输出与 provider 会话标识,文件是模式受限(mode-restricted)而非加密或防篡改的。
下一步切片:先收敛,再打下一场
报告给出的"Required next slice"要求保持设计精简,并在下一次 HTB 运行之前先解决已被证明的控制器阻塞:
- 保留每个已完成分支的紧凑覆盖信息,让旧发现不会从 Supervisor 的投影中消失;
- 拒绝重复的发现/枚举工作,除非更新的规范证据打开了新面(surface);
- 围绕单一活跃利用假设简化 Supervisor 选择:一旦存在立足点,除非指名一个真正的新面,否则拒绝冗余的
DISCOVER/ENUMERATE提案,并要求下一个任务测试或利用价值最高、有证据支撑的假设。
这三条的验证路径也很具体:先在本地多阶段靶机上合格化这些改动,断言项包括任务数、动作数、向利用推进的进展、来源完整性和重启行为;之后才从干净的包构建重复远程资格认证。Provider 轮次计数应保持为性能遥测,不被当作可移植的命令预算。这一方向与 docs/architecture.md 的判断一致:"当前的控制器弱点是收敛,不是数据库容量",并明确下一次设计切片应在"再增加一个 agent 或 RAG"之前改进确定性的策略投影与重复/分支策略。
可复现的本地入口
对于想在授权环境下重演"运行 → 断言 → 恢复"全流程的读者,pentestgpt_agent包提供如下 CLI 入口(摘自 pentestgpt_agent/README.md,适用前提:隔离、一次性部署环境,仅含授权目标与所需 provider 凭证):
uv run pentestgpt-agent \ --goal "Capture the flag from this authorized target." \ --target http://127.0.0.1:8080 \ --backend claude \ --model claude-opus-4-8 \ --effort xhigh \ --run-id example与本次认证直接相关的参数语义(定义见 trial.py):
--effort:Claude 侧接受low/medium/high/xhigh/max,Codex 侧接受low/medium/high/xhigh/none/minimal;认证记录中的xhigh即此参数;--max-decisions(缺省 20,范围 1–1000):Supervisor 决策硬上限,对应循环中decision_limit收尾;--supervisor-max-turns(缺省 4)与--executor-max-turns(缺省 12,范围 2–100):provider 交互轮次预算,不是命令/动作上限;--resume:要求持久化的trial-config.json与新配置逐项精确匹配,包括目标、provider、模型、effort、prompt/schema 哈希、预算、两个角色的FULL_ACCESS策略、provider 环境与unified-agent版本——这正是"现场包身份必须可复述"要求落到 CLI 层的形式。
小结:一份"不合格"记录的工程价值
这份 2026-07-12 的资格认证记录的价值不在于"解出了 Enigma",而在于它把一次失败运行完整物化为可复述的证据链:固定的 wheel 哈希、逐轮的终止原因、五条与compile_execution/compile_plan一一对应的修复规则、以及"复放合格 / 现场合格"两级认证语义。它同时划清了当前架构的能力边界——FULL_ACCESS下的隔离运行时是安全边界、provider 轮次只是遥测、COMPLETED只证明结构性证据引用而非任意语义目标蕴含。对于评估 LLM 自主渗透测试管线的工程团队,这份记录给出了一个可直接套用的模板:先让确定性代码拥有状态与来源,再让模型在受约束的提案空间里推理,失败时把一切降级为保存 trace 的复放测试。
【免费下载链接】PentestGPTAutomated Penetration Testing Agentic Framework Powered by Large Language Models项目地址: https://gitcode.com/GitHub_Trending/pe/PentestGPT
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考