├── acaf-core ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── er1cccc │ │ └── acaf │ │ ├── Launcher.java │ │ ├── config │ │ ├── ACAFConfigurer.java │ │ ├── ControllableParam.java │ │ ├── PassthroughRegistry.java │ │ ├── Sanitize.java │ │ ├── SanitizeRegistry.java │ │ ├── Sink.java │ │ ├── SinkRegistry.java │ │ ├── Source.java │ │ └── SourceRegistry.java │ │ ├── core │ │ ├── audit │ │ │ ├── auditcore │ │ │ │ ├── CallGraph.java │ │ │ │ ├── CallGraphClassVisitor.java │ │ │ │ ├── CallGraphMethodAdapter.java │ │ │ │ ├── CoreMethodAdapter.java │ │ │ │ ├── DataFlowClassVisitor.java │ │ │ │ ├── DataFlowMethodAdapter.java │ │ │ │ ├── DiscoveryClassVisitor.java │ │ │ │ ├── Field.java │ │ │ │ ├── GotoState.java │ │ │ │ ├── InheritanceMap.java │ │ │ │ ├── InheritanceUtil.java │ │ │ │ ├── LocalVariables.java │ │ │ │ ├── MethodCallClassVisitor.java │ │ │ │ ├── MethodCallMethodAdapter.java │ │ │ │ ├── OperandStack.java │ │ │ │ ├── SpringAnnoAdapter.java │ │ │ │ ├── SpringClassVisitor.java │ │ │ │ ├── SpringConstant.java │ │ │ │ ├── SpringMethodAdapter.java │ │ │ │ ├── SpringPathAnnoAdapter.java │ │ │ │ ├── VulnClassVisitor.java │ │ │ │ └── VulnMethodAdapter.java │ │ │ └── discovery │ │ │ │ ├── CallGraphDiscovery.java │ │ │ │ ├── DrawService.java │ │ │ │ ├── MethodDiscovery.java │ │ │ │ ├── PassthroughDiscovery.java │ │ │ │ ├── SpringDiscovery.java │ │ │ │ └── VulnDiscovery.java │ │ ├── entity │ │ │ ├── ClassFile.java │ │ │ ├── ClassReference.java │ │ │ ├── Command.java │ │ │ ├── Instruction.java │ │ │ ├── MethodReference.java │ │ │ ├── SpringController.java │ │ │ ├── SpringMapping.java │ │ │ ├── SpringParam.java │ │ │ └── impl │ │ │ │ └── VisitMethodInsnInfoInstruction.java │ │ ├── resolver │ │ │ ├── Resolver.java │ │ │ └── impl │ │ │ │ ├── CompositResolver.java │ │ │ │ └── VisitMethodInsnInfoResolver.java │ │ └── template │ │ │ ├── VulnTemplateSanitizeVisitor.java │ │ │ └── VulnTemplateSinkVisitor.java │ │ ├── data │ │ ├── DataFactory.java │ │ └── DataLoader.java │ │ └── util │ │ ├── ClassNameClassVisitor.java │ │ ├── DataUtil.java │ │ ├── DirUtil.java │ │ ├── DrawUtil.java │ │ ├── FileUtil.java │ │ ├── IOUtil.java │ │ ├── JarUtil.java │ │ └── RtUtil.java │ └── resources │ └── log4j.properties ├── acaf-example ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── er1cccc │ └── acaf │ └── example │ ├── App.java │ ├── sql │ ├── SqlConfigurer.java │ └── SqlSink.java │ └── ssrf │ ├── SSRFConfigurer.java │ ├── SsrfSink1.java │ ├── SsrfSink2.java │ ├── SsrfSink3.java │ └── SsrfSink4.java ├── acaf ├── image-20220103131703025.png ├── image-20220103132616981.png ├── image-20220103133558167.png ├── image-20220103141501161.png ├── image-20220103141828815.png ├── image-20220103141917976.png ├── image-20220103142558198.png ├── image-20220103142721032.png ├── image-20220103142841443.png ├── image-20220103142903948.png ├── image-20220103143104899.png ├── image-20220103143239083.png ├── image-20220103143422984.png ├── image-20220103143507206.png ├── image-20220103145309189.png ├── image-20220103145355140.png ├── image-20220103145427848.png ├── image-20220103145604152.png ├── image-20220103145810118.png ├── image-20220103150117976.png ├── image-20220103151240038.png ├── image-20220103151733418.png ├── image-20220103151747594.png ├── image-20220103155122573.png ├── image-20220103155809694.png ├── image-20220103160230238.png ├── image-20220103160528500.png ├── image-20220103162135990.png ├── image-20220103162254813.png ├── image-20220103162431545.png ├── image-20220103162532243.png ├── image-20220103162812381.png ├── image-20220103164332924.png ├── image-20220103164537493.png ├── image-20220103164749513.png ├── image-20220103170547318.png └── image-20220103170644290.png ├── pom.xml └── readme.md /acaf-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | 8 | com.er1cccc 9 | acaf 10 | 1.0 11 | 12 | 13 | acaf-core 14 | 15 | 16 | 17 | guru.nidi 18 | graphviz-java 19 | 20 | 21 | ch.qos.logback 22 | logback-classic 23 | 24 | 25 | log4j 26 | log4j 27 | 28 | 29 | com.beust 30 | jcommander 31 | 32 | 33 | org.ow2.asm 34 | asm 35 | 36 | 37 | org.ow2.asm 38 | asm-commons 39 | 40 | 41 | com.google.guava 42 | guava 43 | 44 | 45 | org.projectlombok 46 | lombok 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/Launcher.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf; 2 | 3 | import com.beust.jcommander.JCommander; 4 | import com.er1cccc.acaf.config.*; 5 | import com.er1cccc.acaf.core.audit.auditcore.CallGraph; 6 | import com.er1cccc.acaf.core.audit.auditcore.InheritanceMap; 7 | import com.er1cccc.acaf.core.audit.auditcore.InheritanceUtil; 8 | import com.er1cccc.acaf.core.audit.discovery.*; 9 | import com.er1cccc.acaf.core.entity.*; 10 | import com.er1cccc.acaf.core.resolver.impl.CompositResolver; 11 | import com.er1cccc.acaf.core.template.VulnTemplateSinkVisitor; 12 | import com.er1cccc.acaf.data.DataLoader; 13 | import com.er1cccc.acaf.util.DataUtil; 14 | import com.er1cccc.acaf.util.DirUtil; 15 | import com.er1cccc.acaf.util.RtUtil; 16 | import org.apache.log4j.Logger; 17 | import org.objectweb.asm.ClassReader; 18 | 19 | import java.io.File; 20 | import java.io.IOException; 21 | import java.nio.file.Files; 22 | import java.nio.file.Path; 23 | import java.nio.file.Paths; 24 | import java.util.*; 25 | 26 | public class Launcher { 27 | private static final Logger logger = Logger.getLogger(Launcher.class); 28 | 29 | public static void launch(ACAFConfigurer configurer,String[] args) throws Exception { 30 | logger.info("start code inspector"); 31 | Command command = new Command(); 32 | JCommander jc = JCommander.newBuilder().addObject(command).build(); 33 | jc.parse(args); 34 | if (command.help) { 35 | jc.usage(); 36 | } 37 | if (command.boots != null && command.boots.size() != 0) { 38 | doLaunch(command.boots, command.templates, command.packageName, configurer); 39 | } 40 | } 41 | 42 | private static void doLaunch(List boots, List templates, String packageName, ACAFConfigurer configurer) throws Exception{ 43 | List templateClassFileList = RtUtil.getAllClassesFromJars(templates); 44 | List targetClassFileList = RtUtil.getAllClassesFromBoot(boots, true); 45 | Path targetSaveDataDirPath = Paths.get("audit_target"); 46 | Path templateSaveDataDirPath = Paths.get("audit_template"); 47 | Runtime.getRuntime().addShutdownHook(new Thread(() -> { 48 | DirUtil.removeDir(targetSaveDataDirPath.toFile()); 49 | DirUtil.removeDir(templateSaveDataDirPath.toFile()); 50 | })); 51 | prepareLaunch(templateClassFileList,templateSaveDataDirPath); 52 | prepareLaunch(targetClassFileList,targetSaveDataDirPath); 53 | 54 | // 包名 55 | String finalPackageName = packageName.replace(".", "/"); 56 | // 获取全部controller 57 | SpringDiscovery springDiscovery = new SpringDiscovery(); 58 | springDiscovery.discover(targetClassFileList, finalPackageName,targetSaveDataDirPath); 59 | List controllers = springDiscovery.getControllers(); 60 | 61 | SourceRegistry sourceRegistry = new SourceRegistry(); 62 | SanitizeRegistry sanitizeRegistry = new SanitizeRegistry(); 63 | SinkRegistry sinkRegistry = new SinkRegistry(); 64 | configurer.addSource(sourceRegistry); 65 | configurer.addSanitize(sanitizeRegistry); 66 | configurer.addSink(sinkRegistry); 67 | 68 | VulnDiscovery vulnDiscovery = new VulnDiscovery(targetClassFileList, sourceRegistry,sanitizeRegistry,sinkRegistry, targetSaveDataDirPath,templateSaveDataDirPath); 69 | vulnDiscovery.discover(controllers); 70 | 71 | // // 画出指定package的调用图 72 | // DrawService.start(discoveredCalls, finalPackageName, classMap, methodImplMap); 73 | } 74 | 75 | private static void prepareLaunch(List classFileList, Path saveDataDirPath) throws IOException { 76 | if(!saveDataDirPath.toFile().exists()){ 77 | saveDataDirPath.toFile().mkdirs(); 78 | } 79 | // 读取JDK和输入Jar所有class资源 80 | // 获取所有方法和类 81 | File dir = saveDataDirPath.toFile(); 82 | if (!new File(dir,"classes.dat").exists() || !new File(dir,"methods.dat").exists() 83 | || !new File(dir,"inheritanceMap.dat").exists()) { 84 | logger.info("Running method discovery..."); 85 | MethodDiscovery methodDiscovery = new MethodDiscovery(); 86 | methodDiscovery.discover(classFileList); 87 | methodDiscovery.save(saveDataDirPath); 88 | } 89 | 90 | if (!new File(dir,"passthrough.dat").exists()) { 91 | logger.info("Analyzing methods for passthrough dataflow..."); 92 | PassthroughDiscovery passthroughDiscovery = new PassthroughDiscovery(); 93 | passthroughDiscovery.discover(classFileList,saveDataDirPath); 94 | passthroughDiscovery.save(saveDataDirPath); 95 | } 96 | 97 | 98 | if (!new File(dir,"callgraph.dat").exists()) { 99 | logger.info("Analyzing methods in order to build a call graph..."); 100 | CallGraphDiscovery callGraphDiscovery = new CallGraphDiscovery(); 101 | callGraphDiscovery.discover(classFileList,saveDataDirPath); 102 | callGraphDiscovery.save(saveDataDirPath); 103 | } 104 | } 105 | 106 | 107 | } 108 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/config/ACAFConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.config; 2 | 3 | public interface ACAFConfigurer { 4 | void addSource(SourceRegistry sourceRegistry); 5 | void addSanitize(SanitizeRegistry sanitizeRegistry); 6 | void addSink(SinkRegistry sinkRegistry); 7 | } 8 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/config/ControllableParam.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.config; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class ControllableParam { 7 | private Map params=new HashMap<>(); 8 | 9 | public V put(String key, V value){ 10 | return (V) params.put(key, value); 11 | } 12 | 13 | public Object getParameter(String name){ 14 | return params.get(name); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/config/PassthroughRegistry.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.config; 2 | 3 | import com.er1cccc.acaf.core.audit.discovery.PassthroughDiscovery; 4 | import com.er1cccc.acaf.core.entity.ClassReference; 5 | import com.er1cccc.acaf.core.entity.MethodReference; 6 | import org.objectweb.asm.*; 7 | import java.io.IOException; 8 | import java.lang.reflect.Constructor; 9 | import java.lang.reflect.Method; 10 | import java.util.HashSet; 11 | import java.util.Map; 12 | import java.util.Set; 13 | 14 | public class PassthroughRegistry { 15 | private Map> passthroughDataflow; 16 | 17 | public PassthroughRegistry(Map> passthroughDataflow) { 18 | this.passthroughDataflow=passthroughDataflow; 19 | } 20 | 21 | 22 | public void addPassthrough(Method method,Integer ...args) throws IOException { 23 | ClassReference.Handle classHandle = new ClassReference.Handle(method.getDeclaringClass().getName().replace(".","/")); 24 | MethodReference.Handle methodHandle = new MethodReference.Handle(classHandle, method.getName(), Type.getMethodDescriptor(method)); 25 | HashSet argSet = new HashSet<>(); 26 | for(int arg:args){ 27 | argSet.add(arg); 28 | } 29 | this.passthroughDataflow.put(methodHandle,argSet); 30 | } 31 | 32 | public void addPassthrough(Constructor constructor, Integer ...args) throws IOException { 33 | ClassReference.Handle classHandle = new ClassReference.Handle(constructor.getDeclaringClass().getName().replace(".","/")); 34 | MethodReference.Handle methodHandle = new MethodReference.Handle(classHandle, "", Type.getConstructorDescriptor(constructor)); 35 | HashSet argSet = new HashSet<>(); 36 | for(int arg:args){ 37 | argSet.add(arg); 38 | } 39 | this.passthroughDataflow.put(methodHandle,argSet); 40 | } 41 | 42 | 43 | public static void main(String[] args) throws Exception{ 44 | // PassthroughRegistry passthroughRegistry = new PassthroughRegistry(null); 45 | // passthroughRegistry.addPassthrough(PassthroughRegistry.class.getMethod("main",String[].class),null); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/config/Sanitize.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.config; 2 | 3 | public interface Sanitize { 4 | Object sanitizeMethod() throws Exception; 5 | } 6 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/config/SanitizeRegistry.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.config; 2 | 3 | public class SanitizeRegistry { 4 | } 5 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/config/Sink.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.config; 2 | 3 | public interface Sink { 4 | Object sinkMethod() throws Exception; 5 | 6 | default void addPassthrough(PassthroughRegistry passthroughRegistry) {} 7 | } 8 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/config/SinkRegistry.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.config; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | @Data 9 | public class SinkRegistry { 10 | private List sinkList=new ArrayList<>(); 11 | 12 | public SinkRegistry addSink(Sink sink) { 13 | this.sinkList.add(sink); 14 | return this; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/config/Source.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.config; 2 | 3 | public interface Source { 4 | } 5 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/config/SourceRegistry.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.config; 2 | 3 | public class SourceRegistry { 4 | } 5 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/CallGraph.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | 4 | import com.er1cccc.acaf.core.entity.ClassReference; 5 | import com.er1cccc.acaf.core.entity.MethodReference; 6 | import com.er1cccc.acaf.data.DataFactory; 7 | 8 | import java.util.Objects; 9 | 10 | @SuppressWarnings("all") 11 | public class CallGraph { 12 | private final MethodReference.Handle callerMethod; 13 | private final MethodReference.Handle targetMethod; 14 | private final int callerArgIndex; 15 | private final String callerArgPath; 16 | private final int targetArgIndex; 17 | 18 | public CallGraph(MethodReference.Handle callerMethod, MethodReference.Handle targetMethod, 19 | int callerArgIndex, String callerArgPath, int targetArgIndex) { 20 | this.callerMethod = callerMethod; 21 | this.targetMethod = targetMethod; 22 | this.callerArgIndex = callerArgIndex; 23 | this.callerArgPath = callerArgPath; 24 | this.targetArgIndex = targetArgIndex; 25 | } 26 | 27 | public CallGraph(MethodReference.Handle callerMethod, MethodReference.Handle targetMethod) { 28 | this.callerMethod = callerMethod; 29 | this.targetMethod = targetMethod; 30 | this.callerArgIndex = -1; 31 | this.callerArgPath = ""; 32 | this.targetArgIndex = -1; 33 | } 34 | 35 | public MethodReference.Handle getCallerMethod() { 36 | return callerMethod; 37 | } 38 | 39 | public MethodReference.Handle getTargetMethod() { 40 | return targetMethod; 41 | } 42 | 43 | public int getCallerArgIndex() { 44 | return callerArgIndex; 45 | } 46 | 47 | public String getCallerArgPath() { 48 | return callerArgPath; 49 | } 50 | 51 | public int getTargetArgIndex() { 52 | return targetArgIndex; 53 | } 54 | 55 | @Override 56 | public boolean equals(Object o) { 57 | if (this == o) return true; 58 | if (o == null || getClass() != o.getClass()) return false; 59 | 60 | CallGraph CallGraph = (CallGraph) o; 61 | 62 | if (callerArgIndex != CallGraph.callerArgIndex) return false; 63 | if (targetArgIndex != CallGraph.targetArgIndex) return false; 64 | if (!Objects.equals(callerMethod, CallGraph.callerMethod)) 65 | return false; 66 | if (!Objects.equals(targetMethod, CallGraph.targetMethod)) 67 | return false; 68 | return Objects.equals(callerArgPath, CallGraph.callerArgPath); 69 | } 70 | 71 | @Override 72 | public int hashCode() { 73 | int result = callerMethod != null ? callerMethod.hashCode() : 0; 74 | result = 31 * result + (targetMethod != null ? targetMethod.hashCode() : 0); 75 | result = 31 * result + callerArgIndex; 76 | result = 31 * result + (callerArgPath != null ? callerArgPath.hashCode() : 0); 77 | result = 31 * result + targetArgIndex; 78 | return result; 79 | } 80 | 81 | 82 | public static class Factory implements DataFactory { 83 | 84 | @Override 85 | public CallGraph parse(String[] fields) { 86 | return new CallGraph( 87 | new MethodReference.Handle(new ClassReference.Handle(fields[0]), fields[1], fields[2]), 88 | new MethodReference.Handle(new ClassReference.Handle(fields[3]), fields[4], fields[5]), 89 | Integer.parseInt(fields[6]), 90 | fields[7], 91 | Integer.parseInt(fields[8])); 92 | } 93 | 94 | @Override 95 | public String[] serialize(CallGraph obj) { 96 | return new String[]{ 97 | obj.callerMethod.getClassReference().getName(), obj.callerMethod.getName(), obj.callerMethod.getDesc(), 98 | obj.targetMethod.getClassReference().getName(), obj.targetMethod.getName(), obj.targetMethod.getDesc(), 99 | Integer.toString(obj.callerArgIndex), 100 | obj.callerArgPath, 101 | Integer.toString(obj.targetArgIndex), 102 | }; 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/CallGraphClassVisitor.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import com.er1cccc.acaf.core.entity.ClassReference; 4 | import com.er1cccc.acaf.core.entity.MethodReference; 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.MethodVisitor; 7 | import org.objectweb.asm.Opcodes; 8 | import org.objectweb.asm.commons.JSRInlinerAdapter; 9 | 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | @SuppressWarnings("all") 14 | public class CallGraphClassVisitor extends ClassVisitor { 15 | private final Set discoveredCalls; 16 | private final Map classMap; 17 | private final InheritanceMap inheritanceMap; 18 | private final Map> passthroughDataflow; 19 | 20 | private String name; 21 | private String signature; 22 | private String superName; 23 | private String[] interfaces; 24 | 25 | public CallGraphClassVisitor(Map classMap, 26 | InheritanceMap inheritanceMap, 27 | Map> passthroughDataflow, 28 | Set discoveredCalls) { 29 | super(Opcodes.ASM6); 30 | this.classMap = classMap; 31 | this.inheritanceMap = inheritanceMap; 32 | this.passthroughDataflow = passthroughDataflow; 33 | this.discoveredCalls = discoveredCalls; 34 | } 35 | 36 | @Override 37 | public void visit(int version, int access, String name, String signature, 38 | String superName, String[] interfaces) { 39 | super.visit(version, access, name, signature, superName, interfaces); 40 | this.name = name; 41 | this.signature = signature; 42 | this.superName = superName; 43 | this.interfaces = interfaces; 44 | } 45 | 46 | @Override 47 | public MethodVisitor visitMethod(int access, String name, String desc, 48 | String signature, String[] exceptions) { 49 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 50 | CallGraphMethodAdapter modelGeneratorMethodVisitor = new CallGraphMethodAdapter(classMap, 51 | inheritanceMap, passthroughDataflow, api, discoveredCalls, 52 | mv, this.name, access, name, desc, signature, exceptions); 53 | return new JSRInlinerAdapter(modelGeneratorMethodVisitor, access, name, desc, signature, exceptions); 54 | } 55 | 56 | @Override 57 | public void visitEnd() { 58 | super.visitEnd(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/CallGraphMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | 4 | import com.er1cccc.acaf.core.entity.ClassReference; 5 | import com.er1cccc.acaf.core.entity.MethodReference; 6 | import org.objectweb.asm.MethodVisitor; 7 | import org.objectweb.asm.Opcodes; 8 | import org.objectweb.asm.Type; 9 | 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | @SuppressWarnings("all") 14 | public class CallGraphMethodAdapter extends CoreMethodAdapter { 15 | private final Map classMap; 16 | private final Set discoveredCalls; 17 | private final InheritanceMap inheritanceMap; 18 | private final String owner; 19 | private final int access; 20 | private final String name; 21 | private final String desc; 22 | 23 | public CallGraphMethodAdapter(Map classMap, 24 | InheritanceMap inheritanceMap, 25 | Map> passthroughDataflow, 26 | final int api, Set discoveredCalls, 27 | final MethodVisitor mv, final String owner, int access, String name, String desc, 28 | String signature, String[] exceptions) { 29 | super(inheritanceMap, passthroughDataflow, api, mv, owner, access, name, desc, signature, exceptions); 30 | this.classMap = classMap; 31 | this.inheritanceMap = inheritanceMap; 32 | this.owner = owner; 33 | this.access = access; 34 | this.name = name; 35 | this.desc = desc; 36 | this.discoveredCalls = discoveredCalls; 37 | } 38 | 39 | @Override 40 | public void visitCode() { 41 | super.visitCode(); 42 | int localIndex = 0; 43 | int argIndex = 0; 44 | if ((this.access & Opcodes.ACC_STATIC) == 0) { 45 | localVariables.set(localIndex, "arg" + argIndex); 46 | localIndex += 1; 47 | argIndex += 1; 48 | } 49 | for (Type argType : Type.getArgumentTypes(desc)) { 50 | localVariables.set(localIndex, "arg" + argIndex); 51 | localIndex += argType.getSize(); 52 | argIndex += 1; 53 | } 54 | } 55 | 56 | @Override 57 | public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 58 | Type[] argTypes = Type.getArgumentTypes(desc); 59 | if (opcode != Opcodes.INVOKESTATIC) { 60 | Type[] extendedArgTypes = new Type[argTypes.length + 1]; 61 | System.arraycopy(argTypes, 0, extendedArgTypes, 1, argTypes.length); 62 | extendedArgTypes[0] = Type.getObjectType(owner); 63 | argTypes = extendedArgTypes; 64 | } 65 | switch (opcode) { 66 | case Opcodes.INVOKESTATIC: 67 | case Opcodes.INVOKEVIRTUAL: 68 | case Opcodes.INVOKESPECIAL: 69 | case Opcodes.INVOKEINTERFACE: 70 | int stackIndex = 0; 71 | for (int i = 0; i < argTypes.length; i++) { 72 | int argIndex = argTypes.length - 1 - i; 73 | Type type = argTypes[argIndex]; 74 | Set taint = operandStack.get(stackIndex); 75 | if (taint.size() > 0) { 76 | for (String argSrc : taint) { 77 | if (!argSrc.startsWith("arg")) { 78 | throw new IllegalStateException("invalid taint arg: " + argSrc); 79 | } 80 | int dotIndex = argSrc.indexOf('.'); 81 | int srcArgIndex; 82 | String srcArgPath; 83 | if (dotIndex == -1) { 84 | srcArgIndex = Integer.parseInt(argSrc.substring(3)); 85 | srcArgPath = null; 86 | } else { 87 | srcArgIndex = Integer.parseInt(argSrc.substring(3, dotIndex)); 88 | srcArgPath = argSrc.substring(dotIndex + 1); 89 | } 90 | discoveredCalls.add(new CallGraph( 91 | new MethodReference.Handle( 92 | new ClassReference.Handle(this.owner), this.name, this.desc), 93 | new MethodReference.Handle( 94 | new ClassReference.Handle(owner), name, desc), 95 | srcArgIndex, 96 | srcArgPath, 97 | argIndex)); 98 | } 99 | }else{ 100 | discoveredCalls.add(new CallGraph( 101 | new MethodReference.Handle( 102 | new ClassReference.Handle(this.owner), this.name, this.desc), 103 | new MethodReference.Handle( 104 | new ClassReference.Handle(owner), name, desc) 105 | )); 106 | } 107 | stackIndex += type.getSize(); 108 | } 109 | break; 110 | default: 111 | throw new IllegalStateException("unsupported opcode: " + opcode); 112 | } 113 | super.visitMethodInsn(opcode, owner, name, desc, itf); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/DataFlowClassVisitor.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import com.er1cccc.acaf.core.entity.ClassReference; 4 | import com.er1cccc.acaf.core.entity.MethodReference; 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.FieldVisitor; 7 | import org.objectweb.asm.MethodVisitor; 8 | import org.objectweb.asm.Opcodes; 9 | import org.objectweb.asm.commons.JSRInlinerAdapter; 10 | 11 | import java.util.HashSet; 12 | import java.util.Map; 13 | import java.util.Set; 14 | 15 | @SuppressWarnings("all") 16 | public class DataFlowClassVisitor extends ClassVisitor { 17 | Map classMap; 18 | private final MethodReference.Handle methodToVisit; 19 | private final InheritanceMap inheritanceMap; 20 | private final Map> passthroughDataflow; 21 | 22 | private String name; 23 | private DataFlowMethodAdapter dataFlowMethodAdapter; 24 | 25 | private Set fields; 26 | 27 | public DataFlowClassVisitor(Map classMap, 28 | InheritanceMap inheritanceMap, 29 | Map> passthroughDataflow, 30 | MethodReference.Handle methodToVisit) { 31 | super(Opcodes.ASM6); 32 | this.classMap = classMap; 33 | this.inheritanceMap = inheritanceMap; 34 | this.methodToVisit = methodToVisit; 35 | this.passthroughDataflow = passthroughDataflow; 36 | this.fields=new HashSet<>(); 37 | } 38 | 39 | @Override 40 | public void visit(int version, int access, String name, String signature, 41 | String superName, String[] interfaces) { 42 | super.visit(version, access, name, signature, superName, interfaces); 43 | this.name = name; 44 | } 45 | 46 | @Override 47 | public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { 48 | FieldVisitor fieldVisitor = super.visitField(access, name, descriptor, signature, value); 49 | fields.add(new Field(name,descriptor)); 50 | return fieldVisitor; 51 | } 52 | 53 | @Override 54 | public MethodVisitor visitMethod(int access, String name, String desc, 55 | String signature, String[] exceptions) { 56 | if (!name.equals(methodToVisit.getName()) || !desc.equals(methodToVisit.getDesc())) { 57 | return null; 58 | } 59 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 60 | dataFlowMethodAdapter = new DataFlowMethodAdapter(classMap, inheritanceMap, 61 | this.passthroughDataflow, mv, this.name, access, name, desc, signature, exceptions, this.fields); 62 | return new JSRInlinerAdapter(dataFlowMethodAdapter, access, name, desc, signature, exceptions); 63 | } 64 | 65 | public Set getReturnTaint() { 66 | if (dataFlowMethodAdapter == null) { 67 | return null; 68 | } else { 69 | return dataFlowMethodAdapter.getReturnTaint(); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/DataFlowMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import com.er1cccc.acaf.core.entity.ClassReference; 4 | import com.er1cccc.acaf.core.entity.MethodReference; 5 | import org.objectweb.asm.MethodVisitor; 6 | import org.objectweb.asm.Opcodes; 7 | import org.objectweb.asm.Type; 8 | 9 | import java.util.*; 10 | 11 | @SuppressWarnings("all") 12 | public class DataFlowMethodAdapter extends CoreMethodAdapter { 13 | 14 | private final Map classMap; 15 | private final InheritanceMap inheritanceMap; 16 | private final Map> passthroughDataflow; 17 | 18 | private final int access; 19 | private final String desc; 20 | private final Set returnTaint; 21 | private String owner; 22 | private boolean getter; 23 | 24 | public DataFlowMethodAdapter(Map classMap, 25 | InheritanceMap inheritanceMap, 26 | Map> passthroughDataflow, 27 | MethodVisitor mv, String owner, int access, String name, 28 | String desc, String signature, String[] exceptions, Set fields) { 29 | super(inheritanceMap, passthroughDataflow, 30 | Opcodes.ASM6, mv, owner, access, name, desc, signature, exceptions); 31 | this.classMap = classMap; 32 | this.inheritanceMap = inheritanceMap; 33 | this.passthroughDataflow = passthroughDataflow; 34 | this.access = access; 35 | this.desc = desc; 36 | this.owner=owner; 37 | returnTaint = new HashSet<>(); 38 | 39 | initGetter(name, desc, fields, access); 40 | } 41 | 42 | private void initGetter(String name, String desc, Set fields, int access) { 43 | if ((this.access & Opcodes.ACC_STATIC) !=0) { 44 | getter=false; 45 | }else if(name.startsWith("get")||name.startsWith("is")){ 46 | String fieldName; 47 | if(name.startsWith("get")){ 48 | fieldName=name.replaceFirst("get",""); 49 | }else{ 50 | fieldName=name.replaceFirst("is",""); 51 | } 52 | if(fieldName.length()==0) 53 | return; 54 | char[] chars = fieldName.toCharArray(); 55 | chars[0]= Character.toLowerCase(fieldName.charAt(0)); 56 | fieldName=new String(chars); 57 | Type returnType = Type.getReturnType(desc); 58 | Field field = new Field(fieldName, returnType.getDescriptor()); 59 | getter = fields.contains(field); 60 | } 61 | } 62 | 63 | @Override 64 | public void visitCode() { 65 | super.visitCode(); 66 | int localIndex = 0; 67 | int argIndex = 0; 68 | if ((this.access & Opcodes.ACC_STATIC) == 0) { 69 | localVariables.set(localIndex, argIndex); 70 | localIndex += 1; 71 | argIndex += 1; 72 | } 73 | for (Type argType : Type.getArgumentTypes(desc)) { 74 | localVariables.set(localIndex, argIndex); 75 | localIndex += argType.getSize(); 76 | argIndex += 1; 77 | } 78 | } 79 | 80 | @Override 81 | public void visitInsn(int opcode) { 82 | switch (opcode) { 83 | case Opcodes.IRETURN: 84 | case Opcodes.FRETURN: 85 | case Opcodes.ARETURN: 86 | returnTaint.addAll(operandStack.get(0)); 87 | break; 88 | case Opcodes.LRETURN: 89 | case Opcodes.DRETURN: 90 | returnTaint.addAll(operandStack.get(1)); 91 | break; 92 | case Opcodes.RETURN: 93 | break; 94 | default: 95 | break; 96 | } 97 | super.visitInsn(opcode); 98 | } 99 | 100 | @Override 101 | public void visitFieldInsn(int opcode, String owner, String name, String desc) { 102 | super.visitFieldInsn(opcode,owner,name,desc); 103 | if(isGetter()&&opcode==Opcodes.GETFIELD){ 104 | operandStack.get(0).add(0); 105 | } 106 | } 107 | 108 | private boolean isGetter() { 109 | return getter; 110 | } 111 | 112 | 113 | @Override 114 | public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 115 | Type[] argTypes = Type.getArgumentTypes(desc); 116 | if (opcode != Opcodes.INVOKESTATIC) { 117 | Type[] extendedArgTypes = new Type[argTypes.length+1]; 118 | System.arraycopy(argTypes, 0, extendedArgTypes, 1, argTypes.length); 119 | extendedArgTypes[0] = Type.getObjectType(owner); 120 | argTypes = extendedArgTypes; 121 | } 122 | int retSize = Type.getReturnType(desc).getSize(); 123 | Set resultTaint; 124 | switch (opcode) { 125 | case Opcodes.INVOKESTATIC: 126 | case Opcodes.INVOKEVIRTUAL: 127 | case Opcodes.INVOKESPECIAL: 128 | case Opcodes.INVOKEINTERFACE: 129 | final List> argTaint = new ArrayList<>(argTypes.length); 130 | for (int i = 0; i < argTypes.length; i++) { 131 | argTaint.add(null); 132 | } 133 | int stackIndex = 0; 134 | for (int i = 0; i < argTypes.length; i++) { 135 | Type argType = argTypes[i]; 136 | if (argType.getSize() > 0) { 137 | argTaint.set(argTypes.length - 1 - i, 138 | operandStack.get(stackIndex+argType.getSize()-1)); 139 | } 140 | stackIndex += argType.getSize(); 141 | } 142 | if (name.equals("")) { 143 | resultTaint = argTaint.get(0); 144 | } else { 145 | resultTaint = new HashSet<>(); 146 | } 147 | Set passthrough = passthroughDataflow.get( 148 | new MethodReference.Handle(new ClassReference.Handle(owner), name, desc)); 149 | if (passthrough != null) { 150 | for (Integer passthroughDataflowArg : passthrough) { 151 | resultTaint.addAll(argTaint.get(passthroughDataflowArg)); 152 | } 153 | } 154 | break; 155 | default: 156 | throw new IllegalStateException("unsupported opcode: " + opcode); 157 | } 158 | super.visitMethodInsn(opcode, owner, name, desc, itf); 159 | if (retSize > 0) { 160 | operandStack.get(retSize-1).addAll(resultTaint); 161 | } 162 | } 163 | 164 | public Set getReturnTaint() { 165 | return returnTaint; 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/DiscoveryClassVisitor.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import com.er1cccc.acaf.core.entity.ClassFile; 4 | import com.er1cccc.acaf.core.entity.ClassReference; 5 | import com.er1cccc.acaf.core.entity.MethodReference; 6 | import org.apache.log4j.Logger; 7 | import org.objectweb.asm.*; 8 | 9 | import java.util.*; 10 | 11 | @SuppressWarnings("all") 12 | public class DiscoveryClassVisitor extends ClassVisitor { 13 | private static final Logger logger = Logger.getLogger(DiscoveryClassVisitor.class); 14 | 15 | private String name; 16 | private String superName; 17 | private String[] interfaces; 18 | private boolean isInterface; 19 | private List members; 20 | private ClassReference.Handle classHandle; 21 | private Set annotations; 22 | private List discoveredClasses; 23 | private List discoveredMethods; 24 | private ClassFile classFile; 25 | 26 | public DiscoveryClassVisitor(List discoveredClasses, 27 | List discoveredMethods, 28 | ClassFile classFile) { 29 | super(Opcodes.ASM6); 30 | this.discoveredClasses = discoveredClasses; 31 | this.discoveredMethods = discoveredMethods; 32 | this.classFile=classFile; 33 | } 34 | 35 | @Override 36 | public void visit(int version, int access, String name, 37 | String signature, String superName, String[] interfaces) { 38 | this.name = name; 39 | this.superName = superName; 40 | this.interfaces = interfaces; 41 | this.isInterface = (access & Opcodes.ACC_INTERFACE) != 0; 42 | this.members = new ArrayList<>(); 43 | this.classHandle = new ClassReference.Handle(name); 44 | annotations = new HashSet<>(); 45 | super.visit(version, access, name, signature, superName, interfaces); 46 | } 47 | 48 | @Override 49 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 50 | annotations.add(descriptor); 51 | return super.visitAnnotation(descriptor, visible); 52 | } 53 | 54 | public FieldVisitor visitField(int access, String name, String desc, 55 | String signature, Object value) { 56 | if ((access & Opcodes.ACC_STATIC) == 0) { 57 | Type type = Type.getType(desc); 58 | String typeName; 59 | if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { 60 | typeName = type.getInternalName(); 61 | } else { 62 | typeName = type.getDescriptor(); 63 | } 64 | members.add(new ClassReference.Member(name, access, new ClassReference.Handle(typeName))); 65 | } 66 | return super.visitField(access, name, desc, signature, value); 67 | } 68 | 69 | @Override 70 | public MethodVisitor visitMethod(int access, String name, String desc, 71 | String signature, String[] exceptions) { 72 | boolean isStatic = (access & Opcodes.ACC_STATIC) != 0; 73 | discoveredMethods.add(new MethodReference( 74 | classHandle, 75 | name, 76 | desc, 77 | isStatic)); 78 | return super.visitMethod(access, name, desc, signature, exceptions); 79 | } 80 | 81 | @Override 82 | public void visitEnd() { 83 | ClassReference classReference = new ClassReference( 84 | name, 85 | superName, 86 | Arrays.asList(interfaces), 87 | isInterface, 88 | members, 89 | annotations); 90 | discoveredClasses.add(classReference); 91 | super.visitEnd(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/Field.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class Field { 11 | private String name; 12 | private String descriptor; 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/GotoState.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | @SuppressWarnings("all") 4 | public class GotoState { 5 | private LocalVariables localVariables; 6 | private OperandStack operandStack; 7 | 8 | public LocalVariables getLocalVariables() { 9 | return localVariables; 10 | } 11 | 12 | public void setLocalVariables(LocalVariables localVariables) { 13 | this.localVariables = localVariables; 14 | } 15 | 16 | public OperandStack getOperandStack() { 17 | return operandStack; 18 | } 19 | 20 | public void setOperandStack(OperandStack operandStack) { 21 | this.operandStack = operandStack; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/InheritanceMap.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import com.er1cccc.acaf.core.entity.ClassReference; 4 | import com.er1cccc.acaf.data.DataFactory; 5 | import com.er1cccc.acaf.data.DataLoader; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.nio.file.Path; 10 | import java.nio.file.Paths; 11 | import java.util.*; 12 | 13 | @SuppressWarnings("all") 14 | public class InheritanceMap { 15 | private final Map> inheritanceMap; 16 | private final Map> subClassMap; 17 | 18 | public InheritanceMap(Map> inheritanceMap) { 19 | this.inheritanceMap = inheritanceMap; 20 | subClassMap = new HashMap<>(); 21 | for (Map.Entry> entry : inheritanceMap.entrySet()) { 22 | ClassReference.Handle child = entry.getKey(); 23 | for (ClassReference.Handle parent : entry.getValue()) { 24 | if (!subClassMap.containsKey(parent)) { 25 | Set tempSet = new HashSet<>(); 26 | tempSet.add(child); 27 | subClassMap.put(parent, tempSet); 28 | } else { 29 | subClassMap.get(parent).add(child); 30 | } 31 | } 32 | } 33 | } 34 | 35 | public Set>> entrySet() { 36 | return inheritanceMap.entrySet(); 37 | } 38 | 39 | public Set getSuperClasses(ClassReference.Handle clazz) { 40 | Set parents = inheritanceMap.get(clazz); 41 | if (parents == null) { 42 | return null; 43 | } 44 | return Collections.unmodifiableSet(parents); 45 | } 46 | 47 | public boolean isSubclassOf(ClassReference.Handle clazz, ClassReference.Handle superClass) { 48 | Set parents = inheritanceMap.get(clazz); 49 | if (parents == null) { 50 | return false; 51 | } 52 | return parents.contains(superClass); 53 | } 54 | 55 | public Set getSubClasses(ClassReference.Handle clazz) { 56 | Set subClasses = subClassMap.get(clazz); 57 | if (subClasses == null) { 58 | return null; 59 | } 60 | return Collections.unmodifiableSet(subClasses); 61 | } 62 | 63 | public void save(Path dirPath) throws IOException { 64 | DataLoader.saveData(new File(dirPath.toFile(),"inheritanceMap.dat").toPath(), new InheritanceMapFactory(), inheritanceMap.entrySet()); 65 | } 66 | 67 | public static InheritanceMap load(Path dirPath) throws IOException { 68 | Map> inheritanceMap = new HashMap<>(); 69 | for (Map.Entry> entry : DataLoader.loadData( 70 | new File(dirPath.toFile(),"inheritanceMap.dat").toPath(), new InheritanceMapFactory())) { 71 | inheritanceMap.put(entry.getKey(), entry.getValue()); 72 | } 73 | return new InheritanceMap(inheritanceMap); 74 | } 75 | 76 | private static class InheritanceMapFactory implements DataFactory>> { 77 | @Override 78 | public Map.Entry> parse(String[] fields) { 79 | ClassReference.Handle clazz = new ClassReference.Handle(fields[0]); 80 | Set superClasses = new HashSet<>(); 81 | for (int i = 1; i < fields.length; i++) { 82 | superClasses.add(new ClassReference.Handle(fields[i])); 83 | } 84 | return new AbstractMap.SimpleEntry<>(clazz, superClasses); 85 | } 86 | 87 | @Override 88 | public String[] serialize(Map.Entry> obj) { 89 | final String[] fields = new String[obj.getValue().size()+1]; 90 | fields[0] = obj.getKey().getName(); 91 | int i = 1; 92 | for (ClassReference.Handle handle : obj.getValue()) { 93 | fields[i++] = handle.getName(); 94 | } 95 | return fields; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/InheritanceUtil.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import com.er1cccc.acaf.core.entity.ClassReference; 4 | import com.er1cccc.acaf.core.entity.MethodReference; 5 | 6 | import java.util.HashMap; 7 | import java.util.HashSet; 8 | import java.util.Map; 9 | import java.util.Set; 10 | 11 | @SuppressWarnings("all") 12 | public class InheritanceUtil { 13 | public static InheritanceMap derive(Map classMap) { 14 | Map> implicitInheritance = new HashMap<>(); 15 | for (ClassReference classReference : classMap.values()) { 16 | Set allParents = new HashSet<>(); 17 | getAllParents(classReference, classMap, allParents); 18 | implicitInheritance.put(classReference.getHandle(), allParents); 19 | } 20 | return new InheritanceMap(implicitInheritance); 21 | } 22 | 23 | /** 24 | * 递归查找classReference的祖先类、祖先接口,全部存到allParents里去 25 | * @param classReference 26 | * @param classMap 27 | * @param allParents 28 | */ 29 | private static void getAllParents(ClassReference classReference, 30 | Map classMap, 31 | Set allParents) { 32 | Set parents = new HashSet<>(); 33 | if (classReference.getSuperClass() != null) { 34 | parents.add(new ClassReference.Handle(classReference.getSuperClass())); 35 | } 36 | for (String i : classReference.getInterfaces()) { 37 | parents.add(new ClassReference.Handle(i)); 38 | } 39 | 40 | for (ClassReference.Handle immediateParent : parents) { 41 | ClassReference parentClassReference = classMap.get(immediateParent); 42 | if (parentClassReference == null) { 43 | continue; 44 | } 45 | allParents.add(parentClassReference.getHandle()); 46 | getAllParents(parentClassReference, classMap, allParents); 47 | } 48 | } 49 | 50 | public static Map> getAllMethodImplementations( 51 | InheritanceMap inheritanceMap, Map methodMap) { 52 | Map> methodsByClass = getMethodsByClass(methodMap); 53 | Map> subClassMap = new HashMap<>(); 54 | for (Map.Entry> entry : inheritanceMap.entrySet()) { 55 | for (ClassReference.Handle parent : entry.getValue()) { 56 | if (!subClassMap.containsKey(parent)) { 57 | Set subClasses = new HashSet<>(); 58 | subClasses.add(entry.getKey()); 59 | subClassMap.put(parent, subClasses); 60 | } else { 61 | subClassMap.get(parent).add(entry.getKey()); 62 | } 63 | } 64 | } 65 | Map> methodImplMap = new HashMap<>(); 66 | for (MethodReference method : methodMap.values()) { 67 | if (method.isStatic()) { 68 | continue; 69 | } 70 | Set overridingMethods = new HashSet<>(); 71 | Set subClasses = subClassMap.get(method.getClassReference()); 72 | if (subClasses != null) { 73 | for (ClassReference.Handle subClass : subClasses) { 74 | Set subClassMethods = methodsByClass.get(subClass); 75 | if (subClassMethods != null) { 76 | for (MethodReference.Handle subClassMethod : subClassMethods) { 77 | if (subClassMethod.getName().equals(method.getName()) && 78 | subClassMethod.getDesc().equals(method.getDesc())) { 79 | overridingMethods.add(subClassMethod); 80 | } 81 | } 82 | } 83 | } 84 | } 85 | if (overridingMethods.size() > 0) { 86 | methodImplMap.put(method.getHandle(), overridingMethods); 87 | } 88 | } 89 | return methodImplMap; 90 | } 91 | 92 | public static Map> getMethodsByClass( 93 | Map methodMap) { 94 | Map> methodsByClass = new HashMap<>(); 95 | for (MethodReference.Handle method : methodMap.keySet()) { 96 | ClassReference.Handle classReference = method.getClassReference(); 97 | if (!methodsByClass.containsKey(classReference)) { 98 | Set methods = new HashSet<>(); 99 | methods.add(method); 100 | methodsByClass.put(classReference, methods); 101 | } else { 102 | methodsByClass.get(classReference).add(method); 103 | } 104 | } 105 | return methodsByClass; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/LocalVariables.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashSet; 5 | import java.util.List; 6 | import java.util.Set; 7 | 8 | @SuppressWarnings("all") 9 | public class LocalVariables { 10 | private final ArrayList> array; 11 | 12 | public LocalVariables() { 13 | this.array = new ArrayList<>(); 14 | } 15 | 16 | public void clear(){ 17 | this.array.clear(); 18 | } 19 | 20 | public void add(Set t){ 21 | this.array.add(t); 22 | } 23 | 24 | public void set(int index, Set t) { 25 | array.set(index, t); 26 | } 27 | 28 | public void set(int index, T t) { 29 | Set set = new HashSet<>(); 30 | set.add(t); 31 | array.set(index, set); 32 | } 33 | 34 | public Set get(int index) { 35 | return array.get(index); 36 | } 37 | 38 | public int size(){ 39 | return this.array.size(); 40 | } 41 | 42 | public void remove(int index){ 43 | this.array.remove(index); 44 | } 45 | public List> getList(){ 46 | return this.array; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/MethodCallClassVisitor.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import com.er1cccc.acaf.core.entity.MethodReference; 4 | import org.objectweb.asm.ClassVisitor; 5 | import org.objectweb.asm.MethodVisitor; 6 | import org.objectweb.asm.Opcodes; 7 | import org.objectweb.asm.commons.JSRInlinerAdapter; 8 | 9 | import java.util.Map; 10 | import java.util.Set; 11 | 12 | @SuppressWarnings("all") 13 | public class MethodCallClassVisitor extends ClassVisitor { 14 | private final Map> methodCalls; 15 | 16 | public MethodCallClassVisitor(Map> methodCalls) { 17 | super(Opcodes.ASM6); 18 | this.methodCalls = methodCalls; 19 | } 20 | 21 | private String name = null; 22 | 23 | @Override 24 | public void visit(int version, int access, String name, String signature, 25 | String superName, String[] interfaces) { 26 | super.visit(version, access, name, signature, superName, interfaces); 27 | if (this.name != null) { 28 | throw new IllegalStateException("ClassVisitor already visited a class!"); 29 | } 30 | this.name = name; 31 | } 32 | 33 | public String getName() { 34 | return name; 35 | } 36 | 37 | public Map> getMethodCalls() { 38 | return this.methodCalls; 39 | } 40 | 41 | @Override 42 | public MethodVisitor visitMethod(int access, String name, String desc, 43 | String signature, String[] exceptions) { 44 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 45 | MethodCallMethodAdapter adapter = new MethodCallMethodAdapter( 46 | api, this.methodCalls, mv, this.name, name, desc); 47 | return new JSRInlinerAdapter(adapter, access, name, desc, signature, exceptions); 48 | } 49 | 50 | @Override 51 | public void visitEnd() { 52 | super.visitEnd(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/MethodCallMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import com.er1cccc.acaf.core.entity.ClassReference; 4 | import com.er1cccc.acaf.core.entity.MethodReference; 5 | import org.objectweb.asm.MethodVisitor; 6 | import java.util.HashSet; 7 | import java.util.Map; 8 | import java.util.Set; 9 | 10 | @SuppressWarnings("all") 11 | public class MethodCallMethodAdapter extends MethodVisitor { 12 | private final Set calledMethods; 13 | 14 | public MethodCallMethodAdapter(int api, Map> methodCalls, 15 | final MethodVisitor mv, final String owner, String name, String desc) { 16 | super(api, mv); 17 | this.calledMethods = new HashSet<>(); 18 | methodCalls.put(new MethodReference.Handle(new ClassReference.Handle(owner), name, desc), calledMethods); 19 | } 20 | 21 | @Override 22 | public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 23 | calledMethods.add(new MethodReference.Handle(new ClassReference.Handle(owner), name, desc)); 24 | super.visitMethodInsn(opcode, owner, name, desc, itf); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/OperandStack.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import java.util.HashSet; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | import java.util.Set; 7 | 8 | @SuppressWarnings("all") 9 | public class OperandStack { 10 | private final LinkedList> stack; 11 | 12 | public OperandStack() { 13 | this.stack = new LinkedList<>(); 14 | } 15 | 16 | public Set pop() { 17 | return stack.remove(stack.size() - 1); 18 | } 19 | 20 | public void push(T t) { 21 | Set set = new HashSet<>(); 22 | set.add(t); 23 | stack.add(set); 24 | } 25 | 26 | public void push() { 27 | stack.add(new HashSet<>()); 28 | } 29 | 30 | public void push(Set t) { 31 | stack.add(t); 32 | } 33 | 34 | public void clear() { 35 | stack.clear(); 36 | } 37 | 38 | public Set get(int index) { 39 | return stack.get(stack.size() - index - 1); 40 | } 41 | 42 | public void set(int index, Set t) { 43 | stack.set(stack.size() - index - 1, t); 44 | } 45 | 46 | public void set(int index, T t) { 47 | Set set = new HashSet<>(); 48 | set.add(t); 49 | stack.set(stack.size() - index - 1, set); 50 | } 51 | 52 | public void add(Set t) { 53 | this.stack.add(t); 54 | } 55 | 56 | public int size() { 57 | return this.stack.size(); 58 | } 59 | 60 | public void remove(int index) { 61 | this.stack.remove(index); 62 | } 63 | 64 | public List> getList() { 65 | return this.stack; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/SpringAnnoAdapter.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import com.er1cccc.acaf.core.entity.SpringParam; 4 | import org.objectweb.asm.AnnotationVisitor; 5 | 6 | import java.util.List; 7 | 8 | public class SpringAnnoAdapter extends AnnotationVisitor { 9 | private final List params; 10 | private final int index; 11 | 12 | public SpringAnnoAdapter(int api, AnnotationVisitor annotationVisitor, 13 | List params, int parameter) { 14 | super(api, annotationVisitor); 15 | this.index = parameter; 16 | this.params = params; 17 | } 18 | 19 | @Override 20 | public void visit(String name, Object value) { 21 | if (name.equals("name")) { 22 | SpringParam param = this.params.get(this.index); 23 | param.setReqName(value.toString()); 24 | param.setParamIndex(this.index); 25 | this.params.set(this.index,param); 26 | } 27 | super.visit(name, value); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/SpringClassVisitor.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import com.er1cccc.acaf.core.entity.ClassReference; 4 | import com.er1cccc.acaf.core.entity.MethodReference; 5 | import com.er1cccc.acaf.core.entity.SpringController; 6 | import org.objectweb.asm.ClassVisitor; 7 | import org.objectweb.asm.MethodVisitor; 8 | import org.objectweb.asm.Opcodes; 9 | 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Set; 13 | 14 | public class SpringClassVisitor extends ClassVisitor { 15 | private final Map classMap; 16 | private final Map methodMap; 17 | private final List controllers; 18 | private boolean isSpring; 19 | private SpringController currentController; 20 | private final String packageName; 21 | private String name; 22 | 23 | public SpringClassVisitor(String packageName, List controllers, 24 | Map classMap, 25 | Map methodMap) { 26 | super(Opcodes.ASM6); 27 | this.methodMap = methodMap; 28 | this.controllers = controllers; 29 | this.packageName = packageName; 30 | this.classMap = classMap; 31 | } 32 | 33 | @Override 34 | public void visit(int version, int access, String name, String signature, 35 | String superName, String[] interfaces) { 36 | this.name = name; 37 | if (name.startsWith(this.packageName)) { 38 | Set annotations = classMap.get(new ClassReference.Handle(name)).getAnnotations(); 39 | if (annotations.contains(SpringConstant.ControllerAnno) || 40 | annotations.contains(SpringConstant.RestControllerAnno)) { 41 | this.isSpring = true; 42 | currentController = new SpringController(); 43 | currentController.setClassReference(classMap.get(new ClassReference.Handle(name))); 44 | currentController.setClassName(new ClassReference.Handle(name)); 45 | currentController.setRest(!annotations.contains(SpringConstant.ControllerAnno)); 46 | } 47 | } else { 48 | this.isSpring = false; 49 | } 50 | super.visit(version, access, name, signature, superName, interfaces); 51 | } 52 | 53 | @Override 54 | public MethodVisitor visitMethod(int access, String name, String descriptor, 55 | String signature, String[] exceptions) { 56 | MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); 57 | if (this.isSpring) { 58 | if (this.name.equals("") || this.name.equals("")) { 59 | return mv; 60 | } 61 | return new SpringMethodAdapter(name, descriptor, this.name, Opcodes.ASM6, mv, 62 | currentController, this.methodMap); 63 | } else { 64 | return mv; 65 | } 66 | } 67 | 68 | @Override 69 | public void visitEnd() { 70 | if (isSpring) { 71 | controllers.add(currentController); 72 | } 73 | super.visitEnd(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/SpringConstant.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | public interface SpringConstant { 4 | String ControllerAnno = "Lorg/springframework/stereotype/Controller;"; 5 | String RestControllerAnno = "Lorg/springframework/web/bind/annotation/RestController;"; 6 | String RequestMappingAnno = "Lorg/springframework/web/bind/annotation/RequestMapping;"; 7 | String ResponseBodyAnno = "Lorg/springframework/web/bind/annotation/ResponseBody;"; 8 | String RequestParamAnno = "Lorg/springframework/web/bind/annotation/RequestParam;"; 9 | } 10 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/SpringMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import com.er1cccc.acaf.core.entity.*; 4 | import org.objectweb.asm.AnnotationVisitor; 5 | import org.objectweb.asm.MethodVisitor; 6 | import org.objectweb.asm.Opcodes; 7 | import org.objectweb.asm.Type; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | 14 | public class SpringMethodAdapter extends MethodVisitor { 15 | private final Map methodMap; 16 | private final List requestParam = new ArrayList<>(); 17 | private SpringMapping currentMapping; 18 | private final SpringController controller; 19 | private final String name; 20 | private final String owner; 21 | private final String desc; 22 | 23 | private SpringPathAnnoAdapter pathAnnoAdapter; 24 | 25 | public SpringMethodAdapter(String name, String descriptor, String owner, 26 | int api, MethodVisitor methodVisitor, 27 | SpringController currentController, 28 | Map methodMap) { 29 | super(api, methodVisitor); 30 | this.owner = owner; 31 | this.desc = descriptor; 32 | this.name = name; 33 | this.methodMap = methodMap; 34 | this.controller = currentController; 35 | } 36 | 37 | @Override 38 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 39 | AnnotationVisitor av = super.visitAnnotation(descriptor, visible); 40 | if (descriptor.equals(SpringConstant.RequestMappingAnno)) { 41 | currentMapping = new SpringMapping(); 42 | currentMapping.setMethodName(new MethodReference.Handle( 43 | new ClassReference.Handle(this.owner), this.name, this.desc)); 44 | currentMapping.setController(controller); 45 | currentMapping.setMethodReference(methodMap.get(currentMapping.getMethodName())); 46 | currentMapping.setParamMap(this.requestParam); 47 | pathAnnoAdapter = new SpringPathAnnoAdapter(Opcodes.ASM6, av); 48 | av = pathAnnoAdapter; 49 | } 50 | if (descriptor.equals(SpringConstant.ResponseBodyAnno)) { 51 | currentMapping.setRest(true); 52 | } 53 | return av; 54 | } 55 | 56 | @Override 57 | public void visitCode() { 58 | if (this.currentMapping != null) { 59 | if (pathAnnoAdapter.getResults().size() > 0) { 60 | currentMapping.setPath(pathAnnoAdapter.getResults().get(0)); 61 | } 62 | } 63 | Type[] argTypes = Type.getArgumentTypes(this.desc); 64 | for (int i = 0; i < argTypes.length; i++) { 65 | this.requestParam.get(i).setParamType(argTypes[i].getClassName()); 66 | } 67 | super.visitCode(); 68 | } 69 | 70 | @Override 71 | public AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor, boolean visible) { 72 | AnnotationVisitor av = super.visitParameterAnnotation(parameter, descriptor, visible); 73 | if (descriptor.equals(SpringConstant.RequestParamAnno)) { 74 | return new SpringAnnoAdapter(Opcodes.ASM6, av, requestParam, parameter);//修正请求参数的key 75 | } 76 | return av; 77 | } 78 | 79 | 80 | @Override 81 | public void visitParameter(String name, int access) { 82 | SpringParam param = new SpringParam(); 83 | param.setParamName(name); 84 | this.requestParam.add(param); 85 | super.visitParameter(name, access); 86 | } 87 | 88 | 89 | @Override 90 | public void visitEnd() { 91 | if (currentMapping != null) { 92 | controller.addMapping(currentMapping); 93 | } 94 | super.visitEnd(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/SpringPathAnnoAdapter.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import org.objectweb.asm.AnnotationVisitor; 4 | import org.objectweb.asm.Opcodes; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class SpringPathAnnoAdapter extends AnnotationVisitor { 10 | private final List results = new ArrayList<>(); 11 | 12 | public SpringPathAnnoAdapter(int api, AnnotationVisitor annotationVisitor) { 13 | super(api, annotationVisitor); 14 | } 15 | 16 | public List getResults() { 17 | return results; 18 | } 19 | 20 | @Override 21 | public AnnotationVisitor visitArray(String name) { 22 | AnnotationVisitor av = super.visitArray(name); 23 | return new ArrayVisitor(Opcodes.ASM6, av,results); 24 | } 25 | 26 | static class ArrayVisitor extends AnnotationVisitor { 27 | private final List results; 28 | 29 | public ArrayVisitor(int api, AnnotationVisitor annotationVisitor, List results) { 30 | super(api, annotationVisitor); 31 | this.results = results; 32 | } 33 | 34 | @Override 35 | public void visit(String name, Object value) { 36 | if (!value.toString().trim().equals("")) { 37 | results.add(value.toString()); 38 | } 39 | super.visit(name, value); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/VulnClassVisitor.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import com.er1cccc.acaf.core.entity.MethodReference; 4 | import com.er1cccc.acaf.core.resolver.Resolver; 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.MethodVisitor; 7 | import org.objectweb.asm.Opcodes; 8 | import org.objectweb.asm.commons.JSRInlinerAdapter; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Set; 13 | 14 | public class VulnClassVisitor extends ClassVisitor { 15 | private final InheritanceMap inheritanceMap; 16 | private final Map> dataFlow; 17 | 18 | private String name; 19 | private String signature; 20 | private String superName; 21 | private String[] interfaces; 22 | Resolver vulnResolver; 23 | 24 | private MethodReference.Handle methodHandle; 25 | private int methodArgIndex; 26 | private List pass; 27 | 28 | public VulnClassVisitor(MethodReference.Handle targetMethod, 29 | int targetIndex, InheritanceMap inheritanceMap, 30 | Map> dataFlow, 31 | Resolver vulnResolver) { 32 | super(Opcodes.ASM6); 33 | this.inheritanceMap = inheritanceMap; 34 | this.dataFlow = dataFlow; 35 | this.methodHandle = targetMethod; 36 | this.methodArgIndex = targetIndex; 37 | this.pass = new ArrayList<>(); 38 | this.vulnResolver=vulnResolver; 39 | } 40 | 41 | 42 | @Override 43 | public void visit(int version, int access, String name, String signature, 44 | String superName, String[] interfaces) { 45 | super.visit(version, access, name, signature, superName, interfaces); 46 | this.name = name; 47 | this.signature = signature; 48 | this.superName = superName; 49 | this.interfaces = interfaces; 50 | } 51 | 52 | @Override 53 | public MethodVisitor visitMethod(int access, String name, String descriptor, 54 | String signature, String[] exceptions) { 55 | MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); 56 | if (name.equals(this.methodHandle.getName())) { 57 | VulnMethodAdapter vulnMethodAdapter = new VulnMethodAdapter( 58 | this.methodArgIndex, this.pass, 59 | inheritanceMap, dataFlow, Opcodes.ASM6, mv, 60 | this.name, access, name, descriptor, signature, exceptions, 61 | vulnResolver 62 | ); 63 | return new JSRInlinerAdapter(vulnMethodAdapter, 64 | access, name, descriptor, signature, exceptions); 65 | } 66 | return mv; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/auditcore/VulnMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.auditcore; 2 | 3 | import com.er1cccc.acaf.config.PassthroughRegistry; 4 | import com.er1cccc.acaf.core.entity.MethodReference; 5 | import com.er1cccc.acaf.core.entity.impl.VisitMethodInsnInfoInstruction; 6 | import com.er1cccc.acaf.core.resolver.Resolver; 7 | import com.er1cccc.acaf.core.resolver.impl.CompositResolver; 8 | import org.objectweb.asm.MethodVisitor; 9 | import org.objectweb.asm.Opcodes; 10 | import org.objectweb.asm.Type; 11 | import org.objectweb.asm.TypeReference; 12 | 13 | import java.util.HashSet; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.Set; 17 | 18 | public class VulnMethodAdapter extends CoreMethodAdapter { 19 | private final int access; 20 | private final String desc; 21 | private final int methodArgIndex; 22 | private final List pass; 23 | private Resolver vulnResolver; 24 | 25 | public VulnMethodAdapter(int methodArgIndex, List pass, InheritanceMap inheritanceMap, 26 | Map> passthroughDataflow, 27 | int api, MethodVisitor mv, String owner, int access, String name, 28 | String desc, String signature, String[] exceptions, Resolver vulnResolver) { 29 | super(inheritanceMap, passthroughDataflow, api, mv, owner, access, name, desc, signature, exceptions); 30 | this.access = access; 31 | this.desc = desc; 32 | this.methodArgIndex = methodArgIndex; 33 | this.pass = pass; 34 | this.vulnResolver=vulnResolver; 35 | } 36 | 37 | @Override 38 | public void visitCode() { 39 | super.visitCode(); 40 | int localIndex = 0; 41 | int argIndex = 0; 42 | if ((this.access & Opcodes.ACC_STATIC) == 0) { 43 | localIndex += 1; 44 | argIndex += 1; 45 | } 46 | for (Type argType : Type.getArgumentTypes(desc)) { 47 | if (argIndex == this.methodArgIndex) { 48 | localVariables.set(localIndex, true); 49 | } 50 | localIndex += argType.getSize(); 51 | argIndex += 1; 52 | } 53 | } 54 | 55 | @Override 56 | public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 57 | Set controllableArgIndex = getControllableArgIndex(desc); 58 | boolean resolve = vulnResolver.resolve(new VisitMethodInsnInfoInstruction(opcode, owner, name, desc, itf, controllableArgIndex)); 59 | super.visitMethodInsn(opcode, owner, name, desc, itf); 60 | } 61 | 62 | private Set getControllableArgIndex(String descriptor) { 63 | Type[] argTypes = Type.getArgumentTypes(descriptor); 64 | int stackIndex = 0; 65 | Set controllableArgIndexList=new HashSet<>(); 66 | for (int i = 0; i < argTypes.length; i++) { 67 | int argIndex = argTypes.length - 1 - i; 68 | Type type = argTypes[argIndex]; 69 | Set param = operandStack.get(stackIndex); 70 | if (param.size() > 0 && param.contains(true)) { 71 | controllableArgIndexList.add(argIndex); 72 | } 73 | stackIndex += type.getSize(); 74 | } 75 | return controllableArgIndexList; 76 | } 77 | 78 | // @Override 79 | // public void visitFieldInsn(int opcode, String owner, String name, String desc){ 80 | // if(opcode==Opcodes.GETFIELD){ 81 | // Set obj = operandStack.get(0); 82 | // super.visitFieldInsn(opcode,owner,name,desc); 83 | // //如果对象可控,那么认为对象的字段也是可控的 84 | // if(obj.contains(true)) 85 | // operandStack.get(0).add(true); 86 | // }else{ 87 | // super.visitFieldInsn(opcode,owner,name,desc); 88 | // } 89 | // } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/discovery/CallGraphDiscovery.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.discovery; 2 | 3 | import com.er1cccc.acaf.core.audit.auditcore.CallGraph; 4 | import com.er1cccc.acaf.core.audit.auditcore.CallGraphClassVisitor; 5 | import com.er1cccc.acaf.core.audit.auditcore.InheritanceMap; 6 | import com.er1cccc.acaf.core.entity.ClassFile; 7 | import com.er1cccc.acaf.core.entity.ClassReference; 8 | import com.er1cccc.acaf.core.entity.MethodReference; 9 | import com.er1cccc.acaf.data.DataLoader; 10 | import org.apache.log4j.Logger; 11 | import org.objectweb.asm.ClassReader; 12 | 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.nio.file.Path; 17 | import java.nio.file.Paths; 18 | import java.util.*; 19 | 20 | public class CallGraphDiscovery { 21 | private static final Logger logger = Logger.getLogger(CallGraphDiscovery.class); 22 | private final Set discoveredCalls = new HashSet<>(); 23 | 24 | public void discover(List classFileList,Path saveDataDirPath) throws IOException{ 25 | logger.info("build call graph"); 26 | Map classMap = DataLoader.loadClasses(saveDataDirPath); 27 | InheritanceMap inheritanceMap = InheritanceMap.load(saveDataDirPath); 28 | Map> passthroughDataflow = PassthroughDiscovery.load(saveDataDirPath); 29 | 30 | for (ClassFile classFile:classFileList) { 31 | try (InputStream in = classFile.getInputStream()) { 32 | ClassReader cr = new ClassReader(in); 33 | try { 34 | cr.accept(new CallGraphClassVisitor(classMap, inheritanceMap, passthroughDataflow,discoveredCalls), 35 | ClassReader.EXPAND_FRAMES); 36 | } catch (Exception e) { 37 | logger.error("Error analyzing: " + classFile.getClassName(), e); 38 | } 39 | } 40 | } 41 | 42 | } 43 | 44 | public void save(Path dirPath) throws IOException { 45 | DataLoader.saveData(new File(dirPath.toFile(),"callgraph.dat").toPath(), new CallGraph.Factory(), discoveredCalls); 46 | } 47 | 48 | public static List load(Path dirPath) throws IOException { 49 | return DataLoader.loadData(new File(dirPath.toFile(),"callgraph.dat").toPath(), new CallGraph.Factory()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/discovery/DrawService.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.discovery; 2 | 3 | import com.er1cccc.acaf.core.audit.auditcore.CallGraph; 4 | import com.er1cccc.acaf.core.entity.ClassReference; 5 | import com.er1cccc.acaf.core.entity.MethodReference; 6 | import com.er1cccc.acaf.util.DrawUtil; 7 | 8 | import java.util.HashSet; 9 | import java.util.Map; 10 | import java.util.Set; 11 | 12 | public class DrawService { 13 | public static void start(Set discoveredCalls, String finalPackageName, 14 | Map classMap, 15 | Map> methodImplMap) { 16 | Set targetCallGraphs = new HashSet<>(); 17 | for (CallGraph callGraph : discoveredCalls) { 18 | ClassReference callerClass = classMap.get(callGraph.getCallerMethod().getClassReference()); 19 | ClassReference targetClass = classMap.get(callGraph.getTargetMethod().getClassReference()); 20 | if (targetClass == null) { 21 | continue; 22 | } 23 | if (targetClass.getName().equals("java/lang/Object") && 24 | callGraph.getTargetMethod().getName().equals("") && 25 | callGraph.getTargetMethod().getDesc().equals("()V")) { 26 | continue; 27 | } 28 | if (callerClass.getName().startsWith(finalPackageName)) { 29 | targetCallGraphs.add(callGraph); 30 | } 31 | } 32 | DrawUtil.drawCallGraph(targetCallGraphs); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/discovery/MethodDiscovery.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.discovery; 2 | 3 | import com.er1cccc.acaf.core.audit.auditcore.DiscoveryClassVisitor; 4 | import com.er1cccc.acaf.core.audit.auditcore.InheritanceUtil; 5 | import com.er1cccc.acaf.core.entity.ClassFile; 6 | import com.er1cccc.acaf.core.entity.ClassReference; 7 | import com.er1cccc.acaf.core.entity.MethodReference; 8 | import com.er1cccc.acaf.data.DataLoader; 9 | import org.apache.log4j.Logger; 10 | import org.objectweb.asm.ClassReader; 11 | 12 | import java.io.File; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.nio.file.Path; 16 | import java.nio.file.Paths; 17 | import java.util.ArrayList; 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | public class MethodDiscovery { 23 | private static final Logger logger = Logger.getLogger(MethodDiscovery.class); 24 | private final List discoveredClasses = new ArrayList<>(); 25 | private final List discoveredMethods = new ArrayList<>(); 26 | 27 | public void discover(List classFileList) { 28 | logger.info("discover all classes"); 29 | for (ClassFile file : classFileList) { 30 | try { 31 | DiscoveryClassVisitor dcv = new DiscoveryClassVisitor(discoveredClasses, discoveredMethods,file); 32 | InputStream ins = file.getInputStream(); 33 | ClassReader cr = new ClassReader(ins); 34 | ins.close(); 35 | cr.accept(dcv, ClassReader.EXPAND_FRAMES); 36 | } catch (IOException e) { 37 | e.printStackTrace(); 38 | } 39 | } 40 | } 41 | 42 | public void save(Path dirPath) throws IOException { 43 | DataLoader.saveData(new File(dirPath.toFile(),"classes.dat").toPath(), new ClassReference.Factory(), discoveredClasses); 44 | DataLoader.saveData(new File(dirPath.toFile(),"methods.dat").toPath(), new MethodReference.Factory(), discoveredMethods); 45 | 46 | Map classMap = new HashMap<>(); 47 | for (ClassReference clazz : discoveredClasses) { 48 | classMap.put(clazz.getHandle(), clazz); 49 | } 50 | InheritanceUtil.derive(classMap).save(dirPath); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/discovery/PassthroughDiscovery.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.discovery; 2 | 3 | import com.er1cccc.acaf.core.audit.auditcore.*; 4 | import com.er1cccc.acaf.core.entity.ClassFile; 5 | import com.er1cccc.acaf.core.entity.ClassReference; 6 | import com.er1cccc.acaf.core.entity.MethodReference; 7 | import com.er1cccc.acaf.data.DataFactory; 8 | import com.er1cccc.acaf.data.DataLoader; 9 | import org.apache.log4j.Logger; 10 | import org.objectweb.asm.ClassReader; 11 | import org.objectweb.asm.Opcodes; 12 | 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.nio.file.Path; 17 | import java.nio.file.Paths; 18 | import java.util.*; 19 | 20 | public class PassthroughDiscovery { 21 | private static final Logger logger = Logger.getLogger(PassthroughDiscovery.class); 22 | private final Map> methodCalls = new HashMap<>(); 23 | private Map> passthroughDataflow; 24 | 25 | public void discover(List classFileList,Path saveDataDirPath) throws IOException{ 26 | logger.info("get data flow"); 27 | 28 | Map methodMap = DataLoader.loadMethods(saveDataDirPath); 29 | Map classMap = DataLoader.loadClasses(saveDataDirPath); 30 | InheritanceMap inheritanceMap = InheritanceMap.load(saveDataDirPath); 31 | 32 | Map classFileByName = discoverMethodCalls(classFileList); 33 | 34 | List sortedMethods = topologicallySortMethodCalls(); 35 | passthroughDataflow = calculatePassthroughDataflow(classFileByName, classMap, inheritanceMap, sortedMethods); 36 | } 37 | 38 | private Map> calculatePassthroughDataflow(Map classFileByName, Map classMap, InheritanceMap inheritanceMap, List sortedMethods) { 39 | final Map> passthroughDataflow = new HashMap<>(); 40 | 41 | for (MethodReference.Handle method : sortedMethods) { 42 | if (method.getName().equals("")) { 43 | continue; 44 | } 45 | ClassFile file = classFileByName.get(method.getClassReference().getName()); 46 | try { 47 | InputStream ins = file.getInputStream(); 48 | ClassReader cr = new ClassReader(ins); 49 | ins.close(); 50 | 51 | DataFlowClassVisitor cv = new DataFlowClassVisitor(classMap, inheritanceMap, passthroughDataflow, method); 52 | cr.accept(cv, ClassReader.EXPAND_FRAMES); 53 | passthroughDataflow.put(method, cv.getReturnTaint()); 54 | } catch (IOException e) { 55 | logger.error("Exception analyzing " + method.getClassReference().getName(), e); 56 | } 57 | } 58 | return passthroughDataflow; 59 | } 60 | 61 | private List topologicallySortMethodCalls() { 62 | logger.info("topological sort methods"); 63 | Map> outgoingReferences = new HashMap<>(); 64 | for (Map.Entry> entry : methodCalls.entrySet()) { 65 | MethodReference.Handle method = entry.getKey(); 66 | outgoingReferences.put(method, new HashSet<>(entry.getValue())); 67 | } 68 | Set dfsStack = new HashSet<>(); 69 | Set visitedNodes = new HashSet<>(); 70 | List sortedMethods = new ArrayList<>(outgoingReferences.size()); 71 | for (MethodReference.Handle root : outgoingReferences.keySet()) { 72 | dfsSort(outgoingReferences, sortedMethods, visitedNodes, dfsStack, root); 73 | } 74 | return sortedMethods; 75 | } 76 | 77 | public void dfsSort(Map> outgoingReferences, 78 | List sortedMethods, Set visitedNodes, 79 | Set stack, MethodReference.Handle node) { 80 | if (stack.contains(node)) { 81 | return; 82 | } 83 | if (visitedNodes.contains(node)) { 84 | return; 85 | } 86 | Set outgoingRefs = outgoingReferences.get(node); 87 | if (outgoingRefs == null) { 88 | return; 89 | } 90 | stack.add(node); 91 | for (MethodReference.Handle child : outgoingRefs) { 92 | dfsSort(outgoingReferences, sortedMethods, visitedNodes, stack, child); 93 | } 94 | stack.remove(node); 95 | visitedNodes.add(node); 96 | sortedMethods.add(node); 97 | } 98 | 99 | private Map discoverMethodCalls(final List classFileList) throws IOException { 100 | Map classFileByName = new HashMap<>(); 101 | for (ClassFile file : classFileList) { 102 | try { 103 | MethodCallClassVisitor mcv = new MethodCallClassVisitor(methodCalls); 104 | InputStream ins = file.getInputStream(); 105 | ClassReader cr = new ClassReader(ins); 106 | ins.close(); 107 | cr.accept(mcv, ClassReader.EXPAND_FRAMES); 108 | classFileByName.put(mcv.getName(), file); 109 | } catch (IOException e) { 110 | e.printStackTrace(); 111 | } 112 | } 113 | return classFileByName; 114 | } 115 | 116 | public void save(Path dirPath) throws IOException { 117 | if (passthroughDataflow == null) { 118 | throw new IllegalStateException("Save called before discover()"); 119 | } 120 | DataLoader.saveData(new File(dirPath.toFile(),"passthrough.dat").toPath(), new PassThroughFactory(), passthroughDataflow.entrySet()); 121 | } 122 | 123 | public static class PassThroughFactory implements DataFactory>> { 124 | 125 | @Override 126 | public Map.Entry> parse(String[] fields) { 127 | ClassReference.Handle clazz = new ClassReference.Handle(fields[0]); 128 | MethodReference.Handle method = new MethodReference.Handle(clazz, fields[1], fields[2]); 129 | 130 | Set passthroughArgs = new HashSet<>(); 131 | for (String arg : fields[3].split(",")) { 132 | if (arg.length() > 0) { 133 | passthroughArgs.add(Integer.parseInt(arg)); 134 | } 135 | } 136 | return new AbstractMap.SimpleEntry<>(method, passthroughArgs); 137 | } 138 | 139 | @Override 140 | public String[] serialize(Map.Entry> entry) { 141 | if (entry.getValue()==null||entry.getValue().size() == 0) { 142 | return null; 143 | } 144 | 145 | final String[] fields = new String[4]; 146 | fields[0] = entry.getKey().getClassReference().getName(); 147 | fields[1] = entry.getKey().getName(); 148 | fields[2] = entry.getKey().getDesc(); 149 | 150 | StringBuilder sb = new StringBuilder(); 151 | for (Integer arg : entry.getValue()) { 152 | sb.append(Integer.toString(arg)); 153 | sb.append(","); 154 | } 155 | fields[3] = sb.toString(); 156 | 157 | return fields; 158 | } 159 | } 160 | 161 | public static Map> load(Path dirPath) throws IOException { 162 | Map> passthroughDataflow = new HashMap<>(); 163 | for (Map.Entry> entry : DataLoader.loadData(new File(dirPath.toFile(),"passthrough.dat").toPath(), new PassThroughFactory())) { 164 | passthroughDataflow.put(entry.getKey(), entry.getValue()); 165 | } 166 | return passthroughDataflow; 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/discovery/SpringDiscovery.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.discovery; 2 | 3 | import com.er1cccc.acaf.core.audit.auditcore.SpringClassVisitor; 4 | import com.er1cccc.acaf.core.entity.ClassFile; 5 | import com.er1cccc.acaf.core.entity.ClassReference; 6 | import com.er1cccc.acaf.core.entity.MethodReference; 7 | import com.er1cccc.acaf.core.entity.SpringController; 8 | import com.er1cccc.acaf.data.DataLoader; 9 | import org.apache.log4j.Logger; 10 | import org.objectweb.asm.ClassReader; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.nio.file.Path; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | public class SpringDiscovery { 19 | private static final Logger logger = Logger.getLogger(SpringDiscovery.class); 20 | private List controllers=new ArrayList<>(); 21 | 22 | public void discover(List classFileList, String packageName, Path saveDataDirPath) { 23 | logger.info("discover spring classes"); 24 | Map classMap = DataLoader.loadClasses(saveDataDirPath); 25 | Map methodMap = DataLoader.loadMethods(saveDataDirPath); 26 | 27 | for (ClassFile file : classFileList) { 28 | try { 29 | SpringClassVisitor mcv = new SpringClassVisitor(packageName,controllers,classMap,methodMap); 30 | InputStream ins = file.getInputStream(); 31 | ClassReader cr = new ClassReader(ins); 32 | ins.close(); 33 | cr.accept(mcv, ClassReader.EXPAND_FRAMES); 34 | } catch (IOException e) { 35 | e.printStackTrace(); 36 | } 37 | } 38 | } 39 | 40 | public List getControllers() { 41 | return controllers; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/audit/discovery/VulnDiscovery.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.audit.discovery; 2 | 3 | import com.er1cccc.acaf.config.*; 4 | import com.er1cccc.acaf.core.audit.auditcore.CallGraph; 5 | import com.er1cccc.acaf.core.audit.auditcore.InheritanceMap; 6 | import com.er1cccc.acaf.core.audit.auditcore.InheritanceUtil; 7 | import com.er1cccc.acaf.core.audit.auditcore.VulnClassVisitor; 8 | import com.er1cccc.acaf.core.entity.*; 9 | import com.er1cccc.acaf.core.resolver.Resolver; 10 | import com.er1cccc.acaf.core.resolver.impl.CompositResolver; 11 | import com.er1cccc.acaf.core.template.VulnTemplateSinkVisitor; 12 | import com.er1cccc.acaf.data.DataLoader; 13 | import org.apache.log4j.Logger; 14 | import org.objectweb.asm.ClassReader; 15 | import org.objectweb.asm.Type; 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | import java.nio.file.Path; 19 | import java.util.*; 20 | 21 | public class VulnDiscovery { 22 | private static final Logger logger = Logger.getLogger(VulnDiscovery.class); 23 | 24 | private Map classFileMap; 25 | private InheritanceMap localInheritanceMap; 26 | private Map> localDataFlow; 27 | private Map methodMap; 28 | private Map classMap ; 29 | private List discoveredCalls ; 30 | private SourceRegistry sourceRegistry; 31 | private SanitizeRegistry sanitizeRegistry; 32 | private SinkRegistry sinkRegistry; 33 | private Map> callGraphMap; 34 | private Path templateSaveDataDirPath; 35 | private Map resolverMap=new HashMap<>(); 36 | 37 | public VulnDiscovery(List targetClassFileList, 38 | SourceRegistry sourceRegistry, 39 | SanitizeRegistry sanitizeRegistry, 40 | SinkRegistry sinkRegistry, 41 | Path targetSaveDataDirPath, 42 | Path templateSaveDataDirPath) throws IOException{ 43 | this.sourceRegistry=sourceRegistry; 44 | this.sanitizeRegistry=sanitizeRegistry; 45 | this.sinkRegistry=sinkRegistry; 46 | this.templateSaveDataDirPath=templateSaveDataDirPath; 47 | localInheritanceMap = InheritanceMap.load(targetSaveDataDirPath); 48 | localDataFlow = PassthroughDiscovery.load(targetSaveDataDirPath); 49 | methodMap= DataLoader.loadMethods(targetSaveDataDirPath); 50 | classMap= DataLoader.loadClasses(targetSaveDataDirPath); 51 | discoveredCalls= CallGraphDiscovery.load(targetSaveDataDirPath); 52 | classFileMap=new HashMap<>(); 53 | for (ClassFile classFile : targetClassFileList) { 54 | classFileMap.put(classFile.getClassName(),classFile); 55 | } 56 | } 57 | 58 | private CompositResolver getResolver(Sink sink) throws IOException { 59 | CompositResolver compositResolver = resolverMap.get(sink); 60 | if(compositResolver==null){ 61 | ClassReader classReader = new ClassReader(sink.getClass().getName()); 62 | compositResolver = new CompositResolver(sink); 63 | classReader.accept(new VulnTemplateSinkVisitor(compositResolver,templateSaveDataDirPath, sink),ClassReader.EXPAND_FRAMES); 64 | PassthroughRegistry passthroughRegistry = new PassthroughRegistry(this.localDataFlow); 65 | //TODO 这样的话上一个sink添加的Passthrough会传递给下一个sink,有没有影响? 66 | sink.addPassthrough(passthroughRegistry); 67 | resolverMap.put(sink,compositResolver); 68 | }else{ 69 | //重复利用之前要先重置resolver的ind 70 | compositResolver.reset(); 71 | } 72 | return compositResolver; 73 | } 74 | 75 | 76 | public void discover(List controllers) throws IOException { 77 | logger.info("finding vuln..."); 78 | callGraphMap = prepareCallGraphMap(methodMap); 79 | doDiscover(controllers); 80 | } 81 | 82 | /** 83 | * 准备方法调用图。把抽象方法全部具体化 84 | * @param methodMap 85 | * @return 86 | * @throws IOException 87 | */ 88 | private Map> prepareCallGraphMap(Map methodMap){ 89 | Map> graphCallMap=new HashMap<>(); 90 | 91 | Map> methodImplMap = 92 | InheritanceUtil.getAllMethodImplementations(localInheritanceMap, methodMap); 93 | List tempList = new ArrayList<>(discoveredCalls); 94 | for (int i = 0; i < discoveredCalls.size(); i++) { 95 | CallGraph edge = tempList.get(i); 96 | ClassReference.Handle handle = edge.getTargetMethod().getClassReference(); 97 | ClassReference classReference = classMap.get(handle); 98 | if (classReference != null && classReference.isInterface()) { 99 | Set implSet = methodImplMap.get(edge.getTargetMethod()); 100 | if (implSet == null || implSet.size() == 0) { 101 | continue; 102 | } 103 | for (MethodReference.Handle methodHandle : implSet) { 104 | String callerDesc = methodMap.get(methodHandle).getDesc(); 105 | if (edge.getTargetMethod().getDesc().equals(callerDesc)) { 106 | tempList.add(new CallGraph( 107 | edge.getTargetMethod(), 108 | methodHandle, 109 | edge.getTargetArgIndex(), 110 | null, 111 | edge.getTargetArgIndex() 112 | )); 113 | } 114 | } 115 | } 116 | } 117 | 118 | discoveredCalls.clear(); 119 | discoveredCalls.addAll(tempList); 120 | for (CallGraph graphCall : discoveredCalls) { 121 | MethodReference.Handle caller = graphCall.getCallerMethod(); 122 | if (!graphCallMap.containsKey(caller)) { 123 | Set graphCalls = new HashSet<>(); 124 | graphCalls.add(graphCall); 125 | graphCallMap.put(caller, graphCalls); 126 | } else { 127 | graphCallMap.get(caller).add(graphCall); 128 | } 129 | } 130 | return graphCallMap; 131 | } 132 | 133 | private void doDiscover(List controllers) throws IOException { 134 | for (SpringController controller : controllers) { 135 | for (SpringMapping mapping : controller.getMappings()) { 136 | MethodReference methodReference = mapping.getMethodReference(); 137 | if (methodReference == null) { 138 | continue; 139 | } 140 | Type[] argTypes = Type.getArgumentTypes(methodReference.getDesc()); 141 | Type[] extendedArgTypes = new Type[argTypes.length + 1]; 142 | System.arraycopy(argTypes, 0, extendedArgTypes, 1, argTypes.length); 143 | argTypes = extendedArgTypes; 144 | boolean[] vulnerableIndex = new boolean[argTypes.length]; 145 | for (int i = 1; i < argTypes.length; i++) { 146 | if (!isPrimitive(argTypes[i])) { 147 | vulnerableIndex[i] = true; 148 | } 149 | } 150 | Set calls = callGraphMap.get(methodReference.getHandle()); 151 | if (calls == null || calls.size() == 0) { 152 | continue; 153 | } 154 | // mapping method中的call 155 | for (CallGraph callGraph : calls) { 156 | int callerIndex = callGraph.getCallerArgIndex(); 157 | if (callerIndex == -1) { 158 | continue; 159 | } 160 | if (vulnerableIndex[callerIndex]) { 161 | // 防止循环 162 | for(Sink sink: sinkRegistry.getSinkList()){ 163 | CompositResolver resolver = getResolver(sink); 164 | 165 | List visited = new ArrayList<>(); 166 | LinkedList stack = new LinkedList<>(); 167 | stack.push(callGraph.getCallerMethod()); 168 | doTask(callGraph.getTargetMethod(), callGraph.getTargetArgIndex(), visited, stack, resolver); 169 | } 170 | } 171 | } 172 | } 173 | } 174 | } 175 | 176 | private boolean isPrimitive(Type argType) { 177 | int sort = argType.getSort(); 178 | return sort==Type.BYTE|| 179 | sort==Type.INT|| 180 | sort==Type.SHORT|| 181 | sort==Type.LONG|| 182 | sort==Type.FLOAT|| 183 | sort==Type.DOUBLE|| 184 | sort==Type.BOOLEAN|| 185 | sort==Type.CHAR; 186 | } 187 | 188 | private void doTask(MethodReference.Handle targetMethod, int targetIndex, 189 | List visited, 190 | Deque stack, 191 | CompositResolver vulnResolver) { 192 | if (visited.contains(targetMethod)) { 193 | return; 194 | } else { 195 | visited.add(targetMethod); 196 | } 197 | stack.push(targetMethod); 198 | ClassFile file = classFileMap.get(targetMethod.getClassReference().getName()); 199 | try { 200 | InputStream ins = file.getInputStream(); 201 | ClassReader cr = new ClassReader(ins); 202 | ins.close(); 203 | VulnClassVisitor cv = new VulnClassVisitor( 204 | targetMethod, targetIndex, localInheritanceMap, localDataFlow, vulnResolver); 205 | cr.accept(cv, ClassReader.EXPAND_FRAMES); 206 | if(vulnResolver.resolve(null)){ 207 | logger.info("detect vuln: " + vulnResolver.getSink().getClass().getName()); 208 | printStackTrace(stack); 209 | return; 210 | } 211 | 212 | } catch (IOException e) { 213 | e.printStackTrace(); 214 | return; 215 | } 216 | Set calls = callGraphMap.get(targetMethod); 217 | if (calls == null || calls.size() == 0) { 218 | return; 219 | } 220 | for (CallGraph callGraph : calls) { 221 | if (callGraph.getCallerArgIndex() == targetIndex && targetIndex != -1) { 222 | if (visited.contains(callGraph.getTargetMethod())) { 223 | return; 224 | } 225 | doTask(callGraph.getTargetMethod(), callGraph.getTargetArgIndex(),visited, stack, vulnResolver); 226 | stack.pop(); 227 | } 228 | } 229 | } 230 | 231 | private void printStackTrace(Deque stack) { 232 | Deque copyStack = new LinkedList<>(stack); 233 | StringBuilder prefix=new StringBuilder("\t"); 234 | for (MethodReference.Handle handle : stack) { 235 | logger.info(prefix+handle.getClassReference().getName()+"."+handle.getName()); 236 | prefix.append("\t"); 237 | } 238 | } 239 | 240 | public static void main(String[] args) throws Exception{ 241 | 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/entity/ClassFile.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.entity; 2 | 3 | 4 | import com.er1cccc.acaf.util.IOUtil; 5 | import lombok.Data; 6 | 7 | import java.io.ByteArrayInputStream; 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.util.Objects; 12 | 13 | @Data 14 | public class ClassFile { 15 | private String className; 16 | private InputStream inputStream; 17 | 18 | public ClassFile(String className, InputStream inputStream) { 19 | this.className = className; 20 | this.inputStream = inputStream; 21 | } 22 | 23 | @Override 24 | public boolean equals(Object o) { 25 | if (this == o) { 26 | return true; 27 | } 28 | if (o == null || getClass() != o.getClass()) { 29 | return false; 30 | } 31 | ClassFile classFile = (ClassFile) o; 32 | return Objects.equals(className, classFile.className); 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | return className != null ? className.hashCode() : 0; 38 | } 39 | 40 | public InputStream getInputStream() { 41 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 42 | IOUtil.copy(inputStream, outputStream); 43 | this.inputStream = new ByteArrayInputStream(outputStream.toByteArray()); 44 | return new ByteArrayInputStream(outputStream.toByteArray()); 45 | } 46 | 47 | public void close() { 48 | try { 49 | this.inputStream.close(); 50 | } catch (IOException e) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/entity/ClassReference.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.entity; 2 | 3 | import com.er1cccc.acaf.data.DataFactory; 4 | 5 | import java.util.*; 6 | 7 | public class ClassReference { 8 | private final String name; 9 | private final String superClass; 10 | private final List interfaces; 11 | private final boolean isInterface; 12 | private final List members; 13 | private final Set annotations; 14 | 15 | public static class Member { 16 | private final String name; 17 | private final int modifiers; 18 | private final Handle type; 19 | 20 | public Member(String name, int modifiers, Handle type) { 21 | this.name = name; 22 | this.modifiers = modifiers; 23 | this.type = type; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public int getModifiers() { 31 | return modifiers; 32 | } 33 | 34 | public Handle getType() { 35 | return type; 36 | } 37 | } 38 | 39 | public ClassReference(String name, String superClass, List interfaces, 40 | boolean isInterface, List members, Set annotations) { 41 | this.name = name; 42 | this.superClass = superClass; 43 | this.interfaces = interfaces; 44 | this.isInterface = isInterface; 45 | this.members = members; 46 | this.annotations = annotations; 47 | } 48 | 49 | public String getName() { 50 | return name; 51 | } 52 | 53 | public String getSuperClass() { 54 | return superClass; 55 | } 56 | 57 | public List getInterfaces() { 58 | return interfaces; 59 | } 60 | 61 | public boolean isInterface() { 62 | return isInterface; 63 | } 64 | 65 | public List getMembers() { 66 | return members; 67 | } 68 | 69 | public Handle getHandle() { 70 | return new Handle(name); 71 | } 72 | 73 | public Set getAnnotations() { 74 | return annotations; 75 | } 76 | 77 | public static class Handle { 78 | private final String name; 79 | 80 | public Handle(String name) { 81 | this.name = name; 82 | } 83 | 84 | public String getName() { 85 | return name; 86 | } 87 | 88 | @Override 89 | public boolean equals(Object o) { 90 | if (this == o) return true; 91 | if (o == null || getClass() != o.getClass()) return false; 92 | Handle handle = (Handle) o; 93 | return Objects.equals(name, handle.name); 94 | } 95 | 96 | @Override 97 | public int hashCode() { 98 | return name != null ? name.hashCode() : 0; 99 | } 100 | } 101 | 102 | public static class Factory implements DataFactory { 103 | 104 | @Override 105 | public ClassReference parse(String[] fields) { 106 | String[] interfaces; 107 | if (fields[2].equals("")) { 108 | interfaces = new String[0]; 109 | } else { 110 | interfaces = fields[2].split(","); 111 | } 112 | 113 | String[] memberEntries = fields[4].split("!"); 114 | int size=memberEntries.length/3; 115 | List members = new ArrayList<>(size); 116 | for (int i = 0; i < size; i++) { 117 | members.add(new Member(memberEntries[3*i], Integer.parseInt(memberEntries[3*i+1]), 118 | new ClassReference.Handle(memberEntries[3*i+2]))); 119 | } 120 | 121 | String[] annotationEntries = fields[5].split("!"); 122 | Set annotations = new HashSet<>(); 123 | for (int i = 0; i < annotationEntries.length; i++) { 124 | if(annotationEntries[i]!=null&&!annotationEntries[i].trim().equals("")) 125 | annotations.add(annotationEntries[i]); 126 | } 127 | 128 | return new ClassReference( 129 | fields[0], 130 | fields[1].equals("") ? null : fields[1], 131 | new ArrayList<>(Arrays.asList(interfaces)), 132 | Boolean.parseBoolean(fields[3]), 133 | members, 134 | annotations); 135 | } 136 | 137 | @Override 138 | public String[] serialize(ClassReference obj) { 139 | String interfaces; 140 | if (obj.interfaces.size() > 0) { 141 | StringBuilder interfacesSb = new StringBuilder(); 142 | for (String iface : obj.interfaces) { 143 | interfacesSb.append(",").append(iface); 144 | } 145 | interfaces = interfacesSb.substring(1); 146 | } else { 147 | interfaces = ""; 148 | } 149 | 150 | StringBuilder members = new StringBuilder(); 151 | for (Member member : obj.members) { 152 | members.append("!").append(member.getName()) 153 | .append("!").append(Integer.toString(member.getModifiers())) 154 | .append("!").append(member.getType().getName()); 155 | } 156 | 157 | StringBuilder annotations = new StringBuilder(); 158 | for (String annotation : obj.annotations) { 159 | annotations.append("!").append(annotation); 160 | } 161 | 162 | return new String[]{ 163 | obj.name, 164 | obj.superClass, 165 | interfaces, 166 | Boolean.toString(obj.isInterface), 167 | members.length() == 0 ? null : members.substring(1), 168 | annotations.length()==0? null: annotations.substring(1) 169 | }; 170 | } 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/entity/Command.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.entity; 2 | 3 | import com.beust.jcommander.Parameter; 4 | 5 | import java.util.List; 6 | 7 | public class Command { 8 | @Parameter(names = {"-h", "--help"}, description = "Help Info", help = true) 9 | public boolean help; 10 | 11 | @Parameter(names = {"-j", "--jar"}, description = "Scan Jar File") 12 | public List jars; 13 | 14 | @Parameter(names = {"-b", "--boot"}, description = "Scan SpringBoot File") 15 | public List boots; 16 | 17 | @Parameter(names = {"-t", "--template"}, description = "Scan SpringBoot File") 18 | public List templates; 19 | 20 | @Parameter(names = {"-p", "--pack"}, description = "SpringBoot Package Name") 21 | public String packageName; 22 | } 23 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/entity/Instruction.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.entity; 2 | 3 | public interface Instruction { 4 | } 5 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/entity/MethodReference.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.entity; 2 | 3 | import com.er1cccc.acaf.data.DataFactory; 4 | 5 | import java.util.Objects; 6 | 7 | public class MethodReference { 8 | private final ClassReference.Handle classReference; 9 | private final String name; 10 | private final String desc; 11 | private final boolean isStatic; 12 | 13 | public MethodReference(ClassReference.Handle classReference, 14 | String name, String desc, boolean isStatic) { 15 | this.classReference = classReference; 16 | this.name = name; 17 | this.desc = desc; 18 | this.isStatic = isStatic; 19 | } 20 | 21 | public ClassReference.Handle getClassReference() { 22 | return classReference; 23 | } 24 | 25 | public String getName() { 26 | return name; 27 | } 28 | 29 | public String getDesc() { 30 | return desc; 31 | } 32 | 33 | public boolean isStatic() { 34 | return isStatic; 35 | } 36 | 37 | public Handle getHandle() { 38 | return new Handle(classReference, name, desc); 39 | } 40 | 41 | public static class Handle { 42 | private final ClassReference.Handle classReference; 43 | private final String name; 44 | private final String desc; 45 | 46 | public Handle(ClassReference.Handle classReference, String name, String desc) { 47 | this.classReference = classReference; 48 | this.name = name; 49 | this.desc = desc; 50 | } 51 | 52 | public ClassReference.Handle getClassReference() { 53 | return classReference; 54 | } 55 | 56 | public String getName() { 57 | return name; 58 | } 59 | 60 | public String getDesc() { 61 | return desc; 62 | } 63 | 64 | @Override 65 | public boolean equals(Object o) { 66 | if (this == o) return true; 67 | if (o == null || getClass() != o.getClass()) return false; 68 | Handle handle = (Handle) o; 69 | if (!Objects.equals(classReference, handle.classReference)) 70 | return false; 71 | if (!Objects.equals(name, handle.name)) 72 | return false; 73 | return Objects.equals(desc, handle.desc); 74 | } 75 | 76 | @Override 77 | public int hashCode() { 78 | int result = classReference != null ? classReference.hashCode() : 0; 79 | result = 31 * result + (name != null ? name.hashCode() : 0); 80 | result = 31 * result + (desc != null ? desc.hashCode() : 0); 81 | return result; 82 | } 83 | } 84 | 85 | public static class Factory implements DataFactory { 86 | 87 | @Override 88 | public MethodReference parse(String[] fields) { 89 | return new MethodReference( 90 | new ClassReference.Handle(fields[0]), 91 | fields[1], 92 | fields[2], 93 | Boolean.parseBoolean(fields[3])); 94 | } 95 | 96 | @Override 97 | public String[] serialize(MethodReference obj) { 98 | return new String[] { 99 | obj.classReference.getName(), 100 | obj.name, 101 | obj.desc, 102 | Boolean.toString(obj.isStatic), 103 | }; 104 | } 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/entity/SpringController.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.entity; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class SpringController { 7 | private boolean isRest; 8 | private ClassReference.Handle className; 9 | private ClassReference classReference; 10 | private final List mappings = new ArrayList<>(); 11 | 12 | public boolean isRest() { 13 | return isRest; 14 | } 15 | 16 | public void setRest(boolean rest) { 17 | isRest = rest; 18 | } 19 | 20 | public ClassReference.Handle getClassName() { 21 | return className; 22 | } 23 | 24 | public void setClassName(ClassReference.Handle className) { 25 | this.className = className; 26 | } 27 | 28 | public ClassReference getClassReference() { 29 | return classReference; 30 | } 31 | 32 | public void setClassReference(ClassReference classReference) { 33 | this.classReference = classReference; 34 | } 35 | 36 | public List getMappings() { 37 | return mappings; 38 | } 39 | 40 | public void addMapping(SpringMapping mapping) { 41 | this.mappings.add(mapping); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/entity/SpringMapping.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.entity; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class SpringMapping { 7 | private boolean isRest; 8 | private SpringController controller; 9 | private MethodReference.Handle methodName; 10 | private MethodReference methodReference; 11 | private String path; 12 | private List paramMap = new ArrayList<>(); 13 | 14 | public List getParamMap() { 15 | return paramMap; 16 | } 17 | 18 | public void setParamMap(List paramMap) { 19 | this.paramMap = paramMap; 20 | } 21 | 22 | public boolean isRest() { 23 | return isRest; 24 | } 25 | 26 | public void setRest(boolean rest) { 27 | isRest = rest; 28 | } 29 | 30 | public SpringController getController() { 31 | return controller; 32 | } 33 | 34 | public void setController(SpringController controller) { 35 | this.controller = controller; 36 | } 37 | 38 | public MethodReference.Handle getMethodName() { 39 | return methodName; 40 | } 41 | 42 | public void setMethodName(MethodReference.Handle methodName) { 43 | this.methodName = methodName; 44 | } 45 | 46 | public MethodReference getMethodReference() { 47 | return methodReference; 48 | } 49 | 50 | public void setMethodReference(MethodReference methodReference) { 51 | this.methodReference = methodReference; 52 | } 53 | 54 | public String getPath() { 55 | return path; 56 | } 57 | 58 | public void setPath(String path) { 59 | this.path = path; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/entity/SpringParam.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.entity; 2 | 3 | public class SpringParam { 4 | private int paramIndex; 5 | private String paramName; 6 | private String paramType; 7 | private String reqName; 8 | 9 | public int getParamIndex() { 10 | return paramIndex; 11 | } 12 | 13 | public void setParamIndex(int paramIndex) { 14 | this.paramIndex = paramIndex; 15 | } 16 | 17 | public String getParamName() { 18 | return paramName; 19 | } 20 | 21 | public void setParamName(String paramName) { 22 | this.paramName = paramName; 23 | } 24 | 25 | public String getParamType() { 26 | return paramType; 27 | } 28 | 29 | public void setParamType(String paramType) { 30 | this.paramType = paramType; 31 | } 32 | 33 | public String getReqName() { 34 | return reqName; 35 | } 36 | 37 | public void setReqName(String reqName) { 38 | this.reqName = reqName; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/entity/impl/VisitMethodInsnInfoInstruction.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.entity.impl; 2 | 3 | import com.er1cccc.acaf.core.entity.Instruction; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.Set; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class VisitMethodInsnInfoInstruction implements Instruction { 14 | int opcode; 15 | String owner; 16 | String name; 17 | String descriptor; 18 | boolean isInterface; 19 | Set controllableArgIndex; 20 | } 21 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/resolver/Resolver.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.resolver; 2 | 3 | 4 | import com.er1cccc.acaf.core.entity.Instruction; 5 | 6 | public interface Resolver { 7 | boolean resolve(Instruction instruction); 8 | } 9 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/resolver/impl/CompositResolver.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.resolver.impl; 2 | 3 | import com.er1cccc.acaf.config.Sink; 4 | import com.er1cccc.acaf.core.entity.Instruction; 5 | import com.er1cccc.acaf.core.resolver.Resolver; 6 | import lombok.Data; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | @Data 12 | public class CompositResolver implements Resolver { 13 | List resolverList=new ArrayList<>(); 14 | int ind=0; 15 | Sink sink; 16 | 17 | public CompositResolver(Sink sink) { 18 | this.sink = sink; 19 | } 20 | 21 | @Override 22 | public boolean resolve(Instruction instruction) { 23 | int size=resolverList.size(); 24 | 25 | if(ind==size){ 26 | return true; 27 | } 28 | if(instruction!=null){ 29 | Resolver resolver = resolverList.get(ind); 30 | if(resolver.resolve(instruction)){ 31 | ind++; 32 | return true; 33 | } 34 | } 35 | return false; 36 | } 37 | 38 | public void addResolver(Resolver resolver){ 39 | resolverList.add(resolver); 40 | } 41 | 42 | public void reset(){ 43 | ind=0; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/resolver/impl/VisitMethodInsnInfoResolver.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.resolver.impl; 2 | 3 | import com.er1cccc.acaf.core.entity.Instruction; 4 | import com.er1cccc.acaf.core.entity.impl.VisitMethodInsnInfoInstruction; 5 | import com.er1cccc.acaf.core.resolver.Resolver; 6 | import com.google.common.collect.Sets; 7 | import lombok.AllArgsConstructor; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.util.Set; 11 | 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class VisitMethodInsnInfoResolver implements Resolver { 15 | int opcode; 16 | String owner; 17 | String name; 18 | String descriptor; 19 | boolean isInterface; 20 | Set controllableArgIndex; 21 | 22 | @Override 23 | public boolean resolve(Instruction instruction) { 24 | VisitMethodInsnInfoInstruction inst= (VisitMethodInsnInfoInstruction) instruction; 25 | return owner.equals(inst.getOwner()) && 26 | name.equals(inst.getName()) && 27 | descriptor.equals(inst.getDescriptor())&& 28 | Sets.difference(controllableArgIndex, inst.getControllableArgIndex()).size()==0; 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/template/VulnTemplateSanitizeVisitor.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.template; 2 | 3 | public class VulnTemplateSanitizeVisitor { 4 | } 5 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/core/template/VulnTemplateSinkVisitor.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.core.template; 2 | 3 | import com.er1cccc.acaf.config.PassthroughRegistry; 4 | import com.er1cccc.acaf.config.Sink; 5 | import com.er1cccc.acaf.core.audit.auditcore.CoreMethodAdapter; 6 | import com.er1cccc.acaf.core.audit.auditcore.InheritanceMap; 7 | import com.er1cccc.acaf.core.audit.discovery.PassthroughDiscovery; 8 | import com.er1cccc.acaf.core.entity.MethodReference; 9 | import com.er1cccc.acaf.core.resolver.impl.CompositResolver; 10 | import com.er1cccc.acaf.core.resolver.impl.VisitMethodInsnInfoResolver; 11 | import org.apache.log4j.Logger; 12 | import org.objectweb.asm.ClassVisitor; 13 | import org.objectweb.asm.MethodVisitor; 14 | import org.objectweb.asm.Opcodes; 15 | import org.objectweb.asm.Type; 16 | import org.objectweb.asm.commons.JSRInlinerAdapter; 17 | import java.io.IOException; 18 | import java.nio.file.Path; 19 | import java.util.*; 20 | 21 | public class VulnTemplateSinkVisitor extends ClassVisitor { 22 | private static final Logger logger = Logger.getLogger(VulnTemplateSinkVisitor.class); 23 | private CompositResolver resolver; 24 | private String name; 25 | private String signature; 26 | private String superName; 27 | private String[] interfaces; 28 | private InheritanceMap inheritanceMap; 29 | private Map> passthrough; 30 | 31 | public VulnTemplateSinkVisitor(CompositResolver resolver, Path templateSaveDataDirPath, Sink sink) { 32 | super(Opcodes.ASM6); 33 | this.resolver=resolver; 34 | try{ 35 | inheritanceMap = InheritanceMap.load(templateSaveDataDirPath); 36 | passthrough = PassthroughDiscovery.load(templateSaveDataDirPath); 37 | PassthroughRegistry passthroughRegistry = new PassthroughRegistry(passthrough); 38 | sink.addPassthrough(passthroughRegistry); 39 | }catch (IOException e){ 40 | logger.error("error when loading inheritanceMap or passthrough",e); 41 | } 42 | } 43 | 44 | @Override 45 | public void visit(int version, int access, String name, String signature, 46 | String superName, String[] interfaces) { 47 | super.visit(version, access, name, signature, superName, interfaces); 48 | this.name = name; 49 | this.signature = signature; 50 | this.superName = superName; 51 | this.interfaces = interfaces; 52 | } 53 | 54 | @Override 55 | public MethodVisitor visitMethod(int access, String name, String descriptor, 56 | String signature, String[] exceptions) { 57 | MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); 58 | if (name.equals("sinkMethod")) { 59 | VulnTemplateSinkMethodVisitor vulnTemplateSinkMethodVisitor = new VulnTemplateSinkMethodVisitor(inheritanceMap,passthrough,Opcodes.ASM6,mv,this.name,access,name,descriptor,signature,exceptions,this.resolver); 60 | return new JSRInlinerAdapter(vulnTemplateSinkMethodVisitor, 61 | access, name, descriptor, signature, exceptions); 62 | } 63 | return mv; 64 | } 65 | 66 | private static class VulnTemplateSinkMethodVisitor extends CoreMethodAdapter { 67 | private CompositResolver resolver; 68 | private final int access; 69 | private final String desc; 70 | 71 | public VulnTemplateSinkMethodVisitor(InheritanceMap inheritanceMap, 72 | Map> passthroughDataflow, 73 | int api, MethodVisitor mv, String owner, int access, String name, 74 | String desc, String signature, String[] exceptions, CompositResolver resolver) { 75 | super(inheritanceMap, passthroughDataflow, api, mv, owner, access, name, desc, signature, exceptions); 76 | this.access = access; 77 | this.desc = desc; 78 | this.resolver=resolver; 79 | 80 | } 81 | 82 | 83 | @Override 84 | public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { 85 | if(isGetParamMethod(owner,name,descriptor,isInterface)) { 86 | super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); 87 | operandStack.get(0).add(true); 88 | }else{ 89 | Set controllableArgIndexList = getControllableArgIndex(descriptor); 90 | super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); 91 | Type returnType = Type.getReturnType(descriptor); 92 | if(controllableArgIndexList.size()>0){ 93 | resolver.addResolver(new VisitMethodInsnInfoResolver(opcode,owner,name,descriptor,isInterface,controllableArgIndexList)); 94 | }else if(!"void".equals(returnType.getClassName())){ 95 | if(operandStack.get(0).contains(true)){ 96 | resolver.addResolver(new VisitMethodInsnInfoResolver(opcode,owner,name,descriptor,isInterface,controllableArgIndexList)); 97 | } 98 | } 99 | } 100 | } 101 | 102 | private Set getControllableArgIndex(String descriptor) { 103 | Type[] argTypes = Type.getArgumentTypes(descriptor); 104 | int stackIndex = 0; 105 | Set controllableArgIndexList=new HashSet<>(); 106 | for (int i = 0; i < argTypes.length; i++) { 107 | int argIndex = argTypes.length - 1 - i; 108 | Type type = argTypes[argIndex]; 109 | Set param = operandStack.get(stackIndex); 110 | if (param.size() > 0 && param.contains(true)) { 111 | controllableArgIndexList.add(argIndex); 112 | } 113 | stackIndex += type.getSize(); 114 | } 115 | return controllableArgIndexList; 116 | } 117 | 118 | private boolean isGetParamMethod(String owner, String name, String descriptor, boolean isInterface) { 119 | return owner.equals("com/er1cccc/acaf/config/ControllableParam")&& 120 | name.equals("getParameter")&& 121 | descriptor.equals("(Ljava/lang/String;)Ljava/lang/Object;")&& 122 | !isInterface; 123 | } 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/data/DataFactory.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.data; 2 | 3 | public interface DataFactory { 4 | T parse(String[] fields); 5 | String[] serialize(T obj); 6 | } 7 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/data/DataLoader.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.data; 2 | 3 | import com.er1cccc.acaf.core.entity.ClassReference; 4 | import com.er1cccc.acaf.core.entity.MethodReference; 5 | import com.google.common.io.Files; 6 | 7 | import java.io.BufferedWriter; 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.nio.charset.StandardCharsets; 11 | import java.nio.file.Path; 12 | import java.nio.file.Paths; 13 | import java.util.*; 14 | 15 | public class DataLoader { 16 | public static List loadData(Path filePath, DataFactory factory) throws IOException { 17 | final List lines = Files.readLines(filePath.toFile(), StandardCharsets.UTF_8); 18 | final List values = new ArrayList(lines.size()); 19 | for (String line : lines) { 20 | values.add(factory.parse(line.split("\t", -1))); 21 | } 22 | return values; 23 | } 24 | 25 | public static void saveData(Path filePath, DataFactory factory, Collection values) throws IOException { 26 | try (BufferedWriter writer = Files.newWriter(filePath.toFile(), StandardCharsets.UTF_8)) { 27 | for (T value : values) { 28 | final String[] fields = factory.serialize(value); 29 | if (fields == null) { 30 | continue; 31 | } 32 | 33 | StringBuilder sb = new StringBuilder(); 34 | for (String field : fields) { 35 | if (field == null) { 36 | sb.append("\t"); 37 | } else { 38 | sb.append("\t").append(field); 39 | } 40 | } 41 | writer.write(sb.substring(1)); 42 | writer.write("\n"); 43 | } 44 | } 45 | } 46 | 47 | public static Map loadClasses(Path dirPath) { 48 | try { 49 | Map classMap = new HashMap<>(); 50 | for (ClassReference classReference : loadData(new File(dirPath.toFile(),"classes.dat").toPath(), new ClassReference.Factory())) { 51 | classMap.put(classReference.getHandle(), classReference); 52 | } 53 | return classMap; 54 | } catch (IOException e) { 55 | throw new RuntimeException(e); 56 | } 57 | } 58 | 59 | public static Map loadMethods(Path dirPath) { 60 | try { 61 | Map methodMap = new HashMap<>(); 62 | for (MethodReference methodReference : loadData(new File(dirPath.toFile(),"methods.dat").toPath(), new MethodReference.Factory())) { 63 | methodMap.put(methodReference.getHandle(), methodReference); 64 | } 65 | return methodMap; 66 | } catch (IOException e) { 67 | throw new RuntimeException(e); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/util/ClassNameClassVisitor.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.util; 2 | 3 | 4 | import lombok.Getter; 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.Opcodes; 7 | 8 | @Getter 9 | public class ClassNameClassVisitor extends ClassVisitor { 10 | private String name; 11 | 12 | public ClassNameClassVisitor() { 13 | super(Opcodes.ASM6); 14 | } 15 | 16 | @Override 17 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 18 | this.name=name; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/util/DataUtil.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.util; 2 | 3 | 4 | import com.er1cccc.acaf.core.audit.auditcore.CallGraph; 5 | import com.er1cccc.acaf.core.entity.MethodReference; 6 | 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.nio.file.Paths; 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | public class DataUtil { 14 | private static final String DataFlowFile = "dataflow.dat"; 15 | private static final String CallGraphFile = "callgraph.dat"; 16 | 17 | public static void SaveDataFlows(Map> dataFlow, 18 | Map methodMap) { 19 | try { 20 | Path filePath = Paths.get(DataFlowFile); 21 | if (Files.exists(filePath)) { 22 | Files.delete(filePath); 23 | } 24 | StringBuilder dataFlowStr = new StringBuilder(); 25 | for (MethodReference.Handle handle : dataFlow.keySet()) { 26 | String className = methodMap.get(handle).getClassReference().getName(); 27 | String methodName = handle.getName(); 28 | Set results = dataFlow.get(handle); 29 | if (results != null && results.size() != 0) { 30 | dataFlowStr.append("results:"); 31 | for (Integer i : results) { 32 | dataFlowStr.append(i).append(" "); 33 | } 34 | } else { 35 | continue; 36 | } 37 | dataFlowStr.append(className).append("\t"); 38 | dataFlowStr.append(methodName).append("\t"); 39 | dataFlowStr.append("\n"); 40 | } 41 | FileUtil.writeFile(DataFlowFile, dataFlowStr.toString()); 42 | } catch (Exception e) { 43 | e.printStackTrace(); 44 | } 45 | } 46 | 47 | public static void SaveCallGraphs(Set discoveredCalls) { 48 | try { 49 | Path filePath = Paths.get(CallGraphFile); 50 | if (Files.exists(filePath)) { 51 | Files.delete(filePath); 52 | } 53 | StringBuilder callGraphStr = new StringBuilder(); 54 | for (CallGraph callGraph : discoveredCalls) { 55 | String callerClass = callGraph.getCallerMethod().getClassReference().getName(); 56 | String callerMethod = callGraph.getCallerMethod().getName(); 57 | Integer callerArgIndex = callGraph.getCallerArgIndex(); 58 | String targetClass = callGraph.getTargetMethod().getClassReference().getName(); 59 | String targetMethod = callGraph.getTargetMethod().getName(); 60 | Integer targetArgIndex = callGraph.getTargetArgIndex(); 61 | callGraphStr.append("caller:").append(callerClass).append(".") 62 | .append(callerMethod).append("(").append(callerArgIndex).append(")") 63 | .append("\n").append("target:").append(targetClass).append(".") 64 | .append(targetMethod).append("(").append(targetArgIndex).append(")").append("\n") 65 | .append("--------------------------------------------------------------------\n"); 66 | } 67 | FileUtil.writeFile(CallGraphFile, callGraphStr.toString()); 68 | } catch (Exception e) { 69 | e.printStackTrace(); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/util/DirUtil.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.util; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | public class DirUtil { 8 | private static final List filenames = new ArrayList<>(); 9 | 10 | public static List GetFiles(String path) { 11 | filenames.clear(); 12 | return getFiles(path); 13 | } 14 | 15 | private static List getFiles(String path) { 16 | File file = new File(path); 17 | if (file.isDirectory()) { 18 | File[] files = file.listFiles(); 19 | if (files == null) { 20 | return filenames; 21 | } 22 | for (File value : files) { 23 | if (value.isDirectory()) { 24 | getFiles(value.getPath()); 25 | } else { 26 | filenames.add(value.getAbsolutePath()); 27 | } 28 | } 29 | } else { 30 | filenames.add(file.getAbsolutePath()); 31 | } 32 | return filenames; 33 | } 34 | 35 | public static boolean removeDir(File dir) { 36 | if (dir.isDirectory()) { 37 | String[] children = dir.list(); 38 | if (children != null) { 39 | for (String child : children) { 40 | boolean success = removeDir(new File(dir, child)); 41 | if (!success) { 42 | return false; 43 | } 44 | } 45 | } 46 | } 47 | return dir.delete(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/util/DrawUtil.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.util; 2 | 3 | import com.er1cccc.acaf.core.audit.auditcore.CallGraph; 4 | import guru.nidi.graphviz.attribute.Rank; 5 | import guru.nidi.graphviz.engine.Format; 6 | import guru.nidi.graphviz.engine.Graphviz; 7 | import guru.nidi.graphviz.model.Graph; 8 | import guru.nidi.graphviz.model.Node; 9 | 10 | import java.io.File; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Set; 14 | 15 | import static guru.nidi.graphviz.attribute.Rank.RankDir.LEFT_TO_RIGHT; 16 | import static guru.nidi.graphviz.model.Factory.graph; 17 | import static guru.nidi.graphviz.model.Factory.node; 18 | 19 | public class DrawUtil { 20 | public static void drawCallGraph(Set targetCalls) { 21 | try { 22 | List results = new ArrayList<>(); 23 | for (CallGraph callGraph : targetCalls) { 24 | Node tmp = node(getClassName(callGraph.getCallerMethod().getClassReference().getName()) + 25 | "\n" + callGraph.getCallerMethod().getName()); 26 | tmp = tmp.link(node(getClassName(callGraph.getTargetMethod().getClassReference().getName()) + 27 | "\n" + callGraph.getTargetMethod().getName())); 28 | results.add(tmp); 29 | } 30 | Graph g = graph("example").directed() 31 | .graphAttr().with(Rank.dir(LEFT_TO_RIGHT)) 32 | .linkAttr().with("class", "link-class") 33 | .with(results); 34 | Graphviz.fromGraph(g).height(1000).render(Format.PNG).toFile(new File("example/callgraph.png")); 35 | } catch (Exception e) { 36 | e.printStackTrace(); 37 | } 38 | } 39 | 40 | private static String getClassName(String fullName) { 41 | String[] strings = fullName.split("/"); 42 | return strings[strings.length - 1]; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/util/FileUtil.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.util; 2 | 3 | import org.apache.log4j.Logger; 4 | 5 | import java.io.*; 6 | import java.nio.charset.StandardCharsets; 7 | 8 | public class FileUtil { 9 | private static final Logger logger = Logger.getLogger(FileUtil.class); 10 | 11 | public static String readFile(InputStream is) { 12 | return new String(doReadFile(is), StandardCharsets.UTF_8); 13 | } 14 | 15 | public static byte[] readFileBytes(InputStream is) { 16 | return doReadFile(is); 17 | } 18 | 19 | public static String readFile(String filename) { 20 | try { 21 | InputStream r = new FileInputStream(filename); 22 | return new String(doReadFile(r), StandardCharsets.UTF_8); 23 | } catch (Exception e) { 24 | logger.error("error ", e); 25 | } 26 | return ""; 27 | } 28 | 29 | private static byte[] doReadFile(InputStream is) { 30 | try { 31 | ByteArrayOutputStream byteData = new ByteArrayOutputStream(); 32 | byte[] temp = new byte[1024 * 4]; 33 | byte[] context; 34 | int i; 35 | while ((i = is.read(temp)) > 0) { 36 | byteData.write(temp, 0, i); 37 | } 38 | context = byteData.toByteArray(); 39 | is.close(); 40 | byteData.close(); 41 | return context; 42 | } catch (Exception e) { 43 | logger.error("error ", e); 44 | } 45 | return new byte[0]; 46 | } 47 | 48 | public static void writeFile(String filename, String output) { 49 | try { 50 | File file = new File(filename); 51 | if (!file.exists()) { 52 | if (file.createNewFile()) { 53 | logger.debug("create new output file"); 54 | } 55 | } 56 | BufferedWriter bw = new BufferedWriter( 57 | new OutputStreamWriter( 58 | new FileOutputStream(file), 59 | StandardCharsets.UTF_8)); 60 | bw.write(output); 61 | bw.close(); 62 | } catch (Exception e) { 63 | e.printStackTrace(); 64 | } 65 | } 66 | 67 | public static String getFileExt(String filename) { 68 | String[] splits = filename.split("\\."); 69 | return splits[splits.length - 1]; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/util/IOUtil.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.util; 2 | 3 | import org.apache.log4j.Logger; 4 | 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | 8 | public class IOUtil { 9 | private static final Logger logger = Logger.getLogger(IOUtil.class); 10 | 11 | public static void copy(InputStream inputStream, OutputStream outputStream) { 12 | try { 13 | final byte[] buffer = new byte[4096]; 14 | int n; 15 | while ((n = inputStream.read(buffer)) > 0) { 16 | outputStream.write(buffer, 0, n); 17 | } 18 | } catch (Exception e) { 19 | logger.error("error ", e); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/util/JarUtil.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.util; 2 | 3 | import com.er1cccc.acaf.core.entity.ClassFile; 4 | import org.apache.log4j.Logger; 5 | import org.objectweb.asm.ClassReader; 6 | 7 | import java.io.*; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.nio.file.Paths; 11 | import java.util.ArrayList; 12 | import java.util.HashSet; 13 | import java.util.List; 14 | import java.util.Set; 15 | import java.util.jar.JarEntry; 16 | import java.util.jar.JarInputStream; 17 | 18 | @SuppressWarnings("all") 19 | public class JarUtil { 20 | private static final Logger logger = Logger.getLogger(JarUtil.class); 21 | private static final Set classFileSet = new HashSet<>(); 22 | private static Path tempDirRoot; 23 | 24 | static { 25 | tempDirRoot=Paths.get("acaf_temp"); 26 | tempDirRoot.toFile().mkdirs(); 27 | } 28 | 29 | 30 | public static List resolveSpringBootJarFile(String jarPath,boolean useAllLib) { 31 | try { 32 | int slash = Math.max(jarPath.lastIndexOf(47), jarPath.lastIndexOf(92)); 33 | Runtime.getRuntime().addShutdownHook(new Thread(() -> { 34 | closeAll(); 35 | DirUtil.removeDir(tempDirRoot.toFile()); 36 | })); 37 | resolve(jarPath);//1、把jar包解压到临时文件目录,并给每个class文件创建ClassFile对象 38 | resolveBoot(jarPath);//把jar包里lib下的jar包解压到临时文件目录 39 | File tempDir = getTempDir(jarPath); 40 | if(useAllLib){ 41 | Files.list(tempDir.toPath().resolve("BOOT-INF/lib")).forEach(p -> {//给lib目录下jar包执行1操作 42 | resolveNormalJarFile(p.toFile().getAbsolutePath()); 43 | }); 44 | } 45 | return new ArrayList<>(classFileSet); 46 | } catch (Exception e) { 47 | logger.error("error ", e); 48 | } 49 | return new ArrayList<>(); 50 | } 51 | 52 | public static List resolveNormalJarFile(String jarPath) { 53 | try { 54 | Runtime.getRuntime().addShutdownHook(new Thread(() -> { 55 | closeAll(); 56 | DirUtil.removeDir(tempDirRoot.toFile()); 57 | })); 58 | resolve(jarPath); 59 | return new ArrayList<>(classFileSet); 60 | } catch (Exception e) { 61 | logger.error("error ", e); 62 | } 63 | return new ArrayList<>(); 64 | } 65 | 66 | private static File getTempDir(String jarPath) throws IOException { 67 | int slash = Math.max(jarPath.lastIndexOf(47), jarPath.lastIndexOf(92)); 68 | String tempDir = jarPath; 69 | if (slash > -1) { 70 | tempDir = tempDir.substring(slash + 1); 71 | } 72 | File file = new File(tempDirRoot.toFile(), tempDir); 73 | return file; 74 | } 75 | 76 | private static void resolve(String jarPath) { 77 | try { 78 | File tmpDir = getTempDir(jarPath); 79 | if(!tmpDir.exists()){ 80 | try { 81 | tmpDir.mkdirs(); 82 | InputStream is = new FileInputStream(jarPath); 83 | JarInputStream jarInputStream = new JarInputStream(is); 84 | JarEntry jarEntry; 85 | while ((jarEntry = jarInputStream.getNextJarEntry()) != null) { 86 | Path fullPath = tmpDir.toPath().resolve(jarEntry.getName()); 87 | if (!jarEntry.isDirectory()) { 88 | if (!jarEntry.getName().endsWith(".class")) { 89 | continue; 90 | } 91 | Path dirName = fullPath.getParent(); 92 | if (!Files.exists(dirName)) { 93 | Files.createDirectories(dirName); 94 | } 95 | OutputStream outputStream = Files.newOutputStream(fullPath); 96 | IOUtil.copy(jarInputStream, outputStream); 97 | InputStream fis = new FileInputStream(fullPath.toFile()); 98 | ClassReader classReader = new ClassReader(fis); 99 | ClassNameClassVisitor classNameClassVisitor = new ClassNameClassVisitor(); 100 | classReader.accept(classNameClassVisitor,ClassReader.EXPAND_FRAMES); 101 | 102 | ClassFile classFile = new ClassFile(classNameClassVisitor.getName(), new FileInputStream(fullPath.toFile())); 103 | classFileSet.add(classFile); 104 | } 105 | } 106 | } catch (Exception e) { 107 | logger.error("error ", e); 108 | } 109 | }else{ 110 | try { 111 | Files.walk(tmpDir.toPath()).forEach(p -> { 112 | if(p.toString().endsWith(".class")){ 113 | InputStream fis = null; 114 | try { 115 | fis = new FileInputStream(p.toFile()); 116 | String className=p.toString().substring(tmpDir.toString().length()+1).replace("\\","/"); 117 | ClassFile classFile = new ClassFile(className, fis); 118 | classFileSet.add(classFile); 119 | } catch (FileNotFoundException e) { 120 | e.printStackTrace(); 121 | } 122 | } 123 | }); 124 | } catch (IOException e) { 125 | e.printStackTrace(); 126 | } 127 | } 128 | } catch (IOException e) { 129 | throw new IllegalStateException(e); 130 | } 131 | } 132 | 133 | private static void resolveBoot(String jarPath) { 134 | try { 135 | InputStream is = new FileInputStream(jarPath); 136 | JarInputStream jarInputStream = new JarInputStream(is); 137 | JarEntry jarEntry; 138 | File tempDir = getTempDir(jarPath); 139 | while ((jarEntry = jarInputStream.getNextJarEntry()) != null) { 140 | Path fullPath = tempDir.toPath().resolve(jarEntry.getName()); 141 | if (!jarEntry.isDirectory()) { 142 | if (!jarEntry.getName().endsWith(".jar")) { 143 | continue; 144 | } 145 | Path dirName = fullPath.getParent(); 146 | if (!Files.exists(dirName)) { 147 | Files.createDirectories(dirName); 148 | } 149 | OutputStream outputStream = Files.newOutputStream(fullPath); 150 | IOUtil.copy(jarInputStream, outputStream); 151 | } 152 | } 153 | } catch (Exception e) { 154 | logger.error("error ", e); 155 | } 156 | } 157 | 158 | private static void closeAll() { 159 | List classFileList = new ArrayList<>(classFileSet); 160 | for (ClassFile classFile : classFileList) { 161 | classFile.close(); 162 | } 163 | } 164 | } -------------------------------------------------------------------------------- /acaf-core/src/main/java/com/er1cccc/acaf/util/RtUtil.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.util; 2 | 3 | import com.er1cccc.acaf.core.entity.ClassFile; 4 | import com.google.common.reflect.ClassPath; 5 | import org.apache.log4j.Logger; 6 | import org.objectweb.asm.ClassReader; 7 | 8 | import java.net.JarURLConnection; 9 | import java.net.URL; 10 | import java.net.URLClassLoader; 11 | import java.net.URLConnection; 12 | import java.util.ArrayList; 13 | import java.util.HashSet; 14 | import java.util.List; 15 | import java.util.Set; 16 | 17 | @SuppressWarnings("all") 18 | public class RtUtil { 19 | private static final Logger logger = Logger.getLogger(RtUtil.class); 20 | 21 | public static List getAllClassesFromJars(List jarPathList) { 22 | logger.info("get all classes"); 23 | Set classFileSet = new HashSet<>(); 24 | for (String jarPath : jarPathList) { 25 | classFileSet.addAll(JarUtil.resolveNormalJarFile(jarPath)); 26 | } 27 | classFileSet.addAll(getRuntimeClasses()); 28 | return new ArrayList<>(classFileSet); 29 | } 30 | 31 | public static List getAllClassesFromBoot(List jarPathList, 32 | boolean useAllLib) { 33 | Set classFileSet = new HashSet<>(); 34 | for (String jarPath : jarPathList) { 35 | classFileSet.addAll(JarUtil.resolveSpringBootJarFile(jarPath, useAllLib)); 36 | } 37 | classFileSet.addAll(getRuntimeClasses()); 38 | return new ArrayList<>(classFileSet); 39 | } 40 | 41 | private static List getRuntimeClasses() { 42 | try { 43 | URL stringClassUrl = Object.class.getResource("String.class"); 44 | URLConnection connection = null; 45 | if (stringClassUrl != null) { 46 | connection = stringClassUrl.openConnection(); 47 | } 48 | Set result = new HashSet<>(); 49 | if (connection instanceof JarURLConnection) { 50 | URL runtimeUrl = ((JarURLConnection) connection).getJarFileURL(); 51 | URLClassLoader classLoader = new URLClassLoader(new URL[]{runtimeUrl}); 52 | for (ClassPath.ClassInfo classInfo : ClassPath.from(classLoader).getAllClasses()) { 53 | ClassNameClassVisitor classNameClassVisitor = new ClassNameClassVisitor(); 54 | ClassReader classReader = new ClassReader(classLoader.getResourceAsStream(classInfo.getResourceName())); 55 | classReader.accept(classNameClassVisitor,ClassReader.EXPAND_FRAMES); 56 | 57 | result.add(new ClassFile(classNameClassVisitor.getName(), 58 | classLoader.getResourceAsStream(classInfo.getResourceName()))); 59 | } 60 | } 61 | return new ArrayList<>(result); 62 | } catch (Exception e) { 63 | logger.error("error ", e); 64 | } 65 | return new ArrayList<>(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /acaf-core/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO,console 2 | log4j.additivity.org.apache=true 3 | log4j.appender.console=org.apache.log4j.ConsoleAppender 4 | log4j.appender.console.Threshold=INFO 5 | log4j.appender.console.ImmediateFlush=true 6 | log4j.appender.console.Target=System.out 7 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 8 | log4j.appender.console.layout.ConversionPattern=%d{HH:mm:ss} [%p] [%c] %m%n -------------------------------------------------------------------------------- /acaf-example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | acaf 7 | com.er1cccc 8 | 1.0 9 | 10 | 4.0.0 11 | 12 | acaf-example 13 | 14 | 15 | 16 | com.er1cccc 17 | acaf-core 18 | 1.0 19 | 20 | 21 | mysql 22 | mysql-connector-java 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-jdbc 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-thymeleaf 31 | 32 | 33 | com.squareup.okhttp3 34 | okhttp 35 | 4.9.2 36 | 37 | 38 | org.apache.httpcomponents 39 | httpclient 40 | 4.5.2 41 | 42 | 43 | commons-httpclient 44 | commons-httpclient 45 | 3.1 46 | 47 | 48 | commons-lang 49 | commons-lang 50 | 2.6 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-web 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /acaf-example/src/main/java/com/er1cccc/acaf/example/App.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.example; 2 | 3 | 4 | import com.er1cccc.acaf.Launcher; 5 | import com.er1cccc.acaf.example.sql.SqlConfigurer; 6 | import com.er1cccc.acaf.example.ssrf.SSRFConfigurer; 7 | 8 | public class App { 9 | public static void main( String[] args ) throws Exception{ 10 | Launcher.launch(new SSRFConfigurer(),args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /acaf-example/src/main/java/com/er1cccc/acaf/example/sql/SqlConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.example.sql; 2 | 3 | import com.er1cccc.acaf.config.*; 4 | 5 | public class SqlConfigurer implements ACAFConfigurer { 6 | 7 | @Override 8 | public void addSource(SourceRegistry sourceRegistry) { 9 | 10 | } 11 | 12 | @Override 13 | public void addSanitize(SanitizeRegistry sanitizeRegistry) { 14 | 15 | } 16 | 17 | @Override 18 | public void addSink(SinkRegistry sinkRegistry) { 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /acaf-example/src/main/java/com/er1cccc/acaf/example/sql/SqlSink.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.example.sql; 2 | 3 | import com.er1cccc.acaf.config.ControllableParam; 4 | import com.er1cccc.acaf.config.Sink; 5 | import org.springframework.jdbc.core.JdbcTemplate; 6 | import org.springframework.jdbc.core.ResultSetExtractor; 7 | import org.springframework.jdbc.core.RowMapper; 8 | 9 | import java.net.HttpURLConnection; 10 | import java.net.URL; 11 | import java.util.List; 12 | 13 | public class SqlSink implements Sink { 14 | private ControllableParam params = new ControllableParam(); 15 | 16 | public SqlSink(){ 17 | params.put("name","zhangsan"); 18 | } 19 | private final JdbcTemplate jdbcTemplate=new JdbcTemplate(); 20 | 21 | 22 | @Override 23 | public Object sinkMethod() throws Exception { 24 | // jdbcTemplate.query("select * from t_user where name=\"" + (String)params.getParameter("name") + "\"", (RowMapper) null); 25 | jdbcTemplate.query((String)params.getParameter("name"), (RowMapper) null); 26 | return null; 27 | } 28 | 29 | public static void main(String[] args) throws Exception{ 30 | new SqlSink().sinkMethod(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /acaf-example/src/main/java/com/er1cccc/acaf/example/ssrf/SSRFConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.example.ssrf; 2 | 3 | 4 | import com.er1cccc.acaf.config.*; 5 | 6 | public class SSRFConfigurer implements ACAFConfigurer { 7 | @Override 8 | public void addSource(SourceRegistry sourceRegistry) { 9 | 10 | } 11 | 12 | @Override 13 | public void addSanitize(SanitizeRegistry sanitizeRegistry) { 14 | } 15 | 16 | @Override 17 | public void addSink(SinkRegistry sinkRegistry) { 18 | sinkRegistry.addSink(new SsrfSink1()); 19 | sinkRegistry.addSink(new SsrfSink2()); 20 | sinkRegistry.addSink(new SsrfSink3()); 21 | sinkRegistry.addSink(new SsrfSink4()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /acaf-example/src/main/java/com/er1cccc/acaf/example/ssrf/SsrfSink1.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.example.ssrf; 2 | 3 | import com.er1cccc.acaf.config.ControllableParam; 4 | import com.er1cccc.acaf.config.Sink; 5 | 6 | import java.net.HttpURLConnection; 7 | import java.net.URL; 8 | 9 | public class SsrfSink1 implements Sink { 10 | private ControllableParam params = new ControllableParam(); 11 | 12 | public SsrfSink1(){ 13 | params.put("url","http://localhost"); 14 | } 15 | 16 | @Override 17 | public Object sinkMethod() throws Exception { 18 | URL url = new URL((String) params.getParameter("url")); 19 | HttpURLConnection con = (HttpURLConnection) url.openConnection(); 20 | con.getInputStream(); 21 | return null; 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /acaf-example/src/main/java/com/er1cccc/acaf/example/ssrf/SsrfSink2.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.example.ssrf; 2 | 3 | import com.er1cccc.acaf.config.ControllableParam; 4 | import com.er1cccc.acaf.config.PassthroughRegistry; 5 | import com.er1cccc.acaf.config.Sink; 6 | import org.apache.http.HttpEntity; 7 | import org.apache.http.HttpResponse; 8 | import org.apache.http.client.methods.HttpGet; 9 | import org.apache.http.impl.client.CloseableHttpClient; 10 | import org.apache.http.impl.client.HttpClients; 11 | 12 | import java.net.HttpURLConnection; 13 | import java.net.URL; 14 | 15 | public class SsrfSink2 implements Sink { 16 | 17 | private ControllableParam params = new ControllableParam(); 18 | 19 | public SsrfSink2(){ 20 | params.put("url","http://localhost"); 21 | } 22 | 23 | @Override 24 | public Object sinkMethod() throws Exception { 25 | CloseableHttpClient httpClient = HttpClients.createDefault(); 26 | HttpGet getRequest = new HttpGet((String) params.getParameter("url")); 27 | httpClient.execute(getRequest); 28 | return null; 29 | } 30 | 31 | public static void main(String[] args) throws Exception{ 32 | // new SsrfSink2().sinkMethod(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /acaf-example/src/main/java/com/er1cccc/acaf/example/ssrf/SsrfSink3.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.example.ssrf; 2 | 3 | import com.er1cccc.acaf.config.ControllableParam; 4 | import com.er1cccc.acaf.config.PassthroughRegistry; 5 | import com.er1cccc.acaf.config.Sink; 6 | 7 | import java.io.InputStream; 8 | import java.lang.reflect.Constructor; 9 | import java.lang.reflect.Method; 10 | import java.net.Socket; 11 | import java.nio.channels.SocketChannel; 12 | 13 | public class SsrfSink3 implements Sink { 14 | private ControllableParam params = new ControllableParam(); 15 | 16 | public SsrfSink3(){ 17 | params.put("url","http://localhost"); 18 | } 19 | 20 | @Override 21 | public Object sinkMethod() throws Exception { 22 | Socket socket = new Socket((String) params.getParameter("host"), (Integer) params.getParameter("port")); 23 | InputStream in = socket.getInputStream(); 24 | return null; 25 | } 26 | 27 | @Override 28 | public void addPassthrough(PassthroughRegistry passthroughRegistry) { 29 | try{ 30 | Class socketClass = Socket.class; 31 | Constructor constructor = socketClass.getConstructor(String.class, int.class); 32 | passthroughRegistry.addPassthrough(constructor,1,2); 33 | Method getInputStream = socketClass.getMethod("getInputStream"); 34 | passthroughRegistry.addPassthrough(getInputStream,0); 35 | }catch (Exception e){ 36 | e.printStackTrace(); 37 | } 38 | } 39 | 40 | public static void main(String[] args) throws Exception{ 41 | Class socketClass = Socket.class; 42 | Constructor constructor = socketClass.getConstructor(String.class, int.class); 43 | Method getInputStream = socketClass.getMethod("getInputStream"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /acaf-example/src/main/java/com/er1cccc/acaf/example/ssrf/SsrfSink4.java: -------------------------------------------------------------------------------- 1 | package com.er1cccc.acaf.example.ssrf; 2 | 3 | import com.er1cccc.acaf.config.ControllableParam; 4 | import com.er1cccc.acaf.config.PassthroughRegistry; 5 | import com.er1cccc.acaf.config.Sink; 6 | import okhttp3.OkHttpClient; 7 | import okhttp3.Request; 8 | import okhttp3.Response; 9 | 10 | import java.lang.reflect.Method; 11 | 12 | public class SsrfSink4 implements Sink { 13 | private ControllableParam params = new ControllableParam(); 14 | 15 | public SsrfSink4(){ 16 | params.put("url","http://localhost"); 17 | } 18 | 19 | @Override 20 | public Object sinkMethod() throws Exception { 21 | OkHttpClient httpClient = new OkHttpClient(); 22 | Request request = new Request.Builder().url((String) params.getParameter("url")).build(); 23 | Response response = httpClient.newCall(request).execute(); 24 | return null; 25 | } 26 | 27 | @Override 28 | public void addPassthrough(PassthroughRegistry passthroughRegistry) { 29 | try{ 30 | Class builder = new Request.Builder().getClass(); 31 | Method urlMethod = builder.getMethod("url",String.class); 32 | Method buildMethod = builder.getMethod("build"); 33 | Class okHttpClientClass = OkHttpClient.class; 34 | Method newCall = okHttpClientClass.getMethod("newCall", Request.class); 35 | Class call = newCall.getReturnType(); 36 | Method execute = call.getMethod("execute"); 37 | 38 | passthroughRegistry.addPassthrough(urlMethod,1); 39 | passthroughRegistry.addPassthrough(buildMethod,0); 40 | passthroughRegistry.addPassthrough(newCall,1); 41 | passthroughRegistry.addPassthrough(execute,0); 42 | }catch (Exception e){ 43 | e.printStackTrace(); 44 | } 45 | } 46 | 47 | public static void main(String[] args) throws Exception{ 48 | // new SsrfSink4().sinkMethod(); 49 | Class builder = new Request.Builder().getClass(); 50 | Method urlMethod = builder.getMethod("url",String.class); 51 | Method buildMethod = builder.getMethod("build"); 52 | Class okHttpClientClass = OkHttpClient.class; 53 | Method newCall = okHttpClientClass.getMethod("newCall", Request.class); 54 | Class call = newCall.getReturnType(); 55 | Method execute = call.getMethod("execute"); 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /acaf/image-20220103131703025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103131703025.png -------------------------------------------------------------------------------- /acaf/image-20220103132616981.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103132616981.png -------------------------------------------------------------------------------- /acaf/image-20220103133558167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103133558167.png -------------------------------------------------------------------------------- /acaf/image-20220103141501161.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103141501161.png -------------------------------------------------------------------------------- /acaf/image-20220103141828815.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103141828815.png -------------------------------------------------------------------------------- /acaf/image-20220103141917976.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103141917976.png -------------------------------------------------------------------------------- /acaf/image-20220103142558198.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103142558198.png -------------------------------------------------------------------------------- /acaf/image-20220103142721032.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103142721032.png -------------------------------------------------------------------------------- /acaf/image-20220103142841443.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103142841443.png -------------------------------------------------------------------------------- /acaf/image-20220103142903948.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103142903948.png -------------------------------------------------------------------------------- /acaf/image-20220103143104899.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103143104899.png -------------------------------------------------------------------------------- /acaf/image-20220103143239083.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103143239083.png -------------------------------------------------------------------------------- /acaf/image-20220103143422984.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103143422984.png -------------------------------------------------------------------------------- /acaf/image-20220103143507206.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103143507206.png -------------------------------------------------------------------------------- /acaf/image-20220103145309189.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103145309189.png -------------------------------------------------------------------------------- /acaf/image-20220103145355140.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103145355140.png -------------------------------------------------------------------------------- /acaf/image-20220103145427848.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103145427848.png -------------------------------------------------------------------------------- /acaf/image-20220103145604152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103145604152.png -------------------------------------------------------------------------------- /acaf/image-20220103145810118.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103145810118.png -------------------------------------------------------------------------------- /acaf/image-20220103150117976.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103150117976.png -------------------------------------------------------------------------------- /acaf/image-20220103151240038.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103151240038.png -------------------------------------------------------------------------------- /acaf/image-20220103151733418.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103151733418.png -------------------------------------------------------------------------------- /acaf/image-20220103151747594.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103151747594.png -------------------------------------------------------------------------------- /acaf/image-20220103155122573.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103155122573.png -------------------------------------------------------------------------------- /acaf/image-20220103155809694.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103155809694.png -------------------------------------------------------------------------------- /acaf/image-20220103160230238.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103160230238.png -------------------------------------------------------------------------------- /acaf/image-20220103160528500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103160528500.png -------------------------------------------------------------------------------- /acaf/image-20220103162135990.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103162135990.png -------------------------------------------------------------------------------- /acaf/image-20220103162254813.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103162254813.png -------------------------------------------------------------------------------- /acaf/image-20220103162431545.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103162431545.png -------------------------------------------------------------------------------- /acaf/image-20220103162532243.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103162532243.png -------------------------------------------------------------------------------- /acaf/image-20220103162812381.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103162812381.png -------------------------------------------------------------------------------- /acaf/image-20220103164332924.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103164332924.png -------------------------------------------------------------------------------- /acaf/image-20220103164537493.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103164537493.png -------------------------------------------------------------------------------- /acaf/image-20220103164749513.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103164749513.png -------------------------------------------------------------------------------- /acaf/image-20220103170547318.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103170547318.png -------------------------------------------------------------------------------- /acaf/image-20220103170644290.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testtttter/ACAF/0021a6beb300f3454a160388fd9d4aaf21211a31/acaf/image-20220103170644290.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 2.5.5 11 | 12 | 13 | 14 | com.er1cccc 15 | acaf 16 | pom 17 | 1.0 18 | 19 | 20 | acaf-core 21 | acaf-example 22 | 23 | 24 | 25 | 26 | 8 27 | 8 28 | 9.0 29 | UTF-8 30 | 31 | 32 | 33 | 34 | 35 | guru.nidi 36 | graphviz-java 37 | 0.18.1 38 | 39 | 40 | ch.qos.logback 41 | logback-classic 42 | 1.2.3 43 | 44 | 45 | log4j 46 | log4j 47 | 1.2.17 48 | 49 | 50 | com.beust 51 | jcommander 52 | 1.81 53 | 54 | 55 | org.ow2.asm 56 | asm 57 | ${asm.version} 58 | 59 | 60 | org.ow2.asm 61 | asm-commons 62 | ${asm.version} 63 | 64 | 65 | com.google.guava 66 | guava 67 | 31.0.1-jre 68 | 69 | 70 | org.projectlombok 71 | lombok 72 | 1.18.18 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 前言 2 | 3 | 笔者最近在研究java自动化代码审计这方面的内容,也看了一些相关的文章,其中主要是跟着[4ra1n师傅](https://4ra1n.love)的文章进行学习的。目前学到的有两种自动化审计思路,一是AST,二是ASM。前者基于java源代码,后者基于字节码。个人感觉后者的可操作性会更强一些。同时由于有[gadget-inspector](https://github.com/JackOfMostTrades/gadgetinspector)的铺垫,给我们提供了**使用java代码模拟jvm执行字节码**的思路,导致ASM这种自动化审计思路可以做到一定程度的动态化,实现了污点的动态流动,而不仅仅是停留在静态的代码层面。这也使得ASM这种思路更加具有可玩性。因此,笔者也选择了基于ASM来实现**ACAF(Auto Code Audit Framework)**。 4 | 5 | 在正式介绍之前,还是想先梳理一下笔者学习ASM自动化审计的过程。 6 | 7 | 最开始,笔者先入手的是[gadget-inspector](https://github.com/JackOfMostTrades/gadgetinspector)源码阅读,有一定的jvm基础,加上[三梦师傅](https://xz.aliyun.com/t/7058#toc-6)讲解的非常清晰的博客,花了几天把源码啃得七七八八了。 8 | 9 | 紧接着就开始跟着4ra1n师傅的博客学,学了一下他的[CodeInspector的实现](https://4ra1n.love/post/zA8rsm1ne/#%E6%80%BB%E7%BB%93),以及[基于污点分析的JSP Webshell检测](https://4ra1n.love/post/0GeHo6Oj0/),后者可能跟本文不是直接相关,不过**使用java代码模拟jvm执行字节码**这种思路都是一样的。 10 | 11 | 值得一提的是,CodeInspector是基于gadget-inspector进行实现的,二者的区别在于,CodeInspector是针对springboot项目的自动化审计,而gadget-inspector是专门用来找反序列化链的。 12 | 13 | 4ra1n师傅在实现CodeInspector的时候,是以SSRF的例子来进行实现的,需要我们自行去分析**常见导致SSRF的字节码**,然后把这种特征硬编码到程序当中,才能实现检测。于是笔者就思考了一下能否将这个过程自动化,或者说,让这个工具的使用更加简单一些。于是笔者将CodeInspector进行了一定程度的抽象,写了个**ACAF**。最终实现的效果就是,用户仅仅需要提供**漏洞代码的demo**,然后**手动将部分污点传播链路链接起来**,就能实现自动化代码审计。 14 | 15 | 接下来,笔者将从**使用方法、框架整体概览、漏洞代码特征抽取、手工链接污点传播链路、自动化审计、实际效果**六个方面来对ACAF进行分析(前面说了一堆废话 16 | 17 | # 使用方法 18 | 19 | 以ssrf的例子来说,用户只需要给出会造成ssrf漏洞的常见demo,如下,将漏洞demo写在`sinkMethod`方法中 20 | 21 | ```java 22 | package com.er1cccc.acaf.example.ssrf; 23 | 24 | import com.er1cccc.acaf.config.ControllableParam; 25 | import com.er1cccc.acaf.config.PassthroughRegistry; 26 | import com.er1cccc.acaf.config.Sink; 27 | import okhttp3.OkHttpClient; 28 | import okhttp3.Request; 29 | import okhttp3.Response; 30 | 31 | import java.lang.reflect.Method; 32 | 33 | public class SsrfSink4 implements Sink { 34 | private ControllableParam params = new ControllableParam(); 35 | 36 | public SsrfSink4(){ 37 | params.put("url","http://localhost"); 38 | } 39 | 40 | @Override 41 | public Object sinkMethod() throws Exception { 42 | OkHttpClient httpClient = new OkHttpClient(); 43 | Request request = new Request.Builder().url((String) params.getParameter("url")).build(); 44 | Response response = httpClient.newCall(request).execute(); 45 | return null; 46 | } 47 | } 48 | ``` 49 | 50 | 然后将**手动将部分污点传播链路链接起来**(这部分在本文后面会详细讲解),实现`addPassthrough`方法,最终得到完整的demo代码 51 | 52 | ```java 53 | public class SsrfSink4 implements Sink { 54 | private ControllableParam params = new ControllableParam(); 55 | 56 | public SsrfSink4(){ 57 | params.put("url","http://localhost"); 58 | } 59 | 60 | @Override 61 | public Object sinkMethod() throws Exception { 62 | OkHttpClient httpClient = new OkHttpClient(); 63 | Request request = new Request.Builder().url((String) params.getParameter("url")).build(); 64 | Response response = httpClient.newCall(request).execute(); 65 | return null; 66 | } 67 | 68 | @Override 69 | public void addPassthrough(PassthroughRegistry passthroughRegistry) { 70 | try{ 71 | Class builder = new Request.Builder().getClass(); 72 | Method urlMethod = builder.getMethod("url",String.class); 73 | Method buildMethod = builder.getMethod("build"); 74 | Class okHttpClientClass = OkHttpClient.class; 75 | Method newCall = okHttpClientClass.getMethod("newCall", Request.class); 76 | Class call = newCall.getReturnType(); 77 | Method execute = call.getMethod("execute"); 78 | 79 | passthroughRegistry.addPassthrough(urlMethod,1); 80 | passthroughRegistry.addPassthrough(buildMethod,0); 81 | passthroughRegistry.addPassthrough(newCall,1); 82 | passthroughRegistry.addPassthrough(execute,0); 83 | }catch (Exception e){ 84 | e.printStackTrace(); 85 | } 86 | } 87 | } 88 | ``` 89 | 90 | 然后写一个配置类 91 | 92 | ```java 93 | package com.er1cccc.acaf.example.ssrf; 94 | 95 | 96 | import com.er1cccc.acaf.config.*; 97 | 98 | public class SSRFConfigurer implements ACAFConfigurer { 99 | @Override 100 | public void addSource(SourceRegistry sourceRegistry) { 101 | 102 | } 103 | 104 | @Override 105 | public void addSanitize(SanitizeRegistry sanitizeRegistry) { 106 | } 107 | 108 | @Override 109 | public void addSink(SinkRegistry sinkRegistry) { 110 | sinkRegistry.addSink(new SsrfSink4()); 111 | } 112 | } 113 | ``` 114 | 115 | 最后创建配置类,开始审计 116 | 117 | ```java 118 | package com.er1cccc.acaf.example; 119 | 120 | 121 | import com.er1cccc.acaf.Launcher; 122 | import com.er1cccc.acaf.example.sql.SqlConfigurer; 123 | import com.er1cccc.acaf.example.ssrf.SSRFConfigurer; 124 | 125 | public class App { 126 | public static void main( String[] args ) throws Exception{ 127 | Launcher.launch(new SSRFConfigurer(),args); 128 | } 129 | } 130 | ``` 131 | 132 | # 框架整体概览 133 | 134 | 最初笔者制定的框架结构图是长这个样子的。分别通过VulnTemplateSourceVisitor、VulnTemplateSanitizeVisitor、VulnTemplateSinkVisitor去观察用户给出的source、sanitizer和sink,分析字节码,抽取出特征,然后将特征保存到VulnDiscovery中,再由VulnDiscovery去完成审计功能。 135 | 136 | ![image-20220103133558167](acaf/image-20220103133558167.png) 137 | 138 | 然而,笔者在真正实现时发现,source和sanitize这部分可能不太有必要(灰色部分),因此就仅实现了sink的自定义。 139 | 140 | 先说说source为什么没有实现,一开始之所以规划了source、sanitizer和sink是因为想参考codeql,但是真正落到ACAF上发现,并没有必要去完全套用。codeql之所以提供了自定义source的功能,是因为它是一款针对多种编程语言,多种框架的普适性的代码审计工具,而ACAF的定位是仅仅针对spring web项目的审计。在codeql中,source可能是无穷无尽的,没办法限定死,而ACAF中,也就是在spring web的环境下,source比较局限,可以直接硬编码到框架中。 141 | 142 | 而sanitizer的话,是因为笔者在写这个框架过程中,暂时还没遇到过误报的情况,所以也感觉不太必要,因此就暂时没有实现。如果后续有这种需求,再考虑实现。 143 | 144 | 既然说到这里了,就稍微说说ACAF和codeql的区别。这也是笔者在实现这个框架之前思考过的一个问题,codeql已经做的非常出色了,我有没有必要再去重复造轮子? 145 | 146 | ## ACAF与codeql的区别 147 | 148 | ### 更加自动化 149 | 150 | codeql需要使用者写ql语句对代码进行分析,需要一定的人工投入。而acaf仅需要用户给出项目的jar包/war包(未来可能扩充更多的形式)就能对项目进行常见漏洞的自动化审计,如果是特殊漏洞,用户需要给出漏洞的示例代码。这一点目前笔者仅仅给出了ssrf的几种示例demo,用来审计项目中是否存在ssrf漏洞。未来可能计划补充更多常见漏洞的demo,这样就能实现常见web漏洞的自动化审计。 151 | 152 | **追问:**那直接用codeql写出一些常见漏洞的查询语句不是更好? 153 | **答:**在使用codeql前需要投入一定的时间成本去学习codeql的文档,而且codeql的语句也较为复杂。而ACAF仅仅需要用户给出java编写的漏洞demo,使用起来更加简单 154 | 155 | ### 开源问题 156 | 157 | 目前已知codeql有个很致命的点就是只能对源代码进行分析,如果仅有jar包是没法分析的。虽然说可以把jar包进行反编译成源代码然后再进行分析,但是在反编译过程中可能会遇到项目依赖过于复杂,反编译十分困难,出现反编译后无法编译回去的情况。而ACAF本身就是对jar包(class文件)进行分析,所以不存在这方面的问题。 158 | 159 | # 漏洞代码特征抽取 160 | 161 | 前面说了,使用CodeInspector的时候,需要我们**自行分析导致漏洞的字节码**,然后**自行找出漏洞字节码的特征**,将其硬编码到程序当中,才能实现检测。而**漏洞代码特征抽取**这部分要完成的功能就是,只需要用户**提供导致漏洞的java代码**,让框架**自动去分析它对应的字节码,提取出特征**。 162 | 163 | 明确了这部分的目的之后,来看看代码实现。主要代码逻辑在`com.er1cccc.acaf.core.template.VulnTemplateSinkVisitor`,由于框架给出了接口,要求用户如果想自定义sink需要写一个类来实现`com.er1cccc.acaf.config.Sink`接口,进而实现接口中的`sinkMethod`方法,将漏洞demo写到这个方法中。因此,我们在使用asm观察的时候,就只需要去关注这个`sinkMethod`方法就好 164 | 165 | 在`com.er1cccc.acaf.core.template.VulnTemplateSinkVisitor#visitMethod`中,如果观察到`sinkMethod`方法,则进一步去观察方法体,使用`com.er1cccc.acaf.core.template.VulnTemplateSinkVisitor.VulnTemplateSinkMethodVisitor`进行观察 166 | 167 | ![image-20220103141501161](acaf/image-20220103141501161.png) 168 | 169 | 对与`sinkMethod`的方法体,我们着重观察的是方法体中的方法调用指令,重写`visitMethodInsn`方法对其进行观察。其他的字节码指令我们交给父类`CoreMethodAdapter`去帮助我们模拟出JVM的操作,实现污点的传递。关于模拟JVM操作不太理解的可以先按照**前言**中笔者的学习路径,先去学一下前面的基础。 170 | 171 | ![image-20220103141917976](acaf/image-20220103141917976.png) 172 | 173 | 接下来看看`visitMethodInsn`方法的实现,首先调用了`isGetParamMethod`方法。这个方法的作用就是判断本次方法调用是不是准备调用**获取可控参数的方法**。 174 | 175 | 这里详细解释一下,我们让用户提供漏洞demo,但是我们并不知道漏洞demo中究竟哪些参数可控才能导致该漏洞,因此,与用户约定,可控的参数统一通过`params.getParameter`来进行获取 176 | 177 | 如下,`new Request.Builder().url()`方法的参数可控可以造成漏洞,因此我们通过`params.getParameter`来进行获取。 178 | 179 | ```java 180 | public class SsrfSink4 implements Sink { 181 | private ControllableParam params = new ControllableParam(); 182 | 183 | public SsrfSink4(){ 184 | params.put("url","http://localhost"); 185 | } 186 | 187 | @Override 188 | public Object sinkMethod() throws Exception { 189 | OkHttpClient httpClient = new OkHttpClient(); 190 | Request request = new Request.Builder().url((String) params.getParameter("url")).build(); 191 | Response response = httpClient.newCall(request).execute(); 192 | return null; 193 | } 194 | } 195 | ``` 196 | 197 | 有了这样的约定之后,我们在观察`sinkMethod`方法体中的方法调用时,如果观察到了`params.getParameter`这样的方法调用,我们就能够知道这里存在一个可控的参数,因此我们可以在调用完`params.getParameter`方法之后,把操作数栈栈顶的返回值替换为taint(污点)。 198 | 199 | 回到`isGetParamMethod`方法,我们看看它的实现,实际上就是再判断是否在调用`params.getParameter` 200 | 201 | ![image-20220103142558198](acaf/image-20220103142558198.png) 202 | 203 | 如果是,就像笔者刚才说的那样,把操作数栈栈顶的返回值替换为taint,这里用布尔值true表示污点。到这里实际上我们就完成的污点的播种,接下来就考虑如何把污点传递的特征也给记录下来 204 | 205 | ![image-20220103142721032](acaf/image-20220103142721032.png) 206 | 207 | 我们看else逻辑,首先调用`getControllableArgIndex`获取可控参数的下标,可控参数等同于污点,就是当前操作数栈中为true的参数 208 | 209 | ![image-20220103142903948](acaf/image-20220103142903948.png) 210 | 211 | 看看`getControllableArgIndex`方法的实现 212 | 213 | ![image-20220103143104899](acaf/image-20220103143104899.png) 214 | 215 | 获取到可控的参数下标之后,调用了`super.visitMethodInsn`,这里就是在模拟JVM的执行过程。这行代码结束之后,操作数栈顶就是方法的返回值了。 216 | 217 | ![image-20220103143239083](acaf/image-20220103143239083.png) 218 | 219 | 接下来判断可控的参数列表是否为空,不为空则创建一个`VisitMethodInsnInfoResolver`并加入到`CompositResolver`中,算是提取到一个特征 220 | 221 | ![image-20220103143422984](acaf/image-20220103143422984.png) 222 | 223 | 如果为空,则继续判断方法返回值是否为空,如果不为空查看返回值是否是污点,是的话也创建一个`VisitMethodInsnInfoResolver`并加入到`CompositResolver`中,也算是提取到一个特征 224 | 225 | ![image-20220103143507206](acaf/image-20220103143507206.png) 226 | 227 | 理论终于扯完了,下面上案例来分析,就以上面给出的`SsrfSink4`demo类为例来看看实际的执行过程 228 | 229 | ```java 230 | public class SsrfSink4 implements Sink { 231 | private ControllableParam params = new ControllableParam(); 232 | 233 | public SsrfSink4(){ 234 | params.put("url","http://localhost"); 235 | } 236 | 237 | @Override 238 | public Object sinkMethod() throws Exception { 239 | OkHttpClient httpClient = new OkHttpClient(); 240 | Request request = new Request.Builder().url((String) params.getParameter("url")).build(); 241 | Response response = httpClient.newCall(request).execute(); 242 | return null; 243 | } 244 | } 245 | ``` 246 | 247 | 第一次进入`com.er1cccc.acaf.core.template.VulnTemplateSinkVisitor.VulnTemplateSinkMethodVisitor#visitMethodInsn`是调用`OkHttpClient`的``方法,可以简单理解为是构造方法,不满足if条件,进入else 248 | 249 | ![image-20220103145309189](acaf/image-20220103145309189.png) 250 | 251 | 紧接着判断参数和返回值是否是taint,都不满足,因此什么也没做。 252 | 253 | ![image-20220103145355140](acaf/image-20220103145355140.png) 254 | 255 | 接下来看`sinkMethod`方法体中的第二个方法调用,是`okhttp3/Request$Builder.init`,跟上面一样,不是重点,直接跳过 256 | 257 | ![image-20220103145427848](acaf/image-20220103145427848.png) 258 | 259 | 第三个方法调用是`com/er1cccc/acaf/config/ControllableParam.getParameter`这就是重点了,根据前面的分析,85行的if满足,进入,然后86行先模拟方法执行,87行把方法返回值替换为taint。 260 | 261 | ![image-20220103145604152](acaf/image-20220103145604152.png) 262 | 263 | 继续看下一个方法调用,是`okhttp3/Request$Builder.url`,在调用`getControllableArgIndex`时,获取到参数0是可控的,也就是刚刚观察getParameter方法时,往操作数栈中放入的true。 264 | 265 | ![image-20220103145810118](acaf/image-20220103145810118.png) 266 | 267 | 由于存在可控参数,所以这里会创建一个`VisitMethodInsnInfoResolver`并加入到`CompositResolver`中。 268 | 269 | ![image-20220103150117976](acaf/image-20220103150117976.png) 270 | 271 | 这里有必要详细解释一下。所谓**漏洞代码特征抽取**,实际上就是从`params.getParameter`开始,播种污点,然后看污点随着方法的调用会传递到哪里去,在刚才的例子中,污点就传播到了`okhttp3/Request$Builder.url`方法中。 272 | 273 | 我们在观察`sinkMethod`的方法体时,需要将污点的传播路径给记录下来,记录到`CompositResolver`中,需要注意的是,这里在记录时是以`Resolver`的形式进行记录的。这里创建了一个`VisitMethodInsnInfoResolver`,然后传入了方法调用相关的信息,如方法名name,方法描述符descriptor等,同时还传入了方法调用可控的参数列表,这是为了后续在审计时,比对触发漏洞的方法需要的参数是否可控。 274 | 275 | 解释完继续回来看在`sinkMethod`方法体中的观察到的方法调用,下一个方法调用是`okhttp3/Request$Builder.build`,在这一步的时候会发现一个问题:**污点传播不下去了!**可控的参数列表为空,而且方法返回值也不是taint。 276 | 277 | ![image-20220103151240038](acaf/image-20220103151240038.png) 278 | 279 | 如果继续让程序继续执行,直到`sinkMethod`观察完毕,得到的结果就是,`CompositResolver`只记录到一个特征,就是触发漏洞需要调用`okhttp3/Request$Builder.url`方法,且第0个参数可控。这样很明显是会有问题的,特征提取的不够充分,会引起很严重的误报情况。 280 | 281 | ![image-20220103151747594](acaf/image-20220103151747594.png) 282 | 283 | 看下面这段代码,当url参数可控时,就会被误判成是sink。但是实际上它并不是,仅仅调用`httpClient.newCall(request)`是不会发起请求的,一定要调用`httpClient.newCall(request).execute()`才行。 284 | 285 | ```java 286 | public String ssrf4_fake2(String url) { 287 | try { 288 | OkHttpClient httpClient = new OkHttpClient(); 289 | Request request = new Request.Builder() 290 | .url(url) 291 | .build(); 292 | httpClient.newCall(request); 293 | return ""; 294 | } catch (Exception e) { 295 | e.printStackTrace(); 296 | return e.getMessage(); 297 | } 298 | } 299 | ``` 300 | 301 | 为什么会导致特征提取过少,实际上不难发现是因为污点没有传播下去,在`okhttp3/Request$Builder.build`这一步就断掉了。解决方法也简单,在下一节进行分析。 302 | 303 | # 手工链接污点传播链路 304 | 305 | 上面说到了,污点传播是在`okhttp3/Request$Builder.build`这一步断掉的,这一节,我们主要看污点传播是怎么断的,怎么把它给接回去? 306 | 307 | 先看污点传播是怎么断的,我们回到刚刚断的地方,在`okhttp3/Request$Builder.build`这一步,看看此时的operandStack,发现其中居然没有了污点,我们刚刚观察`params.getParameter`的时候明明播种了污点,但是现在不见了 308 | 309 | ![image-20220103155122573](acaf/image-20220103155122573.png) 310 | 311 | 我们再回看一步,看`okhttp3/Request$Builder.url`方法,在执行这个方法之前,可以看到operandStack中是存在污点的,也就是观察`params.getParameter`的时候播种的污点,那为什么执行完`url`方法后准备执行`build`的时候就没了呢?不难想到,问题就出在`url`方法的执行过程中 312 | 313 | ![image-20220103155809694](acaf/image-20220103155809694.png) 314 | 315 | 我们跟入`url`方法的模拟执行过程,第90行。这里涉及到gadget-inspector的源码,不会说得很细,如果理解不了,可以先按照**前言**中笔者的学习路径,先去学一下前面的基础。我们直接看关键部分的代码 316 | 317 | 在这一步,会从passthrough中查出方法返回值与那个参数有关,查完发现,url方法的返回值与0号参数有关,也就是this,然后把this加入了resultTaint,最后将resultTaint中的值压入了operandStack。但是我们之前标记的污点是1号参数,就相当于污点在这个地方被无视掉了,也就造成了污点传播中断的现象。 318 | 319 | ![image-20220103160230238](acaf/image-20220103160230238.png) 320 | 321 | 可以看出,这种操作很明显是不太合理的,按照我们自己的分析,`url`方法的参数应该是与1号参数有关才对,也就是这里这个String类型的参数。但是gadget-inspector没有这么智能,存在一定的缺陷,所以需要我们人工修正这个passthrough。 322 | 323 | ![image-20220103160528500](acaf/image-20220103160528500.png) 324 | 325 | 到这里就基本知道了为什么污点传播会中断,就是因为gadget-inspector存在一定的局限性。接下来我们需要人工调整这个passthrough,让污点成功传递下去,调整方法很简单,原先我们在定义sink的时候需要重写`sinkMethod`给出sink方法的示例代码,现在我们只需要再重写一个`addPassthrough`方法就可以实现调整passthrough。针对上面污点传播中断的问题,笔者写了这样一段代码来解决(分析请看注释) 326 | 327 | ```java 328 | public class SsrfSink4 implements Sink { 329 | private ControllableParam params = new ControllableParam(); 330 | 331 | public SsrfSink4(){ 332 | params.put("url","http://localhost"); 333 | } 334 | 335 | @Override 336 | public Object sinkMethod() throws Exception { 337 | OkHttpClient httpClient = new OkHttpClient(); 338 | Request request = new Request.Builder().url((String) params.getParameter("url")).build(); 339 | Response response = httpClient.newCall(request).execute(); 340 | return null; 341 | } 342 | 343 | @Override 344 | public void addPassthrough(PassthroughRegistry passthroughRegistry) { 345 | try{ 346 | Class builder = new Request.Builder().getClass(); 347 | Method urlMethod = builder.getMethod("url",String.class); 348 | Method buildMethod = builder.getMethod("build"); 349 | Class okHttpClientClass = OkHttpClient.class; 350 | Method newCall = okHttpClientClass.getMethod("newCall", Request.class); 351 | Class call = newCall.getReturnType(); 352 | Method execute = call.getMethod("execute"); 353 | //在static方法中下标0为方法第一个参数,在非static方法中,下标0为this,下标1才是方法的第一个参数 354 | //表示url方法的返回值与1号下标参数有关 355 | passthroughRegistry.addPassthrough(urlMethod,1); 356 | //表示build方法的返回值与0号下标参数(this)有关 357 | passthroughRegistry.addPassthrough(buildMethod,0); 358 | //表示newCall方法的返回值与1号下标参数(request)有关 359 | passthroughRegistry.addPassthrough(newCall,1); 360 | //表示execute方法的返回值与0号下标参数(this)有关 361 | passthroughRegistry.addPassthrough(execute,0); 362 | }catch (Exception e){ 363 | e.printStackTrace(); 364 | } 365 | } 366 | } 367 | ``` 368 | 369 | 在`addPassthrough`中,我们只需要调用`passthroughRegistry.addPassthrough`这个api即可完成passthrough的修正。经过这个修正之后,我们再来看看特征抽取的效果,回到刚刚污点断掉的地方`okhttp3/Request$Builder.url`,准备跟进`super.visitMethodInsn` 370 | 371 | ![image-20220103162135990](acaf/image-20220103162135990.png) 372 | 373 | 还是回到刚刚那个passthrough的地方,发现此时获取到的`passthroughArgs`就是我们调整过的了,现在程序就会认为`url`方法的返回值与1号参数有关(刚才是0号)。 374 | 375 | ![image-20220103162254813](acaf/image-20220103162254813.png) 376 | 377 | 接下来就会把1号参数,也就是taint给加入到`resultTaint`中 378 | 379 | ![image-20220103162431545](acaf/image-20220103162431545.png) 380 | 381 | 最后,把`resultTaint`中的污点压入到`operandStack`。这样一来,在`url`方法模拟执行这一步,我们成功让污点传播延续了下去。 382 | 383 | ![image-20220103162532243](acaf/image-20220103162532243.png) 384 | 385 | 后面的其他方法调用也是差不多的流程,这里不予赘述,直接看最终提取到的特征。可以看到,现在提取出了4个特征,并且需要哪些参数可控都列举的很清楚。例如`url`方法需要0号参数可控,`newCall`方法也需要0号参数可控。**注意,这里的下标与static没关系,无论是否是static方法,0号参数指的都是方法的第一个参数,不涉及到this。** 386 | 387 | 不难看出,现在的特征提取已经非常准确了,从`url`到最后的`execute`,避免了误报的情况 388 | 389 | ![image-20220103162812381](acaf/image-20220103162812381.png) 390 | 391 | # 自动化审计 392 | 393 | 到了这一步之后,后面的代码就很好写了,有了漏洞特征,直接按照漏洞特征进行判断即可。我们选取controller方法的参数作为source,sink就是用户自定义的sink。根据前面提取的特征,看source是否能经过这样一段链路,这段链路中包含了从demo中提取出的所有特征。如果能,那就说明存在漏洞,反之说明不存在。 394 | 395 | 这个部分主要参考了[4ra1n师傅](https://4ra1n.love)的CodeInspector,将controller方法的参数作为source,然后使用dfs不断地向下追溯污点的传播,同时进行特征匹配,最终查看特征是否完全匹配来判断是否存在漏洞。详细分析请参考[基于GI的自动Java代码审计工具实现](https://4ra1n.love/post/zA8rsm1ne/#%E6%80%BB%E7%BB%93) 396 | 397 | 这里只分析我改动过的重点代码,在`com.er1cccc.acaf.core.audit.auditcore.VulnMethodAdapter#visitMethodInsn`,在观察方法体时,如果遇到方法调用的字节码,则使用前面收集到的漏洞特征进行匹配 398 | 399 | ![image-20220103164332924](acaf/image-20220103164332924.png) 400 | 401 | 跟进`vulnResolver.resolve`,发现`CompositResolver`实际上就是一个`Resolver`集合,调用它的`resolve`方法实际上就是在遍历它的`resolverList`进行特征匹配。 402 | 403 | ![image-20220103164537493](acaf/image-20220103164537493.png) 404 | 405 | 最终,如果`vulnResolver.resolve(null)`返回true,说明特征已经完全匹配上了,那么就输出调用栈 406 | 407 | ![image-20220103164749513](acaf/image-20220103164749513.png) 408 | 409 | 至此,ACAF的源码就介绍完毕了,最后看看实际效果 410 | 411 | # 实际效果 412 | 413 | 笔者根据4ra1n师傅的[CIDemo](https://github.com/EmYiQing/CIDemo)项目,改了改,加上了很多fake sink,来测试误报,同时也写出了其中4个ssrf的demo 414 | 415 | ![image-20220103170644290](acaf/image-20220103170644290.png) 416 | 417 | 看结果,发现4个sink点都找出来了,而fake sink完全没有误报(这里笔者突然发现还有一处漏讲了,就是关于controller复杂参数类型的处理,这里笔者也对CodeInspector进行了优化,使其能够处理复杂类型参数,这里找出的ssrf1_url这个sink就是验证,不过原理有点写不动了,有兴趣的可以看看源码) 418 | 419 | ![image-20220103170547318](acaf/image-20220103170547318.png) 420 | 421 | # 总结 422 | 423 | (其实是个没有总结的总结 424 | 425 | 主要想感谢三梦师傅、4ra1n师傅以及gadget-inspector作者Ian Haken带我入了自动化审计这个坑,非常感谢!然后就是欢迎对这个项目感兴趣的师傅来一起讨论,希望可以完善更多常见漏洞的demo。 426 | 427 | # 参考 428 | 429 | > [gadget-inspector](https://github.com/JackOfMostTrades/gadgetinspector) 430 | > 431 | > [java反序列化利用链自动挖掘工具gadgetinspector源码浅析](https://xz.aliyun.com/t/7058#toc-0) 432 | > 433 | > [基于GI的自动Java代码审计工具实现](https://4ra1n.love/post/zA8rsm1ne/#%E6%80%BB%E7%BB%93) 434 | > 435 | > [基于污点分析的JSP Webshell检测](https://4ra1n.love/post/0GeHo6Oj0/) 436 | 437 | --------------------------------------------------------------------------------