漏洞分析
原理:
服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。
大部分的web服务器架构中,web服务器自身都可以访问互联网和服务器所在的内网
作用:
对外网服务器所在的内网、本地进行端口扫描,获取一些服务的banner信息 。
攻击运行在内网或者本地的应用程序。
对内网web应用进行指纹识别,通过访问默认文件实现 。
攻击内外网的web应用。sql注入、struct2、redis等。
利用file协议读取本地文件等。
php ssrf中的伪协议:
file dict sftp ldap tftp gopher
Java ssrf 中的伪协议:
file ftp mailto http https jar netdoc
产生过程
java中ssrf会分比较多的场景
SSRF中内网探测
@WebServlet("/ssrfServlet")
public class ssrfServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String url = request.getParameter("url"); //接收url的传参
String htmlContent;
PrintWriter writer = response.getWriter(); //获取响应的打印流对象
URL u = new URL(url); //实例化url的对象
try {
URLConnection urlConnection = u.openConnection();//打开一个URL连接,并运行客户端访问资源。
HttpURLConnection httpUrl = (HttpURLConnection) urlConnection; //强转为HttpURLConnection
BufferedReader base = new BufferedReader(new InputStreamReader(httpUrl.getInputStream(), "UTF-8")); //获取url中的资源
StringBuffer html = new StringBuffer();
while ((htmlContent = base.readLine()) != null) {
html.append(htmlContent); //htmlContent添加到html里面
}
base.close();
writer.println(html);//响应中输出读取的资源
writer.flush();
} catch (Exception e) {
e.printStackTrace();
writer.println("请求失败");
writer.flush();
}
}
代码中HttpURLConnection httpUrl = (HttpURLConnection) urlConnection;
,这个地方进行了强制转换
URLConnection:可以走邮件、文件传输协议。
HttpURLConnection 只能走浏览器的HTTP协议
也就是说使用了强转为HttpURLConnection后,利用中只能使用http协议去探测该服务器内网的其他应用。
http://localhost:8080/ssrfServlet?url=http://www.baidu.com
在代码中,我们未对接收过来的url进行校验,校验其url是否是白名单的url就直接进行了创建url对象进行访问和读取资源,导致了ssrf的产生
尝试一下能不能读取文件
发现根本读取不了,因为这里只支持http和https的协议。
下面来试试,在不强制转换成HttpURLConnection的情况下试试
代码如下:
@WebServlet("/ssrfServlet")
public class ssrfServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String url = request.getParameter("url"); //接收url的传参
String htmlContent;
PrintWriter writer = response.getWriter(); //获取响应的打印流对象
URL u = new URL(url); //实例化url的对象
try {
URLConnection urlConnection = u.openConnection();//打开一个URL连接,并运行客户端访问资源。
// HttpURLConnection httpUrl = (HttpURLConnection) urlConnection; //强转为HttpURLConnection
BufferedReader base = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8")); //获取url中的资源
StringBuffer html = new StringBuffer();
while ((htmlContent = base.readLine()) != null) {
html.append(htmlContent); //htmlContent添加到html里面
}
base.close();
writer.println(html);//响应中输出读取的资源
writer.flush();
} catch (Exception e) {
e.printStackTrace();
writer.println("请求失败");
writer.flush();
}
http://localhost:8080/ssrfServlet?url=file:///c:%5c%5cwindows%5c%5cwin.ini
SSRF中的读取文件
码如下:
@WebServlet("/readfileServlet")
public class downloadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String url = request.getParameter("url");
int len;
OutputStream outputStream = response.getOutputStream();
URL file = new URL(url);
byte[] bytes = new byte[1024];
InputStream inputStream = file.openStream();
while ((len = inputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, len);
}
}
}
和上面的代码对比一下,大致相同,唯一不同的地方是一个是用openStream
方法获取对象,一个是用openConnection
获取对象。两个方法类似。
openConnection():返回一个实例,该实例表示与所引用的远程对象的连接。 返回类型: URLConnection
openStream():打开与此连接,并返回一个值以从该连接读取。 返回类型: InputStream
官方文档说明:
openConnection:返回一个URLConnection对象,它表示到URL所引用的远程对象的连接。每次调用此URL的协议处理程序的openConnection方法都打开一个新的连接。如果URL的协议(例如,HTTP或JAR)存在属于以下包或其子包之一的公共、专用URLConnection子类:java.lang、java.io、java.util、java.net,返回的连接将为该子类的类型。例如,对于HTTP,将返回HttpURLConnection,对于JAR,将返回JarURLConnection。(返回到该URL的URLConnection!)
openStream():打开到此URL的连接并返回一个用于从该连接读入的InputStream。
payload
http://127.0.0.1:8080//downloadServlet?url=file:///C:%5c%5c1.txt
SSRF中的文件下载
@WebServlet("/downloadServlet")
public class downloadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String filename = "1.txt";
String url = request.getParameter("url");
response.setHeader("content-disposition", "attachment;fileName=" + filename);
int len;
OutputStream outputStream = response.getOutputStream();
URL file = new URL(url);
byte[] bytes = new byte[1024];
InputStream inputStream = file.openStream();
while ((len = inputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, len);
}
}
}
payload:
http://localhost:8080/downloadServlet?url=file:///c:%5c%5c1.txt
这样就把文件给下载下来了,ssrf中的文件下载和文件读取不同点在于响应头
response.setHeader("content-disposition", "attachment;fileName=" + filename);
这段代码,设置mime类型为文件类型,访问浏览器的时候就会被下载下来
拓展
imageIO中的SSRF
imageIO类是jdk中自带的一个类,主要用于操作一些图片文件。比如读写、压缩图片。
ssrf 类:
public class ssrf {
public static BufferedImage read(URL url) throws IOException {
if (url == null) {
System.out.println("输入内容为空");
}
InputStream istream = url.openStream();
ImageInputStream stream = ImageIO.createImageInputStream(istream); //获取文件流
BufferedImage bi = ImageIO.read(stream); //返回 BufferedImage作为供给的解码结果
return bi;
}
}
servlet:
@WebServlet("/httpclientServlet")
public class httpclientServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String geturl = request.getParameter("url");
ServletOutputStream outputStream = response.getOutputStream();
ByteArrayOutputStream os = new ByteArrayOutputStream();
URL url = new URL(geturl);
BufferedImage image = ssrf.read(url);
ImageIO.write(image, "png", os);
InputStream input = new ByteArrayInputStream(os.toByteArray());
int len;
byte[] bytes = new byte[1024];
while ((len = input.read(bytes)) > 0) {
outputStream.write(bytes, 0, len);
}
}
}
先来测试一下传入一个图片的url试试
成功输出出来了。证明远程请求图片的功能是没问题的
再来读取一下本地文件试试
发现报错了 貌似读取不了除了图片以外的文件。
再来尝试一下读取本地的图片试试
小结
SSRF漏洞出现的场景有很多,如在线翻译、转码服务、图片收藏/下载、信息采集、邮件系统或者从远程服务器请求资源等。通常可以从一些http请求函数入手
除表6-1中列举的部分敏感函数外,还有很多需要关注的类,如HttpClient类、URL类等。根据实际场景的不同,这些类中的一些方法同样可能存在着SSRF漏洞。此外,还有一些封装后的类同样需要留意,如封装HttpClient后的Request类。审计
此漏洞时,首先应该确定被审计的源程序有哪些功能,通常情况下从其他服务器应用获取数据的功能出现的概率较大,确定好功能后再审计对应功能的源代码能使漏洞挖掘事半功倍。
参考文章
标签:Java,SSRF,url,request,URL,https,new,response From: https://www.cnblogs.com/gk0d/p/16867112.htmlhttps://xz.aliyun.com/t/2761#toc-1
https://xz.aliyun.com/t/206/
https://xz.aliyun.com/t/7186
https://www.cnblogs.com/nice0e3/p/13683023.html
https://docs.ioin.in/writeup/joychou.org/_index_php_web_javassrf_html/index.html
https://www.fatalerrors.org/a/ssrf-of-java-audit.html
https://xz.aliyun.com/t/7405
https://xz.aliyun.com/t/6993
https://opnsec.com/2018/07/into-the-borg-ssrf-inside-google-production-network/
https://blog.appsecco.com/finding-ssrf-via-html-injection-inside-a-pdf-file-on-aws-ec2-214cc5ec5d90
https://mp.weixin.qq.com/s?__biz=MzIzOTQ5NjUzOQ==&mid=2247483742&idx=1&sn=e7265d5351a6d9ed30d90be1c17be041
https://xz.aliyun.com/t/2761
https://xz.aliyun.com/t/5665