JAVA代码审计之XXE与SSRF
字数 995 2025-08-27 12:33:31
Java代码审计之XXE与SSRF漏洞详解
一、XXE漏洞
1. XXE漏洞简介
XXE(XML外部实体注入,XML External Entity)是应用程序在解析XML输入时,当允许引用外部实体时,攻击者可构造恶意内容导致的安全漏洞。利用XXE可以:
- 读取任意文件
- 探测内网端口
- 攻击内网网站
- 发起DoS拒绝服务攻击
- 执行系统命令
Java中的XXE支持sun.net.www.protocol里的所有协议:http, https, file, ftp, mailto, jar, netdoc。
2. XML基础概念
XML & DTD
- XML(可扩展标记语言):用于传输和存储数据的标记语言
- DTD(文档类型定义):定义XML文档的合法构建模块
实体(ENTITY)类型
- 字符实体:类似HTML实体编码,如
&a;(十进制)或&a;(十六进制) - 命名实体(内部实体):
<!ENTITY x "First Param!"> - 外部普通实体:
<!ENTITY outfile SYSTEM "outfile.xml"> - 外部参数实体:
<!ENTITY % param1 "Hello">
3. XXE审计函数
需要关注的XML解析接口:
javax.xml.parsers.DocumentBuilderFactory;
javax.xml.parsers.SAXParser
javax.xml.transform.TransformerFactory
javax.xml.validation.Validator
javax.xml.validation.SchemaFactory
javax.xml.transform.sax.SAXTransformerFactory
javax.xml.transform.sax.SAXSource
org.xml.sax.XMLReader
org.xml.sax.helpers.XMLReaderFactory
org.dom4j.io.SAXReader
org.jdom.input.SAXBuilder
org.jdom2.input.SAXBuilder
javax.xml.bind.Unmarshaller
javax.xml.xpath.XpathExpression
javax.xml.stream.XMLStreamReader
org.apache.commons.digester3.Digester
4. 常用测试POC
有回显时(利用file协议读取文件)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE lltest[
<!ENTITY xxe SYSTEM "file:///C:/Windows/win.ini">
]>
<user><username>&xxe;</username><password>123456</password></user>
无回显时(利用http协议探测)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note[
<!ENTITY % lltest SYSTEM "http:7777/lltest_xxe666">
%lltest;
]>
5. XXE漏洞代码示例
1) DOM解析方式
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(request.getInputStream());
2) DOM4J解析方式
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(request.getInputStream());
3) JDOM2解析方式
SAXBuilder builder = new SAXBuilder();
Document document = builder.build(request.getInputStream());
4) SAX解析方式
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxparser = factory.newSAXParser();
saxparser.parse(request.getInputStream(), handler);
6. XXE漏洞防御
以DOM - DocumentBuilderFactory为例的防御代码:
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); //禁用DTDs
// 如果不能禁用DTDs,使用以下两项
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
二、SSRF漏洞
1. SSRF漏洞简介
SSRF(Server-Side Request Forge, 服务端请求伪造)是攻击者让服务端发起指定请求的攻击方式,目标通常是从外网无法访问的内网系统。
Java中SSRF支持的协议:http, https, file, ftp, mailto, jar, netdoc。
2. SSRF审计函数
需要关注的发起HTTP请求的类及函数:
HttpURLConnection.getInputStream
URLConnection.getInputStream
Request.Get.execute
Request.Post.execute
URL.openStream
ImageIO.read
OkHttpClient.newCall.execute
HttpClients.execute
HttpClient.execute
3. SSRF漏洞代码示例
1) HttpURLConnection方式
String url = request.getParameter("url");
URL u = new URL(url);
URLConnection urlConnection = u.openConnection();
HttpURLConnection httpUrl = (HttpURLConnection)urlConnection;
BufferedReader in = new BufferedReader(new InputStreamReader(httpUrl.getInputStream()));
2) URLConnection方式
String url = request.getParameter("url");
URL u = new URL(url);
URLConnection urlConnection = u.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
3) ImageIO方式
String url = request.getParameter("url");
URL u = new URL(url);
BufferedImage img = ImageIO.read(u);
4) 其他方式
// Request方式
Request.Get(url).execute().returnContent().toString();
// openStream方式
URL u = new URL(url);
inputStream = u.openStream();
// OkHttpClient方式
OkHttpClient client = new OkHttpClient();
Request ok_http = new Request.Builder().url(url).build();
client.newCall(ok_http).execute();
// HttpClients方式
CloseableHttpClient client = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
HttpResponse httpResponse = client.execute(httpGet);
4. SSRF漏洞防御
防御措施:
- 限制协议为HTTP、HTTPS协议
- 禁止30x跳转
- 设置URL白名单或者限制内网IP
- 限制请求的端口为http常用端口
示例防御代码:
String url = request.getParameter("url");
if (!SSRFHostCheck(url)) {
System.out.println("warning!!! illegal url:" + url);
return;
}
URL u = new URL(url);
URLConnection urlConnection = u.openConnection();
HttpURLConnection httpUrl = (HttpURLConnection)urlConnection;
httpUrl.setInstanceFollowRedirects(false); //禁止30x跳转
public static Boolean SSRFHostCheck(String url) {
try {
URL u = new URL(url);
// 限制为http和https协议
if (!u.getProtocol().startsWith("http") && !u.getProtocol().startsWith("https")) {
return false;
}
// 获取域名或IP
String host = u.getHost().toLowerCase();
String hostwhitelist = "192.168.199.209"; //白名单
return host.equals(hostwhitelist);
} catch (Exception e) {
return false;
}
}