├── .gitignore ├── README.md ├── dependency-reduced-pom.xml ├── pom.xml └── src └── main ├── java └── com │ └── ctfagent │ ├── Agent.java │ ├── core │ ├── Blacklist.java │ ├── ClassModifier.java │ ├── DefaultTransformer.java │ └── MethodBodyDumper.java │ ├── exception │ └── BlacklistedClassException.java │ ├── rule │ ├── ApplicationFilterChainFilterMem.java │ ├── RequestForwarderRule.java │ ├── SaveRequestsDataRule.java │ ├── SpringBootFilterMem.java │ └── SpringSecurityInjector.java │ └── utils │ └── SpringBootApplicationFinder.java └── resources ├── META-INF └── MANIFEST.MF └── rules ├── ctfagent-rules.yaml ├── forwarder-config.yaml └── rule.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Project exclude paths 2 | /target/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CTFAgent 2 | 3 | ## 介绍 4 | 通过java agent去修复awd的java赛题,现有如下规则策略 5 | 6 | - com.ctfagent.rule.SpringBootFilterMem.SpringBootFilterMem :根据配置文件动态拦截需要拦截的流量 7 | - com.ctfagent.rule.SaveRequestsDataRule :根据配置文件补充请求的流量 8 | - com.ctfagent.rule.RequestForwarderRule :根据规则文件转发全部或者特定的流量到指定目标 9 | 10 | 11 | 12 | ## 简单使用 13 | 14 | 指定参数启动: 15 | ```bash 16 | java -javaagent:./CtfAgent-1.0-SNAPSHOT.jar -jar vuln-0.0.1-SNAPSHOT.jar 17 | ``` 18 | 19 | 远程注入启动: 20 | 21 | https://github.com/jattach/jattach 下载jattach,获取到java进程pid执行如下命令 22 | ```bash 23 | chmod +x ./jattach 24 | ./jattach 42 load instrument false ./CtfAgent-1.0-SNAPSHOT.jar 25 | ``` 26 | 27 | ## 规则列表 28 | 29 | 流量转发规则 30 | ```yaml 31 | # 流量转发配置文件 32 | # 目标队伍服务器,按需修改IP和端口 33 | target_urls: 34 | - "http://www.baidu.com" 35 | 36 | # 是否转发所有流量(true)或只转发check流量(false) 37 | forward_all_traffic: true 38 | 39 | # 是否转发check流量(通常设为true) 40 | forward_check_traffic: true 41 | 42 | # check流量特征(用于识别check请求) 43 | check_patterns: 44 | - user_agent_contains: "check" 45 | - remote_addr_starts_with: "10.0." 46 | - remote_addr_equals: "127.0.0.1" 47 | - uri_contains: "/check" 48 | 49 | # 转发策略(轮询/随机/健康优先) 50 | strategy: "round_robin" 51 | ``` -------------------------------------------------------------------------------- /dependency-reduced-pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.example 5 | CtfAgent 6 | 1.0-SNAPSHOT 7 | 8 | 9 | 10 | maven-compiler-plugin 11 | 3.8.1 12 | 13 | 1.8 14 | 1.8 15 | 16 | 17 | 18 | maven-shade-plugin 19 | 3.2.4 20 | 21 | 22 | package 23 | 24 | shade 25 | 26 | 27 | 28 | 29 | com.ctfagent.Agent 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.springframework.security 41 | spring-security-bom 42 | 5.1.5.RELEASE 43 | pom 44 | import 45 | 46 | 47 | 48 | UTF-8 49 | 8 50 | 8 51 | 52 | 53 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.example 8 | CtfAgent 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 8 13 | 8 14 | UTF-8 15 | 16 | 17 | 18 | 19 | org.javassist 20 | javassist 21 | 3.26.0-GA 22 | 23 | 24 | org.yaml 25 | snakeyaml 26 | 2.0 27 | 28 | 29 | 30 | javax.servlet 31 | javax.servlet-api 32 | 4.0.1 33 | provided 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-jar-plugin 42 | 3.1.0 43 | 44 | 45 | 46 | 47 | true 48 | 49 | 50 | com.ctfagent.Agent 51 | com.ctfagent.Agent 52 | true 53 | true 54 | 55 | 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-compiler-plugin 61 | 3.8.1 62 | 63 | 1.8 64 | 1.8 65 | 66 | 67 | 68 | org.apache.maven.plugins 69 | maven-shade-plugin 70 | 3.2.4 71 | 72 | 73 | package 74 | 75 | shade 76 | 77 | 78 | false 79 | 80 | 81 | com.ctfagent.Agent 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/main/java/com/ctfagent/Agent.java: -------------------------------------------------------------------------------- 1 | package com.ctfagent; 2 | 3 | import com.ctfagent.core.DefaultTransformer; 4 | //import com.ctfagent.core.ProxyMiddlewareTransformer; 5 | 6 | import java.io.IOException; 7 | import java.io.UnsupportedEncodingException; 8 | import java.lang.instrument.Instrumentation; 9 | import java.lang.instrument.UnmodifiableClassException; 10 | import java.net.URL; 11 | import java.util.jar.JarFile; 12 | import java.net.URLDecoder; 13 | 14 | public class Agent { 15 | 16 | public static void premain(String agentArgs, Instrumentation inst) throws IOException { 17 | System.out.println("Agent loaded with arguments: " + agentArgs); 18 | addJarToBootstrap(inst); 19 | inst.addTransformer(new DefaultTransformer()); 20 | // inst.addTransformer(new ProxyMiddlewareTransformer()); 21 | } 22 | 23 | public static void agentmain(String agentArgs, Instrumentation inst) throws Exception { 24 | System.out.println("Agent dynamically loaded with arguments: " + agentArgs); 25 | addJarToBootstrap(inst); 26 | inst.addTransformer(new DefaultTransformer(), true); 27 | for (Class clazz : inst.getAllLoadedClasses()) { 28 | if (clazz.getName().startsWith("javax.servlet.http.HttpServlet") || clazz.getName().startsWith("org.apache.catalina.core.ApplicationFilterChain")) { 29 | inst.retransformClasses(clazz); 30 | } 31 | } 32 | } 33 | 34 | public static void addJarToBootstrap(Instrumentation inst) throws IOException { 35 | String localJarPath = getLocalJarPath(); 36 | inst.appendToBootstrapClassLoaderSearch(new JarFile(localJarPath)); 37 | } 38 | 39 | /** 40 | * 获取当前所在jar包的路径 41 | * 42 | * @return jar包路径 43 | */ 44 | public static String getLocalJarPath() { 45 | URL localUrl = Agent.class.getProtectionDomain().getCodeSource().getLocation(); 46 | String path = null; 47 | try { 48 | path = URLDecoder.decode( 49 | localUrl.getFile().replace("+", "%2B"), "UTF-8"); 50 | } catch (UnsupportedEncodingException e) { 51 | System.err.println("[OpenRASP] Failed to get jarFile path."); 52 | e.printStackTrace(); 53 | } 54 | return path; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/ctfagent/core/Blacklist.java: -------------------------------------------------------------------------------- 1 | package com.ctfagent.core; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | public class Blacklist { 7 | public static final List CLASS_BLACKLIST = Arrays.asList( 8 | "java.util.Scanner", 9 | "java.lang.ProcessBuilder" 10 | // 添加更多黑名单类名 11 | ); 12 | } -------------------------------------------------------------------------------- /src/main/java/com/ctfagent/core/ClassModifier.java: -------------------------------------------------------------------------------- 1 | package com.ctfagent.core; 2 | 3 | public interface ClassModifier { 4 | byte[] modify(ClassLoader loader, String className, Class classBeingRedefined, byte[] classfileBuffer); 5 | } -------------------------------------------------------------------------------- /src/main/java/com/ctfagent/core/DefaultTransformer.java: -------------------------------------------------------------------------------- 1 | package com.ctfagent.core; 2 | 3 | import com.ctfagent.rule.RequestForwarderRule; 4 | import com.ctfagent.rule.SpringBootFilterMem; 5 | import javassist.ClassPool; 6 | import javassist.CtClass; 7 | import javassist.CtConstructor; 8 | import javassist.CtMethod; 9 | 10 | import java.io.ByteArrayInputStream; 11 | import java.io.File; 12 | import java.io.FileOutputStream; 13 | import java.io.IOException; 14 | import java.lang.instrument.ClassFileTransformer; 15 | import java.lang.instrument.IllegalClassFormatException; 16 | import java.security.ProtectionDomain; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | public class DefaultTransformer implements ClassFileTransformer { 21 | private final Map modifiers = new HashMap<>(); 22 | 23 | public DefaultTransformer() { 24 | // 注册规则 25 | // modifiers.put("com.example.vuln.VulnApplication", new SpringSecurityInjector()); 26 | 27 | // modifiers.put("org.apache.catalina.core.ApplicationFilterChain", new SpringBootFilterMem()); 28 | // modifiers.put("javax.servlet.http.HttpServlet", new CheckDownFlagRule()); 29 | modifiers.put("javax.servlet.http.HttpServlet", new RequestForwarderRule()); 30 | // modifiers.put("javax.servlet.http.HttpServlet", new SpringBootFilterMem()); 31 | } 32 | 33 | @Override 34 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { 35 | 36 | String dotClassName = className.replace("/", "."); 37 | try { 38 | 39 | // 如果需要dump原始字节码,则输出到文件 40 | if (shouldDump(dotClassName)) { 41 | saveBytecode(dotClassName, classfileBuffer, "original"); 42 | } 43 | 44 | // 决定是否需要进行拦截,默认不开启拦截,只进行监测 45 | boolean flag = false; 46 | 47 | // 检查黑名单 48 | if (Blacklist.CLASS_BLACKLIST.contains(dotClassName) && flag) { 49 | System.out.println("Blocked class: " + dotClassName); 50 | // 修改字节码,抛出异常 51 | ClassPool pool = ClassPool.getDefault(); 52 | pool.appendClassPath(new javassist.LoaderClassPath(loader)); 53 | CtClass cc = pool.makeClass(new ByteArrayInputStream(classfileBuffer)); 54 | for (CtConstructor constructor : cc.getDeclaredConstructors()) { 55 | constructor.setBody("{ throw new com.ctfagent.exception.BlacklistedClassException(\"" + dotClassName + "\"); }"); 56 | } 57 | for (CtMethod method : cc.getDeclaredMethods()) { 58 | method.setBody("{ throw new com.ctfagent.exception.BlacklistedClassException(\"" + dotClassName + "\"); }"); 59 | } 60 | return cc.toBytecode(); 61 | } 62 | 63 | // 如果类名匹配,才进行处理 64 | if (modifiers.containsKey(dotClassName)) { 65 | System.out.println("doClassName: " + dotClassName); 66 | ClassPool pool = ClassPool.getDefault(); 67 | pool.appendClassPath(new javassist.LoaderClassPath(loader)); 68 | CtClass cc = pool.makeClass(new ByteArrayInputStream(classfileBuffer)); 69 | 70 | saveBytecode("javax.servlet.http.HttpServlet", classfileBuffer, "original"); 71 | 72 | ClassModifier modifier = modifiers.get(dotClassName); 73 | if (modifier != null) { 74 | return modifier.modify(loader, dotClassName, classBeingRedefined, cc.toBytecode()); 75 | } 76 | } 77 | } catch (Exception e) { 78 | e.printStackTrace(); 79 | } 80 | 81 | return null; 82 | } 83 | 84 | /** 85 | * 判断是否需要dump指定类的字节码, 86 | * 通过系统属性 "ctfagent.dump" 来指定要dump的类名 87 | */ 88 | private boolean shouldDump(String className) { 89 | String dumpClass = "sun.print.UnixPrintJob"; 90 | return dumpClass != null && dumpClass.equals(className); 91 | } 92 | 93 | /** 94 | * 将字节码保存到 dump 目录下,文件名格式为 "类名_suffix.class" 95 | * 96 | * @param className 类名(点分隔格式) 97 | * @param bytecode 字节码数据 98 | * @param suffix 描述类型,比如 "original" 或 "modified" 99 | */ 100 | private void saveBytecode(String className, byte[] bytecode, String suffix) { 101 | try { 102 | File dumpDir = new File("dump"); 103 | if (!dumpDir.exists()) { 104 | dumpDir.mkdirs(); 105 | } 106 | // 将包名中的点替换为下划线,避免文件夹问题 107 | String fileName = className.replace('.', '_') + "_" + suffix + ".class"; 108 | File file = new File(dumpDir, fileName); 109 | try (FileOutputStream fos = new FileOutputStream(file)) { 110 | fos.write(bytecode); 111 | } 112 | System.out.println("Dumped bytecode for " + className + " (" + suffix + ") to " + file.getAbsolutePath()); 113 | } catch (IOException e) { 114 | e.printStackTrace(); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/ctfagent/core/MethodBodyDumper.java: -------------------------------------------------------------------------------- 1 | package com.ctfagent.core; 2 | 3 | import javassist.CtMethod; 4 | 5 | import java.io.FileWriter; 6 | import java.io.IOException; 7 | 8 | /** 9 | * 简单的方法体输出工具 10 | */ 11 | public class MethodBodyDumper { 12 | 13 | /** 14 | * 将方法体输出到文件 15 | * 16 | * @param method 要输出的方法 17 | * @param fileName 输出文件名 18 | */ 19 | public static void dumpMethodBody(CtMethod method, String fileName) { 20 | try { 21 | // 获取方法体 22 | String body = null; 23 | try { 24 | body = method.getMethodInfo().getCodeAttribute().toString(); 25 | } catch (Exception e) { 26 | body = "无法获取方法体: " + e.getMessage(); 27 | } 28 | 29 | // 完整的文件路径 30 | String filePath = "/tmp/" + fileName; 31 | 32 | // 写入文件 33 | FileWriter writer = new FileWriter(filePath); 34 | writer.write("类名: " + method.getDeclaringClass().getName() + "\n"); 35 | writer.write("方法名: " + method.getName() + "\n"); 36 | writer.write("方法签名: " + method.getSignature() + "\n\n"); 37 | writer.write("方法体:\n"); 38 | writer.write(body + "\n"); 39 | writer.close(); 40 | 41 | System.out.println("方法体已输出到文件: " + filePath); 42 | } catch (IOException e) { 43 | System.out.println("输出方法体时出错: " + e.getMessage()); 44 | } 45 | } 46 | 47 | /** 48 | * 记录代码字符串到文件 49 | * 50 | * @param code 要记录的代码 51 | * @param fileName 文件名 52 | */ 53 | public static void dumpCode(String code, String fileName) { 54 | try { 55 | String filePath = "/tmp/" + fileName; 56 | FileWriter writer = new FileWriter(filePath); 57 | writer.write(code); 58 | writer.close(); 59 | System.out.println("代码已输出到文件: " + filePath); 60 | } catch (IOException e) { 61 | System.out.println("输出代码时出错: " + e.getMessage()); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/main/java/com/ctfagent/exception/BlacklistedClassException.java: -------------------------------------------------------------------------------- 1 | package com.ctfagent.exception; 2 | 3 | public class BlacklistedClassException extends RuntimeException { 4 | public BlacklistedClassException(String className) { 5 | super("Attempted to load blacklisted class: " + className); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/ctfagent/rule/ApplicationFilterChainFilterMem.java: -------------------------------------------------------------------------------- 1 | package com.ctfagent.rule; 2 | 3 | import com.ctfagent.core.ClassModifier; 4 | import javassist.*; 5 | import org.yaml.snakeyaml.Yaml; 6 | 7 | import java.io.InputStream; 8 | import java.nio.file.Files; 9 | import java.nio.file.Paths; 10 | import java.util.HashSet; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Set; 14 | 15 | public class ApplicationFilterChainFilterMem implements ClassModifier { 16 | private Set keywords = new HashSet<>(); 17 | 18 | public ApplicationFilterChainFilterMem() { 19 | loadKeywordsFromYaml(); 20 | } 21 | 22 | private void loadKeywordsFromYaml() { 23 | try (InputStream inputStream = Files.newInputStream(Paths.get("/tmp/rule.yaml"))) { 24 | Yaml yaml = new Yaml(); 25 | Map> data = yaml.load(inputStream); 26 | if (data.containsKey("keywords")) { 27 | keywords.addAll(data.get("keywords")); 28 | } 29 | } catch (Exception e) { 30 | e.printStackTrace(); 31 | } 32 | } 33 | 34 | public String generateKeyword() { 35 | StringBuilder sb = new StringBuilder(); 36 | sb.append("("); 37 | boolean first = true; 38 | for (String keyword : keywords) { 39 | if (!first) { 40 | sb.append(" || "); 41 | } 42 | sb.append("paramValue.contains(\"").append(keyword).append("\")"); 43 | first = false; 44 | } 45 | sb.append(")"); 46 | return sb.toString(); 47 | } 48 | 49 | 50 | @Override 51 | public byte[] modify(ClassLoader loader, String className, Class classBeingRedefined, byte[] classfileBuffer) { 52 | try { 53 | ClassPool pool = ClassPool.getDefault(); 54 | if (classBeingRedefined != null) { 55 | ClassClassPath classClassPath = new ClassClassPath(classBeingRedefined); 56 | pool.insertClassPath(classClassPath); 57 | } 58 | 59 | CtClass ctClass = pool.get(className); 60 | if (ctClass.isFrozen()) { 61 | ctClass.defrost(); // 解冻类 62 | } 63 | CtMethod publicServiceMethod = ctClass.getDeclaredMethod("doFilter"); 64 | 65 | if (publicServiceMethod != null) { 66 | String body = 67 | "javax.servlet.http.HttpServletRequest request = (javax.servlet.http.HttpServletRequest) $1;\n" + 68 | "javax.servlet.http.HttpServletResponse response = (javax.servlet.http.HttpServletResponse) $2;\n" + 69 | "boolean containsKeyword = false;\n" + 70 | "StringBuilder requestBody = new StringBuilder();\n" + 71 | "requestBody.append(\"Request URL: \").append(request.getRequestURL().toString()).append(\"\\n\");\n" + 72 | "try {\n" + 73 | " java.util.Map paramMap = request.getParameterMap();\n" + 74 | " java.util.Iterator paramIterator = paramMap.entrySet().iterator();\n" + 75 | " while (paramIterator.hasNext()) {\n" + 76 | " java.util.Map.Entry entry = (java.util.Map.Entry) paramIterator.next();\n" + 77 | " String paramName = (String) entry.getKey();\n" + 78 | " String[] paramValues = (String[]) entry.getValue();\n" + 79 | " requestBody.append(paramName).append(\": \");\n" + 80 | " int i = 0;\n" + 81 | " while (i < paramValues.length) {\n" + 82 | " String paramValue = paramValues[i];\n" + 83 | " requestBody.append(paramValue).append(\", \");\n" + 84 | " if (paramValue != null && " + generateKeyword() + ") {\n" + 85 | " containsKeyword = true;\n" + 86 | " break;\n" + 87 | " }\n" + 88 | " i++;\n" + 89 | " }\n" + 90 | " requestBody.append(\"\\n\");\n" + 91 | " if (containsKeyword) {\n" + 92 | " response.getOutputStream().print(\"500\");\n" + 93 | " response.getOutputStream().flush();\n" + 94 | " response.getOutputStream().close();\n" + 95 | " break;\n" + 96 | " }\n" + 97 | " }\n" + 98 | " // 读取请求体内容\n" + 99 | " java.io.BufferedReader reader = request.getReader();\n" + 100 | " String line;\n" + 101 | " while ((line = reader.readLine()) != null) {\n" + 102 | " requestBody.append(line).append(\"\\n\");\n" + 103 | " }\n" + 104 | "} catch (Exception e) {\n" + 105 | " e.printStackTrace();\n" + 106 | "}\n" + 107 | // "// 保存请求内容到文件\n" + 108 | "java.io.FileWriter writer = new java.io.FileWriter(\"/tmp/requestByApplication.log\", true);\n" + 109 | "writer.write(requestBody.toString());\n" + 110 | "writer.close();"; 111 | 112 | 113 | System.out.println(body); 114 | publicServiceMethod.insertBefore(body); 115 | return ctClass.toBytecode(); 116 | } 117 | } catch (Exception e) { 118 | e.printStackTrace(); 119 | } 120 | return null; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/ctfagent/rule/RequestForwarderRule.java: -------------------------------------------------------------------------------- 1 | package com.ctfagent.rule; 2 | 3 | import com.ctfagent.core.ClassModifier; 4 | import javassist.*; 5 | import org.yaml.snakeyaml.Yaml; 6 | 7 | import java.io.InputStream; 8 | import java.nio.file.Files; 9 | import java.nio.file.Paths; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | public class RequestForwarderRule implements ClassModifier { 15 | private List targetUrls = new ArrayList<>(); 16 | private boolean forwardAllTraffic = false; 17 | private boolean forwardCheckTraffic = true; 18 | 19 | public RequestForwarderRule() { 20 | loadConfigFromYaml(); 21 | } 22 | 23 | private void loadConfigFromYaml() { 24 | try (InputStream inputStream = Files.newInputStream(Paths.get("forwarder-config.yaml"))) { 25 | Yaml yaml = new Yaml(); 26 | Map data = yaml.load(inputStream); 27 | 28 | if (data.containsKey("target_urls")) { 29 | List urls = (List) data.get("target_urls"); 30 | targetUrls.addAll(urls); 31 | } 32 | 33 | if (data.containsKey("forward_all_traffic")) { 34 | forwardAllTraffic = (Boolean) data.get("forward_all_traffic"); 35 | } 36 | 37 | if (data.containsKey("forward_check_traffic")) { 38 | forwardCheckTraffic = (Boolean) data.get("forward_check_traffic"); 39 | } 40 | } catch (Exception e) { 41 | System.err.println("Error loading forwarder configuration: " + e.getMessage()); 42 | } 43 | } 44 | 45 | @Override 46 | public byte[] modify(ClassLoader loader, String className, Class classBeingRedefined, byte[] classfileBuffer) { 47 | try { 48 | ClassPool pool = ClassPool.getDefault(); 49 | if (classBeingRedefined != null) { 50 | ClassClassPath classClassPath = new ClassClassPath(classBeingRedefined); 51 | pool.insertClassPath(classClassPath); 52 | } 53 | 54 | CtClass ctClass = pool.get(className); 55 | if (ctClass.isFrozen()) { 56 | ctClass.defrost(); 57 | } 58 | 59 | CtMethod[] methods = ctClass.getDeclaredMethods(); 60 | CtMethod publicServiceMethod = null; 61 | for (CtMethod method : methods) { 62 | if ("service".equals(method.getName()) && Modifier.isPublic(method.getModifiers())) { 63 | publicServiceMethod = method; 64 | break; 65 | } 66 | } 67 | 68 | if (publicServiceMethod != null) { 69 | // 构建拦截代码 70 | String injectCode = buildForwarderCode(); 71 | 72 | // 插入代码 73 | publicServiceMethod.insertBefore(injectCode); 74 | 75 | System.out.println("成功修改 service 方法,添加了转发功能"); 76 | return ctClass.toBytecode(); 77 | } 78 | } catch (Exception e) { 79 | System.err.println("Error in RequestForwarderRule: " + e.getMessage()); 80 | e.printStackTrace(); 81 | } 82 | return null; 83 | } 84 | 85 | /** 86 | * 构建转发代码 87 | * 将代码逻辑分离,便于维护和调试 88 | */ 89 | private String buildForwarderCode() { 90 | StringBuilder code = new StringBuilder(); 91 | 92 | // 基础变量声明 93 | code.append("{\n"); // 注意这里的大括号很重要 94 | code.append(" javax.servlet.http.HttpServletRequest request = (javax.servlet.http.HttpServletRequest) $1;\n"); 95 | code.append(" javax.servlet.http.HttpServletResponse response = (javax.servlet.http.HttpServletResponse) $2;\n"); 96 | 97 | // 判断条件 98 | code.append(" boolean shouldForward = false;\n"); 99 | 100 | if (!forwardAllTraffic && forwardCheckTraffic) { 101 | code.append(" String userAgent = request.getHeader(\"User-Agent\");\n"); 102 | code.append(" String remoteAddr = request.getRemoteAddr();\n"); 103 | code.append(" String requestURI = request.getRequestURI();\n"); 104 | 105 | code.append(" if ((userAgent != null && userAgent.contains(\"check\")) || \n"); 106 | code.append(" (remoteAddr != null && (remoteAddr.startsWith(\"10.0.\") || remoteAddr.equals(\"127.0.0.1\"))) || \n"); 107 | code.append(" (requestURI != null && requestURI.contains(\"/check\"))) {\n"); 108 | code.append(" shouldForward = true;\n"); 109 | code.append(" }\n"); 110 | } else if (forwardAllTraffic) { 111 | code.append(" shouldForward = true;\n"); 112 | } 113 | 114 | // 转发逻辑 115 | code.append(" if (shouldForward) {\n"); 116 | code.append(" try {\n"); 117 | 118 | // 选择目标服务器 119 | if (targetUrls.size() > 0) { 120 | code.append(" String targetUrl = \"").append(targetUrls.get(0)).append("\";\n"); 121 | } else { 122 | code.append(" String targetUrl = \"http://localhost:8080\";\n"); 123 | } 124 | 125 | // 构建目标URL 126 | code.append(" String requestUrl = request.getRequestURL().toString();\n"); 127 | code.append(" String queryString = request.getQueryString();\n"); 128 | code.append(" String path = request.getRequestURI();\n"); 129 | code.append(" String fullTargetUrl = targetUrl + path + (queryString != null ? \"?\" + queryString : \"\");\n"); 130 | 131 | // 创建HTTP连接 132 | code.append(" java.net.URL url = new java.net.URL(fullTargetUrl);\n"); 133 | code.append(" java.net.HttpURLConnection conn = (java.net.HttpURLConnection) url.openConnection();\n"); 134 | code.append(" conn.setRequestMethod(request.getMethod());\n"); 135 | code.append(" conn.setDoOutput(true);\n"); 136 | code.append(" conn.setDoInput(true);\n"); 137 | code.append(" conn.setConnectTimeout(3000);\n"); 138 | code.append(" conn.setReadTimeout(5000);\n"); 139 | 140 | // 复制请求头 141 | code.append(" java.util.Enumeration headerNames = request.getHeaderNames();\n"); 142 | code.append(" while (headerNames.hasMoreElements()) {\n"); 143 | code.append(" String headerName = (String) headerNames.nextElement();\n"); 144 | code.append(" String headerValue = request.getHeader(headerName);\n"); 145 | code.append(" if (!headerName.equalsIgnoreCase(\"host\") && !headerName.equalsIgnoreCase(\"content-length\")) {\n"); 146 | code.append(" conn.setRequestProperty(headerName, headerValue);\n"); 147 | code.append(" }\n"); 148 | code.append(" }\n"); 149 | 150 | // 复制请求体 151 | code.append(" if (\"POST\".equalsIgnoreCase(request.getMethod()) || \"PUT\".equalsIgnoreCase(request.getMethod())) {\n"); 152 | code.append(" java.io.InputStream requestBody = request.getInputStream();\n"); 153 | code.append(" byte[] buffer = new byte[4096];\n"); 154 | code.append(" int bytesRead = 0;\n"); // 初始化变量 155 | code.append(" java.io.OutputStream connOut = conn.getOutputStream();\n"); 156 | code.append(" while ((bytesRead = requestBody.read(buffer)) != -1) {\n"); 157 | code.append(" connOut.write(buffer, 0, bytesRead);\n"); 158 | code.append(" }\n"); 159 | code.append(" connOut.flush();\n"); 160 | code.append(" connOut.close();\n"); 161 | code.append(" }\n"); 162 | 163 | // 获取响应 164 | code.append(" int statusCode = conn.getResponseCode();\n"); 165 | code.append(" response.setStatus(statusCode);\n"); 166 | 167 | // 复制响应头 168 | code.append(" java.util.Map headerFields = conn.getHeaderFields();\n"); 169 | code.append(" java.util.Iterator it = headerFields.entrySet().iterator();\n"); 170 | code.append(" while (it.hasNext()) {\n"); 171 | code.append(" java.util.Map.Entry entry = (java.util.Map.Entry)it.next();\n"); 172 | code.append(" String key = (String)entry.getKey();\n"); 173 | code.append(" if (key != null && !key.equalsIgnoreCase(\"content-length\") && !key.equalsIgnoreCase(\"transfer-encoding\")) {\n"); 174 | code.append(" java.util.List values = (java.util.List)entry.getValue();\n"); 175 | code.append(" for (int i = 0; i < values.size(); i++) {\n"); 176 | code.append(" String value = (String)values.get(i);\n"); 177 | code.append(" response.addHeader(key, value);\n"); 178 | code.append(" }\n"); 179 | code.append(" }\n"); 180 | code.append(" }\n"); 181 | 182 | // 复制响应体 183 | code.append(" java.io.InputStream responseBody = null;\n"); // 初始化变量 184 | code.append(" try {\n"); 185 | code.append(" responseBody = conn.getInputStream();\n"); 186 | code.append(" } catch (java.io.IOException e) {\n"); 187 | code.append(" responseBody = conn.getErrorStream();\n"); 188 | code.append(" }\n"); 189 | code.append(" if (responseBody != null) {\n"); 190 | code.append(" java.io.OutputStream output = response.getOutputStream();\n"); 191 | code.append(" byte[] buffer = new byte[4096];\n"); 192 | code.append(" int bytesRead = 0;\n"); // 初始化变量 193 | code.append(" while ((bytesRead = responseBody.read(buffer)) != -1) {\n"); 194 | code.append(" output.write(buffer, 0, bytesRead);\n"); 195 | code.append(" }\n"); 196 | code.append(" output.flush();\n"); 197 | code.append(" }\n"); 198 | 199 | code.append(" System.out.println(\"forward success!!! \");\n"); 200 | 201 | // 记录转发信息 202 | code.append(" java.io.FileWriter writer = new java.io.FileWriter(\"/tmp/forward.log\", true);\n"); 203 | code.append(" writer.write(\"Forwarded request: \" + requestUrl + \" to \" + fullTargetUrl + \"\\n\");\n"); 204 | code.append(" writer.close();\n"); 205 | 206 | // 成功转发后返回,不执行原始方法 207 | code.append(" return;\n"); 208 | 209 | // 异常处理 210 | code.append(" } catch (Exception e) {\n"); 211 | code.append(" try {\n"); 212 | code.append(" java.io.FileWriter writer = new java.io.FileWriter(\"/tmp/forward-error.log\", true);\n"); 213 | code.append(" writer.write(\"Forward error: \" + e.getMessage() + \"\\n\");\n"); 214 | code.append(" writer.close();\n"); 215 | code.append(" } catch (Exception ex) {\n"); 216 | code.append(" ex.printStackTrace();\n"); 217 | code.append(" }\n"); 218 | code.append(" }\n"); 219 | code.append(" }\n"); 220 | code.append("}\n"); // 注意最后的闭合括号 221 | 222 | 223 | // System.out.println(code.toString()); 224 | 225 | return code.toString(); 226 | } 227 | } -------------------------------------------------------------------------------- /src/main/java/com/ctfagent/rule/SaveRequestsDataRule.java: -------------------------------------------------------------------------------- 1 | package com.ctfagent.rule; 2 | 3 | import com.ctfagent.core.ClassModifier; 4 | import javassist.*; 5 | import org.yaml.snakeyaml.Yaml; 6 | 7 | import java.io.InputStream; 8 | import java.nio.file.Files; 9 | import java.nio.file.Paths; 10 | import java.util.HashSet; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Set; 14 | 15 | public class SaveRequestsDataRule implements ClassModifier { 16 | private Set keywords = new HashSet<>(); 17 | 18 | public SaveRequestsDataRule() { 19 | loadKeywordsFromYaml(); 20 | } 21 | 22 | private void loadKeywordsFromYaml() { 23 | try (InputStream inputStream = Files.newInputStream(Paths.get("rule.yaml"))) { 24 | Yaml yaml = new Yaml(); 25 | Map> data = yaml.load(inputStream); 26 | if (data.containsKey("keywords")) { 27 | keywords.addAll(data.get("keywords")); 28 | } 29 | } catch (Exception e) { 30 | e.printStackTrace(); 31 | } 32 | } 33 | 34 | public String generateKeyword() { 35 | StringBuilder sb = new StringBuilder(); 36 | sb.append("("); 37 | boolean first = true; 38 | for (String keyword : keywords) { 39 | if (!first) { 40 | sb.append(" || "); 41 | } 42 | sb.append("paramValue.contains(\"").append(keyword).append("\")"); 43 | first = false; 44 | } 45 | sb.append(")"); 46 | return sb.toString(); 47 | } 48 | 49 | 50 | @Override 51 | public byte[] modify(ClassLoader loader, String className, Class classBeingRedefined, byte[] classfileBuffer) { 52 | try { 53 | ClassPool pool = ClassPool.getDefault(); 54 | if (classBeingRedefined != null) { 55 | ClassClassPath classClassPath = new ClassClassPath(classBeingRedefined); 56 | pool.insertClassPath(classClassPath); 57 | } 58 | 59 | CtClass ctClass = pool.get(className); 60 | if (ctClass.isFrozen()) { 61 | ctClass.defrost(); // 解冻类 62 | } 63 | CtMethod[] methods = ctClass.getDeclaredMethods(); 64 | CtMethod publicServiceMethod = null; 65 | for (CtMethod method : methods) { 66 | if ("service".equals(method.getName()) && Modifier.isPublic(method.getModifiers())) { 67 | publicServiceMethod = method; 68 | break; 69 | } 70 | } 71 | 72 | if (publicServiceMethod != null) { 73 | String body = 74 | "javax.servlet.http.HttpServletRequest request = (javax.servlet.http.HttpServletRequest) $1;\n" + 75 | "javax.servlet.http.HttpServletResponse response = (javax.servlet.http.HttpServletResponse) $2;\n" + 76 | "boolean containsKeyword = false;\n" + 77 | "StringBuilder requestBody = new StringBuilder();\n" + 78 | "requestBody.append(\"---------------------------------------------------------------------\\n\");\n" + 79 | "requestBody.append(\"Request URL: \").append(request.getRequestURL().toString()).append(\"\\n\");\n" + 80 | "try {\n" + 81 | " java.util.Map paramMap = request.getParameterMap();\n" + 82 | " java.util.Iterator paramIterator = paramMap.entrySet().iterator();\n" + 83 | " while (paramIterator.hasNext()) {\n" + 84 | " java.util.Map.Entry entry = (java.util.Map.Entry) paramIterator.next();\n" + 85 | " String paramName = (String) entry.getKey();\n" + 86 | " String[] paramValues = (String[]) entry.getValue();\n" + 87 | " requestBody.append(paramName).append(\": \");\n" + 88 | " int i = 0;\n" + 89 | " while (i < paramValues.length) {\n" + 90 | " String paramValue = paramValues[i];\n" + 91 | " requestBody.append(paramValue).append(\", \");\n" + 92 | " if (paramValue != null && paramValue.contains(\"cmd\")) {\n" + 93 | " containsKeyword = true;\n" + 94 | " break;\n" + 95 | " }\n" + 96 | " i++;\n" + 97 | " }\n" + 98 | " requestBody.append(\"\\n\");\n" + 99 | " if (containsKeyword) {\n" + 100 | " response.getOutputStream().print(\"500\");\n" + 101 | " response.getOutputStream().flush();\n" + 102 | " response.getOutputStream().close();\n" + 103 | " break;\n" + 104 | " }\n" + 105 | " }\n" + 106 | " // 读取请求体内容\n" + 107 | " java.io.BufferedReader reader = request.getReader();\n" + 108 | " String line;\n" + 109 | " while ((line = reader.readLine()) != null) {\n" + 110 | " requestBody.append(line).append(\"\\n\");\n" + 111 | " }\n" + 112 | "} catch (Exception e) {\n" + 113 | " e.printStackTrace();\n" + 114 | "}\n" + 115 | "// 保存请求内容到文件\n" + 116 | "java.io.FileWriter writer = new java.io.FileWriter(\"/tmp/request.log\", true);\n" + 117 | "writer.write(requestBody.toString());\n" + 118 | "writer.close();"; 119 | 120 | 121 | System.out.println(body); 122 | publicServiceMethod.insertBefore(body); 123 | return ctClass.toBytecode(); 124 | } 125 | } catch (Exception e) { 126 | e.printStackTrace(); 127 | } 128 | return null; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/com/ctfagent/rule/SpringBootFilterMem.java: -------------------------------------------------------------------------------- 1 | package com.ctfagent.rule; 2 | 3 | import com.ctfagent.core.ClassModifier; 4 | import javassist.*; 5 | import org.yaml.snakeyaml.Yaml; 6 | 7 | import java.io.FileInputStream; 8 | import java.io.InputStream; 9 | import java.nio.file.Files; 10 | import java.nio.file.Paths; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Set; 14 | import java.util.HashSet; 15 | 16 | public class SpringBootFilterMem implements ClassModifier { 17 | 18 | private Set keywords = new HashSet<>(); 19 | 20 | public SpringBootFilterMem() { 21 | loadKeywordsFromYaml(); 22 | } 23 | 24 | /** 25 | * 根据配置文件动态拦截需要拦截的流量 26 | */ 27 | private void loadKeywordsFromYaml() { 28 | try (InputStream inputStream = Files.newInputStream(Paths.get("/tmp/rule.yaml"))) { 29 | Yaml yaml = new Yaml(); 30 | Map> data = yaml.load(inputStream); 31 | if (data.containsKey("keywords")) { 32 | keywords.addAll(data.get("keywords")); 33 | } 34 | } catch (Exception e) { 35 | e.printStackTrace(); 36 | } 37 | } 38 | 39 | public String generateKeyword() { 40 | StringBuilder sb = new StringBuilder(); 41 | sb.append("("); 42 | boolean first = true; 43 | for (String keyword : keywords) { 44 | if (!first) { 45 | sb.append(" || "); 46 | } 47 | sb.append("paramValue.contains(\"").append(keyword).append("\")"); 48 | first = false; 49 | } 50 | sb.append(")"); 51 | return sb.toString(); 52 | } 53 | 54 | @Override 55 | public byte[] modify(ClassLoader loader, String className, Class classBeingRedefined, byte[] classfileBuffer) { 56 | try { 57 | ClassPool pool = ClassPool.getDefault(); 58 | if (classBeingRedefined != null) { 59 | ClassClassPath classClassPath = new ClassClassPath(classBeingRedefined); 60 | pool.insertClassPath(classClassPath); 61 | } 62 | 63 | CtClass ctClass = pool.get(className); 64 | if (ctClass.isFrozen()) { 65 | ctClass.defrost(); // 解冻类 66 | } 67 | CtMethod[] methods = ctClass.getDeclaredMethods(); 68 | CtMethod publicServiceMethod = null; 69 | for (CtMethod method : methods) { 70 | if ("service".equals(method.getName()) && Modifier.isPublic(method.getModifiers())) { 71 | publicServiceMethod = method; 72 | break; 73 | } 74 | } 75 | 76 | if (publicServiceMethod != null) { 77 | String body = 78 | "javax.servlet.http.HttpServletRequest request = (javax.servlet.http.HttpServletRequest) $1;\n" + 79 | "javax.servlet.http.HttpServletResponse response = (javax.servlet.http.HttpServletResponse) $2;\n" + 80 | "boolean containsKeyword = false;\n" + 81 | "StringBuilder requestBody = new StringBuilder();\n" + 82 | "requestBody.append(\"---------------------------------------------------------------------\\n\");\n" + 83 | "requestBody.append(\"Request URL: \").append(request.getRequestURL().toString()).append(\"\\n\");\n" + 84 | "try {\n" + 85 | " java.util.Map paramMap = request.getParameterMap();\n" + 86 | " java.util.Iterator paramIterator = paramMap.entrySet().iterator();\n" + 87 | " while (paramIterator.hasNext()) {\n" + 88 | " java.util.Map.Entry entry = (java.util.Map.Entry) paramIterator.next();\n" + 89 | " String paramName = (String) entry.getKey();\n" + 90 | " String[] paramValues = (String[]) entry.getValue();\n" + 91 | " requestBody.append(paramName).append(\": \");\n" + 92 | " int i = 0;\n" + 93 | " while (i < paramValues.length) {\n" + 94 | " String paramValue = paramValues[i];\n" + 95 | " requestBody.append(paramValue).append(\", \");\n" + 96 | " if (paramValue != null && " + generateKeyword() + ") {\n" + 97 | " containsKeyword = true;\n" + 98 | " break;\n" + 99 | " }\n" + 100 | " i++;\n" + 101 | " }\n" + 102 | " requestBody.append(\"\\n\");\n" + 103 | " if (containsKeyword) {\n" + 104 | " response.getOutputStream().print(\"500\");\n" + 105 | " response.getOutputStream().flush();\n" + 106 | " response.getOutputStream().close();\n" + 107 | " break;\n" + 108 | " }\n" + 109 | " }\n" + 110 | " // 读取请求体内容\n" + 111 | " java.io.BufferedReader reader = request.getReader();\n" + 112 | " String line;\n" + 113 | " while ((line = reader.readLine()) != null) {\n" + 114 | " requestBody.append(line).append(\"\\n\");\n" + 115 | " }\n" + 116 | "} catch (Exception e) {\n" + 117 | " e.printStackTrace();\n" + 118 | "}\n" + 119 | "// 保存请求内容到文件\n" + 120 | "java.io.FileWriter writer = new java.io.FileWriter(\"/tmp/request.log\", true);\n" + 121 | "writer.write(requestBody.toString());\n" + 122 | "writer.close();"; 123 | 124 | System.out.println(body); 125 | publicServiceMethod.insertBefore(body); 126 | return ctClass.toBytecode(); 127 | } 128 | } catch (Exception e) { 129 | e.printStackTrace(); 130 | } 131 | return null; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/com/ctfagent/rule/SpringSecurityInjector.java: -------------------------------------------------------------------------------- 1 | package com.ctfagent.rule; 2 | 3 | import com.ctfagent.utils.SpringBootApplicationFinder; 4 | import com.ctfagent.core.ClassModifier; 5 | import javassist.ClassPool; 6 | import javassist.CtClass; 7 | import javassist.CtMethod; 8 | 9 | import java.net.URL; 10 | import java.net.URLClassLoader; 11 | 12 | public class SpringSecurityInjector implements ClassModifier { 13 | @Override 14 | public byte[] modify(ClassLoader loader, String className, Class classBeingRedefined, byte[] classfileBuffer) { 15 | System.out.println("加载 SpringSecurityInjector 模块"); 16 | 17 | try { 18 | ClassPool pool = ClassPool.getDefault(); 19 | pool.appendClassPath(new javassist.LoaderClassPath(loader)); 20 | 21 | // pool.appendClassPath("/Users/a1234/data/tools/ctfAgent/spring-security-config-4.2.12.RELEASE.jar"); 22 | // pool.appendClassPath("/Users/a1234/data/tools/ctfAgent/spring-security-core-5.8.12.jar"); 23 | 24 | // 动态获取依赖的JAR包路径并添加到ClassPool 25 | if (loader instanceof URLClassLoader) { 26 | URL[] urls = ((URLClassLoader) loader).getURLs(); 27 | for (URL url : urls) { 28 | System.out.println("Appending class path: " + url.getPath()); 29 | pool.appendClassPath(url.getPath()); 30 | } 31 | } 32 | CtClass cc = pool.get(className); 33 | 34 | if (cc.isFrozen()) { 35 | cc.defrost(); // 解冻类 36 | } 37 | 38 | // 确保主类已经加载 39 | System.out.println("mainClassBase" + SpringBootApplicationFinder.getSpringBootApplicationClasses()); 40 | System.out.println("mainClass: " + className); 41 | if (className.equals(className)) { 42 | System.out.println("Modifying main class: " + className); 43 | CtMethod method = cc.getDeclaredMethod("main"); 44 | method.insertBefore("{\n" + 45 | " try {\n" + 46 | " Class.forName(\"org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration\");\n" + 47 | " System.out.println(\"Spring Security added by agent\");\n" + 48 | " } catch (ClassNotFoundException e) {\n" + 49 | " e.printStackTrace();\n" + 50 | " }\n" + 51 | "}"); 52 | 53 | // 添加自定义的Security配置类 54 | CtClass configClass = pool.makeClass(className + "SecurityConfig"); 55 | configClass.setSuperclass(pool.get("org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter")); 56 | 57 | // 覆盖configure方法以禁用所有路由 58 | CtMethod configureMethod = new CtMethod(CtClass.voidType, "configure", new CtClass[]{pool.get("org.springframework.security.config.annotation.web.builders.HttpSecurity")}, configClass); 59 | configureMethod.setBody("{\n" + 60 | " $1.authorizeRequests().anyRequest().denyAll();\n" + 61 | "}"); 62 | configClass.addMethod(configureMethod); 63 | 64 | configClass.toClass(loader, classBeingRedefined.getProtectionDomain()); 65 | System.out.println("Spring Security configuration injected."); 66 | } 67 | 68 | 69 | return cc.toBytecode(); 70 | } catch (Exception e) { 71 | e.printStackTrace(); 72 | } 73 | 74 | return null; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/ctfagent/utils/SpringBootApplicationFinder.java: -------------------------------------------------------------------------------- 1 | package com.ctfagent.utils; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | public class SpringBootApplicationFinder { 7 | private static final Set springBootApplicationClasses = new HashSet<>(); 8 | 9 | public static void addSpringBootApplicationClass(String className) { 10 | springBootApplicationClasses.add(className); 11 | } 12 | 13 | public static Set getSpringBootApplicationClasses() { 14 | return springBootApplicationClasses; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Premain-Class: com.ctfagent.Agent 3 | Agent-Class: com.ctfagent.Agent 4 | Can-Redefine-Classes: true 5 | Can-Retransform-Classes: true 6 | -------------------------------------------------------------------------------- /src/main/resources/rules/ctfagent-rules.yaml: -------------------------------------------------------------------------------- 1 | # CTF Agent 配置 2 | # 规则按优先级顺序应用(数字越大,优先级越高,越先执行) 3 | 4 | rules: 5 | - targetClass: javax.servlet.http.HttpServlet 6 | ruleClass: com.ctfagent.rule.RequestForwarderRule 7 | priority: 30 8 | enabled: true 9 | parameters: 10 | forwardAllTraffic: false 11 | forwardCheckTraffic: true 12 | 13 | - targetClass: javax.servlet.http.HttpServlet 14 | ruleClass: com.ctfagent.rule.SpringBootFilterMem 15 | priority: 20 16 | enabled: true 17 | parameters: 18 | logFile: /tmp/request.log 19 | 20 | - targetClass: javax.servlet.http.HttpServlet 21 | ruleClass: com.ctfagent.rule.SaveRequestsDataRule 22 | priority: 10 23 | enabled: true 24 | 25 | # 在这里添加更多规则 -------------------------------------------------------------------------------- /src/main/resources/rules/forwarder-config.yaml: -------------------------------------------------------------------------------- 1 | # 流量转发配置文件 2 | # 目标队伍服务器,按需修改IP和端口 3 | target_urls: 4 | - "http://www.baidu.com" 5 | 6 | # 是否转发所有流量(true)或只转发check流量(false) 7 | forward_all_traffic: true 8 | 9 | # 是否转发check流量(通常设为true) 10 | forward_check_traffic: true 11 | 12 | # check流量特征(用于识别check请求) 13 | check_patterns: 14 | - user_agent_contains: "check" 15 | - remote_addr_starts_with: "10.0." 16 | - remote_addr_equals: "127.0.0.1" 17 | - uri_contains: "/check" 18 | 19 | # 转发策略(轮询/随机/健康优先) 20 | strategy: "round_robin" -------------------------------------------------------------------------------- /src/main/resources/rules/rule.yaml: -------------------------------------------------------------------------------- 1 | keywords: 2 | - /bin/bash 3 | - /bin/sh 4 | - /tcp 5 | - select 6 | - from 7 | - delect 8 | - flag --------------------------------------------------------------------------------