首页 » NetworkSec » Penetration » 正文

burpsuite插件开发总结

0x00 基础

burpsuite api:

interface:

用java开发插件就把这个burp文件夹放到工程目录即可

javadoc

在线版

https://portswigger.net/burp/extender/api/index.html

 

官方教程:
https://portswigger.net/burp/extender/

https://portswigger.net/blog/writing-your-first-burp-suite-extension

https://portswigger.net/blog/archive?y=2012_12_01_archive.html

 

推荐用 Java,执行效率高,不会出现奇奇怪怪的问题……

//图片来源于网络

插件入口和帮助接口类:

IBurpExtender、IBurpExtenderCallbacks、IExtensionHelpers、IExtensionStateListener

IBurpExtender接口类是Burp插件的入口,所有Burp的插件均需要实现此接口,并且类命名为BurpExtender。 IBurpExtenderCallbacks接口类是IBurpExtender接口的实现类与Burp其他各个组件(Scanner、Intruder、Spider……)、各个通信对象(HttpRequestResponse、HttpService、SessionHandlingAction)之间的纽带。

IExtensionHelpers、IExtensionStateListener这两个接口类是插件的帮助和管理操作的接口定义。

 

UI相关接口类:

IContextMenuFactory、IContextMenuInvocation、ITab、ITextEditor、IMessageEditor、IMenuItemHandler

这类接口类主要是定义Burp插件的UI显示和动作的处理事件,主要是软件交互中使用。

 

Burp工具组件接口类:

IInterceptedProxyMessage、IIntruderAttack、IIntruderPayloadGenerator、IIntruderPayloadGeneratorFactory、IIntruderPayloadProcessor、IProxyListener、IScanIssue、IScannerCheck、IScannerInsertionPoint、IScannerInsertionPointProvider、IScannerListener、IScanQueueItem、IScopeChangeListener

这些接口类的功能非常好理解,Burp在接口定义的命名中使用了的见名知意的规范,看到接口类的名称,基本就能猜测出来这个接口是适用于哪个工具组件。

 

HTTP消息处理接口类:

ICookie、IHttpListener、IHttpRequestResponse、IHttpRequestResponsePersisted、IHttpRequestResponseWithMarkers、IHttpService、IRequestInfo、IParameter、IResponseInfo

这些接口的定义主要是围绕HTTP消息通信过程中涉及的Cookie、Request、Response、Parameter几大消息对象,通过对通信消息头、消息体的数据处理,来达到控制HTTP消息传递的目的。

https://t0data.gitbooks.io/burpsuite/content/chapter16.html

 

0x01 常用的接口和方法

public interface IBurpExtender

void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)

demo:

public class BurpExtender implements IBurpExtender, ITab, IContextMenuFactory, ActionListener{


public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)         {

        callbacks.setExtensionName("burp-info-extractor");

        this.hps = callbacks.getHelpers();
        this.cbs = callbacks;


callbacks.registerContextMenuFactory(this);

 

public interface IBurpExtenderCallbacks

demo:

callbacks.setExtensionName(“burp-info-extractor”);

callbacks.registerContextMenuFactory(this);

 

public interface IExtensionHelpers

IRequestInfo analyzeRequest(IHttpRequestResponse request);
    IResponseInfo analyzeResponse(byte[] response);

demo:

 reqHeaders = self._helpers.analyzeRequest(requestResponse).getHeaders()

 

public interface IScannerCheck

List<IScanIssue> doPassiveScan(IHttpRequestResponse baseRequestResponse);



demo:
    def doPassiveScan(self, baseRequestResponse):

        islaunchBurpUnauthChecker = int(self.launchBurpUnauthCheckerCheckBox.isSelected())

        if (not islaunchBurpUnauthChecker) or (self.isFilterSuffix(baseRequestResponse)) or (self.isFilterStatusCode(baseRequestResponse)):
            return


        scan_issues = []

        newRequestResponse = self.sendUnauthenticatedRequest(baseRequestResponse)

        #print str(self._helpers.analyzeRequest(baseRequestResponse).getUrl()) + '\n'

        issue = self.compareResponses(newRequestResponse, baseRequestResponse)


        scan_issues.append(issue)
        return scan_issues


    def consolidateDuplicateIssues(self, isb, isa):
        return -1

 

public interface IHttpListener

void processHttpMessage(int toolFlag,

boolean messageIsRequest,

IHttpRequestResponse messageInfo);

 

toolFlag在public interface IBurpExtenderCallbacks中可查看

/**

* Flag used to identify Burp Suite as a whole.

*/

int TOOL_SUITE = 0x00000001;

/**

* Flag used to identify the Burp Target tool.

*/

int TOOL_TARGET = 0x00000002;

/**

* Flag used to identify the Burp Proxy tool.

*/

int TOOL_PROXY = 0x00000004;

/**

* Flag used to identify the Burp Spider tool.

*/

int TOOL_SPIDER = 0x00000008;

/**

* Flag used to identify the Burp Scanner tool.

*/

int TOOL_SCANNER = 0x00000010;

/**

* Flag used to identify the Burp Intruder tool.

*/

int TOOL_INTRUDER = 0x00000020;

/**

* Flag used to identify the Burp Repeater tool.

*/

int TOOL_REPEATER = 0x00000040;

/**

* Flag used to identify the Burp Sequencer tool.

*/

int TOOL_SEQUENCER = 0x00000080;

/**

* Flag used to identify the Burp Decoder tool.

*/

int TOOL_DECODER = 0x00000100;

/**

* Flag used to identify the Burp Comparer tool.

*/

int TOOL_COMPARER = 0x00000200;

/**

* Flag used to identify the Burp Extender tool.

*/

int TOOL_EXTENDER = 0x00000400;

demo:

def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):

if messageIsRequest and toolFlag==4:

 

public interface IHttpRequestResponse

byte[] getRequest();
byte[] getResponse();

demo:

reqBodyByte = requestResponse.getRequest().tostring()[reqBodyOffset:]

 

public interface IContextMenuFactory

 List<JMenuItem> createMenuItems(IContextMenuInvocation invocation);


demo:


    public List<JMenuItem> createMenuItems(IContextMenuInvocation invocation) {
        // TODO Auto-generated method stub
        selectedItems = invocation.getSelectedMessages();       
        
        List<JMenuItem> menu = new ArrayList<JMenuItem>();
        
        JMenuItem itemManualTesting = new JMenuItem("Send rsp to BIE");
        itemManualTesting.setActionCommand("sendRSPToBIE");
        itemManualTesting.addActionListener(this);
        
        menu.add(itemManualTesting);
        
        return menu;
    }

 

开发burpsuite插件关键在于处理http请求和响应

可以参考

https://github.com/bit4woo/burp-api-drops

再以jython为例:

获取header:通过IRequestInfo对象

reqHeaders = self._helpers.analyzeRequest(requestResponse).getHeaders()

 

获取parameter:通过IRequestInfo对象

paramList = self._helpers.analyzeRequest(requestResponse).getParameters()

for para in paramList:

            paramType= para.getType()

            if (paramType == 0) or (paramType == 1):

                paramKey = para.getName()

                paramValue = para.getValue()

#参数共有7种格式,0是URL参数,1是body参数,2是cookie参数,6是json格式参数

 

获取body:通过String格式的request截取

reqBodyOffset = self._helpers.analyzeRequest(requestResponse).getBodyOffset()

reqBodyByte = requestResponse.getRequest().tostring()[reqBodyOffset:]

 

更新参数值:

if paramKey.lower() in authParamsList:
                    newAuthParam = self._helpers.buildParameter(paramKey, newAuthParamValue, paramType)
                    newRemoveGetPostAuthParamsRequest = self._helpers.updateParameter(newRemoveGetPostAuthParamsRequest, newAuthParam)

 

更新header:

newHeaders = [] 

        newAuthHeaderVal = self.replaceHeaderValWithTextField.getText()


        for header in reqHeaders:
            headerName = header.split(':')[0]
        #    if headerName.lower() not in self.authParamsList:
        #        newHeaders.append(header)
        #return self._helpers.buildHttpMessage(newHeaders, None)
            if headerName.lower() in self.authParamsList:
                header = headerName + ": " + newAuthHeaderVal
                newHeaders.append(header)
            else:
                newHeaders.append(header)

 

更新body:

if paramType == 6:

                paramKey = para.getName()
                paramValue = para.getValue()

                print paramKey + ":" + paramValue

                reqJsonBodyOffset = self._helpers.analyzeRequest(requestResponse).getBodyOffset()
                reqJsonBodyString = requestResponse.getRequest().tostring()[reqJsonBodyOffset:]

                print reqJsonBodyString

                reqJsonBodyStringDict = json.loads(reqJsonBodyString)

                #reqJsonBodyStringDict = ast.literal_eval(reqJsonBodyString)

                for authParamName in authParamsList:
                    if authParamName in reqJsonBodyStringDict.keys():
                        reqJsonBodyStringDict[authParamName] = newAuthParamValue

 

重新构造请求包:

newRemoveGetPostAuthParamsRequest = self._helpers.buildHttpMessage(jsonReqHeaders, newReqJsonBodyString)

                    
return self._callbacks.makeHttpRequest(requestResponse.getHttpService(), newRequest)

 

0x02 开发插件

未授权访问漏洞检测:

https://github.com/theLSA/burp-unauth-checker

敏感参数提取:

https://github.com/theLSA/burp-sensitive-param-extractor

信息提取:

https://github.com/theLSA/burp-info-extractor

 

burp-unauth-checker

需求

自动化检测未授权访问
autorize
authz
authmatrix
auto repeater
上几个插件都挺好,但是还是不太符合,想要的是在浏览的时候就能自动检测是否有未授权访问漏洞。

实现

python编写,实现IScannerCheck接口doPassiveScan方法

authParams.cfg文件存储授权的参数,如token,cookie等

在UI输入框增加授权参数要以英文逗号(,)分隔,并点击save按钮保存,其他操作不需要点击save按钮。

show post body即显示post数据的body内容。

show rspContent即显示响应body内容,建议尽量不开启。

一些授权参数是在get/post参数中的,如user/list?token=xxx,这时可以勾选replace GET/POST Auth Params with替换授权参数值。

默认过滤后缀列表filterSuffixList = “jpg,jpeg,png,gif,ico,bmp,svg,js,css,html,avi,mp4,mkv,mp3,txt”

应对一些特殊情况,设置了排除的授权参数列表excludeAuthParamsList

onlyIncludeStatusCode:设置检测的响应码,比如只检测200的响应

原本想直接取消掉授权参数,但是可能造成响应失败,所以把授权参数值替换成自定义的数据,如:cookie:[空],token=unauthp

sendUnauthenticatedRequest发送替换了授权参数值的请求

授权参数分两种
1-在http的header中,如cookie,authorization等
2-在http参数中,如post数据中的token等
实现:
1-直接将header的授权参数值替换即可:
if headerName.lower() in self.authParamsList:
header = headerName + “: ” + newAuthHeaderVal
2-
get/post请求的参数,常规操作buildParameter,updateParameter即可。
json参数,直接将body数据解析为字典再替换授权参数值,然后再将字典转字符串,最后构造新请求数据包。

if paramType == 6:

                paramKey = para.getName()
                paramValue = para.getValue()

                print paramKey + ":" + paramValue

                reqJsonBodyOffset = self._helpers.analyzeRequest(requestResponse).getBodyOffset()
                reqJsonBodyString = requestResponse.getRequest().tostring()[reqJsonBodyOffset:]

                print reqJsonBodyString

                reqJsonBodyStringDict = json.loads(reqJsonBodyString)

                #reqJsonBodyStringDict = ast.literal_eval(reqJsonBodyString)

                for authParamName in authParamsList:
                    if authParamName in reqJsonBodyStringDict.keys():
                        reqJsonBodyStringDict[authParamName] = newAuthParamValue

有两个问题

1-字典有【u’’】,

2-还有将字符串转为字典,json数据有空格和单引号

json.loads重新构造的json数据有空格

尝试使用

d = json.dumps(s, separators=(‘,’, ‘:’))

发现类型是str不方便处理,loads()没有separators参数

ast.literal_eval()也一样有空格和单引号……

想直接replace掉空格,但是有些数据可能会有空格……

两个问题的解决方案:

reqJsonBodyStringDict = json.loads(reqJsonBodyString)

……#替换授权参数值

newReqJsonBodyString = json.dumps(reqJsonBodyStringDict,separators=(‘,’, ‘:’))

即先用json.loads()将字符串转字典进行处理后再将字典用json.dumps()转换为字符串来构造请求数据包。

最后再对比原请求和替换了授权参数值的响应body
compareResponses:
if (str(nBody).split() == str(oBody).split()):

一致则为未授权访问漏洞。

暂不提供在ui界面删除授权参数的功能,如要删除直接在authParams.cfg里面删除,切记要将光标移动到最后一个授权参数(末行)的结尾。

坑点

1】dopassivescan的request不能直接用,要复制一个再用,否则会出现请求只能read-only的异常,因为这个请求响应已经完成了,再编辑是没意义的。

解决:

newRemoveGetPostAuthParamsRequest = newRemoveAuthHeaderRequest

2】python中的空要用None,java是null

效果图

 

burp-sensitive-param-extractor

需求

检测并提取请求参数中的敏感参数名,如userid,username,方便测试越权漏洞,并形成敏感参数字典。

实现

使用python开发,实现IHttpListener接口,processHttpMessage方法。

param-regular.cfg:参数正则配置文件,id表示检测请求参数中包含id的参数,如userid,idcard等

支持4种参数检测:

self.requestParamDict[‘urlParams’] = []
self.requestParamDict[‘BodyParams’] = []
self.requestParamDict[‘cookieParams’] = []
self.requestParamDict[‘jsonParams’] = []

先获取请求的所有类型参数,放到requestParamDict再findSensitiveParam最后write2file。

界面右侧的列表即参数正则,可实时增删,删除只需单击列表元素再点击删除按钮即可。

IHttpListener好像无法实现请求关键词参数高亮,IScanIssue-ScanIssue-self._callbacks.applyMarkers可以,暂不实现参数高亮了。

无法处理较复杂的请求json参数,比如多层次嵌套,这种情况较少,暂不做处理。

坑点

1】据说json的get请求参数可能无法获取最底层键值对?

解决:这种请求的情况较少见,暂不处理。

效果图

 

burp-info-extractor

需求

快速提取数据中有价值的信息,如HTTP响应数据包中的用户名,密码等。

比如一个api(/user/list)返回大量用户名/密码,大多数是json格式(jsonarray),就可以使用此工具快速提取信息。

实现

使用java开发,实现IContextMenuFactory接口createMenuItems方法。

采用两种提取方式:
1.json格式提取
2.正则提取

【1】json格式提取:
采用google gson

【2】正则提取
re库常规操作即可

坑点

1】

现象:

send rsp to bie中空行问题,复制的http请求和手动换行复制body没问题,直接send to 的请求就无法以\n\n获取body,可能是有\t,\r,\n之类的字符

解决:
rspBody1 = rspBody1.replaceAll(“\r\n|\r|\n|\t”, “”);

2】

现象:

runnable里面run(),无法直接使用动作监听,如:
cleanRspBodyButton.setActionCommand(“cleanrspbody”);

cleanRspBodyButton.addActionListener(BurpExtender);

解决:
cleanRspBodyButton.addActionListener(new ActionListener() {

@Override

public void actionPerformed(ActionEvent e) {

// TODO Auto-generated method stub

rspBodyArea.setText(“”);

}

效果图

 

0x03 常见问题

1. 多次加载插件出现outofmomoryerror

原因:内存被占满了,重启burp或者加大burp的内存,在启动 Burp 时设置 JVM 的 XX 参数即可,如: java -XX:MaxPermSize=1G -jar burp.jar

2、如何调试

jython目前只能想到print大法……

java的目前我也是用println大法,虽然有可以联动ide进行调试的方法,但还是要编译jar,比较麻。

3、py开发的插件用到第三方库,如requests,但是即使本机安装了requests库,加载插件时候还是提示找不到

原因:jython和python的库不同

解决:

在插件目录放进第三方库即可

或者填入第三方库的位置,如site-package

0x04 结语

学习优秀的插件,不用重复造轮子。

参考官方API。

 

0x05 参考资料

https://t0data.gitbooks.io/burpsuite/content/chapter16.html

https://blog.csdn.net/zhangmiaoping23/article/details/79786187

https://blog.csdn.net/zhangmiaoping23/article/details/79786631

https://wooyun.js.org/drops/BurpSuite插件开发指南之 Java 篇.html

https://wooyun.js.org/drops/BurpSuite插件开发指南之 Python 篇.html

sh1yan.top/2020/03/02/Writing-the-burpseuite-plug-in-beginner/

www.code2sec.com/burpsuitecha-jian-kai-fa-tips.html

https://github.com/bit4woo/burp-api-drops

https://xz.aliyun.com/t/7065

 

Comment

please input captcha *