Skip to content

Commit

Permalink
Merge pull request #650 from bytedance/feat-jvm-reflect
Browse files Browse the repository at this point in the history
feat reflect field and method
  • Loading branch information
yoloyyh authored Jul 9, 2024
2 parents 3e5557c + 0751f0c commit c0418e0
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,7 @@ public void start() {
SmithLogger.logger.info("init ClassUploadTransformer");
ClassUploadTransformer.getInstance().start(client, inst);

inst.addTransformer(this, true);
reloadClasses();


Thread clientThread = new Thread(client::start);

Expand All @@ -172,6 +171,10 @@ public void run() {
TimeUnit.MINUTES.toMillis(1)
);
smithProxy = SmithProbeProxy.getInstance();
SmithProbeProxy.getInstance().setReflectField();
SmithProbeProxy.getInstance().setClient(client);
SmithProbeProxy.getInstance().setDisruptor(disruptor);
SmithProbeProxy.getInstance().setReflectMethod();
new Timer(true).schedule(
new TimerTask() {
@Override
Expand All @@ -182,8 +185,8 @@ public void run() {
0,
TimeUnit.MINUTES.toMillis(1)
);
SmithProbeProxy.getInstance().setClient(client);
SmithProbeProxy.getInstance().setDisruptor(disruptor);
inst.addTransformer(this, true);
reloadClasses();
}

private void reloadClasses() {
Expand Down Expand Up @@ -751,6 +754,7 @@ public void onScanAllClass() {
}
classFilter.setTransId();
classFilter.setRuleId(rule_id);
classFilter.setHashCode(clazz.hashCode());
classFilter.setStackTrace(Thread.currentThread().getStackTrace());

client.write(Operate.SCANCLASS, classFilter);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.security.smith;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicIntegerArray;
Expand All @@ -25,13 +26,15 @@

public class SmithProbeProxy {
private static final SmithProbeProxy ourInstance = new SmithProbeProxy();
private static final int CLASS_MAX_ID = 30;
private static final int CLASS_MAX_ID = 50;
private static final int METHOD_MAX_ID = 20;
private static final int DEFAULT_QUOTA = 12000;

private final AtomicIntegerArray[] quotas;
private Disruptor<Trace> disruptor;
private Client client;
private Map<String, String[]> reflectField = new HashMap<>();
private Map<String, String[]> reflectMethod = new HashMap<>();

public static InheritableThreadLocal<Object> localfilterConfig = new InheritableThreadLocal<Object>() {
@Override
Expand Down Expand Up @@ -77,6 +80,112 @@ public void setDisruptor(Disruptor<Trace> disruptor) {
this.disruptor = disruptor;
}

public void setReflectField() {
String[] values1 = {"theUnsafe", "unsafe", "fieldFilterMap", "methodFilterMap"};
String[] values2 = {"launchMechanism"};
String[] values3 = {"handlerMap", "adaptedInterceptors"};
String[] values4 = {"context"};
String[] values5 = {"delegate"};
String[] values6 = {"handlerAdapters", "handlerMappings"};
String[] values7 = {"chain"};
String[] values8 = {"httpUpgradeProtocols"};
String[] values9 = {"executor"};
String[] values10 = {"connector"};

reflectField.put("*", values1);
reflectField.put("java.lang.UNIXProcess", values2);
reflectField.put("java.lang.ProcessImpl", values2);
reflectField.put("org.springframework.web.servlet.handler.AbstractUrlHandlerMapping", values3);
reflectField.put("org.apache.catalina.core.ApplicationContext", values4);
reflectField.put("org.springframework.context.ApplicationListener", values5);
reflectField.put("org.springframework.web.servlet.DispatcherServlet", values6);
reflectField.put("org.springframework.web.server.handler.FilteringWebHandler", values7);
reflectField.put("org.apache.coyote.http11.AbstractHttp11Protocol", values8);
reflectField.put("org.apache.tomcat.util.net.AbstractEndpoint", values9);
reflectField.put("org.apache.catalina.connector.CoyoteAdapter", values10);
}

public void setReflectMethod() {

String[] values1 = {"*"};
String[] values2 = {"load"};
String[] values3 = {"forkAndExec"};
String[] values4 = {"create"};
String[] values5 = {"defineClass"};
reflectMethod.put("java.lang.Unsafe", values1);
reflectMethod.put("java.lang.ClassLoader$NativeLibrary", values2);
reflectMethod.put("java.lang.UNIXProcess", values3);
reflectMethod.put("java.lang.ProcessImpl", values4);
reflectMethod.put("java.lang.ClassLoader", values5);
}

public Map<String, String[]> getReflectMethod() {
return this.reflectMethod;
}

public Map<String, String[]> getReflectField() {
return this.reflectField;
}

public boolean checkReflectFeildEvil(String classname, String fieldname) {
if (classname == null || fieldname == null) {
return false;
}
Map<String, String[]> refieldMap = getReflectField();
if (refieldMap == null) {
return false;
}
if (refieldMap.containsKey(classname)) {
String[] values = refieldMap.get(classname);
for (String value : values) {
if (value.equals(fieldname) || value.equals("*")) {
return true;
}
}
} else {
String[] values = refieldMap.get("*");
if (values == null) {
return false;
}
for (String value : values) {
if (value.equals(fieldname) || value.equals("*")) {
return true;
}
}
}
return false;
}


public boolean checkReflectMethodEvil(String classname, String methodname) {
if (classname == null || methodname == null) {
return false;
}
Map<String, String[]> refieldMap = getReflectMethod();
if (refieldMap == null) {
return false;
}
if (refieldMap.containsKey(classname)) {
String[] values = refieldMap.get(classname);
for (String value : values) {
if (value.equals(methodname) || value.equals("*")) {
return true;
}
}
} else {
String[] values = refieldMap.get("*");
if (values == null) {
return false;
}
for (String value : values) {
if (value.equals(methodname) || value.equals("*")) {
return true;
}
}
}
return false;
}

public void detect(int classID, int methodID, Object[] args) {
Map<Pair<Integer, Integer>, Block> blocks = SmithProbe.getInstance().GetBlocks();
if (blocks == null)
Expand Down Expand Up @@ -156,6 +265,7 @@ public void sendMetadataClass(Class<?> cla) {
SmithHandler.queryClassFilter(cla, classFilter);
classFilter.setTransId();
classFilter.setRuleId(-1);
classFilter.setHashCode(cla.hashCode());
classFilter.setStackTrace(Thread.currentThread().getStackTrace());
if (client != null) {
client.write(Operate.SCANCLASS, classFilter);
Expand Down Expand Up @@ -602,6 +712,45 @@ public void checkWildflyaddFilterPre(int classID, int methodID, Object[] args) {
}
}

public void handleReflectField(int classID, int methodID, Object[] args, Object ret, boolean blocked) {
if (args.length < 2) {
return ;
}
try {
Class<?> clas = (Class<?>)args[0];
String reflectClass = clas.getName();
String feild = (String)args[1];
if (reflectClass.startsWith("com.security.smith") || reflectClass.startsWith("rasp.")) {
return ;
} else {
if (checkReflectFeildEvil(reflectClass, feild)) {
trace(classID, methodID, args, ret, blocked);
}
}
} catch (Throwable e) {
SmithLogger.exception(e);
}
}

public void handleReflectMethod(int classID, int methodID, Object[] args, Object ret, boolean blocked) {
if (args.length < 2) {
return ;
}
try {
Class<?> clas = (Class<?>)args[0];
String reflectClass = clas.getName();
String feild = (String)args[1];
if (reflectClass.startsWith("com.security.smith") || reflectClass.startsWith("rasp.")) {
return ;
} else {
if (checkReflectMethodEvil(reflectClass, feild)) {
trace(classID, methodID, args, ret, blocked);
}
}
} catch (Throwable e) {
SmithLogger.exception(e);
}

/*
* used for glassfish org.apache.felix.framework.BundleWiringImpl$BundleClassLoader findClass loadClass hook
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class ClassFilter {
private String parentClassName = "";
private String parentClassLoaderName = "";
private long ruleId;
private int hashCode;
@JsonSerialize(converter = StackTraceConverter.class)
private StackTraceElement[] stackTrace = {};

Expand Down Expand Up @@ -84,6 +85,14 @@ public void setRuleId(long ruleId) {
this.ruleId = ruleId;
}

public int getHashCode() {
return hashCode;
}

public void setHashCode(int hashCode) {
this.hashCode = hashCode;
}

public StackTraceElement[] getStackTrace() {
return stackTrace;
}
Expand All @@ -101,6 +110,7 @@ public String toString() {
", classLoaderName: '" + classLoaderName + '\'' +
", parentClassName: '" + parentClassName + '\'' +
", parentClassLoaderName: '" + parentClassLoaderName + '\'' +
", hashCode: '" + hashCode + '\'' +
", ruleId: " + ruleId +
", timestamp: " + Instant.now().getEpochSecond() +
'}';
Expand Down
14 changes: 14 additions & 0 deletions rasp/jvm/JVMProbe/src/main/resources/class.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -385,3 +385,17 @@
name: loadClass
desc: (Ljava/lang/String;Z)Ljava/lang/Class;
exceptionHook: processGlassfishClassLoaderfindClassException
- id: 42
name: java.lang.reflect.Field
methods:
- id: 0
name: <init>
desc: ""
postHook: handleReflectField
- id: 43
name: java.lang.reflect.Method
methods:
- id: 0
name: <init>
desc: ""
postHook: handleReflectMethod

0 comments on commit c0418e0

Please sign in to comment.