一次耗时的安全测试

简介

接到一个安全测试任务,数据包使用安全控件进行了加密。通过开发插件,实现明文测试,最终发现了2个越权。

加解密过程分析

  1. 访问网站首页,需要先安装一个控件。安装完成后,访问网站发现数据包加密处理。
  2. 按照以前的测试思路,先打开发浏览器开发工具,根据关键字“encrypt”进行搜索,搜索得的到的结果非常多,不知道从何分析起。
  3. 无意间查看开发工具的网络请求模块,发现请求127.0.0.1的流量中,有“encrypt”、“decrypt”等关键字(如果Burp 原本抓不到 127.0.0.1 的流量,可在浏览器代理插件的不代理地址中添加 “<-loopback>”)。

分析encrypt接口发现该接口是加密使用的,其请求body是明文数据(json格式)字符串,返回body是对应的密文字符串及hash值。分析decrypt接口发现该接口是解密使用的,其请求body是密文字符串,返回body是对应的明文字符串值。整体过程如下图

插件开发

已经知道了加密、解密过程。为了实现明文测试,需要开发插件支持,插件需要完成以下功能:

  1. 针对请求体/返回体的密文,对密文进行解密(得到明文,方便repeater模块修改明文)。
  2. 针对repeater模块的明文请求,自动完成加密,然后发送请求数据包。

整体架构如下:

# -*- coding: UTF-8 -*-
from burp import IBurpExtender, IMessageEditorTabFactory, IMessageEditorTab, IProxyListener, IHttpListener
from java.io import PrintWriter
from javax.swing import JPanel, JScrollPane, JTextArea, SwingUtilities
from java.awt import BorderLayout
import json
import traceback


class BurpExtender(IBurpExtender, IMessageEditorTabFactory, IProxyListener, IHttpListener):
    def registerExtenderCallbacks(self, callbacks):
        # 注册回调和帮助器对象
        self.callbacks = callbacks
        self.helpers = callbacks.getHelpers()
        self.stdout = PrintWriter(callbacks.getStdout(), True)

        # 设置扩展名称并注册消息编辑器标签工厂
        callbacks.setExtensionName("Encrypt And Decryption")
        callbacks.registerMessageEditorTabFactory(self)

        # 注册代理监听器
        callbacks.registerProxyListener(self)

        self.stdout.println("Encrypt And Decryption")

    def createNewInstance(self, controller, editable):
        # 创建自定义标签的新实例
        return CustomTab(self, controller, editable)

class CustomTab(IMessageEditorTab):
    def __init__(self, extender, controller, editable):
        # 初始化自定义标签
        self.extender = extender
        self.controller = controller
        self.editable = editable

        self.tabPanel = JPanel(BorderLayout())

        # 使用JTextArea实现自动换行
        self.textArea = JTextArea()
        self.textArea.setLineWrap(True)
        self.textArea.setWrapStyleWord(True)
        self.textArea.setEditable(editable)

        scrollPane = JScrollPane(self.textArea)
        self.tabPanel.add(scrollPane, BorderLayout.CENTER)

        self.currentMessage = None
        self.isRequest = False
        self.cache = {}
        self.originalText = ""

    def getTabCaption(self):
        # 返回标签的标题
        return "Encrypt And Decryption"

    def getUiComponent(self):
        # 返回UI组件
        return self.tabPanel

    def isEnabled(self, content, isRequest):
        # 确定标签是否应启用
        self.isRequest = isRequest
        return True

    def setMessage(self, content, isRequest):
        # 设置当前消息,并根据请求/响应进行解密
        self.currentMessage = content
        if content is None:
            self.textArea.setText("")
        else:
            try:
                if isRequest:
                    # 处理请求体解密
                    info = self.extender.helpers.analyzeRequest(content)
                    body = content[info.getBodyOffset():]
                    body_string = body.tostring().decode('utf-8', errors='replace')
                    jsonBody = json.loads(body_string)
                    # 获取密文字符串
                    enStr = jsonBody["enStr"]

                    dencryptData = self.decrypt(enStr)
                    jsonBody["enStr"] = dencryptData
                    formattedBody = json.dumps(jsonBody, indent=4)
                    self.textArea.setText(formattedBody.encode("utf-8").decode("utf-8"))

                else:
                    # 处理响应体解密
                    info = self.extender.helpers.analyzeResponse(content)
                    body = content[info.getBodyOffset():]
                    body_string = body.tostring().decode('utf-8', errors='replace')
                    jsonBody = json.loads(body_string)

                    # 获取密文字符串
                    enStr = jsonBody["enStr"]

                    dencryptData = self.decrypt(enStr)
                    jsonBody["enStr"] = dencryptData
                    formattedBody = json.dumps(jsonBody, indent=4)
                    self.textArea.setText(formattedBody.encode("utf-8").decode("utf-8"))

            except Exception as e:
                # 处理解密过程中发生的错误
                self.extender.stdout.println("Error occurred: {} at line {}".format(str(e), traceback.format_exc()))
                self.extender.stdout.println("Traceback: {}".format(traceback.format_exc()))
                self.textArea.setText(body_string)
                self.originalText = body_string

    def getMessage(self):
        # 获取当前消息,如果已修改则更新消息内容
        if self.currentMessage is None:
            return None
        if self.textArea.isEditable() and self.isTextModified():
            if self.isRequest:
                info = self.extender.helpers.analyzeRequest(self.currentMessage)
            else:
                info = self.extender.helpers.analyzeResponse(self.currentMessage)
            headers = info.getHeaders()
            body = self.textArea.getText().encode('utf-8')
            self.currentMessage = self.extender.helpers.buildHttpMessage(headers, body)
        return self.currentMessage

    def isModified(self):
        # 判断文本是否已被修改
        return self.textArea.isEditable() and self.isTextModified()

    def getSelectedData(self):
        # 获取选中的文本
        return self.textArea.getSelectedText()

    def isTextModified(self):
        # 判断文本内容是否被修改
        return self.textArea.getText() != self.originalText

    def decrypt(self, enStr):
        '''
        解密函数
        :param enStr:
        :return:
        '''
        pass

“针对repeater模块的明文请求,自动完成加密”使用burp开发时没有成功,没有找到原因。处于项目时间紧迫,使用了mitmproxy作为burp的上游代理,开发脚本,完成对明文加密。

from mitmproxy import http
import json


def request(flow: http.HTTPFlow) -> None:

    if (flow.request.host == "targetHost") and not checkSuffix(flow.request.path) and "targetPath" in flow.request.path and flow.request.method == "POST":
        if "application/json" in flow.request.headers.get("content-type"):
            try:
                bodyJson = flow.request.json()
                # 如果是请求是明文进行加密操作
                if ":" in bodyJson["enStr"]:
                    bodyJson["enStr"] = encrypt(bodyJson["enStr"])
                    # 发送密文请求到目标服务器
                    flow.request.set_text(json.dumps(bodyJson))
            except Exception as e:
                print("error", str(e))


def checkSuffix(url):
    extension = url.split('.')[-1].lower()
    return extension in ['js', 'png', 'jpg', 'jpeg']


def encrypt(plan_txt):
    '''
    加密方法
    :param plan_txt:
    :return:
    '''
    pass

测试结果

通过以上插件,修改数据包的参数发现越权2个(常规思路)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值