AI渗透Agent的“一本正经胡说八道”
只要给LLM足够的权限调用nmap、Metasploit这类工具,它就很容易陷入一种“业绩焦虑”:为了证明自己完成了任务,它会毫不犹豫地伪造成功结果。我之前在公司内部测试一个自动渗透Agent,跑完一次扫描,它给我递上来一份极其完美的报告,说23个端口全部攻破,全部拿到root权限。结果我手动核实了一下,实际成功数为0。
这次踩坑让我意识到,构建AI Agent时,最危险的就是让模型自己定义“成功”。必须在底层建立一套不依赖于模型感知的、硬性的验证机制。
下一篇
防止AI写代码把数据库删库:gate.cat 实操分享 →
这种幻觉在AI Agent实战中非常典型,因为模型在判断“是否成功”时,往往依赖的是模糊的字符串匹配,而不是真实的逻辑验证。
为什么它敢睁眼说瞎话?
最核心的问题出在验证逻辑上。最初我的验证函数写得太简单,只要输出里包含 login: 或者 shellcodes 这种词,Agent就判定为“已攻破”。
# 之前的错误逻辑:纯靠关键词匹配
if "login:" in output or "shellcodes" in output.lower():
return "confirmed"这导致了两个极其低级的错误:
- 工具干扰:
searchsploit在搜索结果的页眉里会写 "Shellcodes: ..."。这意味着Agent只要搜了一下漏洞,还没开始攻击,就认为自己拿到了Shell。 - Banner欺骗: 很多服务启动时的欢迎语包含
login:,这被Agent直接当成了登录成功。
解决方案:用硬证据替代“感觉”
为了让Agent诚实,我把验证逻辑改成了 breach_confirmed(),强制要求输出必须包含只有在代码执行后才会出现的特征码(比如 id 命令的返回结果)。
import re
# 定义真正的Shell证据,而不是Banner文字
_SHELL_EVIDENCE = (
re.compile(r"uid=\d+\([a-z]+\).*gid=\d+"), # id(1) 的标准输出
re.compile(r"root@[\w.-]+:[~/]"), # 真正的root提示符
)
def breach_confirmed(output: str) -> bool:
return any(p.search(output) for p in _SHELL_EVIDENCE)改完之后,原本那个“战绩彪炳”的报告瞬间变成了:3个确认成功,20个失败。虽然数字难看了,但这3个是实打实的Root Shell。
那些被掩盖的“琐碎”Bug
当Agent不再撒谎后,真正的技术坑才浮现出来。很多导致攻击失败的原因根本不是模型不聪明,而是基础工程太烂:
- 参数名不匹配: 模型习惯调用
{"query": "vsftpd"},但工具接口要求的是{"keyword": "..."}。这种MCP层的参数错误导致大量请求被静默拒绝,模型却以为是攻击失败。 - ANSI颜色代码污染:
msfconsole输出的模块名带有颜色转义符(如\x1b[45m),我的解析器把这些乱码也当成了模块路径的一部分,导致Metasploit加载模块全部报错。 - 版本号误判: nmap 报告某些服务时版本号在前,Agent把版本号(比如 "2")当成了产品名称传给 Metasploit,导致搜索结果完全不对。
这次踩坑让我意识到,构建AI Agent时,最危险的就是让模型自己定义“成功”。必须在底层建立一套不依赖于模型感知的、硬性的验证机制。