From d3c4d835f978ab4700f80b2c5e13bd1eb4d9d0e6 Mon Sep 17 00:00:00 2001 From: yoloyyh <1764163852@qq.com> Date: Mon, 18 Dec 2023 20:03:32 +0800 Subject: [PATCH] feat filter memshell when godzilla --- .../java/com/security/smith/SmithProbe.java | 219 +++---------- .../com/security/smith/SmithProbeProxy.java | 294 ++++++++++++++++++ .../smith/asm/SmithMethodVisitor.java | 21 +- .../JVMProbe/src/main/resources/class.yaml | 9 +- 4 files changed, 353 insertions(+), 190 deletions(-) create mode 100644 rasp/jvm/JVMProbe/src/main/java/com/security/smith/SmithProbeProxy.java diff --git a/rasp/jvm/JVMProbe/src/main/java/com/security/smith/SmithProbe.java b/rasp/jvm/JVMProbe/src/main/java/com/security/smith/SmithProbe.java index d2b8a7942..5520bcf91 100644 --- a/rasp/jvm/JVMProbe/src/main/java/com/security/smith/SmithProbe.java +++ b/rasp/jvm/JVMProbe/src/main/java/com/security/smith/SmithProbe.java @@ -4,14 +4,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.lmax.disruptor.EventHandler; -import com.lmax.disruptor.InsufficientCapacityException; -import com.lmax.disruptor.RingBuffer; + import com.lmax.disruptor.dsl.Disruptor; import com.lmax.disruptor.util.DaemonThreadFactory; import com.security.smith.asm.SmithClassVisitor; import com.security.smith.asm.SmithClassWriter; import com.security.smith.client.message.*; -import com.security.smith.common.Reflection; + import com.security.smith.common.SmithHandler; import com.security.smith.log.SmithLogger; import com.security.smith.module.Patcher; @@ -41,33 +40,32 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicIntegerArray; + import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.Stream; + public class SmithProbe implements ClassFileTransformer, MessageHandler, EventHandler { private static final SmithProbe ourInstance = new SmithProbe(); private static final int TRACE_BUFFER_SIZE = 1024; - private static final int CLASS_MAX_ID = 30; - private static final int METHOD_MAX_ID = 20; - private static final int DEFAULT_QUOTA = 12000; private Boolean disable; private Boolean scanswitch; private Instrumentation inst; private final Client client; private final Heartbeat heartbeat; - private final Disruptor disruptor; + private final Map smithClasses; private final Map patchers; private final Map, Filter> filters; private final Map, Block> blocks; private final Map, Integer> limits; - private final AtomicIntegerArray[] quotas; + private final Disruptor disruptor; + private final Rule_Mgr rulemgr; private final Rule_Config ruleconfig; + private SmithProbeProxy smithProxy; enum Action { STOP, @@ -90,9 +88,8 @@ public SmithProbe() { heartbeat = new Heartbeat(); client = new Client(this); + disruptor = new Disruptor<>(Trace::new, TRACE_BUFFER_SIZE, DaemonThreadFactory.INSTANCE); - quotas = Stream.generate(() -> new AtomicIntegerArray(METHOD_MAX_ID)).limit(CLASS_MAX_ID).toArray(AtomicIntegerArray[]::new); - rulemgr = new Rule_Mgr(); ruleconfig = new Rule_Config(rulemgr); } @@ -121,13 +118,11 @@ public void start() { Thread clientThread = new Thread(client::start); - clientThread.setDaemon(true); - clientThread.start(); - - - disruptor.handleEventsWith(this); disruptor.start(); + + clientThread.setDaemon(true); + clientThread.start(); new Timer(true).schedule( new TimerTask() { @@ -138,17 +133,19 @@ public void run() { }, TimeUnit.MINUTES.toMillis(1) ); - + smithProxy = SmithProbeProxy.getInstance(); new Timer(true).schedule( new TimerTask() { @Override public void run() { - onTimer(); + smithProxy.onTimer(); } }, 0, TimeUnit.MINUTES.toMillis(1) ); + SmithProbeProxy.getInstance().setClient(client); + SmithProbeProxy.getInstance().setDisruptor(disruptor); } private void reloadClasses() { @@ -168,147 +165,6 @@ private void reloadClasses(Collection classes) { } } - public void detect(int classID, int methodID, Object[] args) { - Block block = blocks.get(new ImmutablePair<>(classID, methodID)); - - if (block == null) - return; - - if (Arrays.stream(block.getRules()).anyMatch(rule -> { - if (rule.getIndex() >= args.length) - return false; - - return Pattern.compile(rule.getRegex()).matcher(args[rule.getIndex()].toString()).find(); - })) { - throw new SecurityException("API blocked by RASP"); - } - } - - public void checkAddServletPre(int classID, int methodID, Object[] args) { - SmithLogger.logger.info("checkAddServlet post_hook call success"); - if (args.length < 3) { - return; - } - try { - Object context = args[0]; - String name = (String)args[2]; - if (context != null) { - Class[] argTypes = new Class[]{String.class}; - - Object wrapper = Reflection.invokeMethod(context, "findChild", argTypes, name); - - if(wrapper != null) { - Class[] emptyArgTypes = new Class[]{}; - - Object servlet = Reflection.invokeMethod(wrapper, "getServlet", emptyArgTypes); - if(servlet != null) { - ClassFilter classFilter = new ClassFilter(); - //classFilter.setClassName(name); - SmithHandler.queryClassFilter(servlet.getClass(), classFilter); - classFilter.setTransId(); - classFilter.setRuleId(-1); - classFilter.setStackTrace(Thread.currentThread().getStackTrace()); - client.write(Operate.SCANCLASS, classFilter); - SmithLogger.logger.info("send metadata: " + classFilter.toString()); - sendClass(servlet.getClass(), classFilter.getTransId()); - } - } - } - - } catch (Exception e) { - SmithLogger.exception(e); - } - } - - public void checkAddFilterPre(int classID, int methodID, Object[] args) { - SmithLogger.logger.info("checkAddFilter post_hook call success"); - if (args.length < 2) { - return; - } - try { - Object filterDef = args[1]; - Object filter = null; - if (filterDef != null) { - Class[] emptyArgTypes = new Class[]{}; - filter = Reflection.invokeMethod(filterDef, "getFilter", emptyArgTypes); - if (filter != null) { - ClassFilter classFilter = new ClassFilter(); - SmithHandler.queryClassFilter(filter.getClass(), classFilter); - classFilter.setTransId(); - classFilter.setRuleId(-1); - classFilter.setStackTrace(Thread.currentThread().getStackTrace()); - client.write(Operate.SCANCLASS, classFilter); - SmithLogger.logger.info("send metadata: " + classFilter.toString()); - sendClass(filter.getClass(), classFilter.getTransId()); - } - - } - - } catch (Exception e) { - SmithLogger.exception(e); - } - } - - public void checkAddValvePre(int classID, int methodID, Object[] args) { - if (args.length < 2) { - return; - } - try { - Object valve = args[1]; - if (valve != null) { - ClassFilter classFilter = new ClassFilter(); - SmithHandler.queryClassFilter(valve.getClass(), classFilter); - classFilter.setTransId(); - classFilter.setRuleId(-1); - classFilter.setStackTrace(Thread.currentThread().getStackTrace()); - client.write(Operate.SCANCLASS, classFilter); - SmithLogger.logger.info("send metadata: " + classFilter.toString()); - sendClass(valve.getClass(), classFilter.getTransId()); - } - - } catch (Exception e) { - SmithLogger.exception(e); - } - } - - public void checkAddListenerPre(int classID, int methodID, Object[] args) { - checkAddValvePre(classID, methodID, args); - } - - public void trace(int classID, int methodID, Object[] args, Object ret, boolean blocked) { - if (classID >= CLASS_MAX_ID || methodID >= METHOD_MAX_ID) - return; - - while (true) { - int quota = quotas[classID].get(methodID); - - if (quota <= 0) - return; - - if (quotas[classID].compareAndSet(methodID, quota, quota - 1)) - break; - } - - RingBuffer ringBuffer = disruptor.getRingBuffer(); - - try { - long sequence = ringBuffer.tryNext(); - - Trace trace = ringBuffer.get(sequence); - - trace.setClassID(classID); - trace.setMethodID(methodID); - trace.setBlocked(blocked); - trace.setRet(ret); - trace.setArgs(args); - trace.setStackTrace(Thread.currentThread().getStackTrace()); - - ringBuffer.publish(sequence); - } catch (InsufficientCapacityException ignored) { - - } - } - @Override public void onEvent(Trace trace, long sequence, boolean endOfBatch) { Filter filter = filters.get(new ImmutablePair<>(trace.getClassID(), trace.getMethodID())); @@ -339,23 +195,6 @@ public void onEvent(Trace trace, long sequence, boolean endOfBatch) { client.write(Operate.TRACE, trace); } - private void onTimer() { - client.write(Operate.HEARTBEAT, heartbeat); - - for (int i = 0; i < CLASS_MAX_ID; i++) { - for (int j = 0; j < METHOD_MAX_ID; j++) { - Integer quota = limits.get(new ImmutablePair<>(i, j)); - - if (quota == null) { - quotas[i].set(j, DEFAULT_QUOTA); - continue; - } - - quotas[i].set(j, quota); - } - } - } - public void printClassfilter(ClassFilter data) { /* SmithLogger.logger.info("className:" + data.getClassName()); @@ -774,7 +613,7 @@ public void onScanAllClass() { /* * send class file */ - private void sendClass(Class clazz, String transId) { + public void sendClass(Class clazz, String transId) { if (clazz == null || transId == null) { return; } @@ -828,4 +667,28 @@ private void sendByte(byte[] data, String transId) { //} } + public Heartbeat getHeartbeat() { + return heartbeat; + } + + public Map, Integer> getLimits() { + return limits; + } + + public Map, Block> GetBlocks() { + return blocks; + } + + public Map, Filter> GetFiltes() { + return filters; + } + + public Client getClient() { + return client; + } + + public Disruptor getDisruptor() { + return disruptor; + } + } diff --git a/rasp/jvm/JVMProbe/src/main/java/com/security/smith/SmithProbeProxy.java b/rasp/jvm/JVMProbe/src/main/java/com/security/smith/SmithProbeProxy.java new file mode 100644 index 000000000..32588f079 --- /dev/null +++ b/rasp/jvm/JVMProbe/src/main/java/com/security/smith/SmithProbeProxy.java @@ -0,0 +1,294 @@ +package com.security.smith; + +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; + +import com.lmax.disruptor.InsufficientCapacityException; +import com.lmax.disruptor.RingBuffer; +import com.lmax.disruptor.dsl.Disruptor; +import com.security.smith.client.Client; +import com.security.smith.client.Operate; +import com.security.smith.client.message.ClassFilter; +import com.security.smith.client.message.Heartbeat; +import com.security.smith.client.message.Trace; +import com.security.smith.client.message.Block; +import com.security.smith.common.Reflection; +import com.security.smith.common.SmithHandler; +import com.security.smith.log.SmithLogger; + +public class SmithProbeProxy { + private static final SmithProbeProxy ourInstance = new SmithProbeProxy(); + private static final int CLASS_MAX_ID = 30; + private static final int METHOD_MAX_ID = 20; + private static final int DEFAULT_QUOTA = 12000; + + private final AtomicIntegerArray[] quotas; + private Disruptor disruptor; + private Client client; + + public static InheritableThreadLocal localfilterConfig = new InheritableThreadLocal() { + @Override + protected Object initialValue() { + return null; + } + }; + + public static InheritableThreadLocal localfilterDef = new InheritableThreadLocal() { + @Override + protected Object initialValue() { + return null; + } + }; + + public SmithProbeProxy() { + quotas = Stream.generate(() -> new AtomicIntegerArray(METHOD_MAX_ID)).limit(CLASS_MAX_ID).toArray(AtomicIntegerArray[]::new); + } + + public static SmithProbeProxy getInstance() { + return ourInstance; + } + + public void setClient(Client client) { + this.client = client; + } + + public void setDisruptor(Disruptor disruptor) { + this.disruptor = disruptor; + } + + public void detect(int classID, int methodID, Object[] args) { + Map, Block> blocks = SmithProbe.getInstance().GetBlocks(); + if (blocks == null) + return; + Block block = blocks.get(new ImmutablePair<>(classID, methodID)); + + if (block == null) + return; + + if (Arrays.stream(block.getRules()).anyMatch(rule -> { + if (rule.getIndex() >= args.length) + return false; + + return Pattern.compile(rule.getRegex()).matcher(args[rule.getIndex()].toString()).find(); + })) { + throw new SecurityException("API blocked by RASP"); + } + } + + public void trace(int classID, int methodID, Object[] args, Object ret, boolean blocked) { + if (classID >= CLASS_MAX_ID || methodID >= METHOD_MAX_ID) + return; + + while (true) { + int quota = quotas[classID].get(methodID); + + if (quota <= 0) + return; + + if (quotas[classID].compareAndSet(methodID, quota, quota - 1)) + break; + } + if (disruptor == null) + return; + RingBuffer ringBuffer = disruptor.getRingBuffer(); + + try { + long sequence = ringBuffer.tryNext(); + + Trace trace = ringBuffer.get(sequence); + + trace.setClassID(classID); + trace.setMethodID(methodID); + trace.setBlocked(blocked); + trace.setRet(ret); + trace.setArgs(args); + trace.setStackTrace(Thread.currentThread().getStackTrace()); + + ringBuffer.publish(sequence); + } catch (InsufficientCapacityException ignored) { + + } + } + + public void checkAddServletPre(int classID, int methodID, Object[] args) { + SmithLogger.logger.info("checkAddServlet pre_hook call success"); + if (args.length < 3) { + return; + } + try { + Object context = args[0]; + String name = (String)args[2]; + if (context != null) { + Class[] argTypes = new Class[]{String.class}; + + Object wrapper = Reflection.invokeMethod(context, "findChild", argTypes, name); + + if(wrapper != null) { + Class[] emptyArgTypes = new Class[]{}; + + Object servlet = Reflection.invokeMethod(wrapper, "getServlet", emptyArgTypes); + if(servlet != null) { + ClassFilter classFilter = new ClassFilter(); + //classFilter.setClassName(name); + SmithHandler.queryClassFilter(servlet.getClass(), classFilter); + classFilter.setTransId(); + classFilter.setRuleId(-1); + classFilter.setStackTrace(Thread.currentThread().getStackTrace()); + if (client != null) { + client.write(Operate.SCANCLASS, classFilter); + SmithLogger.logger.info("send metadata: " + classFilter.toString()); + SmithProbe.getInstance().sendClass(servlet.getClass(), classFilter.getTransId()); + } + } + } + } + + } catch (Exception e) { + SmithLogger.exception(e); + } + } + + private Object getFilterFromConfig(Object filterConfig) { + if (filterConfig == null) { + return null; + } + Object filter = null; + try { + filter = Reflection.getField(filterConfig, "filter"); + } catch (Exception e) { + SmithLogger.exception(e); + } + return filter; + } + + private Class getFilterFromLoader(Object context, String filterName) { + Class filter = null; + if (context == null || filterName == null) + return filter; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) + classLoader = context.getClass().getClassLoader(); + try { + filter = classLoader.loadClass(filterName); + } catch (Exception e) { + } + return filter; + } + + public void checkAddFilterPre(int classID, int methodID, Object[] args) { + SmithLogger.logger.info("checkAddFilter pre_hook call success"); + try { + Object filterdef = args[1]; + Object filter = null; + Class filterClass = null; + if (filterdef != null) { + Class[] emptyArgTypes = new Class[]{}; + filter = Reflection.invokeMethod(filterdef, "getFilter", emptyArgTypes); + String filterName = ""; + if (filter == null) { + // Godzilla filter check + if (localfilterDef != null && localfilterConfig != null && filterdef == localfilterDef.get()) { + filter = getFilterFromConfig(localfilterConfig.get()); + } else { + filterName = (String)Reflection.invokeMethod(filterdef, "getFilterClass", emptyArgTypes); + filterClass = getFilterFromLoader(args[0], filterName); + } + } + if (filter != null || filterClass != null) { + ClassFilter classFilter = new ClassFilter(); + if (filterClass != null) { + SmithHandler.queryClassFilter(filterClass, classFilter); + } else { + SmithHandler.queryClassFilter(filter.getClass(), classFilter); + } + + classFilter.setTransId(); + + classFilter.setRuleId(-1); + classFilter.setStackTrace(Thread.currentThread().getStackTrace()); + if (client != null) { + client.write(Operate.SCANCLASS, classFilter); + SmithLogger.logger.info("send metadata: " + classFilter.toString()); + if (filterClass != null) { + SmithProbe.getInstance().sendClass(filterClass, classFilter.getTransId()); + } + else { + SmithProbe.getInstance().sendClass(filter.getClass(), classFilter.getTransId()); + } + } + } + } + } catch (Exception e) { + SmithLogger.exception(e); + } + } + public void checkFilterConfigPost(int classID, int methodID, Object[] args, Object ret, boolean blocked) { + SmithLogger.logger.info("checkAddFilter post_hook call success"); + if (ret == null || args.length < 2) { + return; + } + try { + localfilterConfig.set(ret); + localfilterDef.set(args[1]); + } catch(Exception e) { + SmithLogger.exception(e); + } + + } + + public void checkAddValvePre(int classID, int methodID, Object[] args) { + if (args.length < 2) { + return; + } + try { + Object valve = args[1]; + if (valve != null) { + ClassFilter classFilter = new ClassFilter(); + SmithHandler.queryClassFilter(valve.getClass(), classFilter); + classFilter.setTransId(); + classFilter.setRuleId(-1); + classFilter.setStackTrace(Thread.currentThread().getStackTrace()); + if (client != null) { + client.write(Operate.SCANCLASS, classFilter); + SmithLogger.logger.info("send metadata: " + classFilter.toString()); + SmithProbe.getInstance().sendClass(valve.getClass(), classFilter.getTransId()); + } + } + + } catch (Exception e) { + SmithLogger.exception(e); + } + } + + public void checkAddListenerPre(int classID, int methodID, Object[] args) { + checkAddValvePre(classID, methodID, args); + } + + public void onTimer() { + Heartbeat heartbeat = SmithProbe.getInstance().getHeartbeat(); + if (client != null) + client.write(Operate.HEARTBEAT, heartbeat); + + Map, Integer> limits = SmithProbe.getInstance().getLimits(); + + for (int i = 0; i < CLASS_MAX_ID; i++) { + for (int j = 0; j < METHOD_MAX_ID; j++) { + Integer quota = limits.get(new ImmutablePair<>(i, j)); + + if (quota == null) { + quotas[i].set(j, DEFAULT_QUOTA); + continue; + } + + quotas[i].set(j, quota); + } + } + } + +} diff --git a/rasp/jvm/JVMProbe/src/main/java/com/security/smith/asm/SmithMethodVisitor.java b/rasp/jvm/JVMProbe/src/main/java/com/security/smith/asm/SmithMethodVisitor.java index 7bf822307..1d15806ae 100644 --- a/rasp/jvm/JVMProbe/src/main/java/com/security/smith/asm/SmithMethodVisitor.java +++ b/rasp/jvm/JVMProbe/src/main/java/com/security/smith/asm/SmithMethodVisitor.java @@ -1,7 +1,6 @@ package com.security.smith.asm; -import com.security.smith.SmithProbe; -import com.security.smith.common.SmithHandler; +import com.security.smith.SmithProbeProxy; import com.security.smith.processor.*; import org.apache.commons.lang3.ArrayUtils; import org.objectweb.asm.Label; @@ -115,10 +114,10 @@ protected void onMethodEnter() { } invokeStatic( - Type.getType(SmithProbe.class), + Type.getType(SmithProbeProxy.class), new Method( "getInstance", - Type.getType(SmithProbe.class), + Type.getType(SmithProbeProxy.class), new Type[]{} ) ); @@ -128,7 +127,7 @@ protected void onMethodEnter() { loadLocal(argumentsVariable); invokeVirtual( - Type.getType(SmithProbe.class), + Type.getType(SmithProbeProxy.class), new Method( preHook, Type.VOID_TYPE, @@ -177,10 +176,10 @@ protected void onMethodExit(int opcode) { } invokeStatic( - Type.getType(SmithProbe.class), + Type.getType(SmithProbeProxy.class), new Method( "getInstance", - Type.getType(SmithProbe.class), + Type.getType(SmithProbeProxy.class), new Type[]{} ) ); @@ -194,7 +193,7 @@ protected void onMethodExit(int opcode) { invokeVirtual( - Type.getType(SmithProbe.class), + Type.getType(SmithProbeProxy.class), new Method( postHook, Type.VOID_TYPE, @@ -253,10 +252,10 @@ public void visitMaxs(final int maxStack, final int maxLocals) { storeLocal(returnVariable + 1, Type.getType(Exception.class)); invokeStatic( - Type.getType(SmithProbe.class), + Type.getType(SmithProbeProxy.class), new Method( "getInstance", - Type.getType(SmithProbe.class), + Type.getType(SmithProbeProxy.class), new Type[]{} ) ); @@ -274,7 +273,7 @@ public void visitMaxs(final int maxStack, final int maxLocals) { } invokeVirtual( - Type.getType(SmithProbe.class), + Type.getType(SmithProbeProxy.class), new Method( "trace", Type.VOID_TYPE, diff --git a/rasp/jvm/JVMProbe/src/main/resources/class.yaml b/rasp/jvm/JVMProbe/src/main/resources/class.yaml index 1ba018111..c37465678 100644 --- a/rasp/jvm/JVMProbe/src/main/resources/class.yaml +++ b/rasp/jvm/JVMProbe/src/main/resources/class.yaml @@ -197,4 +197,11 @@ - id: 3 name: addServletMappingDecoded desc: (Ljava/lang/String;Ljava/lang/String;Z)V - preHook: checkAddServletPre \ No newline at end of file + preHook: checkAddServletPre +- id: 16 + name: org.apache.catalina.core.ApplicationFilterConfig + methods: + - id: 0 + name: + desc: (Lorg/apache/catalina/Context;Lorg/apache/tomcat/util/descriptor/web/FilterDef;)V + postHook: checkFilterConfigPost \ No newline at end of file