我想写一篇文章,关于burpsuite插件开发入门。去年我写了一些burp插件,用于辅助渗透和漏洞挖掘,这给我带来了很多方便,可以捡到一些安全漏洞。
本人以第一视角说下本人是如何学习burpsuite插件开发的。本文只是入门,如果想要深入学习插件开发,还需要更多的学习和参考。
1.环境配置和搭建
idea+maven+jdk14(可按照需求,自定义设置jdk版本):
(1)idea创建maven项目 忽略步骤:
删除掉标签里的内容
(2)新增Burp API依赖,pom中引入相关依赖:
<dependencies\>
<!-- https://mvnrepository.com/artifact/net.portswigger.burp.extender/burp-extender-api \-->
<dependency\>
<groupId\>net.portswigger.burp.extender</groupId\>
<artifactId\>burp-extender-api</artifactId\>
<version\>1.7.22</version\>
</dependency\>
</dependencies\>
刷新maven加载jar包:
至此引入依赖成功:
2. 插件开发基础部分:
需求:UI控制台打印hello world:
package com.test.DevDemo;
import burp.IBurpExtender;
import burp.IBurpExtenderCallbacks;
import burp.IExtensionHelpers;
import java.io.PrintWriter;
public class BurpExtender implements IBurpExtender {
private IExtensionHelpers helpers;
private IBurpExtenderCallbacks callbacks;
private PrintWriter stdout;
@Override
public void registerExtenderCallbacks(IBurpExtenderCallbacks iBurpExtenderCallbacks) {
this.callbacks = iBurpExtenderCallbacks;
//设置插件名字
this.callbacks.setExtensionName("第一个程序,这是我们的插件名字");
//打印信息在UI控制台页面,打印内容为hello world
this.stdout = new PrintWriter(callbacks.getStdout(),true);
stdout.println("hello world");
}
}
目录名必须设置成burp目录,类名即文件名必须是BurpExtender:
**
**
(2)mvn打包jar包,配置打包jar包所需的依赖:
<build\>
<plugins\>
<plugin\>
<groupId\>org.apache.maven.plugins</groupId\>
<artifactId\>maven-assembly-plugin</artifactId\>
<executions\>
<execution\>
<phase\>package</phase\>
<goals\>
<goal\>single</goal\>
</goals\>
</execution\>
</executions\>
<configuration\>
<descriptorRefs\>
<descriptorRef\>jar-with-dependencies</descriptorRef\>
</descriptorRefs\>
</configuration\>
</plugin\>
</plugins\>
</build\>
(3)idea打开当前终端,输入打包jar包命令:
mvn clean install
(4)打开当前目录→target
open . 打开当前目录
点击target目录,选择BurpPlugDev-1.0-SNAPSHOT-jar-with-dependencies.jar 这个带dependencies的jar包
解释:
BurpPlugDev-1.0\-SNAPSHOT.jar 为不带依赖的jar包
BurpPlugDev\-1.0\-SNAPSHOT-jar-with-dependencies.jar 为带依赖的jar包
(5)burpsuite引入我们打包的jar包
1.选择插件,选择ADD添加
2.选择jar包,点击NEXT下一步
3.加载成功,显示插件名字并且打印输出hello world
第一部分结束。
Burp监听器,侦听器 测试demo使用:
IExtensionStateListener
IHttpListener
IProxyListener
IScannerListener
IScopeChangeListener
编写测试代码,参考官方案例:
https://github.com/PortSwigger/example-event-listeners/blob/master/java/BurpExtender.java
复制粘贴抄下:
package burp;
import burp.IBurpExtender;
import burp.IBurpExtenderCallbacks;
import burp.IExtensionHelpers;
import java.io.PrintWriter;
public class BurpExtender implements IBurpExtender, IHttpListener,
IProxyListener, IScannerListener, IExtensionStateListener{
private IExtensionHelpers helpers;
private IBurpExtenderCallbacks callbacks;
private PrintWriter stdout;
@Override
public void registerExtenderCallbacks(IBurpExtenderCallbacks iBurpExtenderCallbacks) {
this.callbacks = iBurpExtenderCallbacks;
//设置插件名字
this.callbacks.setExtensionName("第一个程序,这是我们的插件名字");
//打印信息在UI控制台页面,打印内容为hello world
this.stdout = new PrintWriter(callbacks.getStdout(),true);
stdout.println("hello world");
//一定要注册监听器,不然下面的函数无法生效
callbacks.registerHttpListener(this);
callbacks.registerProxyListener(this);
callbacks.registerScannerListener(this);
callbacks.registerExtensionStateListener(this);
}
@Override
public void extensionUnloaded() {
stdout.println("Extension was uploaded");
}
@Override
public void processHttpMessage(int i, boolean b, IHttpRequestResponse iHttpRequestResponse) {
stdout.println(
(b ? "HTTP request to ":"HTTP response from ")+iHttpRequestResponse.getHttpService()+"\["+callbacks.getToolName(i)+"\]"
);
}
@Override
public void processProxyMessage(boolean b, IInterceptedProxyMessage iInterceptedProxyMessage) {
stdout.println(
(b ? "Proxy request to ":"Proxy response from ")+iInterceptedProxyMessage.getMessageInfo().getHttpService()
);
}
@Override
public void newScanIssue(IScanIssue iScanIssue) {
stdout.println("New scan issue "+ iScanIssue.getIssueName());
}
}
按照前面说的方法,进行打包运行:
挂上代理访问网站:
可以发现在控制台可以看到Proxy请求信息和HTTP request的请求信息
发现其他信息并没有打印输出?
首先是IScannerListener接口含义,简单来说就是想触发这个接口,需要调度Burpsuite的Scanner扫描功能
其他的类似接口是类似的情况。需要特定场景才可以触发。
结论:访问一个http/https请求,默认触发IHttpListener和IProxyListener
如果写测试案例的话,需要大量依赖这两个监听器。
插件开发第二部分:http数据包获取
http请求数据包的几个部分
http请求:
1.header 请求头
2.body 请求body->非get/options请求,多于POST,PUT等
3.parameter 请求参数
4.主机头端口协议
http响应:
1.header 请求头
2.body
3.主机头端口协议
测试用例,只涉及查,不涉及修改确认等操作,仔细看注释部分:
package burp;
import burp.IBurpExtender;
import burp.IBurpExtenderCallbacks;
import burp.IExtensionHelpers;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class BurpExtender implements IBurpExtender, IHttpListener{
private IExtensionHelpers helpers;
private IBurpExtenderCallbacks callbacks;
private PrintWriter stdout;
private PrintWriter stderr;
@Override
public void registerExtenderCallbacks(IBurpExtenderCallbacks iBurpExtenderCallbacks) {
this.callbacks = iBurpExtenderCallbacks;
//设置插件名字
callbacks.setExtensionName("熟悉HTTP数据包各种操作");
this.helpers = callbacks.getHelpers();
//打印信息在UI控制台页面,打印内容为hello world
this.stdout = new PrintWriter(callbacks.getStdout(),true);
this.stderr = new PrintWriter(callbacks.getStderr(),true);
stdout.println("hello world");
//一定要注册监听器,不然下面的函数无法生效
callbacks.registerHttpListener(this);
}
@Override
public void processHttpMessage(int i, boolean b, IHttpRequestResponse iHttpRequestResponse) {
//切换http监听模块为Burpsuiteproxy模块
if(i==IBurpExtenderCallbacks.TOOL\_PROXY){
//对请求包进行处理
if(b){
//对消息体进行解析,messageInfo是整个HTTP请求和响应消息体的总和,各种HTTP相关信息的获取都来自于它,HTTP流量的修改都是围绕它进行的。
IRequestInfo analyzeRequest = helpers.analyzeRequest(iHttpRequestResponse);
/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*获取参数\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
List<IParameter> parameList = analyzeRequest.getParameters();
//获取参数的方法
//遍历参数
for(IParameter para:parameList){
//获取参数
String key = para.getName();
//获取参数值(value)
String value = para.getValue();
int type = para.getType();
stdout.println("参数key value type:"+key+" "+value+" "+type);
}
//获取headers方法:
List<String> headers = analyzeRequest.getHeaders();
//新增header
headers.add("myheader:hello world");
//遍历请求头
for(String header:headers){
stdout.println("header: "+header);
}
//获取协议 端口 和主机名
IHttpService service = iHttpRequestResponse.getHttpService();
stdout.println("协议 主机 端口 "+service.getProtocol()+" "+service.getHost()+" "+service.getPort());
}
}else{//这个逻辑是处理响应包
IResponseInfo analyzeResponse = helpers.analyzeResponse(iHttpRequestResponse.getResponse());
//获取响应码信息
short statusCode = analyzeResponse.getStatusCode();
stdout.println("status= "+statusCode);
//获取响应头信息
List<String> headers = analyzeResponse.getHeaders();
for(String header:headers){
stdout.println("header:"+header);
}
// 获取响应信息
String resp = new String(iHttpRequestResponse.getResponse());
int bodyOffset = analyzeResponse.getBodyOffset();
String body \= resp.substring(bodyOffset);
stdout.println("response body="+body);
}
}
}
一图胜千言,图片和代码参考于:https://github.com/bit4woo/burp-api-drops
演示运行效果,当访问网站时,插件中会显示详细信息:
包含(参数,类型,请求头,主机端口协议等)
学到了这里,我们直接分析一个现成的安全burp插件工具:
实战案例分析2个burp安全插件:
**分析案例1:**https://github.com/Daybr4ak/ShiroScan
这款工具主要用于检测是否使用Shiro框架,以及是否存在shiro key,如果存在key,我们才好方便下一步跑利用链。
直接下载源码:
主要看BurpExtender.java这个文件,idea导入相关代码文件
发现除了BurpExtender类,其余都是接口类。它这个项目没有使用maven工程,使用的是导入接口
首先是查看实现类:
继承和实现了如下接口
extends AbstractTableModel implements IBurpExtender, IScannerCheck, ITab, IMessageEditorController
我们知道java实现接口,必须实现接口的对应方法:
其中doPassiveScan为被动扫描,doActiveScan为主动扫描,因为shiroscan是被动扫描器,所以这里的大部分业务逻辑都会写在doPassiveScan中。
再往下说:
其中比较重要的就是IScannerCheck接口
ITab, IMessageEditorController为GUI设计,我们只需要copy。
自定义部分字段:
大部分都是GUI需要使用的字段,可以学习了解,也可以不学习,其余定义的字段,在前面我们已经学到。
字段定义如下:
private IBurpExtenderCallbacks callbacks;
private IExtensionHelpers helpers;
private PrintWriter stdout;
private JSplitPane mjSplitPane;
private List<TablesData> Udatas = new ArrayList<TablesData>();
private List<Ulist> ulists = new ArrayList<Ulist>();
private IMessageEditor HRequestTextEditor;
private IMessageEditor HResponseTextEditor;
private IHttpRequestResponse currentlyDisplayedItem;
private URLTable Utable;
private JScrollPane UscrollPane;
private JSplitPane HjSplitPane;
private JPanel mjPane;
private JTabbedPane Ltable;
private JTabbedPane Rtable;
查看接口所需要实现的方法:
加载插件就会显示这部分内容,即上面标注的代码含义。
继续往下看代码:
为ui图形化界面,这块先忽略,晚点再看。继续往下看代码:
注册扫描器,注释写的很清楚了。因为想要使用被动/主动扫描功能,必须先注册。
往下看被动扫描操作函数,因为shiro检测是被动扫描,所以几乎所有核心逻辑的实现都会在doPassiveScan函数内:
我们都知道shiro检测的特征是:cookie中输入rememberMe=1,响应中输出rememberMe=deleteMe;
那么我们的检测逻辑很简单:cookie中植入rememberMe=1,响应中如果包rememberMe=deleteMe
即存在使用了shiro框架,存在shiro框架后,随之检测key
核心逻辑第一部分:
byte\[\] request = baseRequestResponse.getRequest();
// 设置参数
IParameter newParameter = helpers.buildParameter("rememberMe","1", (byte) 2);
// 为request包添加设置好的参数
byte\[\] newRequest = helpers.updateParameter(request, newParameter);
// 创建一个新的HTTP请求
IHttpService httpService = baseRequestResponse.getHttpService();
IHttpRequestResponse newIHttpRequestResponse \= callbacks.makeHttpRequest(httpService,newRequest);
// 获取新HTTP请求的响应
byte\[\] response = newIHttpRequestResponse.getResponse();
这段很重要,代表着设置cookie中,植入参数rememberMe=1
IParameter newParameter = helpers.buildParameter("rememberMe","1", (byte) 2);
这个2,代表声明的参数类型是cookie。
所以这里的代码还可以这样写:
继续往下解读:
带着新的rememberMe=1后,开始走到判断响应包含逻辑:
if (getRememberMeNumber(response) > 0 && checUrl(httpService.getHost(), httpService.getPort()))
重复url检测,对url做去重:
符合条件就新增host和端口到gui中:
都是gui操作,继续往下看,看业务逻辑,发现shiro框架后,下面就是检测key:
检测出来,就在Scanner的issue中新增,在GUI中新增
List<Object> mes = FindKey(newIHttpRequestResponse, getRememberMeNumber(response));
跟进FinkKey:
public List<Object> FindKey(IHttpRequestResponse baseRequestResponse, int num){
try {
SimplePrincipalCollection simplePrincipalCollection \= new SimplePrincipalCollection();
byte\[\] exp = getBytes(simplePrincipalCollection);
String\[\] keys \= new String\[\]{
"kPH+bIxk5D2deZiIxcaaaA==", "Z3VucwAAAAAAAAAAAAAAAA==", "wGiHplamyXlVB11UXWol8g==", "2AvVhdsgUs0FSA3SDFAdag==", "3AvVhmFLUs0KTA3Kprsdag==", "4AvVhmFLUs0KTA3Kprsdag==", "bWljcm9zAAAAAAAAAAAAAA==", "WcfHGU25gNnTxTlmJMeSpw==", "fCq+/xW488hMTCD+cmJ3aQ==", "kPv59vyqzj00x11LXJZTjJ2UHW48jzHN",
"6ZmI6I2j5Y+R5aSn5ZOlAA==", "1QWLxg+NYmxraMoxAXu/Iw==", "a2VlcE9uR29pbmdBbmRGaQ==", "5aaC5qKm5oqA5pyvAAAAAA==", "1AvVhdsgUs0FSA3SDFAdag==", "5RC7uBZLkByfFfJm22q/Zw==", "3AvVhdAgUs0FSA4SDFAdBg==", "a3dvbmcAAAAAAAAAAAAAAA==", "eXNmAAAAAAAAAAAAAAAAAA==", "U0hGX2d1bnMAAAAAAAAAAA==",
"Ymx1ZXdoYWxlAAAAAAAAAA==", "L7RioUULEFhRyxM7a2R/Yg==", "UGlzMjAxNiVLeUVlXiEjLw==", "bWluZS1hc3NldC1rZXk6QQ==", "ZUdsaGJuSmxibVI2ZHc9PQ==", "7AvVhmFLUs0KTA3Kprsdag==", "MTIzNDU2Nzg5MGFiY2RlZg==", "OY//C4rhfwNxCQAQCrQQ1Q==", "bTBANVpaOUw0ampRWG43TVJFcF5iXjdJ", "FP7qKJzdJOGkzoQzo2wTmA==",
"nhNhwZ6X7xzgXnnZBxWFQLwCGQtJojL3", "LEGEND-CAMPUS-CIPHERKEY==", "r0e3c16IdVkouZgk1TKVMg==", "ZWvohmPdUsAWT3=KpPqda", "k3+XHEg6D8tb2mGm7VJ3nQ==", "U3ByaW5nQmxhZGUAAAAAAA==", "tiVV6g3uZBGfgshesAQbjA==", "ZAvph3dsQs0FSL3SDFAdag==", "0AvVhmFLUs0KTA3Kprsdag==", "25BsmdYwjnfcWmnhAciDDg==",
"3JvYhmBLUs0ETA5Kprsdag==", "5AvVhmFLUs0KTA3Kprsdag==", "6AvVhmFLUs0KTA3Kprsdag==", "6NfXkC7YVCV5DASIrEm1Rg==", "cmVtZW1iZXJNZQAAAAAAAA==", "8AvVhmFLUs0KTA3Kprsdag==", "8BvVhmFLUs0KTA3Kprsdag==", "9AvVhmFLUs0KTA3Kprsdag==", "OUHYQzxQ/W9e/UjiAGu6rg==", "aU1pcmFjbGVpTWlyYWNsZQ==",
"bXRvbnMAAAAAAAAAAAAAAA==", "5J7bIJIV0LQSN3c9LPitBQ==", "bya2HkYo57u6fWh5theAWw==", "f/SY5TIve5WWzT4aQlABJA==", "WuB+y2gcHRnY2Lg9+Aqmqg==", "3qDVdLawoIr1xFd6ietnwg==", "YI1+nBV//m7ELrIyDHm6DQ==", "6Zm+6I2j5Y+R5aS+5ZOlAA==", "2A2V+RFLUs+eTA3Kpr+dag==", "6ZmI6I2j3Y+R1aSn5BOlAA==",
"SkZpbmFsQmxhZGUAAAAAAA==", "2cVtiE83c4lIrELJwKGJUw==", "fsHspZw/92PrS3XrPW+vxw==", "XTx6CKLo/SdSgub+OPHSrw==", "sHdIjUN6tzhl8xZMG3ULCQ==", "O4pdf+7e+mZe8NyxMTPJmQ==", "HWrBltGvEZc14h9VpMvZWw==", "rPNqM6uKFCyaL10AK51UkQ==", "Y1JxNSPXVwMkyvES/kJGeQ==", "lT2UvDUmQwewm6mMoiw4Ig==",
"MPdCMZ9urzEA50JDlDYYDg==", "xVmmoltfpb8tTceuT5R7Bw==", "c+3hFGPjbgzGdrC+MHgoRQ==", "ClLk69oNcA3m+s0jIMIkpg==", "Bf7MfkNR0axGGptozrebag==", "1tC/xrDYs8ey+sa3emtiYw==", "ZmFsYWRvLnh5ei5zaGlybw==", "cGhyYWNrY3RmREUhfiMkZA==", "IduElDUpDDXE677ZkhhKnQ==", "yeAAo1E8BOeAYfBlm4NG9Q==",
"cGljYXMAAAAAAAAAAAAAAA==", "2itfW92XazYRi5ltW0M2yA==", "XgGkgqGqYrix9lI6vxcrRw==", "ertVhmFLUs0KTA3Kprsdag==", "5AvVhmFLUS0ATA4Kprsdag==", "s0KTA3mFLUprK4AvVhsdag==", "hBlzKg78ajaZuTE0VLzDDg==", "9FvVhtFLUs0KnA3Kprsdyg==", "d2ViUmVtZW1iZXJNZUtleQ==", "yNeUgSzL/CfiWw1GALg6Ag==",
"NGk/3cQ6F5/UNPRh8LpMIg==", "4BvVhmFLUs0KTA3Kprsdag==", "MzVeSkYyWTI2OFVLZjRzZg==", "CrownKey==a12d/dakdad", "empodDEyMwAAAAAAAAAAAA==", "A7UzJgh1+EWj5oBFi+mSgw==", "c2hpcm9fYmF0aXMzMgAAAA==", "i45FVt72K2kLgvFrJtoZRw==", "66v1O8keKNV3TTcGPK1wzg==", "U3BAbW5nQmxhZGUAAAAAAA==",
"ZnJlc2h6Y24xMjM0NTY3OA==", "Jt3C93kMR9D5e8QzwfsiMw==", "MTIzNDU2NzgxMjM0NTY3OA==", "vXP33AonIp9bFwGl7aT7rA==", "V2hhdCBUaGUgSGVsbAAAAA==", "Q01TX0JGTFlLRVlfMjAxOQ==", "Is9zJ3pzNh2cgTHB4ua3+Q==", "SDKOLKn2J1j/2BHjeZwAoQ==", "NsZXjXVklWPZwOfkvk6kUA==", "GAevYnznvgNCURavBhCr1w=="};
for (int i = 0; i < keys.length; i++) {
String rememberMe \= shiroEncrypt(keys\[i\], exp);
IParameter newParameter \= helpers.buildParameter("rememberMe", rememberMe, (byte) 2);
byte\[\] newRequest = helpers.updateParameter(baseRequestResponse.getRequest(), newParameter);
IHttpService httpService \= baseRequestResponse.getHttpService();
IHttpRequestResponse newIHttpRequestResponse \= callbacks.makeHttpRequest(httpService, newRequest);
byte\[\] response = newIHttpRequestResponse.getResponse();
if (getRememberMeNumber(response) < num) {
return Arrays.asList("\[+\] Found Shiro Key:" + keys\[i\],newIHttpRequestResponse);
}
}
} catch (Exception e) {
stdout.println(e);
}
return Arrays.asList("\[-\] Not Found Shiro Key...", baseRequestResponse);
}
这里的检测逻辑还是容易看懂的
1.构造一个继承 PrincipalCollection 的序列化对象。
2.key正确情况下不返回 deleteMe ,key错误情况下返回 deleteMe 。
所以会有这个逻辑
key检测参考:http://www.lmxspace.com/2020/08/24/一种另类的shiro检测方式/
核心逻辑到此结束。
再往下就是GUI自定义Table和显示数据存储,直接copy即可。
数据存储:
总结:
1.GUI可以抄
2.修改参数->重放->生成新的数据 (buildParameter,updateParameter,makeHttpRequest,getResponse)
分析burp插件2:https://github.com/YoDiDi/cors-jsonpz
这里不看GUI了,GUI代码直接copy即可。看核心逻辑怎么写的
首先是继承:
extends AbstractTableModel implements IBurpExtender, IScannerCheck, ITab, IMessageEditorController
看下GUI定义类:
发现GUI声明类几乎一致。说明作者有参考另一个作者的代码
因为是检测jsonp,那么稍微要比shiro检测复杂一些,shiro检测的话,cookie里面输入rememberMe即可,而jsonp检测,肯定要处理下请求后缀:
这里的作者是获取请求头的第一行:
String firstrequest\_header = request\_header.get(0); //第一行请求
if(firstrequest\_header.contains(".png") || firstrequest\_header.contains(".js") || firstrequest\_header.contains(".jpg") || firstrequest\_header.contains(".jpeg") || firstrequest\_header.contains(".svg") || firstrequest\_header.contains(".mp4") || firstrequest\_header.contains(".css") || firstrequest\_header.contains(".mp3") ){
return null;
}
else里面的逻辑
String\[\] firstheaders = firstrequest\_header.split(" ");
if(firstheaders\[1\].contains("callback="))
firstheaders\[1\] = firstheaders\[1\].replace("callback=","callback=testjsonp"); // 原始请求含有callback,直接替换
else {
if (firstheaders\[1\].endsWith("?"))
firstheaders\[1\] = firstheaders\[1\] + "callback=testjsonp"; // 含有参数的项,?结尾
else if (firstheaders\[1\].contains("?") && !firstheaders\[1\].endsWith("?"))
firstheaders\[1\] = firstheaders\[1\] + "&callback=testjsonp"; // 含有参数的项,含有?且不是?结尾
else
firstheaders\[1\] = firstheaders\[1\] + "?callback=testjsonp"; // 含有参数的项,直接参数后面加callback参数
}
//重新设置header,封装到request\_header中
request\_header.set(0,firstheaders\[0\] + " " + firstheaders\[1\] + " " + firstheaders\[2\]);
这里作者的注释写的很清楚了。
三种情况
?callback=\* 替换成 callback=testjsonp
?a=1&callback=testjsonp
?callback=testjsonp
简单解释代码含义:
以空格分割
String firstrequest\_header = request\_header.get(0);
String\[\] firstheaders \= firstrequest\_header.split(" ");
GET /ssrf.php?a=1 HTTP/1.1
按照burpsuite的显示方式,firstheaders[1]一定是路径,对路径进行判断即可。
因为他又做了cors劫持的检测:
// 去除源请求包里的Origin参数
/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*删除header\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
request\_header.removeIf(header \-> header.startsWith("Origin"));
request\_header.add("Origin: baitdu.com"); // 请求头增加
注释写的很清楚。这块代码,自己在平时开发的时候,也能用得上。
修改了headers,删除了原有的请求头,修改成了新的唯一性的Origin,下面就是具体的逻辑判断
正常的获取body方法,这段代码可以在https://github.com/bit4woo/burp-api-drops中找到:
作者这里采用了获取body方法1
再往下是核心逻辑:重新构建整个请求header
String reqMethod = this.helpers.analyzeRequest(baseRequestResponse).getMethod();
//stdout.println(newParameter);
////构建一个请求、响应消息,常用于拦截请求并修改其中的参数时会使用
byte\[\] newRequest = this.helpers.buildHttpMessage(request\_header, request\_bodys);
IHttpService httpService \= baseRequestResponse.getHttpService();
IHttpRequestResponse newIHttpRequestResponse \= this.callbacks.makeHttpRequest(httpService, newRequest);
byte\[\] response = newIHttpRequestResponse.getResponse();
IResponseInfo analyzedResponse \= helpers.analyzeResponse(response);
buildHttpMessage然后makeHttpRequest:
此方法生成包含指定标头和消息体的 HTTP 消息。如果适用,将根据主体的长度添加或更新 Content-Length 标头
可参考:
https://portswigger.net/burp/extender/api/burp/iextensionhelpers.html
https://portswigger.net/burp/extender/api/burp/iburpextendercallbacks.html
修改重建请求后,就是做判断逻辑:
定义了三个变量
int IsCorsControl = 0;
int IsCorstrue = 0;
int IsJsonp = 0;
然后遍历请求headers,符合这些条件就变量就自增
通过这段代码,我们就能知道了cors检测的逻辑,这里不多说了,代码写的很清楚了。
继续往下看
如果变量>0,就在GUI中输出信息
this.Udatas.add(new TablesData(row, reqMethod, url.toString(), this.helpers.analyzeResponse(response).getStatusCode() + "", "Cors vuln " + corsword, newIHttpRequestResponse, httpService.getHost(), httpService.getPort()));
其实他可以直接输出结果的,但是他做了一层标识符的操作
这层标识符的深意:jsonp劫持和cors劫持误报很多。只有存在敏感信息的response,才有价值。
这里的检测逻辑是这样的
1.替换header,让特殊变量自增
2.拿到特殊的自增变量,二次判断输出,输出的是必须有敏感标识符的jsonp和cors劫持
如果IsJsonp>1,说明是存在jsonp劫持的:
这个插件的判断劣势如下:
1.存在callback,但是不一定存在jsonp劫持,可以说存在jsonp
2.如果要jsonp劫持的话,需要删除referer
3.需要判断请求header\[1\]中是否存在csrf\_token等字段
4.最后的标识符判断可以修改成regexp正则匹配
。。。。
自己动手写一个简单的漏洞检测程序,案例如下:
web缓存xfh头攻击
原理很简单,请求头插入X-Forwarded-Host: asdasd.com ,如果response中包含asdasd.com即存在web cache xfh头注入
来试试
GUI部分直接依葫芦画瓢抄即可。
完整代码:
package burp;
import burp.IBurpExtender;
import burp.IBurpExtenderCallbacks;
import burp.IExtensionHelpers;
import javax.swing.\*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import java.awt.\*;
import java.io.PrintWriter;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class BurpExtender extends AbstractTableModel implements IBurpExtender, IScannerCheck, ITab, IMessageEditorController{
private IBurpExtenderCallbacks callbacks;
private IExtensionHelpers helpers;
private PrintWriter stdout;
private JSplitPane mjSplitPane;
private List<TablesData> Udatas = new ArrayList<>();
private IMessageEditor HRequestTextEditor;
private IMessageEditor HResponseTextEditor;
private IHttpRequestResponse currentlyDisplayedItem;
private URLTable Utable;
private JScrollPane UscrollPane;
private JSplitPane HjSplitPane;
private JSplitPane HjSplitPane2;
private JPanel mjPane;
private JTabbedPane Ltable;
private JTabbedPane Rtable;
private JPanel panel1;
@Override
public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
this.callbacks = callbacks;
this.helpers = callbacks.getHelpers();
this.stdout = new PrintWriter(callbacks.getStdout(), true);
callbacks.setExtensionName("cache\_vuln\_test");
this.stdout.println("===========================");
this.stdout.println("\[+\] load successful! ");
this.stdout.println("\[+\] cache vulnerability test v0.1 ");
this.stdout.println("\[+\] code by test ");
this.stdout.println("\[+\] ");
this.stdout.println("===========================");
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
mjSplitPane \= new JSplitPane(JSplitPane.VERTICAL\_SPLIT);
Utable \= new URLTable(BurpExtender.this);
UscrollPane \= new JScrollPane(Utable);
HjSplitPane \= new JSplitPane();
HjSplitPane.setDividerLocation(0.5D);
Ltable \= new JTabbedPane();
HRequestTextEditor \= BurpExtender.this.callbacks.createMessageEditor(BurpExtender.this,false);
Ltable.addTab("Request",HRequestTextEditor.getComponent());
Rtable \= new JTabbedPane();
HResponseTextEditor \= BurpExtender.this.callbacks.createMessageEditor(BurpExtender.this,false);
Rtable.addTab("Response",HResponseTextEditor.getComponent());
HjSplitPane.add(Ltable,"left");
HjSplitPane.add(Rtable,"right");
mjSplitPane.add(UscrollPane,"left");
mjSplitPane.add(HjSplitPane,"right");
BurpExtender.this.callbacks.customizeUiComponent(mjSplitPane);
BurpExtender.this.callbacks.addSuiteTab(BurpExtender.this);
}
});
callbacks.registerScannerCheck(this);
}
@Override
public IHttpService getHttpService() {
return this.currentlyDisplayedItem.getHttpService();
}
@Override
public byte\[\] getRequest() {
return this.currentlyDisplayedItem.getRequest();
}
@Override
public byte\[\] getResponse() {
return currentlyDisplayedItem.getResponse();
}
@Override
public List<IScanIssue> doPassiveScan(IHttpRequestResponse iHttpRequestResponse) {
byte\[\] request = iHttpRequestResponse.getRequest();
URL url \= this.helpers.analyzeRequest(iHttpRequestResponse).getUrl();
String method \= this.helpers.analyzeRequest(iHttpRequestResponse).getMethod();
IRequestInfo iRequestInfo \= this.helpers.analyzeRequest(request);
List<String> headers = this.helpers.analyzeRequest(request).getHeaders();
//删除header
headers.removeIf(header -> header.startsWith("X-Forwarded-Host"));
//新增一个header头
headers.add("X-Forwarded-Host: asdasd.com");
int bodyOffset = iRequestInfo.getBodyOffset();
byte\[\] byte\_Request = iHttpRequestResponse.getRequest();
String request2 \= new String(byte\_Request);
String body \= request2.substring(bodyOffset);
byte\[\] byte\_body = body.getBytes();
byte\[\] newRequest = this.helpers.buildHttpMessage(headers, byte\_body);
IHttpService httpService \= iHttpRequestResponse.getHttpService();
IHttpRequestResponse newIHttpRequestResponse \= this.callbacks.makeHttpRequest(httpService, newRequest);
byte\[\] response = newIHttpRequestResponse.getResponse();
String string\_response \= new String(response);
this.stdout.println(string\_response);
if(string\_response.contains("asdasd.com")){
synchronized (this.Udatas) {
int row = this.Udatas.size();
this.Udatas.add(new TablesData(row, method, url.toString(), this.helpers.analyzeResponse(response).getStatusCode() + "", "cache vuln" , newIHttpRequestResponse));
fireTableRowsInserted(row, row);
List<IScanIssue> issues = new ArrayList<>(1);
return issues;
}
}
return null;
}
@Override
public List<IScanIssue> doActiveScan(IHttpRequestResponse iHttpRequestResponse, IScannerInsertionPoint iScannerInsertionPoint) {
byte\[\] request = iHttpRequestResponse.getRequest();
return null;
}
@Override
public int consolidateDuplicateIssues(IScanIssue existingIssue, IScanIssue newIssue) {
if (existingIssue.getIssueName().equals(newIssue.getIssueName()))
return -1;
else return 0;
}
@Override
public String getTabCaption() {
return "cache\_scan";
}
@Override
public Component getUiComponent() {
return mjSplitPane;
}
@Override
public int getRowCount() {
return this.Udatas.size();
}
@Override
public int getColumnCount() {
return 5;
}
public String getColumnName(int columnIndex) {
switch (columnIndex) {
case 0:
return "#";
case 1:
return "Method";
case 2:
return "URL";
case 3:
return "Status";
case 4:
return "Issue";
}
return null;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
TablesData datas \= this.Udatas.get(rowIndex);
switch (columnIndex) {
case 0:
return Integer.valueOf(datas.Id);
case 1:
return datas.Method;
case 2:
return datas.URL;
case 3:
return datas.Status;
case 4:
return datas.issue;
}
return null;
}
public class URLTable extends JTable{
public URLTable(TableModel tableModel) {
super(tableModel);
}
public void changeSelection(int row, int col, boolean toggle, boolean extend) {
TablesData dataEntry \= BurpExtender.this.Udatas.get(convertRowIndexToModel(row));
HRequestTextEditor.setMessage(dataEntry.requestResponse.getRequest(), true);
HResponseTextEditor.setMessage(dataEntry.requestResponse.getResponse(),false);
currentlyDisplayedItem \= dataEntry.requestResponse;
super.changeSelection(row, col, toggle, extend);
}
}
public static class TablesData {
final int Id;
final String Method;
final String URL;
final String Status;
final String issue;
final IHttpRequestResponse requestResponse;
// final String host;
// final int port;
public TablesData(int id, String method, String url, String status, String issue,IHttpRequestResponse requestResponse) {
this.Id = id;
this.Method = method;
this.URL = url;
this.Status = status;
this.issue = issue;
this.requestResponse = requestResponse;
// this.host = host;
// this.port = port;
}
}
}
演示运行效果:
测试demo:http://119.45.227.86/header.php
抓包访问,查看GUI:
至此,本人先分享到这里。
此文只能算burpsuite插件入门文章,想要更深入的学习,了解burpsuite插件开发,可以移步到:https://github.com/pmiaowu 进行深层次的学习,参考其他人写的burpsuite插件也可以。
TRANSLATE with x
English
TRANSLATE with
COPY THE URL BELOW
Back
EMBED THE SNIPPET BELOW IN YOUR SITE
Enable collaborative features and customize widget: Bing Webmaster Portal
Back