From 53e9f25a1d26a9b46accff705f5109bf9565686b Mon Sep 17 00:00:00 2001 From: Ng Yat Yan Date: Sun, 13 Oct 2024 16:54:00 +0800 Subject: [PATCH] V1.1.0 RegEx Pattern Matching --- .gitignore | 3 +- conf/hash-replies.properties | 20 +++++++++++ conf/log4j2.xml | 16 +++++++-- conf/regex-mapping.properties | 1 + conf/replies.properties | 7 ---- conf/springboot.yml | 7 ++-- pom.xml | 2 +- .../com/example/sshd/config/SshConfig.java | 24 +++++++++++--- .../{config => core}/EchoShellFactory.java | 21 ++++++------ .../sshd/{config => core}/OnetimeCommand.java | 9 +++-- .../java/com/example/sshd/util/ReplyUtil.java | 33 ++++++++++++++----- 11 files changed, 103 insertions(+), 40 deletions(-) create mode 100644 conf/hash-replies.properties create mode 100644 conf/regex-mapping.properties delete mode 100644 conf/replies.properties rename src/main/java/com/example/sshd/{config => core}/EchoShellFactory.java (81%) rename src/main/java/com/example/sshd/{config => core}/OnetimeCommand.java (86%) diff --git a/.gitignore b/.gitignore index 8801278..da7a791 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target/ /.classpath /.project -/.settings \ No newline at end of file +/.settings/ +/logs/ diff --git a/conf/hash-replies.properties b/conf/hash-replies.properties new file mode 100644 index 0000000..7b07a35 --- /dev/null +++ b/conf/hash-replies.properties @@ -0,0 +1,20 @@ +prompt=root@mail.vidconnect.cyou$ +5BAABD7C4581F90088133C0E945302C2=\t0\t0\t0 - +uname=Linux +9DD46246144D353C914D7572AEDF8EA6=Linux mail.vidconnect.cyou 4.15.0-70-generic #79-Ubuntu SMP Tue Nov 12 10:36:10 UTC 2019 aarch64 aarch64 aarch64 GNU/Linux +whoami=root +4DE8A3B5E72E54D9D2A70BD43CB5EA9E=Model:\t\t0 +99EFF9F4022855955653078ECB2B4CE4=1.9G +39B28F3F76277A5E25665A3DBB12CC5B=3942 2493 512 9 936 1275 +B9B7DA613BBB90640EB8E5F657E41E6B=-rwxr-xr-x 1 root root 123K Jan 18 2018 /bin/ls +F806FB8A02384A22ECC110F111367FF7=crontabs/root/: fopen: Permission denied +C1B46A786F0DD463D37DF505EA57FCE4=Permission denied +36CAB7DA5AB930336E239DD8008DCBBD=Permission denied +w= 12:28:36 up 654 days, 1:37, 1 user, load average: 0.00, 0.00, 0.00\r\nUSER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT\r\nroot pts/0 58.176.72.32 10:46 1.00s 13.14s 0.00s w +top=Permission denied +43AEB525EBC517BAF8C01E7AC83F63CE=aarch64 +3AA823F5C5095BD60BE4AA37D603BBA4= 0 0 0 - +8B456C4EB5A75DA148EAD2E83B334E5E=Permission denied +FE5B6F84E749B84114423D5AD1A11034= +83809E84C1396C58D4156E8FF7782299=[ Uname ] Linux mail.vidconnect.cyou 4.15.0-70-generic #79-Ubuntu SMP Tue Nov 12 10:36:10 UTC 2019 aarch64 aarch64 aarch64 GNU/Linux\r\n[ Uptime ] 15:23:06 up 655 days, 4:32, 1 user, load average: 0.00, 0.00, 0.00\r\n +chpasswd=Password updated \ No newline at end of file diff --git a/conf/log4j2.xml b/conf/log4j2.xml index 8fb6fff..6b237ca 100644 --- a/conf/log4j2.xml +++ b/conf/log4j2.xml @@ -14,10 +14,22 @@ + + + + + + - - + + + + + \ No newline at end of file diff --git a/conf/regex-mapping.properties b/conf/regex-mapping.properties new file mode 100644 index 0000000..b78553f --- /dev/null +++ b/conf/regex-mapping.properties @@ -0,0 +1 @@ +.*chpasswd.*=chpasswd \ No newline at end of file diff --git a/conf/replies.properties b/conf/replies.properties deleted file mode 100644 index 015e2db..0000000 --- a/conf/replies.properties +++ /dev/null @@ -1,7 +0,0 @@ -prompt=root@localhost$ -5BAABD7C4581F90088133C0E945302C2=\t0\t0\t0 - -uname=Linux -9DD46246144D353C914D7572AEDF8EA6=Linux localhost 4.15.0-70-generic #79-Ubuntu SMP Tue Nov 12 10:36:10 UTC 2019 aarch64 aarch64 aarch64 GNU/Linux -whoami=root -4DE8A3B5E72E54D9D2A70BD43CB5EA9E=Model:\t\t0 -99EFF9F4022855955653078ECB2B4CE4=1.9G diff --git a/conf/springboot.yml b/conf/springboot.yml index 2f8d567..6fd22eb 100644 --- a/conf/springboot.yml +++ b/conf/springboot.yml @@ -2,5 +2,8 @@ ssh-server: port: 1022 private-key: location: "conf/private.key" - automatic-replies: - location: "conf/replies.properties" + hash-replies: + location: "conf/hash-replies.properties" + regex-mapping: + location: "conf/regex-mapping.properties" + diff --git a/pom.xml b/pom.xml index cface4a..3986876 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.example.sshd echo-sshd-server - 1.0.4 + 1.1.0 ECHO SSH SERVER Learning Apache Mina SSHD library diff --git a/src/main/java/com/example/sshd/config/SshConfig.java b/src/main/java/com/example/sshd/config/SshConfig.java index 1ae0db6..14ad355 100644 --- a/src/main/java/com/example/sshd/config/SshConfig.java +++ b/src/main/java/com/example/sshd/config/SshConfig.java @@ -21,6 +21,9 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; +import com.example.sshd.core.EchoShellFactory; +import com.example.sshd.core.OnetimeCommand; + @Configuration public class SshConfig { @@ -35,8 +38,11 @@ public class SshConfig { @Value("${ssh-server.root.username:root}") private String rootUsername; - @Value("${ssh-server.automatic-replies.location}") - private String repliesProperties; + @Value("${ssh-server.hash-replies.location}") + private String hashReplies; + + @Value("${ssh-server.regex-mapping.location}") + private String regexMapping; @Autowired ApplicationContext applicationContext; @@ -62,9 +68,19 @@ public class SshConfig { @Bean @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) - public Properties repliesProperties() throws IOException { + public Properties hashReplies() throws IOException { + Properties prop = new Properties(); + File configFile = new File(hashReplies); + FileInputStream stream = new FileInputStream(configFile); + prop.load(stream); + return prop; + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) + public Properties regexMapping() throws IOException { Properties prop = new Properties(); - File configFile = new File(repliesProperties); + File configFile = new File(regexMapping); FileInputStream stream = new FileInputStream(configFile); prop.load(stream); return prop; diff --git a/src/main/java/com/example/sshd/config/EchoShellFactory.java b/src/main/java/com/example/sshd/core/EchoShellFactory.java similarity index 81% rename from src/main/java/com/example/sshd/config/EchoShellFactory.java rename to src/main/java/com/example/sshd/core/EchoShellFactory.java index 2e5c09c..5e6eaf5 100644 --- a/src/main/java/com/example/sshd/config/EchoShellFactory.java +++ b/src/main/java/com/example/sshd/core/EchoShellFactory.java @@ -1,4 +1,4 @@ -package com.example.sshd.config; +package com.example.sshd.core; import java.io.BufferedReader; import java.io.IOException; @@ -26,17 +26,20 @@ public class EchoShellFactory implements Factory { private static final Logger logger = LoggerFactory.getLogger(EchoShellFactory.class); @Autowired - Properties repliesProperties; + Properties hashReplies; + + @Autowired + Properties regexMapping; @Autowired ApplicationContext applicationContext; @Override public Command create() { - return new EchoShell(repliesProperties); + return new EchoShell(); } - public static class EchoShell implements Command, Runnable { + public class EchoShell implements Command, Runnable { private InputStream in; private OutputStream out; @@ -44,11 +47,6 @@ public class EchoShellFactory implements Factory { private ExitCallback callback; private Environment environment; private Thread thread; - private Properties repliesProperties; - - public EchoShell(Properties repliesProperties) { - this.repliesProperties = repliesProperties; - } public InputStream getIn() { return in; @@ -100,7 +98,7 @@ public class EchoShellFactory implements Factory { @Override public void run() { - String prompt = repliesProperties.getProperty("prompt", "$ "); + String prompt = hashReplies.getProperty("prompt", "$ "); try { out.write(prompt.getBytes()); out.flush(); @@ -111,7 +109,8 @@ public class EchoShellFactory implements Factory { while (!Thread.currentThread().isInterrupted()) { int s = r.read(); if (s == 13 || s == 10) { - if (!ReplyUtil.replyToCommand(repliesProperties, command, out, prompt)) { + if (!ReplyUtil.replyToCommand(command, out, prompt, hashReplies, regexMapping)) { + out.flush(); return; } command = ""; diff --git a/src/main/java/com/example/sshd/config/OnetimeCommand.java b/src/main/java/com/example/sshd/core/OnetimeCommand.java similarity index 86% rename from src/main/java/com/example/sshd/config/OnetimeCommand.java rename to src/main/java/com/example/sshd/core/OnetimeCommand.java index 88b87b1..966a4a4 100644 --- a/src/main/java/com/example/sshd/config/OnetimeCommand.java +++ b/src/main/java/com/example/sshd/core/OnetimeCommand.java @@ -1,4 +1,4 @@ -package com.example.sshd.config; +package com.example.sshd.core; import java.io.IOException; import java.io.InputStream; @@ -20,7 +20,10 @@ import com.example.sshd.util.ReplyUtil; public class OnetimeCommand implements Command { @Autowired - Properties repliesProperties; + Properties hashReplies; + + @Autowired + Properties regexMapping; private InputStream in; private OutputStream out; @@ -72,7 +75,7 @@ public class OnetimeCommand implements Command { @Override public void start(Environment env) throws IOException { environment = env; - ReplyUtil.replyToCommand(repliesProperties, command, out, ""); + ReplyUtil.replyToCommand(command, out, "", hashReplies, regexMapping); out.flush(); callback.onExit(0); } diff --git a/src/main/java/com/example/sshd/util/ReplyUtil.java b/src/main/java/com/example/sshd/util/ReplyUtil.java index 7c952d7..238c14b 100644 --- a/src/main/java/com/example/sshd/util/ReplyUtil.java +++ b/src/main/java/com/example/sshd/util/ReplyUtil.java @@ -2,19 +2,22 @@ package com.example.sshd.util; import java.io.IOException; import java.io.OutputStream; +import java.util.Optional; import java.util.Properties; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ReplyUtil { private static final Logger logger = LoggerFactory.getLogger(ReplyUtil.class); + private static final Logger notFoundLogger = LoggerFactory.getLogger("not_found"); - public static boolean replyToCommand(Properties repliesProperties, String command, OutputStream out, String prompt) - throws IOException { + public static boolean replyToCommand(String command, OutputStream out, String prompt, Properties hashReplies, + Properties regexMapping) throws IOException { String cmdHash = DigestUtils.md5Hex(command.trim()).toUpperCase(); @@ -22,19 +25,31 @@ public class ReplyUtil { logger.info("[{}] Exiting command detected: {}", cmdHash, command.trim()); out.write(("\r\nExiting...\r\n").getBytes()); return false; - } else if (repliesProperties.containsKey(command.trim())) { + } else if (hashReplies.containsKey(command.trim())) { logger.info("[{}] Known command detected: {}", cmdHash, command.trim()); - String reply = repliesProperties.getProperty(command.trim()).replace("\\r", "\r").replace("\\n", "\n") + String reply = hashReplies.getProperty(command.trim()).replace("\\r", "\r").replace("\\n", "\n") .replace("\\t", "\t"); out.write(String.format("\r\n%s\r\n%s", reply, prompt).getBytes()); - } else if (repliesProperties.containsKey(cmdHash)) { + } else if (hashReplies.containsKey(cmdHash)) { logger.info("[{}] Known command-hash detected: {}", cmdHash, command.trim()); - String reply = repliesProperties.getProperty(cmdHash).replace("\\r", "\r").replace("\\n", "\n") - .replace("\\t", "\t"); + String reply = hashReplies.getProperty(cmdHash).replace("\\r", "\r").replace("\\n", "\n").replace("\\t", + "\t"); out.write(String.format("\r\n%s\r\n%s", reply, prompt).getBytes()); } else { - logger.info("[{}] Command not found: {}", cmdHash, command.trim()); - out.write(String.format("\r\nCommand '%s' not found. Try 'exit'.\r\n%s", command.trim(), prompt).getBytes()); + Optional> o = regexMapping.entrySet().stream() + .filter(e -> command.trim().matches(((String) e.getKey()))) + .map(e -> Pair.of((String) e.getKey(), (String) e.getValue())).findAny(); + if (o.isPresent()) { + logger.info("[{}] Known pattern detected: {} ({})", cmdHash, command.trim(), o.get()); + String reply = hashReplies.getProperty(o.get().getRight(), "").replace("\\r", "\r").replace("\\n", "\n") + .replace("\\t", "\t"); + out.write(String.format("\r\n%s\r\n%s", reply, prompt).getBytes()); + } else { + logger.info("[{}] Command not found: {}", cmdHash, command.trim()); + notFoundLogger.info("[{}] Command not found: {}", cmdHash, command.trim()); + out.write(String.format("\r\nCommand '%s' not found. Try 'exit'.\r\n%s", command.trim(), prompt) + .getBytes()); + } } return true; }