diff --git a/HaE/HaE.py b/HaE/HaE.py new file mode 100644 index 0000000..32322cb --- /dev/null +++ b/HaE/HaE.py @@ -0,0 +1,233 @@ +# -*- coding:utf-8 -*- +# Author: Vulkey_Chen +# Blog: gh0st.cn +# Team: MSTSEC + +import json, re, jsbeautifier + +from burp import IBurpExtender, ITab, IHttpListener, IMessageEditorTabFactory, IMessageEditorTab +from javax.swing import JPanel, JLabel, JButton, JTextArea, JTextField, JCheckBox, JTabbedPane, JScrollPane, SwingConstants +from java.awt import BorderLayout + +from java.io import PrintWriter + +# color list +colors = ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'pink', 'magenta', 'gray'] + +# config +configFile = "config.json" + +# 获取配置文件内容 +def getConfig(): + config = "" + with open(configFile, 'r') as content: + config = json.load(content) + return config + +# 寻找内容 +def findContent(info, message): + info = getConfig() + results = {} + for i in info: + regex = re.compile(info[i]['regex']) + regexRes = regex.findall(message) + if regexRes != []: + results[i] = ','.join(list(set(regexRes))) + return results + +class BurpExtender(IBurpExtender, ITab,IHttpListener, IMessageEditorTabFactory): + def registerExtenderCallbacks(self, callbacks): + self._callbacks = callbacks + self._helpers = callbacks.getHelpers() + callbacks.setExtensionName("HaE(Highlighter and Extractor)") + self._stdout = PrintWriter(callbacks.getStdout(), True) + callbacks.registerHttpListener(self) + callbacks.registerMessageEditorTabFactory(self) + print 'HaE(Highlighter and Extractor)\nAuthor: Vulkey_Chen\nBlog: gh0st.cn\nTeam: MSTSEC' + self._callbacks.customizeUiComponent(self.getUiComponent()) + self._callbacks.addSuiteTab(self) + self.endColors = [] + + def getTabCaption(self): + return 'HaE' + + def createNewInstance(self, controller, editable): + return MarkINFOTab(self, controller, editable) + + def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): + if messageIsRequest: + return + content = messageInfo.getResponse() + r = self._helpers.analyzeResponse(content) + msg = content[r.getBodyOffset():].tostring() + # msg 为响应正文信息 + info = getConfig() + results = findContent(info, msg) + colorList = [] + if results != {}: + for i in results: + if info[i]['highlight'] == 1 : + if info[i]['color'] == 'red': + messageInfo.setHighlight(info[i]['color']) + break + else: + colorList.append(info[i]['color']) + + if not messageInfo.getHighlight(): + colorsList = [colors.index(i) for i in colorList] + colorsList.sort() + # print(colorsList) + self.helper(colorsList) + endColor = [colors.index(x) for x in self.endColors] + # print(endColor) + messageInfo.setHighlight(colors[min(endColor)]) + + # 颜色升级 + def helper(self, mylist): + l = len(mylist) + i = 0 + stack = [] + while i < l: + if not stack: + stack.append(mylist[i]) + i += 1 + else: + if mylist[i] != stack[-1]: + stack.append(mylist[i]) + i += 1 + else: + stack[-1] -= 1 + i += 1 + + if len(stack) == len(set(stack)): + self.endColors = [colors[i] for i in stack] + else: + self.helper(stack) + + def addConfig(self, event): + nameText = self.nameTextField.getText() + regexText = self.regexTextField.getText() + colorText = self.colorTextField.getText() + isHighlight = int(self.highlightCheckBox.isSelected()) + isExtract = int(self.extractCheckBox.isSelected()) + if colorText in colors: + with open(configFile, 'r+') as content: + dicts = json.load(content) + # 解决r+写入问题 + content.seek(0,0) + content.truncate() + if nameText in dicts: + self.tipString.setText("Name is existed!") + elif not(isHighlight or isExtract): + self.tipString.setText("Highlight or Extract?") + else: + dicts[nameText] = {"regex": regexText, "highlight": isHighlight, "extract": isExtract, "color": colorText} + content.write(jsbeautifier.beautify(json.dumps(dicts))) + #print(dicts) + self.tipString.setText("Save Successfully!") + else: + self.tipString.setText("Not in colors list.") + + def reloadConfig(self, event): + with open(configFile, 'r') as content: + self.configTextArea.setText(content.read()) + + def getUiComponent(self): + self.HaEPanel = JPanel() + self.HaEPanel.setBorder(None) + self.HaEPanel.setLayout(BorderLayout(0, 0)) + self.panel = JPanel() + self.HaEPanel.add(self.panel, BorderLayout.NORTH) + self.panel.setLayout(BorderLayout(0, 0)) + self.tabbedPane = JTabbedPane(JTabbedPane.TOP) + self.panel.add(self.tabbedPane, BorderLayout.CENTER) + self.setPanel = JPanel() + self.tabbedPane.addTab("Set", None, self.setPanel, None) + self.setPanel.setLayout(BorderLayout(0, 0)) + self.setPanel_1 = JPanel() + self.setPanel.add(self.setPanel_1, BorderLayout.NORTH) + self.nameString = JLabel("Name") + self.setPanel_1.add(self.nameString) + self.nameTextField = JTextField() + self.setPanel_1.add(self.nameTextField) + self.nameTextField.setColumns(10) + self.regexString = JLabel("Regex") + self.setPanel_1.add(self.regexString) + self.regexTextField = JTextField() + self.setPanel_1.add(self.regexTextField) + self.regexTextField.setColumns(10) + self.extractCheckBox = JCheckBox("Extract") + self.setPanel_1.add(self.extractCheckBox) + self.highlightCheckBox = JCheckBox("Highlight") + self.setPanel_1.add(self.highlightCheckBox) + self.setPanel_2 = JPanel() + self.setPanel.add(self.setPanel_2) + self.colorString = JLabel("Color") + self.setPanel_2.add(self.colorString) + self.colorTextField = JTextField() + self.setPanel_2.add(self.colorTextField) + self.colorTextField.setColumns(5) + self.addBottun = JButton("Add", actionPerformed=self.addConfig) + self.setPanel_2.add(self.addBottun) + self.tipString = JLabel(""); + self.setPanel_2.add(self.tipString) + self.configPanel = JPanel() + self.tabbedPane.addTab("Config", None, self.configPanel, None) + self.configPanel.setLayout(BorderLayout(0, 0)) + self.configString = JLabel("This is config file content.") + self.configString.setHorizontalAlignment(SwingConstants.CENTER) + self.configPanel.add(self.configString, BorderLayout.NORTH) + self.configTextArea = JTextArea() + self.configTextArea.setEnabled(False) + self.configTextArea.setTabSize(4) + self.configTextArea.setLineWrap(True) + self.configTextArea.setRows(20) + self.configPanel.add(self.configTextArea, BorderLayout.SOUTH) + self.scrollPane = JScrollPane(self.configTextArea) + self.configPanel.add(self.scrollPane, BorderLayout.SOUTH) + self.reloadButton = JButton("Reload", actionPerformed=self.reloadConfig) + self.configPanel.add(self.reloadButton, BorderLayout.CENTER) + return self.HaEPanel + +class MarkINFOTab(IMessageEditorTab): + def __init__(self, extender, controller, editable): + self._extender = extender + self._helpers = extender._helpers + self._editable = editable + self._txtInput = extender._callbacks.createTextEditor() + self._txtInput.setEditable(editable) + + def getTabCaption(self): + return "MarkINFO" + + def getUiComponent(self): + return self._txtInput.getComponent() + + # 非响应 没有匹配到不返回Tab标签页 + def isEnabled(self, content, isRequest): + r = self._helpers.analyzeResponse(content) + msg = content[r.getBodyOffset():].tostring() + info = getConfig() + if not isRequest: + content = findContent(info, msg) + if content != {}: + for i in content: + if info[i]['extract'] == 1 : + return True + + # 设置Tab的内容 + def setMessage(self, content, isRequest): + # 判断是否有内容 + if content: + if not isRequest: + r = self._helpers.analyzeResponse(content) + msg = content[r.getBodyOffset():].tostring() + info = getConfig() + contents = findContent(info, msg) + result = "" + for i in contents: + if info[i]['extract'] == 1 : + result += "[{}] {}\n".format(i,contents[i]) + self._txtInput.setText(result) + else: + return False \ No newline at end of file diff --git a/HaE/config.json b/HaE/config.json new file mode 100644 index 0000000..c64f1ac --- /dev/null +++ b/HaE/config.json @@ -0,0 +1,26 @@ +{ + "Email": { + "color": "yellow", + "highlight": 1, + "regex": "[\\w-]+(?:\\.[\\w-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[\\w](?:[\\w-]*[\\w])?", + "extract": 1 + }, + "Chinese IDCard": { + "color": "orange", + "highlight": 1, + "regex": "[1-9]\\d{5}(?:19|20)\\d\\d(?:0[1-9]|1[012])(?:0[1-9]|[12]\\d|3[01])\\d{3}(?:\\d|X)", + "extract": 1 + }, + "Chinese Mobile": { + "color": "yellow", + "highlight": 1, + "regex": "[^0-9]+(1[3-9]\\d{9})[^0-9]+", + "extract": 1 + }, + "Internal IP Address": { + "color": "yellow", + "highlight": 1, + "regex": "(?:10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|(?:172\\.(?:(?:1[6-9])|(?:2\\d)|(?:3[01]))\\.\\d{1,3}\\.\\d{1,3})|(?:192\\.168\\.\\d{1,3}\\.\\d{1,3})", + "extract": 1 + } +} \ No newline at end of file diff --git a/README.md b/README.md index 48ed840..6b7c57b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,159 @@ -# HaE -HaE - BurpSuite Highlighter and Extractor +# HaE - 信息高亮与提取者 + +## 前言 + +HaE(Highlight and Extractor),是基于MarkINFO插件(地址:https://github.com/gh0stkey/BurpSuite-Extender-MarkInfo )的基础进行重构。 + +用处: +- 高亮标记请求,针对高亮的请求进行深度挖掘 +- 敏感信息泄露发现 + +## 设计想法 + +语言:Python + +![-w902](images/15845160387850.jpg) + +功能: +- 自定义正则 +- 自定义高亮颜色 +- 自定义高亮或提取 + +## 设计过程 + +### 可视化界面 + +UI设计(基于Eclipse可视化设计),基于Java Swing + +![-w1143](images/15845162741518.jpg) + +然后将Java代码转换为Python代码即可(**有很多坑~**) + +使用BurpSuite接口:`ITab`创建Tab + +![-w1276](images/15845160114052.jpg) + +### 高亮颜色 + +将BurpSuite的所有高亮颜色集成:(仅支持:`red, orange, yellow, green, cyan, blue, pink, magenta, gray`) + +![-w96](images/15844769689459.jpg) + +![-w518](images/15845164967615.jpg) + +### 配置文件格式 + +选用JSON格式,格式为 + +``` +name: {"regex": regexText, "highlight": isHighlight, "extract": isExtract, "color": colorText} +``` + +### 颜色优先级和升级 + +定义Colors变量: + +`colors = ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'pink', 'magenta', 'gray']` + +利用下标的方式进行优先级排序,当满足2个同颜色条件则以优先级顺序上升颜色。(例如:**两个正则,颜色为橘黄色,该请求两个正则都匹配到了,那么将升级为红色**) + +## 使用方法 + +贴一些案例,仅供参考,还有更多玩法,可以自我琢磨~ + +### 环境设置 + +进入Extender - Options - Python Environment + +![-w840](images/15845168078333.jpg) + +载入Jython的Jar包以及载入python的包路径。 + +加载插件,选择HaE.py文件: + +![-w858](images/15845168915243.jpg) + +加载成功: + +![-w743](images/15845169108559.jpg) + + +### RUN IT + +#### 添加自定义正则 + +``` +名字:Email + +正则:[\\w-]+(?:\\.[\\w-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[\\w](?:[\\w-]*[\\w])? + +高亮颜色:red + +是否高亮和提取:是 +``` + +转到HaE标签页,进行设置,点击Add按钮即可添加 + +![-w1278](images/15845171413470.jpg) + +HaE - Config查看是否进行配置,点击Reload按钮: + +![-w1277](images/15845172203307.jpg) + +#### 高亮请求 + +在Proxy - HTTP History中可以看见高亮请求,响应标签页中含有`MarkINFO`标签,其中将匹配到的邮箱提取了出来 + +![-w1268](images/15845175423506.jpg) + +#### 正则优化(参考Demo) + +在正则匹配手机号、身份证号码的时候(纯数字类)会存在一些误报(这里匹配身份证号码无法进行校验,误报率很高),但手机号处理这一块可以解决: + +原正则: + +``` +(1[3-9]\d{9}) +``` + +误报场景:`12315188888888123`,这时候会匹配到`15188888888`,而实际上这一段并不是手机号,所以完全可以修改正则为: + +``` +[^0-9]+(1[3-9]\d{9})[^0-9]+ +``` + +也就是要求匹配的手机号前后不能为0-9的数字。 + +## 实战用法 + +### CMS指纹识别 + +例如:识别Discuz,名字:CMS-Discuz,正则:`Powered by Discuz!`,高亮颜色:blue + +![-w1272](images/15845485039330.jpg) + +Add保存到配置文件中: + +![-w294](images/15845485770190.jpg) + +请求识别: + +![-w1037](images/15845486471590.jpg) + +### OSS对象存储信息泄露 + +名字:INFO-OSS + +正则:`[A|a]ccess[K|k]ey[I|i]d|[A|a]ccess[K|k]ey[S|s]ecret` + +高亮颜色:cyan + +![-w1002](images/15845490590739.jpg) + +Add保存到配置文件中: + +![-w482](images/15845490802944.jpg) + +请求中识别并提取: + +![-w1278](images/15845492243895.jpg) diff --git a/images/15844769689459.jpg b/images/15844769689459.jpg new file mode 100644 index 0000000..924906b Binary files /dev/null and b/images/15844769689459.jpg differ diff --git a/images/15845160114052.jpg b/images/15845160114052.jpg new file mode 100644 index 0000000..762fe8b Binary files /dev/null and b/images/15845160114052.jpg differ diff --git a/images/15845160387850.jpg b/images/15845160387850.jpg new file mode 100644 index 0000000..8cff33f Binary files /dev/null and b/images/15845160387850.jpg differ diff --git a/images/15845162741518.jpg b/images/15845162741518.jpg new file mode 100644 index 0000000..4e3344b Binary files /dev/null and b/images/15845162741518.jpg differ diff --git a/images/15845164967615.jpg b/images/15845164967615.jpg new file mode 100644 index 0000000..35af662 Binary files /dev/null and b/images/15845164967615.jpg differ diff --git a/images/15845168078333.jpg b/images/15845168078333.jpg new file mode 100644 index 0000000..1a971f9 Binary files /dev/null and b/images/15845168078333.jpg differ diff --git a/images/15845168915243.jpg b/images/15845168915243.jpg new file mode 100644 index 0000000..e1ffeae Binary files /dev/null and b/images/15845168915243.jpg differ diff --git a/images/15845169108559.jpg b/images/15845169108559.jpg new file mode 100644 index 0000000..4355b17 Binary files /dev/null and b/images/15845169108559.jpg differ diff --git a/images/15845171413470.jpg b/images/15845171413470.jpg new file mode 100644 index 0000000..8bb6771 Binary files /dev/null and b/images/15845171413470.jpg differ diff --git a/images/15845172203307.jpg b/images/15845172203307.jpg new file mode 100644 index 0000000..89e2d08 Binary files /dev/null and b/images/15845172203307.jpg differ diff --git a/images/15845175423506.jpg b/images/15845175423506.jpg new file mode 100644 index 0000000..3553dbc Binary files /dev/null and b/images/15845175423506.jpg differ diff --git a/images/15845485039330.jpg b/images/15845485039330.jpg new file mode 100644 index 0000000..d921098 Binary files /dev/null and b/images/15845485039330.jpg differ diff --git a/images/15845485770190.jpg b/images/15845485770190.jpg new file mode 100644 index 0000000..8c3ca7e Binary files /dev/null and b/images/15845485770190.jpg differ diff --git a/images/15845486471590.jpg b/images/15845486471590.jpg new file mode 100644 index 0000000..663d29e Binary files /dev/null and b/images/15845486471590.jpg differ diff --git a/images/15845490590739.jpg b/images/15845490590739.jpg new file mode 100644 index 0000000..390df7a Binary files /dev/null and b/images/15845490590739.jpg differ diff --git a/images/15845490802944.jpg b/images/15845490802944.jpg new file mode 100644 index 0000000..8856e5d Binary files /dev/null and b/images/15845490802944.jpg differ diff --git a/images/15845492243895.jpg b/images/15845492243895.jpg new file mode 100644 index 0000000..3b630dd Binary files /dev/null and b/images/15845492243895.jpg differ