聚焦 Java SSRF 漏洞及其绕过方法:从PHP开始
字数 1786 2025-08-11 08:36:26
Java SSRF漏洞及其绕过方法详解
1. SSRF漏洞概述
服务器端请求伪造(Server-Side Request Forgery,简称SSRF)是一种攻击技术,攻击者通过构造恶意请求,欺骗服务器发起请求,从而导致服务器攻击自身或攻击其他受信任的服务器和应用程序。
2. PHP与Java的SSRF差异
PHP支持的协议
- 当PHP加载curl组件时,支持的协议更广泛
- 常用协议包括:http、https、ftp、ftps、file、gopher等
- PHP特有的伪协议
Java支持的协议
- 从
import sun.net.www.protocol可以看到支持的协议:- file
- ftp
- http
- https
- jar
- mailto
- netdoc
- JDK版本差异:
- JDK 1.7:存在gopher协议但实际不可用
- JDK 1.8:netdoc协议可用
- JDK 11:netdoc被弃用
3. Java发起网络请求的类
1. HttpClient
- 只能使用http协议
- 示例代码:
CloseableHttpClient client = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
HttpResponse httpResponse = client.execute(httpGet);
BufferedReader rd = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));
String line;
while ((line = rd.readLine()) != null) {
result.append(line);
}
result.toString()
2. Request类
- 封装了HttpClient类,只能使用http协议
- 示例:
Request.Get(url).execute().returnContent().toString();
3. HttpURLConnection类
- 继承URLConnection,只能发送http请求
- 示例:
String url = "https://www.baidu.com/";
URL url = new URL(url);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();
int responseCode = connection.getResponseCode();
if(responseCode == HttpURLConnection.HTTP_OK){
InputStream inputStream = connection.getInputStream();
String result = is2String(inputStream);
}
4. URLConnection类
- 支持jar、http、file、ftp、netdoc协议
- 示例:
URL u = new URL(url);
URLConnection urlConnection = u.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
String inputLine;
StringBuilder html = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
html.append(inputLine);
}
in.close();
return html.toString();
5. URL类
- 支持所有jar、http、file、ftp、jar、netdoc协议
- 示例:
String urlContent = "";
final URL url = new URL(url);
final BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
String inputLine = "";
while ((inputLine = in.readLine()) != null) {
urlContent = urlContent + inputLine + "\n";
}
in.close();
System.out.println(urlContent);
6. okhttp类
- 只能使用http协议
- 示例:
OkHttpClient client = new OkHttpClient();
com.squareup.okhttp.Request ok_http = new com.squareup.okhttp.Request.Builder().url(url).build();
return client.newCall(ok_http).execute().body().string();
7. ImageIO类
- 通过URL类发起请求
- 示例:
String url = request.getParameter("url");
URL u = new URL(url);
BufferedImage img = ImageIO.read(u); // 发起请求,触发漏洞
4. 关键函数
openStream(): 打开与此连接,并返回一个输入流getInputStream(): 发送连接获取资源openConnection(): 打开与此连接HttpGet executeURLConnectionImageIO.read
5. SSRF绕过方法
协议绕过
-
低版本中的file协议利用:
- file协议=netdoc协议(高版本中已经淘汰netdoc协议,1.8还可以用)
-
高版本中的file协议利用:
- 加载url:
url:file:///c:/windows/win.ini(用的url类、urlconnection类) url:http://127.0.0.1(url:为大小写都可以)URL:http://127.0.0.1
- 加载url:
-
绕过正则匹配的协议限制:
- url发送请求使用
%0a、%0d、%20 - ASCII小于空格的同样可以正常发送请求
- url发送请求使用
地址绕过
-
域名绕过:
- 利用函数进行域名后缀过滤的逻辑缺陷漏洞
- 示例:
String url="http://bypasstest.com"; URI uri = new URI(url); String host=uri.getHost().toLowerCase(); boolean b = host.endsWith("test.com"); // 只要以test.com结尾即可绕过 -
本地地址绕过:
# 等价于127.0.0.1 http://127.0.0.1:80 http://127.1:80 http://0.0.0.0:80 http://localhost:80 http://0.0.0.0 http://127.127.127.127 # ipv6地址 http://[::]:80/ http://[0000::1]:80/ http://[::1]:80/ http://[0:0:0:0:0:ffff:127.0.0.1] # 解析为127.0.0.1的域名 http://localtest.me = http://127.0.0.1 http://a.b.c.localtest.me = http://127.0.0.1 # 通配符dns http://customer1.app.localhost.my.company.127.0.0.1.nip.io = http://127.0.0.1 http://127.0.0.1.nip.io = http://127.0.0.1 # 10进制 http://2130706433/ = http://127.0.0.1 http://3232235521/ = http://192.168.0.1 http://3232235777/ = http://192.168.1.1 # 8进制 http://0177.0000.0000.0001 = http://192.168.1.1 # 16进制 http://0x7F000001 = http://127.0.0.1 # 利用@符号进行url跳转 https://www.baidu.com@www.jd.com = https://www.jd.com
路径拓展绕过
-
场景1:代码限制只能访问某路径或后缀结尾
https://domain/vulerable/path#/expected/path https://domain/vulerable/path#.extension -
场景2:代码限制只能访问限定的路径
https://domain/expected/path/../..//vulnerable/path
重定向绕过
- Java默认跟随重定向,且重定向必须和url协议一致,不一致返回空页面
- 手动处理重定向示例:
@RequestMapping(value = "/ssrf/vuln4", method = {RequestMethod.POST, RequestMethod.GET}) @ResponseBody public Integer redirect(String url) throws IOException { HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); connection.setInstanceFollowRedirects(false); connection.setUseCaches(false); connection.setRequestMethod("GET"); connection.connect(); int responseCode = connection.getResponseCode(); System.out.println(responseCode); return responseCode; }
DNS Rebinding绕过
-
Java默认TTL为10,PHP默认TTL为0
-
修改TTL的方法:
- JVM添加启动参数
-Dsun.net.inetaddr.ttl=0 - 通过代码修改:
java.security.Security.setProperty("networkaddress.cache.negative.ttl" , "0"); - 修改
java.security里的networkaddress.cache.negative.ttl变量为0
- JVM添加启动参数
-
实现步骤:
- 域名解析设置解析到指定服务器
- 在VPS上开启DNS服务器(开启UDP 53端口)
- 使用twisted实现DNS服务,设置TTL为0
6. 总结要点
- Java默认跟随重定向,且重定向必须和url协议一致,不一致返回空页面
- Java默认TTL为10,php默认TTL为0
- 是否受DNS Rebinding影响取决于缓存
- url类和urlConnection类使用
url:协议可绕过检测黑名单协议 - 如果发起网络请求的类是带HTTP开头,那只支持HTTP、HTTPS协议
- java主要利用http、netdoc、file、jar协议
- php可以用的协议超过了java协议,其中包括gopher、伪协议等