目录
本文通过CTFHub的SSRF URL bypass关卡的渗透实战,深入剖析SSRF的本质与成因,指出其核心在于利用服务器高权限访问内网资源的特性。重点讲解了@符号在URL中的特殊语法及其绕过原理,通过对比过滤程序与底层网络库的解析差异,揭示如何利用该特性欺骗安全检测。最后结合CTF实战案例,演示如何构造http://notfound.ctfhub.com@127.0.0.1/flag.php这样的Payload成功绕过限制,访问内网敏感文件。文章通过借刀杀人的生动比喻,完整呈现了SSRF攻击链中权限滥用和逻辑。
一、SSRF
1、本质
SSRF 的本质是:利用应用程序的功能,将原本用于访问外部资源的信任和权限,转化为攻击者访问内部受限网络的代理或跳板。 简单来说,就是 "借刀杀人"。
-
"刀":就是存在SSRF的服务器。它通常拥有较高的网络权限(可以访问内网、管理后台等),并且受信任(能绕过防火墙规则)。
-
"借":就是利用应用程序中一个"可以发起网络请求"的功能(如图片加载、URL预览、数据导入、Webhook测试等),通过篡改输入参数来控制请求的目标。
-
"杀人":就是让服务器这把"刀"去访问那些攻击者无法直接触及的敏感内部资源(如数据库、缓存服务、元数据接口、内部管理平台),从而造成信息泄露或进一步攻击。
2、成因
根本成因 :对用户输入信任过度,缺乏校验。应用程序在处理上述功能时,犯了一个关键错误:盲目信任用户输入的URL,没有对其进行严格的验证和过滤。
-
缺乏协议校验 :允许使用
http(s)
之外的危险协议,如file://
(读取本地文件)、dict://
(端口探测)、gopher://
(发送原始TCP数据包)。 -
缺乏目标校验 :没有检查请求的目标主机是否是内网IP(
127.0.0.1
,192.168.x.x
,10.x.x.x
,172.16.x.x
)或域名(localhost
)。 -
缺乏权限控制:没有考虑"当前用户是否有权让服务器去访问这个目标资源"。
二、@绕过原理
1、@语法
在 URL(Uniform Resource Locator,统一资源定位符)的语法体系中,@
符号是一个具有特殊功能的分隔符,核心作用是区分 "用户认证信息" 与 "资源主机地址" ,其规则设计初衷是为了简化 "带身份验证的资源访问"。在标准 URL 语法中,@
符号用于分隔 "用户信息" 和 "主机地址",格式为:
协议://[用户名[:密码]@]主机地址/路径
根据互联网标准 RFC 3986(Uniform Resource Identifier URI: Generic Syntax),一个通用 URI 的语法结构如下:
[scheme:] [//[user[:password]@]host[:port]] [/path] [?query] [#fragment]
组成部分 | 含义说明 | 是否可选 |
---|---|---|
scheme |
协议标识(如 http、https、ftp、ssh) | 必选 |
userinfo |
用户认证信息,格式为用户名[:密码] (密码可选),后接@ 符号 |
可选 |
@ |
分隔符,用于明确区分userinfo 与host (主机地址) |
仅userinfo 存在时必选 |
host |
资源所在的主机地址(域名 / IP,如www.example.com、127.0.0.1) | 必选 |
port |
端口号(如 http 默认 80、https 默认 443),用: 与host 分隔 |
可选 |
path |
资源在主机上的路径(如 /index.html、/api/data) | 可选 |
query |
查询参数(如?id=1&name=test),用? 与path 分隔 |
可选 |
fragment |
锚点(如 #section1),用于定位页面内的特定位置,用# 与query 分隔 |
可选 |
2、@绕过
当 URL 中包含@
符号时,所有 URL 解析器(浏览器、请求库如 curl/requests、服务器端解析组件)都会遵循统一规则:
- 分割 "用户信息" 与 "主机地址" :从左到右扫描 URL,找到第一个
@
符号后,将@
左侧的内容判定为userinfo
(即使左侧内容看起来像域名 / IP),@
右侧的内容判定为 "主机地址 + 后续路径"; - 忽略
@
左侧的无效认证信息 :若@
左侧的userinfo
格式不合法(如不是 "用户名:密码" 结构,或包含无效字符),解析器不会报错,仅会忽略该部分认证信息,直接使用@
右侧的主机地址发起请求; - 优先级高于其他分隔符 :
@
的分割优先级高于:
(端口分隔符)、/
(路径分隔符),即无论@
右侧是否包含端口或路径,都会先完整识别@
右侧的 "主机地址" 部分。
3、比喻
想象一下一家公司(vulnerable 服务器**)有一个在线下单系统,允许客户(** 用户**)提交一个地址(** URL**),公司会派快递员(**网络请求库)去该地址取货。
-
正常流程 :客户提交一个公共仓库的地址(
http://public-warehouse.com/box
),公司派车取回货物。一切正常。 -
SSRF攻击流程:
-
一个邪恶的客户(攻击者 )提交了一张订单,地址写的是:"公司总裁办公室:@ 公司机密档案室" (
http://ceo-office@internal-secret-archive/blueprint.pdf
)。 -
鲁莽的订单处理员(有SSRF的代码 )只看了冒号前的内容:"公司总裁办公室",心想:"哦,是总裁要的东西,没问题,批准!"。(这里进行了错误的过滤)
-
快递员(网络库 )拿到订单后,严格按照标准流程解读:"@"符号之后才是真实地址 。于是他开车进入了公司内部禁区"机密档案室" ,拿到了绝密蓝图,并把它交给了门口的邪恶客户。(攻击完成)
-
这个比喻完美体现了SSRF的本质:利用系统内部人员的信任和权限(快递员能进禁区),通过欺骗订单审核员(有SSRF的过滤逻辑),最终达到窃取内部资产的目的。
三、内网访问渗透实战
1、打开靶场
打开关卡如下所示,提示信息为"请求的URL中必须包含http://notfound.ctfhub.com,来尝试利用URL的一些特殊地方绕过这个限制吧",提示本关卡的URL要包含http://notfound.ctfhub.com。点击打开题目,此时系统自动创建Docker环境,下图蓝色部分的URL地址就是靶场环境。

复制靶场链接(challenge-f7599d398742d78e.sandbox.ctfhub.com:10800)并访问,如下所示被重定向到了?url=_中。

2、@法绕过
根据提示,在当前页面的 URL 必须以http://notfound.ctfhub.com开头。CTFHub靶场SSRF系列关卡的最终目标是通过SSRF来访问本机127.0.0.1
上的flag.php
文件(127.0.0.1/flag.php),即可拿到flag。也就是说,URL中需要包括两个内容才能渗透成功。
-
http://notfound.ctfhub.com:页面提示,必须包含。
-
127.0.0.1/flag.php
:这是攻击者推测存在于服务器本机Web根目录下的flag文件。
使用@法绕过构造URL,?url=http://notfound.ctfhub.com@127.0.0.1:80/flag.php,具体含义如下所示。
notfound.ctfhub.com:
被解析为 "用户信息" 部分(无实际意义),满足了包含指定网址的校验;@分隔符:
在 URL 中,@
用于分隔用户信息 和主机信息@
后的127.0.0.1:80:
实际被访问的目标主机和端口,最终请求会发送到本地的flag.php
。
完整的URL地址如下所示,其含义为利用URL中 @
符号的语法特性,构造一个"用户名@主机"的格式,欺骗上层过滤逻辑使其只看到"用户名"(一个无害的外网域名),而底层网络库则正确解析出"主机"(危险的内网IP),从而实现绕过黑名单过滤,访问到本地的敏感资源 flag.php
。
http://challenge-f7599d398742d78e.sandbox.ctfhub.com:10800/?url=http://notfound.ctfhub.com@127.0.0.1:80/flag.php
运行结果如下所示,在页面上直接显示flag,渗透成功。

其工作原理如下所示,根本原因在于过滤逻辑 和 底层网络库的解析逻辑 存在不一致。
(1)过滤程序看到的(错误的理解)
过滤程序(WAF或简单字符串匹配)可能会这样解析这个Payload:
-
它试图提取出主机名(hostname)。
-
它看到
http://
之后第一个/
之前的内容是notfound.ctfhub.com@127.0.0.1:80
。 -
它错误地将
@
符号之前的部分识别为主机名 ,即notfound.ctfhub.com
。 -
它检查
notfound.ctfhub.com
:-
这是一个外网域名。
-
它不在内网IP黑名单(
127.0.0.1
,192.168.x.x
等)里。
-
-
结论 :
notfound.ctfhub.com
是合法的,允许访问。
(2)底层网络库看到的(正确的解析)
当程序将Payload传递给底层网络请求库(如curl、urllib等)时,库会严格按照 URL 标准(RFC 3986)进行解析:
-
它识别出
http://
是协议。 -
它找到
@
符号,并知道@
前面的notfound.ctfhub.com
是用户信息(username)。 -
@
后面的127.0.0.1:80
才是真正的主机(host)和端口(port)。 -
/flag.php
是路径。 -
最终发起的真实请求是:
http://127.0.0.1:80/flag.php
。