├── .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
--------------------------------------------------------------------------------