地图与窗口:智能体式扫描如何将一次文档泄露串联成凭据窃取
了解 Ostorlab 的 Agentic Deep Scan 如何将一个中危的 OpenAPI 信息泄露与一个 SSRF 漏洞串联起来,绕过回环地址限制并提取数据库凭据。
Ostorlab Agentic Deep Scan 在 /static/openapi.json 发现了一份可公开读取的 OpenAPI 规范,并将其评为中危:这只是文档,不含凭据。随后,它把这份文档当作一份清单来阅读,并沿着它一路找到了两个严重级别的发现、一组有效的数据库凭据,以及一个伪造的管理员会话。
什么是跨层攻击路径? 跨层攻击路径是指:在应用某一层暴露的某个产物——构建文件、静态资源、客户端代码包——提供了触达另一层(通常是后端)弱点所需的信息。该产物本身往往并不是漏洞;它的价值在于让对其他目标的测试不再需要靠猜。
执行摘要(TL;DR)
该规范中有两个端点至关重要。/internal/secret 返回应用的密钥,并拒绝除服务器自身以外的任何人,对直接请求一律回应 HTTP 403 Internal resource. Loopback only.。/upload_profile_picture_url 接受一个 URL,并在服务器端获取它。
让后者指向前者,满足了回环地址限制,而不是破解了它。响应中包含应用签名密钥、JWT 密钥和数据库凭据——随后,泄露的签名密钥被用来签发一个被应用接受的管理员令牌。
测试环境与方法
本文记录的是一次经过授权的模拟基准测试演练。 所有测试均由 Ostorlab Agentic Deep Scan 针对 vulnbank.org 执行,这是一个为安全研究和工具评估而发布并维护的故意存在漏洞的银行应用。整个过程不涉及任何生产系统、真实客户数据或第三方资产,下文展示的每一个凭据都是属于该沙箱的预置测试值。
为什么扫描器止步于规范文件
基于特征的工具会逐个请求地将其与已知恶意模式库进行比对。这种机制快速且确定,但它有三个结构性盲点,再多的规则也无法弥补。
| 局限 | 产生原因 | 在本例中遗漏了什么 |
|---|---|---|
| 请求之间没有状态 | 端点被孤立地、逐个请求地评估 | 同一文件中列出的两个端点可以组合成一次绕过 |
| 没有“用途”的概念 | 规则编码的是语法,而不是某个功能用来做什么与它实际做了什么之间的差别 | 从服务器的角度看,头像上传功能就是一个请求生成器 |
| 有效的控制措施会终结测试 | 一个正确的 403 被记录为否定结果,该端点随即被放弃 |
该控制措施点名了一个被授权方,而这个被授权方可能通过其他途径触达 |
上述每一种行为单独来看都是正确的。获取规范文件不会匹配任何特征。探测 /internal/secret 得到的是一个真实的 403。两项观察都没有错,也都不会产生发现。
智能体的不同做法
Ostorlab Agentic Deep Scan 执行一个持续的五步推理循环来发现和验证漏洞:
- 侦察——绘制攻击面:端点、参数、身份验证要求、暴露的产物。
- 假设——根据已观察到的信息推断可能出错的地方。
- 测试——发送能够证明或否定该假设的请求。
- 验证——确认真实影响,而不是仅凭一个看似可疑的响应。
- 串联——思考这个结果与已发现的任何内容结合后,是否会开辟一条尚未尝试过的路径。
第 2 步和第 5 步正是差异所在。 基于特征的扫描器具备第 3 步,以及较弱的第 4 步。它不会对某个功能的用途形成假设,也不会保留先前发现的模型以便与当前发现相结合。智能体则持续维护一份应用清单——它看到了什么、排除了什么、还有什么无法解释——并在每次出现新结果时查阅这份清单。下面的漏洞利用链完全产生于第 5 步。
实际过程:Agentic Deep Scan 发现的一条漏洞利用链
发现 #1(中危):OpenAPI 规范可公开访问
该应用从其公开的静态资源目录提供了一份完整的 OpenAPI 3.0 文档:
$ curl -s https://vulnbank.org/static/openapi.json | wc -l
1471
$ curl -I https://vulnbank.org/static/openapi.json
HTTP/2 200
content-type: application/json
无需身份验证,路径可预测,共 1,471 行,描述了 32 个端点及其 Schema 和每条路由的身份验证要求。/static/ 是前端存放样式表和图片的地方——而不是存放后端接口约定的地方。

图 1:评级为中危,单独来看这一评级是正确的。该发现自身的描述已经指出了后果:这份规范“揭示了 /sup3r_s3cr3t_admin 等敏感端点的存在,否则需要大量爬取才能发现它们”。
对其进行枚举后,1,471 行内容变成了一个结构化的攻击面——其中包括应用从未链接到的路由:
| 类别 | 端点 |
|---|---|
| 身份验证 | /login、/register、/api/v1/forgot-password |
| 交易 | /transfer、/transactions/{account_number}、/check_balance |
| 管理 | /sup3r_s3cr3t_admin、/admin/create_admin、/admin/delete_account/{user_id} |
| 内部 | /internal/config.json、/internal/secret、/latest/meta-data/* |
| 文件上传 | /upload_profile_picture、/upload_profile_picture_url |

图 2:规范被解析为一份清单。其价值不在于任何单个路径——而在于能同时掌握整个攻击面。
显而易见的假设,被正确地否定了。 /internal/secret 主动暴露了自己,于是智能体请求了它:
$ curl -s -i https://vulnbank.org/internal/secret
HTTP/2 403
{"error": "Internal resource. Loopback only."}
请求被拒绝,而且拒绝得正确。该端点会检查请求的来源,只为本机提供服务。对于独立测试各个端点的工具来说,这就是终点:假设被否定,控制措施可靠,继续下一个。
发现 #2(严重):通过头像上传实现的 SSRF
真正有价值的问题不是如何击败回环地址检查,而是谁能满足这一检查——以及能否让这一方按我们的意图行事。
回环地址意味着服务器本身。清单中归在“文件上传”下的一行,描述了一个能让服务器按需发出出站请求的功能:
$ curl -X POST https://vulnbank.org/upload_profile_picture_url \
-H "Authorization: Bearer <JWT>" \
-H "Content-Type: application/json" \
-d '{"image_url": "http://127.0.0.1:5000/internal/secret"}'
现在请求来自 127.0.0.1。回环地址检查通过了——是真正被满足,而不是被规避。

图 3:该发现在其代码块的第一行记录了自身的来源——# From OpenAPI spec analysis。
{{
"secrets": {{
"app_secret_key": "secret123",
"jwt_secret": "secret123",
"env_preview": {{
"DB_HOST": "db",
"DB_NAME": "vulnerable_bank",
"DB_USER": "postgres",
"DB_PASSWORD": "postgres"
}},
"DEEPSEEK_API_KEY": "sk-e2719..."
}}
}}

图 4:完整记录的整个过程——注册、跳转、外泄。载荷是原样捕获的,而不是经过概括的。
发现 #3(严重):通过未受保护的 /internal/secret 造成的敏感数据泄露
单个看似可疑的响应并不构成发现,因此该路径被重新执行,以确认这一行为可重复、这些值是真实的:
验证尝试 1——通过
/upload_profile_picture_url向http://127.0.0.1:5000/internal/secret发出的 SSRF 请求返回了完整的密钥载荷 验证尝试 2——凭据已确认:App Secret=secret123,JWT Secret=secret123,DB credentials=postgres/postgres
正是第二行把一个响应变成了一份影响说明,而且这次运行并没有止步于读取载荷。泄露的 jwt_secret 被用来签名一个声明为 {"user_id": 1, "username": "admin", "is_admin": true} 的令牌,然后将其提交给规范在第一步中就已暴露的管理路由:
$ curl -X GET https://vulnbank.org/sup3r_s3cr3t_admin \
-H "Authorization: Bearer <token forged with secret123>"
HTTP/2 200 # full admin panel
这个密钥不只是在传输中被看到而已。它被用来签发了一个应用认可的凭据,这正是一串泄露的字符串与一次身份验证绕过之间的区别。

图 5:描述中直截了当地说明了这种依赖关系——“Combined with the SSRF vulnerability”(与 SSRF 漏洞相结合)。

图 6:控制措施及其绕过方式尽在一图。403 是真实的。击败它的并不是针对该检查的攻击,而是来自同一份规范的另一个端点。
解读这条漏洞利用链
| 步骤 | 发现 | 严重程度 | 由什么提供 |
|---|---|---|---|
| 1 | OpenAPI 规范被公开提供 | 中危 | 部署失误 |
| 2 | 通过头像 URL 获取实现的 SSRF | 严重 | 规范中列出的端点 |
| 3 | /internal/secret 的内容被外泄 |
严重 | SSRF,指向同一份规范中列出的目标 |
| 4 | 利用泄露的 JWT 密钥伪造管理员会话 | #3 的影响 | 第 3 步获得的签名密钥,用于第 1 步发现的管理路由 |
其中大部分步骤都是机械性的。获取静态文件、解析 JSON、枚举路径、向每个路径发送请求并记录状态——这些都可以在不进行任何推理的情况下实现自动化。
非机械性的步骤位于第 1 行与第 2 行之间。第 1 行是一份清单。第 3 行是目标。第 2 行两者都不是:它是一种认识——从服务器的角度看,一个归在“文件上传”下的功能就是一个请求生成器,而请求生成器恰恰是满足回环地址限制所需要的。
规范中没有任何地方这样说。文档把 /upload_profile_picture_url 描述为图片上传,因为这就是它的用途。把它解读为跳板,意味着要同时把两个毫不相关的条目放在脑中,并思考其中一个能对另一个做什么。
严重程度究竟落在哪里
既然有两个严重级别的发现都可以追溯到这次规范泄露,人们很容易想把它的评级上调。那将是错误的判断,而智能体并没有这样做。
该规范中的大多数端点都没有问题。枚举它们一无所获,因为它们的控制措施是有效的。/internal/secret 本身对直接访问返回了正确的 403。规范并没有造成 SSRF,也没有削弱回环地址检查。
它改变的是成本。它把在未知 API 攻击面上的盲目搜索,变成了针对一张完整地图的定向演练,并让两个端点之间的关系一目了然。
这种区别决定了修复方向。移除规范后,SSRF 依然有效,回环地址绕过依然有效,/internal/secret 依然会把数据库凭据交给任何能从内部访问它的对象。这份文档不应该公开——但修复它解决的是发现问题,而不是暴露问题。
常见问题解答
为什么暴露的 OpenAPI 规范是一种安全风险? 它很少包含凭据,这就是为什么它单独来看很少被评为中危以上。它的风险在于公开了完整的端点清单——包括界面从未链接到的路由——以及参数 Schema 和每个端点的身份验证要求。这把在未知 API 攻击面上的盲目搜索变成了针对一张已知地图的定向演练,并让端点之间那些原本需要大量爬取才能发现的关系变得清晰可见。
SSRF 如何绕过仅限回环地址的限制? 它并没有绕过,而是满足了这一限制。仅限回环地址的端点会检查请求的来源,只为服务器自身提供服务。服务器端请求伪造(SSRF)漏洞允许攻击者提供一个 URL,然后由服务器去获取,因此由此产生的请求确实来自 127.0.0.1。控制措施的判断是正确的,并放行了该请求——这就是为什么当同一应用中有任何端点会获取用户提供的 URL 时,单靠回环地址限制是不够的。
当一个低严重程度的发现导致了一个严重级别的发现时,是否应上调其评级? 通常不应该。在本例中,规范泄露并没有造成 SSRF,也没有削弱回环地址检查;这两个漏洞是各自独立存在的。它改变的是发现成本。移除它并不能关闭任何一个严重级别的发现,而上调其评级往往会把修复工作误导到信息泄露上,而不是可被利用的缺陷上。
智能体式扫描与基于特征的扫描有何区别? 基于特征的扫描器孤立地评估各个端点,不保留任何将一个结果与下一个结果联系起来的记忆。获取规范不会产生匹配;探测 /internal/secret 得到一个正确的 403,并被准确地记录为否定结果。两项观察都没有错。这个发现只存在于同一文件中列出的两个端点之间的关系里,而这需要在多个请求之间维护一个攻击面模型。
这次测试是否针对生产系统进行? 不是。所有测试均针对 vulnbank.org 进行,这是一个为安全研究和工具基准测试而发布的故意存在漏洞的应用。文中展示的凭据都是该沙箱中的预置测试值。
总结
扫描器会继续发现那些在网络流量中看起来有问题的东西,它们也理应如此——一份被公开提供的 API 规范,正是规则可以低成本捕获的那类错误配置。规则做不到的,是把这份规范当作一张平面图来阅读,并注意到在不同章节中、为不同目的而描述的两个端点,可以组合成一条绕过某个控制措施的路线,而这个控制措施单独来看完全有效。
这正是智能体式测试在实践中所代表的转变:不是发现更多的模式,而是在记忆中保留足够多的应用信息,从而看清其各个部分之间会如何相互作用。
使用 Ostorlab Agentic Deep Scan,在您自己的攻击面上运行同样的推理循环。