From 9e602f50d6a7ef1278c453841d5c517616a19440 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Thu, 23 Dec 2021 14:27:33 -0600 Subject: [PATCH] Initial mitigation for folks updating Dynmap but not patching MC for some reason --- .../src/main/java/org/dynmap/DynmapCore.java | 52 ++++++++++++++++++- .../dynmap/JsonFileClientUpdateComponent.java | 8 +-- .../dynmap/servlet/SendMessageServlet.java | 9 ++-- 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/DynmapCore/src/main/java/org/dynmap/DynmapCore.java b/DynmapCore/src/main/java/org/dynmap/DynmapCore.java index 17b7681b..efbeed47 100644 --- a/DynmapCore/src/main/java/org/dynmap/DynmapCore.java +++ b/DynmapCore/src/main/java/org/dynmap/DynmapCore.java @@ -141,6 +141,7 @@ public class DynmapCore implements DynmapCommonAPI { private boolean loginRequired; + private String hackAttemptSub = "(IaM5uchA1337Haxr-Ban Me!)"; // WEBP support private String cwebpPath; private String dwebpPath; @@ -599,11 +600,13 @@ public class DynmapCore implements DynmapCommonAPI { updateConfigHashcode(); /* Initialize/update config hashcode */ loginRequired = configuration.getBoolean("login-required", false); + hackAttemptSub = configuration.getString("hackAttemptBlurb", "(IaM5uchA1337Haxr-Ban Me!)"); // If not disabled, load and initialize the internal web server if (!isInternalWebServerDisabled) { loadWebserver(); } + enabledTriggers.clear(); List triggers = configuration.getStrings("render-triggers", new ArrayList()); @@ -2297,13 +2300,60 @@ public class DynmapCore implements DynmapCommonAPI { public String getDefImageFormat() { return def_image_format; } + public String scanAndReplaceLog4JMacro(String msg) { + int nestcnt = 0; + int off = 0; + int firsthit = -1; + boolean done = false; + String orig = msg; + while (!done) { + int idx = msg.indexOf("${", off); // Look for next ${ + if (idx >= 0) { // Hit + if (nestcnt == 0) firsthit = idx; // Record start of hit + nestcnt++; + off = idx + 2; + } + else { + idx = msg.indexOf("}", off); // Next } + if (idx >= 0) { + if (nestcnt > 0) { + nestcnt--; + if ((nestcnt == 0) && (firsthit >= 0)) { // If back to zero, time to strip it + String newmsg = msg.substring(0, firsthit) + hackAttemptSub + msg.substring(idx+1); + msg = newmsg; // Switch to new version, and restart + off = 0; + firsthit = -1; // And restart scan + } + } + off = idx + 1; + } + else { // At end without a close + if (firsthit >= 0) { // Open strip? + String newmsg = msg.substring(0, firsthit) + hackAttemptSub; // Replace rest + msg = newmsg; // Switch to new version, and restart + } + done = true; + } + } + } + return msg; + } public void webChat(final String name, final String message) { if(mapManager == null) return; + // Check for folks trying to exploit Log4J + final String cleanname = scanAndReplaceLog4JMacro(name); + final String cleanmsg = scanAndReplaceLog4JMacro(message); + if (!cleanname.equals(name)) { + Log.severe("Possible hack attempt blocked: name contains Log4J macro - " + name.replaceAll("\\$", "_")); + } + if (!cleanmsg.equals(message)) { + Log.severe("Possible hack attempt blocked: message contains Log4J macro (from " + cleanname + ") - " + message.replaceAll("\\$", "_")); + } Runnable c = new Runnable() { @Override public void run() { - ChatEvent event = new ChatEvent("web", name, message); + ChatEvent event = new ChatEvent("web", cleanname, cleanmsg); events.trigger("webchat", event); } }; diff --git a/DynmapCore/src/main/java/org/dynmap/JsonFileClientUpdateComponent.java b/DynmapCore/src/main/java/org/dynmap/JsonFileClientUpdateComponent.java index 0ca7f1f9..79f9d37d 100644 --- a/DynmapCore/src/main/java/org/dynmap/JsonFileClientUpdateComponent.java +++ b/DynmapCore/src/main/java/org/dynmap/JsonFileClientUpdateComponent.java @@ -49,6 +49,8 @@ public class JsonFileClientUpdateComponent extends ClientUpdateComponent { private MapStorage storage; private File baseStandaloneDir; + private String safeString(String s) { return s.replaceAll("\\$", "_"); } + private static class FileToWrite { String filename; byte[] content; @@ -420,17 +422,17 @@ public class JsonFileClientUpdateComponent extends ClientUpdateComponent { isip = false; if(checkuserban) { if(core.getServer().isPlayerBanned(name)) { - Log.info("Ignore message from '" + ip + "' - banned player (" + name + ")"); + Log.info("Ignore message from '" + ip + "' - banned player (" + safeString(name) + ")"); ok = false; } } if(chat_perms && !core.getServer().checkPlayerPermission(name, "webchat")) { - Log.info("Rejected web chat from " + ip + ": not permitted (" + name + ")"); + Log.info("Rejected web chat from " + ip + ": not permitted (" + safeString(name) + ")"); ok = false; } } else if(requireplayerloginip) { - Log.info("Ignore message from '" + name + "' - no matching player login recorded"); + Log.info("Ignore message from '" + safeString(name) + "' - no matching player login recorded"); ok = false; } } diff --git a/DynmapCore/src/main/java/org/dynmap/servlet/SendMessageServlet.java b/DynmapCore/src/main/java/org/dynmap/servlet/SendMessageServlet.java index cd20acdc..ae74de97 100644 --- a/DynmapCore/src/main/java/org/dynmap/servlet/SendMessageServlet.java +++ b/DynmapCore/src/main/java/org/dynmap/servlet/SendMessageServlet.java @@ -52,6 +52,7 @@ public class SendMessageServlet extends HttpServlet { public DynmapCore core; public HashSet proxyaddress = new HashSet(); + private String safeString(String s) { return s.replaceAll("\\$", "_"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { byte[] bytes; @@ -65,7 +66,7 @@ public class SendMessageServlet extends HttpServlet { } else if(chat_requires_login && (!userID.equals(LoginServlet.USERID_GUEST)) && chat_perms && (!core.checkPermission(userID, "webchat"))) { - Log.info("Rejected web chat by " + userID + ": not permitted"); + Log.info("Rejected web chat by " + safeString(userID) + ": not permitted"); error = "not-permitted"; } else { @@ -118,20 +119,20 @@ public class SendMessageServlet extends HttpServlet { String id = ids.get(0); if (check_user_ban) { if (core.getServer().isPlayerBanned(id)) { - Log.info("Ignore message from '" + message.name + "' - banned player (" + id + ")"); + Log.info("Ignore message from '" + safeString(message.name) + "' - banned player (" + id + ")"); error = "not-allowed"; ok = false; } } if (chat_perms && !core.getServer().checkPlayerPermission(id, "webchat")) { - Log.info("Rejected web chat from '" + message.name + "': not permitted (" + id + ")"); + Log.info("Rejected web chat from '" + safeString(message.name) + "': not permitted (" + id + ")"); error = "not-allowed"; ok = false; } message.name = id; isip = false; } else if (require_player_login_ip) { - Log.info("Ignore message from '" + message.name + "' - no matching player login recorded"); + Log.info("Ignore message from '" + safeString(message.name) + "' - no matching player login recorded"); error = "not-allowed"; ok = false; }