0x00 基础
burpsuite api:
interface:
用java开发插件就把这个burp文件夹放到工程目录即可
javadoc
在线版
推荐用 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消息传递的目的。
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 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中可查看
int TOOL_SUITE = 0x00000001;
int TOOL_TARGET = 0x00000002;
int TOOL_PROXY = 0x00000004;
int TOOL_SPIDER = 0x00000008;
int TOOL_SCANNER = 0x00000010;
int TOOL_INTRUDER = 0x00000020;
int TOOL_REPEATER = 0x00000040;
int TOOL_SEQUENCER = 0x00000080;
int TOOL_DECODER = 0x00000100;
int TOOL_COMPARER = 0x00000200;
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 createMenuItems(IContextMenuInvocation invocation);
demo:
public List createMenuItems(IContextMenuInvocation invocation) {
// TODO Auto-generated method stub
selectedItems = invocation.getSelectedMessages();
List menu = new ArrayList();
JMenuItem itemManualTesting = new JMenuItem("Send rsp to BIE");
itemManualTesting.setActionCommand("sendRSPToBIE");
itemManualTesting.addActionListener(this);
menu.add(itemManualTesting);
return menu;
}
开发burpsuite插件关键在于处理http请求和响应
可以参考
再以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 开发插件
未授权访问漏洞检测:
敏感参数提取:
信息提取:
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 参考资料