burp 日志插件从原理到实践
本文作者:鶇(首次投稿)
Logger++ 是 nccgroup 开源的一个 burp 扩展,主要功能是记录经过 Burp Suite 的所有 HTTP 请求 和 HTTP 响应。
相较于 Burp 自带的 Proxy 组件中的 HTTP History, logger++ 的优势是记录了更完整的流量,并且支持对这些流量进行基于正则表达式的简易分析,对相关流量记录进行着色展示,将流量导入到 elasticsearch 平台等。Burp 基本组件 Proxy 中的 HTTP History 则只记录经过代理的 HTTP 流量,对于 Repeater, Scanner, Intruder 等组件的流量,并不会在它的标签中展示。
对于笔者而言,常用的功能主要有两个:
1、基于正则表达式的简易 HTTP 流量分析
2、记录流量日志 (导出 csv 便于后续代码分析)
代码简要分析
https://github.com/nccgroup/BurpSuiteLoggerPlusPlus/blob/master/src/main/java/burp/BurpExtender.java
从入口 src/main/java/burp/BurpExtender.java
开始看,一开始只是继承 loggerplusplus.LoggerPlusPlus 类。
package burp;
import loggerplusplus.LoggerPlusPlus;
public class BurpExtender extends LoggerPlusPlus{ public static void main(String [] args){ System.out.println("You have built the Logger++. You shall play with the jar file now!"); }}
切换到 src/main/java/loggerplusplus/LoggerPlusPlus.java
与记录 HTTP 日志有关的语句是 logManager = new LogManager(loggerPreferences);
。
@Override public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks){ //Burp Specific LoggerPlusPlus.callbacks = callbacks; LoggerPlusPlus.instance = this; LoggerPlusPlus.contextMenuFactory = new LoggerContextMenuFactory();
callbacks.setExtensionName("Logger++");
filterListeners = new ArrayList<>(); loggerPreferences = new LoggerPreferences(LoggerPlusPlus.this); logManager = new LogManager(loggerPreferences); elasticSearchLogger = new ElasticSearchLogger(logManager, loggerPreferences);
if(!callbacks.isExtensionBapp() && loggerPreferences.checkUpdatesOnStartup()){ MoreHelp.checkForUpdate(false); }
buildUI(); }
继续跟踪到 src/main/java/loggerplusplus/LogManager.java
。
基本思路就是继承和实现 Burp 提供的 IHttpListener 接口 和 IProxyListener 接口,重写 processHttpMessage
和 processProxyMessage
方法,存储流经的 HTTP 流量。还有一些对 HTTP 请求/响应 的处理细节可以在 src/main/java/loggerplusplus/LogEntry.java
中的 processRequest
和 processResponse
中找到。(其实大部分情况下 Burp 自带的 LoggerPlusPlus.getCallbacks().getHelpers().analyzeRequest(requestResponse)
已经帮我们把需要的字段解析完成了。)
关键代码如下:
@Override public void processHttpMessage(final int toolFlag, final boolean messageIsRequest, final IHttpRequestResponse requestResponse) { // Only process scanner messages which contain the request and response. if(!messageIsRequest) { final LogEntry logEntry = new LogEntry(); processHttpMessage(logEntry, toolFlag, requestResponse); } }
// Wrapper to allow a custom LogEntry to be passed as a parameter // Custom LogEntry used when importing proxy history. // messageIsRequest is removed as not needed. public void processHttpMessage(final LogEntry logEntry, final int toolFlag, final IHttpRequestResponse requestResponse){ executorService.submit(new Runnable() { @Override public void run() { if(toolFlag != IBurpExtenderCallbacks.TOOL_PROXY || logEntry.isImported){ if(requestResponse == null || !prefs.isEnabled()) return; IRequestInfo analyzedReq = LoggerPlusPlus.getCallbacks().getHelpers().analyzeRequest(requestResponse); URL uUrl = analyzedReq.getUrl(); if (isValidTool(toolFlag) && (!prefs.isRestrictedToScope() || LoggerPlusPlus.getCallbacks().isInScope(uUrl))){ // We will not need to change messageInfo so save to temp file IHttpRequestResponse savedReqResp = LoggerPlusPlus.getCallbacks().saveBuffersToTempFiles(requestResponse); logEntry.processRequest(toolFlag, savedReqResp, uUrl, analyzedReq, null); if(requestResponse.getResponse() != null) logEntry.processResponse(savedReqResp); // Check entry against colorfilters. for (ColorFilter colorFilter : prefs.getColorFilters().values()) { logEntry.testColorFilter(colorFilter, false); }
addNewRequest(logEntry, true); // Complete Request and Response Added for (LogEntryListener logEntryListener : logEntryListeners) { logEntryListener.onResponseUpdated(logEntry); } } } } }); }
@Override public void processProxyMessage(final boolean messageIsRequest, final IInterceptedProxyMessage proxyMessage) { //REQUEST AND RESPONSE SEPARATE final LogEntry.PendingRequestEntry logEntry; if(messageIsRequest){ logEntry = new LogEntry.PendingRequestEntry(); }else{ synchronized (pendingRequests) { logEntry = pendingRequests.remove(proxyMessage.getMessageReference()); } } executorService.submit(new Runnable() { @Override public void run() { if(proxyMessage == null || !prefs.isEnabled()) return; IHttpRequestResponse requestResponse = proxyMessage.getMessageInfo(); IRequestInfo analyzedReq = LoggerPlusPlus.getCallbacks().getHelpers().analyzeRequest(requestResponse); URL uUrl = analyzedReq.getUrl(); int toolFlag = LoggerPlusPlus.getCallbacks().TOOL_PROXY; if (isValidTool(toolFlag) && (!prefs.isRestrictedToScope() || LoggerPlusPlus.getCallbacks().isInScope(uUrl))){ if(messageIsRequest){ //New Proxy Request //We need to change messageInfo when we get a response so do not save to buffers logEntry.processRequest(toolFlag, requestResponse, uUrl, analyzedReq, proxyMessage); for (ColorFilter colorFilter : prefs.getColorFilters().values()) { logEntry.testColorFilter(colorFilter, false); } synchronized (pendingRequests) { pendingRequests.put(proxyMessage.getMessageReference(), logEntry); } addNewRequest(logEntry, false); // Request added without response }else{ // Existing Proxy Request, update existing if (logEntry != null) { updatePendingRequest(logEntry, requestResponse); } else { lateResponses++; if(totalRequests > 100 && ((float)lateResponses)/totalRequests > 0.17){ MoreHelp.showWarningMessage(lateResponses + " responses have been delivered after the Logger++ timeout. Consider increasing this value."); //Reset late responses to prevent message being displayed again so soon. lateResponses = 0; } } } } } }); }
功能说明
主界面
先看下 Logger++ 的基本界面,其实和 Proxy 中的 HTTP History 基本一致,稍微新增了一些字段。
正常经过代理的流量 Tool 字段都是 Proxy;Repeater, Intruder 等工具发出的请求也会进行记录。
如果 Tool 为 Scanner 是 Burp 自带的扫描器发送的请求;Tool 为 Extender 是其他 Burp 插件发送的请求。在这个场景插件发出的请求都是来自 Active Scan++,利用这个特性你可以使用 Logger++ 以 "黑盒" 的方式分析 Burp 一些的扫描能力增强插件的原理 / payload 。
Proxy / Repeater / Intruder 案例:
Scanner / Extender 案例:
记录上图时的插件情况
基于正则表达式的简易流量分析
过滤器也是 Logger++ 中一个很方便的功能。笔者通常会使用这个功能,来寻找一些敏感信息泄露和潜在的漏洞请求(这里提供的规则并不是 100% 能确定漏洞的,只是作为一个辅助手段,还需要进一步手工验证)。在该工具的 Filter Library 里也有几个不错的过滤器示例可以参考。
关于过滤器支持的其他字段可以在 Logger++ 的项目 wiki 里查到,链接如下
https://github.com/nccgroup/BurpSuiteLoggerPlusPlus/wiki/Filter-Fields
这里提供一些过滤器的示例作为抛砖引玉
信息泄露 (内网 IP) 规则 #1
Internal IP Address
RESPONSE == /(10(.(25[0-5]|2[0-4][0-9]|1[0-9]{1,2}|[0-9]{1,2})){3}|((172.(1[6-9]|2[0-9]|3[01]))|192.168)(.(25[0-5]|2[0-4][0-9]|1[0-9]{1,2}|[0-9]{1,2})){2})/
如果你需要确定具体匹配的值,可以在 Message Editor 里用正则表达式搜索。
也可以使用 Grep Values 标签来汇总所有请求中符合过滤器筛选内容的字符串值。
如果要新增自定义过滤器,只要点击 Saved Filters, 然后点击 Add Filter 即可,另外在 Options 选项卡中可以批量导入/导出过滤器设置。
信息泄露 (身份证号) #2
RESPONSE == /((d{6})(18|19|20)?(d{2})([01]d)([0123]d)(d{3})(d|X))/
信息泄露 (电子邮件) #3
匹配所有邮箱
RESPONSE == /(([A-Za-z0-9_-.])+@([A-Za-z0-9_-.])+.([A-Za-z]{2,4}))/
匹配特定邮箱
RESPONSE == /(([A-Za-z0-9_-.])+@test.com)/
模糊关键字匹配邮箱
RESPONSE == /(([A-Za-z0-9_-.])+@(.*)test(.*))/
对结果进行去重
潜在的 CORS 配置不当 #4
RESPONSEHEADERS == /Access-Control-Allow-Origin: null/
RESPONSEHEADERS == /Access-Control-Allow-Origin: */
寻找潜在的 SSRF / 开放重定向 #5
开放重定向
根据响应头
ResponseHeaders == /(Location)/
根据参数名称
QUERY == /(url(.*)=)/ || REQUEST == /(url(.*)=)/
QUERY == /(uri(.*)=)/ || REQUEST == /(uri(.*)=)/
QUERY == /(path(.*)=)/ || REQUEST == /(path(.*)=)/
QUERY == /(href(.*)=)/ || REQUEST == /(href(.*)=)/
QUERY == /(redirect(.*)=)/ || REQUEST == /(redirect(.*)=)/
寻找参数中的图片
QUERY == /(img(.*)=)/ || REQUEST == /(img(.*)=)/
QUERY == /(pic(.*)=)/ || REQUEST == /(pic(.*)=)/
QUERY == /(.png)/ || REQUEST == /(.png)/
QUERY == /(.jpg)/ || REQUEST == /(.jpg)/
QUERY == /(.gif)/ || REQUEST == /(.gif)/
寻找潜在的 JSONP 调用 #6
基于参数
REQUEST == /(callback(.*)=)/ || QUERY == /(callback(.*)=)/
基于响应特征
RESPONSE == /(.+([(.*)]))/ && RESPONSEHEADERS == /application/json/
寻找潜在的越权漏洞 #7
REQUEST == /(id(.*)=)/ || QUERY == /(id(.*)=)/
着色过滤器 (color filter)
举一个 内网 IP 泄露检测规则 和 身份证号检测规则 配置的例子
Logger++ => View Logs => Colorize
着色过滤器配置也可以在 Options 中配置批量导入/导出。
着色效果如下
记录流量日志
Logger++ => Options => Export => Save log table as csv.
结语
充分利用 Logger++ 插件的过滤器,能帮助你在海量 HTTP 请求中更方便快捷地定位到某些脆弱的 HTTP 请求。如果你有其他不错的过滤器规则,也欢迎在下面留言。
参考资料
BurpSuiteLoggerPlusPlus Wiki
https://github.com/nccgroup/BurpSuiteLoggerPlusPlus/wiki
Logger++ 示例过滤器
https://github.com/nccgroup/BurpSuiteLoggerPlusPlus/wiki/Example-Filters
Burp Suite 存储日志分析
https://zhuanlan.zhihu.com/p/28284124
Burp Suite神器的日志存储
https://zhuanlan.zhihu.com/p/28231222
经验分享 Burpsuite 插件的使用
https://cloud.tencent.com/developer/article/1015187
- hihoCoder #1082 : 然而沼跃鱼早就看穿了一切(字符串处理)
- 51Nod 1182 完美字符串(字符串处理 贪心 Facebook Hacker Cup选拔)
- 51Nod 1080 两个数的平方和(数论,经典题)
- Selenium3+python自动化50-环境搭建(firefox)
- Selenium2+python自动化51-unittest简介
- 开发者的如何优雅的使用OSX
- 1082 与7无关的数(思维题,巨坑)
- Facebook构建高性能Android视频组件实践之路
- Samba服务的配置总结
- 大型互联网系统的监控流水线
- 10-移动端开发教程-移动端事件
- 灰色理论预测模型
- tweet情感分析流程
- Selenium2+python自动化52-unittest执行顺序
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法