burpsuite字典_从头开发一个BurpSuite数据收集插件

一段时间没写公众号了,最近写了个 burpsuite 数据收集的插件,于是想出一篇从头编写一个 burpsuite 插件的教程。 这个插件的目的收集 burpsuite 请求中的数据,如请求中的子域名、文件名、目录名、参数名等,保存到数据库,然后根据出现的次数进行排序,出现次数多的排在前面,从而强化我们的字典。 插件效果演示 先来看看插件的效果图: 53920a2fb0d39880d01324fe1d11d283.png 该插件会在 burp 上面新建一个标签页,用来保存一些配置,如数据库 ip 地址、端口、账号密码等。 还可以从数据库导出数据到文件作为字典,或者从文件中导入数据到数据库中,用于和别人分享及备份数据库。 导出的文件效果如下: burpsuite字典_从头开发一个BurpSuite数据收集插件_第1张图片 burpsuite字典_从头开发一个BurpSuite数据收集插件_第2张图片 导出的字典文件是 txt 文件,主要是作为字典来使用,其中的内容是根据出现的次数来排序的, 如 /test/ 目录在 a.baidu.com 出现了一次,然后在 b.baidu.com 出现了一次,那么它的 count 值就是 2 。 目录在不同网站出现的次数越多,那么排名就会越前,证明该目录是最常现出的,因此应该把它放在前面提高命中率。   同理,对于请求中的参数名、文件名、子域名等数据,也是通过出现的次数来排序。 上图中,导出的还有 csv 文件,该文件含有出现的次数,可以通过该文件通过插件导入其它数据库,以实现备份或分享的功能。 插件开发 现在开始插件的开发,开发插件需要些前置的知识。 编写 burpsuite 插件需要对 Java 或 Python 语言有一定的基础,在这里我使用的是 Java,因为 Java 编写的插件在 burpsuite 加载得更快,性能更好。 在这里我使用的开发环境是 IDEA ,因为 IDEA 的智能补全功能就像知道我想打什么代码一样,十分强大。 在 IDEA 新建一个 gradle 项目,点击 Create New Project  burpsuite字典_从头开发一个BurpSuite数据收集插件_第3张图片 选择 Gradle 项目, Gradle 是一个构建工具,可以方便加载所需的代码仓库。 点击下一步。 burpsuite字典_从头开发一个BurpSuite数据收集插件_第4张图片 给项目起个名称 b5c0d6a1b6545ab83c5a48ebc96c0914.png 新建完后,在 build.gradle 文件中添加以下依赖,也就是加载 burpsuite 插件API ,如果提示 auto import, 可以点击,从而自动从远程仓库加载 burpsuite API 。
compile('net.portswigger.burp.extender:burp-extender-api:1.7.13')
‍ 同时在 plugins 里面添加 shadow 插件,该插件可以方便把项目打包成 jar 包。
id 'com.github.johnrengelman.shadow' version '5.2.0'
burpsuite字典_从头开发一个BurpSuite数据收集插件_第5张图片 接着在 /src/main/java 目录处创建一个名为 burp 的包名,在 java 目录处右键 -> New -> Package  burpsuite字典_从头开发一个BurpSuite数据收集插件_第6张图片 burpsuite字典_从头开发一个BurpSuite数据收集插件_第7张图片 接着在该包上右键,新建一个名为 BurpExtender 的类。 burpsuite字典_从头开发一个BurpSuite数据收集插件_第8张图片 burpsuite字典_从头开发一个BurpSuite数据收集插件_第9张图片 这个包名和类名是固定的,burpsuite 加载插件时就是通过 burp.BurpExtender 来查找的,如果不这样起名,会报 ClassNotFoundException 。 BurpExtender 类需要实现 IBurpExtender 接口,burp 在加载插件时,会调用该接口,并传递 IBurpExtenderCallbacks 接口仅我们使用。 点击错误提示,实现接口中的方法 burpsuite字典_从头开发一个BurpSuite数据收集插件_第10张图片 burpsuite字典_从头开发一个BurpSuite数据收集插件_第11张图片 然后我们添加下面的代码为插件设置名称,并打印一 success 字符串
callbacks.setExtensionName("data-collect2");callbacks.printOutput("load success");
burpsuite字典_从头开发一个BurpSuite数据收集插件_第12张图片 接着可以编译该项目成 jar 包,然后让 burp 加载看看效果。 点击右侧的 gradle 菜单,展开菜单,双击 shadowjar ,gradle 会自动编译项目成 jar 包,jar 包位于 build 目录中的 libs 目录中。 burpsuite字典_从头开发一个BurpSuite数据收集插件_第13张图片 接着在 burp 的扩展选项卡添加该 jar 包,点击 add  burpsuite字典_从头开发一个BurpSuite数据收集插件_第14张图片 选择 Java 扩展类型,选择我们的 jar 包,点击下一步 burpsuite字典_从头开发一个BurpSuite数据收集插件_第15张图片 可以看到加载插件后成功打印了 success 字符串。 burpsuite字典_从头开发一个BurpSuite数据收集插件_第16张图片 添加标签页 接着我们要为插件创建一个标签页,先在IDEA中创建一个 Form,用于设计UI burpsuite字典_从头开发一个BurpSuite数据收集插件_第17张图片 burpsuite字典_从头开发一个BurpSuite数据收集插件_第18张图片 创建的界面如下,左边的窗口中创建了两个文件,一个是 DataCollectGUI.java 文件,该文件与 form 文件绑定,一个是 DataCollectGUI.form 文件,可以在此文件上拖动控件来设计 UI 界面,当界面更新时,会自动生成代码插入 DataCollectGUI.java 文件中。 burpsuite字典_从头开发一个BurpSuite数据收集插件_第19张图片 接着从右边拖一个 JLabel(标签)、一个 JTextField(输入框)和 JButton(按钮) 到设计面板中,效果如下: burpsuite字典_从头开发一个BurpSuite数据收集插件_第20张图片 然后给根面板设置个变量名,用于后面生成代码: burpsuite字典_从头开发一个BurpSuite数据收集插件_第21张图片 为了让 IDEA 打包 GUI 界面的类,需要在 build.gradle 添加以下依赖
compile('com.intellij:forms_rt:7.0.3')
burpsuite字典_从头开发一个BurpSuite数据收集插件_第22张图片 接着在设置中设置根据 Form 界面自动生成 Java 源码: burpsuite字典_从头开发一个BurpSuite数据收集插件_第23张图片 burpsuite字典_从头开发一个BurpSuite数据收集插件_第24张图片 然后在 Gradle 的编译选项中设置编译器是 IDEA 自带的编译器,这样才能自动更新 form 文件中的控件到代码中: burpsuite字典_从头开发一个BurpSuite数据收集插件_第25张图片 设置好后,点击构建图标,就会自动生成和 form 文件相关的代码,可以看到在 $$$setupUI$$$() 方法中自动生成了我们拖到界面中的3个控件。 注意,$$$getRootComponent$$$() 方法需要给界面中的JPanel 控件设置变量名才可以生成,参考上面的步骤。 burpsuite字典_从头开发一个BurpSuite数据收集插件_第26张图片 接着需要回到 BurpExtender 类中,要为插件添加一个标签页,需要实现 ITab 接口 burpsuite字典_从头开发一个BurpSuite数据收集插件_第27张图片 实现 ITab 接口后,会有两个方法需要实现,其中 getTabCaption() 方法返回标签页的名称, getUiComponent() 方法返回我们创建的 UI 面板。 注意,需要在14行下调用 callbacks.addSuiteTab(this) 来注册接口。 接着双击 gradle 中的 shadowjar 按钮重新打包 jar 包,然后在 burp 重新加载插件,就可以看到效果图了: burpsuite字典_从头开发一个BurpSuite数据收集插件_第28张图片 获取标签页设置内容 接下来我们需要获取标签页中的配置内容,可以通过添加事件监听器来实现。 回到 IDEA 的 form 文件中,在按钮上右键,点击 Create Listener,选择 ActionListener burpsuite字典_从头开发一个BurpSuite数据收集插件_第29张图片 burpsuite字典_从头开发一个BurpSuite数据收集插件_第30张图片 创建好后就可以编写点击按键时的代码逻辑了 burpsuite字典_从头开发一个BurpSuite数据收集插件_第31张图片 在这里简单地把输入框中的内容打印在插件日志中,要把内容打印到插件日志中,我们需要获取 IBurpExtenderCallbacks 对象,可以修改构造函数,在初始化时传入: burpsuite字典_从头开发一个BurpSuite数据收集插件_第32张图片 还需要修改 BurpExtender 中的代码,传入 callbacks 对象 burpsuite字典_从头开发一个BurpSuite数据收集插件_第33张图片 接着在监听器中实现获取标题内容并打印到日志的代码,代码中29行通过 getText()方法获取输入框架的内容,然后在30行处通过 callbacks.printOutput()方法打印内容到日志中。 burpsuite字典_从头开发一个BurpSuite数据收集插件_第34张图片 接着双击 gradle 中的 shadowjar 按钮重新打包 jar 包,然后在 burp 重新加载插件,在插件输入框中输入 hello world, 点击按钮,就会在插件日志中打印输入框中的内容了。 burpsuite字典_从头开发一个BurpSuite数据收集插件_第35张图片 burpsuite字典_从头开发一个BurpSuite数据收集插件_第36张图片 到此,我们可以获取插件输入框中的内容,这样我们就可以开始开发数据收集的插件了。 开发核心功能 由于插件代码比较多,这里就不一一介绍了,先导入开发完成的代码,可以到我的 github 上下载: https://github.com/QdghJ/burp_data_collector 使用 IDEA 导入项目 burpsuite字典_从头开发一个BurpSuite数据收集插件_第37张图片 选择 gradle 项目 burpsuite字典_从头开发一个BurpSuite数据收集插件_第38张图片 导入后,我们来看看开发数据收集插件需要用到哪些库,首先需要 mysql 的 jdbc 驱动,然后是导出 csv 文件时用到的库: burpsuite字典_从头开发一个BurpSuite数据收集插件_第39张图片 接着来看看项目的文件结构,在 dao 包中的主要是数据库的操作类,封装了数据库操作的代码,每个表一个类来插入和查询数据。 而 gui 包中的就是界面类。 burpsuite字典_从头开发一个BurpSuite数据收集插件_第40张图片 核心要点 这个插件的核心要点如下:
  1.  解析功能,解析请求中的各种数据,然后插入到数据库中。
  2. 数据库设计,关键体现在查询和插入数据的性能上,在被我的开发朋友调教后,重新设计SQL语句,从导入一次数据需要十几分钟,优化到了几秒内完成。
  3. 内存去重,在内存中保存已经插入的数据,如果重复了,就不插入数据库,可以极大地减少数据库的操作。
  4. 定时收集数据,每10分钟收集一次数据。
  5. 退出时收集数据,当 burp 退出时会进行最后一次数据收集,不用人工收集。
解析请求 首先是解析功能,该功能在 BurpExtender 类的 saveData() 方法中,该方法会在点击 export data to database 按钮后或定时收集时调用,用于解析请求历史中的数据,并保存到数据库中。 在 329 行处调用 callbacks.getProxyHistory() 方法获取请求历史中的所有请求, 接着在 332 行处使用 for 循环遍历所有的请求, 在333行处解析请求内容,并在 334行处获取请求的 host,也就是主机名,如 www.baidu.com。 在 335 行处获取请求的路径,如 /aaa/bbb/ccc.php ,这里就收集到了请求中的路径。 然后在339行处插入到内存中,等到最后一次性插入数据库。 接着在 334 行到 359 行之间收集分离的目录,如 /aaa/bbb/ccc/ 目录,会收集 /aaa/bbb/ccc/, /aaa/bbb/ , /aaa/ 三个目录。 burpsuite字典_从头开发一个BurpSuite数据收集插件_第41张图片 接着在 361 行处从 path 中分离出目录,如 /aaa/bbb/ccc/, 那么会收集 /aaa/, /bbb/, /ccc/ 。   接着在 370 行处收集文件名, 如 /js/jquery.js, 那么会收集 jquery.js 。 然后在 376 行处分离主机名,也就是分离域名,只获取子域名,如域名是 aa.bb.cc.dd.ee ,那么会收集先收集分离后的子域名,如 aa, bb, cc, 然后收集 aa.bb.cc, bb.cc, cc 几个子域名。 burpsuite字典_从头开发一个BurpSuite数据收集插件_第42张图片 最后在 400 行处获取请求中的所有参数,402 行处的 2 代表 cookie 参数名,暂时不收集 cookie, 然后收集符合条件的参数名。 burpsuite字典_从头开发一个BurpSuite数据收集插件_第43张图片 数据库设计 数据库设计主要包括表结构的设计,导出语句的的设计,插入语句的设计。 数据库的设计对性能的影响很大,开始时我写的SQL语句收集一次数据需要20分钟,经过做开发的朋友改进后,只需1秒,所以写数据库代码就是写SQL语句。 首先是表的设计,本次要收集的数据上面已经介绍过,主要的表如下: burpsuite字典_从头开发一个BurpSuite数据收集插件_第44张图片 burpsuite字典_从头开发一个BurpSuite数据收集插件_第45张图片 表的类型有两种,一种是 host_xxx_map 表,如 host_dir_map 表用于收集 host 信息及其对应的内容,表的字段有 host 和 dir ,上图可以看出 0.gravatar.com 这个 host 对应的目录有3个。 另一种表如,dir 表,用于导入 csv 文件中的数据,表的字段是 dir , count,分别是目录名和出现的数目,主要用于导入别人导出的 dir_import.csv, 当导出数据时,会合并这种表中的数据,再进行导出。 创建表的代码在 dao/DatabaseUtil 类中 burpsuite字典_从头开发一个BurpSuite数据收集插件_第46张图片 接下来说下插入数据时的语句,插入数据时应该怎么处理呢,我一开始的时候设计的是这样的: 假设要插入一条数据, host 值是 www.baidu.com, dir 值是 /js/ 下面是伪代码
数据库是否存在该记录 // 此处一条查询语句如果存在不插入如果不存在,插入该记录 //此处一条插入语句
假设最坏的情况,我们访问的网站是从来没访问过的,第一次收集数据的时候所有数据都不存在,当该网站有1000条请求时,这里就会进行2000次数据库处理操作。 这样必然是分钟级别的插入,在这样设计时,我需要20分钟才完成一次数据收集。 当我的开发朋友看到这种操作时,内心是崩溃的,于是给我优化了下。 首先是数据库是否存在记录的问题,可以使用 INSERT IGNORE 来解决,该语句在插入数据时,如果存在,则不插入。 接着可以使用一条语句插入所有数据,语句如下:
INSERT IGNORE INTO host_dir_map(host, dir) VALUES("www.baidu.com", "dir1"), ("www.baidu.com", "dir2"), ...("www.baidu.com", "dir1000")
这样,2000条语句的操作就变成了一条语句完成,从20分钟的插入时间变成了1秒。 接着是导出数据的语句,如果要统计一个目录在所有 host 中出现的次数,需要使用到 group by 语句,语句如下:
SELECT hdm.dir, COUNT(*) AS dirCount FROM host_dir_map hdm GROUP BY hdm.dir ORDER BY `dirCount` DESC
通过使用 dir 字段来分组,然后通过 count(*) 来统计出现的次数,查询结果如下: burpsuite字典_从头开发一个BurpSuite数据收集插件_第47张图片 具体的导出代码如下: burpsuite字典_从头开发一个BurpSuite数据收集插件_第48张图片 内存去重 插件是可以定时获取请求内容中的数据并插入数据库的,但每次定时操作时,不知道数据是否插入过数据库了,如果每次都插入全部数据到数据库,还是会影响性能。 因此有必要在每插入一个数据的时候,在内存中记录下该数据已经被插入,在第二次定时收集的时候,遇到已经插入过数据库的数据,就可以跳过此数据。 在内存中检查比在数据库中快很多,这样在导出一次全量的数据后,第二次定时收集的时候会快很多,因为只需要收集新出现的请求。 具体怎样实现呢? 我们需要使用 HashMap 和 HashSet 数据结构相结合。 HashMap 是一个键值对数据结构,也就是一个哈希桶,可以把一些键值对保存到该结构中进行快速查找。 HashSet 是一个集合,里面的数据是不重复的元素,也就是说 ,里面只会出现 aa,bb,cc ,而不会出现 aa,aa,bb,cc。 我们可以组合两个数据结构来实现保存 host 对应的 dir 。 具体结构如下图: burpsuite字典_从头开发一个BurpSuite数据收集插件_第49张图片 上图中,www.baidu.com 是一个键,对应的值是一个 hashset 结构,hashset里面装有这个 host  所有的目录。 由于有多个表的内容要保存,只要加多一层 HashMap 就可以实现保存所有表了。 burpsuite字典_从头开发一个BurpSuite数据收集插件_第50张图片 具体实现代码如下:
private boolean addToMemory(String host, String value, String flag) {        boolean result = true;        HashMap> hostHashMap = memoryHostValueMap.get(host);if (hostHashMap == null) {            hostHashMap = new HashMap<>();            HashSet hostHashSet = new HashSet<>();            hostHashSet.add(value);            hostHashMap.put(flag, hostHashSet);            memoryHostValueMap.put(host, hostHashMap);            result = false;        } else {            HashSet hostHashSet = hostHashMap.get(flag);if (hostHashSet == null) {                hostHashSet = new HashSet<>();                hostHashSet.add(value);                hostHashMap.put(flag, hostHashSet);                result = false;            } else {if (!hostHashSet.contains(value)) {                    hostHashSet.add(value);                    result = false;                }            }        }return result;    }
flag 值代表保存的是哪种类型的数据,如 dir、path。 以上代码的逻辑是判断内存中是否存在该数据,不存在则插入并返回 false。 在解析数据的时候,可以根据 addToMemory() 函数的返回值来决定是否把数据添加到插入队列中。 具体流程如下: 解析数据时,先通过 checkDir 函数检查是否需要插入数据库 burpsuite字典_从头开发一个BurpSuite数据收集插件_第51张图片 checkDir() 函数调用 addToMemory() 函数判断内存中是否存在该数据了 burpsuite字典_从头开发一个BurpSuite数据收集插件_第52张图片 回到上面的367行处,接着调用 addToInsertMap() 函数把数据保存到插入队列中,addToInsertMap() 函数和 addToMemory() 相似,区别是只保存新增的数据: burpsuite字典_从头开发一个BurpSuite数据收集插件_第53张图片 在 saveData() 函数的最后会取出内存中保存的新增数据,一次性插入数据库中: burpsuite字典_从头开发一个BurpSuite数据收集插件_第54张图片 小细节 这个插件比较方便的是会定时保存数据以及在退出 burp 时保存数据。 定时保存数据使用 ScheduledExecutorService 对象定时执行任务:
service = Executors.newSingleThreadScheduledExecutor();service.scheduleWithFixedDelay(new Runnable() {@Overridepublic void run() {        BurpExtender.this.saveData();        callbacks.printOutput("Scheduled export execution completed");    }}, 0, 10, TimeUnit.MINUTES);
退出 burp 时保存数据需要实现 IExtensionStateListener,  并在  extensionUnloaded() 方法中保存数据 burpsuite字典_从头开发一个BurpSuite数据收集插件_第55张图片 插件的开发到这里就完成了,具体的实现可以下载源码来查看,喜欢我的文章的可以关注下我的公众号,以后可能会继续发些工具开发的文章。 https://github.com/QdghJ/burp_data_collector 这个插件的开发要感谢我的基友,让我的数据库性能得到了极大的提升。
往期文章python协程学习——写个并发获取网站标题的工具一些相见恨晚的BurpSuite插件推荐

	

你可能感兴趣的:(burpsuite字典_从头开发一个BurpSuite数据收集插件)