扩展BurpSuite:用MontoyaAPI给你的工具加个”解密”标签(第四部分)

admin 2026-05-14 13:16:41 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细介绍了如何使用BurpSuiteMontoyaAPI创建自定义选项卡来处理AES加密的HTTP请求和响应。通过具体移动应用测试场景,演示了实现HttpRequestEditor/HttpResponseEditor插件的完整流程,包括解密显示、编辑后自动重新加密等功能。文章提供了完整的Flask后端示例和Java插件代码实现,帮助安全测试人员高效处理加密流量。 综合评分: 85 文章分类: WEB安全,安全工具,应用安全,移动安全,安全开发


cover_image

扩展Burp Suite:用Montoya API给你的工具加个”解密”标签(第四部分)

幻泉之洲

2026年5月9日 10:02 北京

在小说阅读器读本章

去阅读

本文教你用Burp Suite Montoya API创建自定义选项卡,一键解密HTTP请求和响应。通过一个AES加密的移动应用场景,手把手实现HttpRequestEditor/HttpResponseEditor插件,让安全测试不用每次手动加解密。

Hi,各位。

今天聊怎么给Burp Suite界面加自定义组件,方便处理各种加密场景。重点放在创建新选项卡,用来处理HTTP请求和响应。

老规矩,先讲一个具体场景。假设你在测一个移动应用,它对HTTP请求体和响应体都做了AES加密。客户端发请求前用AES加密体,收到响应后用同样方式解密——后端也是这么干的。移动端常见这情况,但Web端也有,浏览器用JavaScript库做加解密。

我给你写个简单的Flask Python应用,演示这个过程(加解密代码直接抄的StackOverflow,需要的包:flask和pycryptodome):

import flask from flask import request import base64 from Crypto import Random from Crypto.Cipher import AES

app = flask.Flask(__name__)

bs = AES.block_size key = bytes.fromhex(“eeb27c55483270a92682dab01b85fdea”) iv = bytes.fromhex(“ecbc1312cfdc2a0e1027b1eaf577dce8”)

def encrypt(raw):     raw = _pad(raw)     cipher = AES.new(key, AES.MODE_CBC, iv)     return base64.b64encode(cipher.encrypt(raw.encode()))

def decrypt(enc):     enc = base64.b64decode(enc)     cipher = AES.new(key, AES.MODE_CBC, iv)     return _unpad(cipher.decrypt(enc)).decode(‘utf-8’)

def _pad(s):     return s + (bs – len(s) % bs) * chr(bs – len(s) % bs)

def _unpad(s):     return s[:-ord(s[len(s)-1:])]

@app.route(‘/’, methods=[‘POST’]) def handle_request():     encrypted_body = request.get_data()     decrypted_body = decrypt(encrypted_body)     response = “Your request was: \”” + decrypted_body + “\””     encrypted_response = encrypt(response)     return encrypted_response

app.run(host=”127.0.0.1″, port=5000, debug=True)

这个应用接收加密的请求体(AES/CBC,固定Key和IV,Base64编码),返回同样加密的响应。所以请求和响应都是密文,没Burp插件根本没法测。

懒得写个移动应用demo,直接在Burp Repeater里伪造一个HTTP请求。用CyberChef加密请求体,再解密响应。等插件写好,这些脏活就交给插件了。

先用固定Key和IV加密一个测试句子:

然后通过Burp Repeater发给后端:

再用CyberChef解密响应:

好,后端代码没毛病。

现在想想怎么解决问题。目标是方便地分析应用,不用每次输完payload手动加密请求、解密响应看攻击结果。

一个思路是实现HttpHandler插件(参考本系列第二部分),透明地解密进入Burp的请求,再自动重新加密发送出去。这样Burp里看到的流量就像没加密一样,好处是Scanner也能无缝工作。但我通常只在Scanner和Intruder用这种插件,Proxy和Repeater不用。为啥?我想保留原始流量记录,而不是经过插件处理后的,方便后续回看。

下面说两种替代方案:用HttpRequestEditor/HttpResponseEditor插件(我常用的),和ContextMenuItem插件(灵活性差点,但适合请求格式变化大的场景)。这两种都要从UserInterface对象注册,UserInterface从MontoyaApi拿到。

这篇文章重点讲HttpRequestEditor/HttpResponseEditor。ContextMenuItem留到下一篇。

这类插件可以在Burp显示请求/响应的区域加一个选项卡。点一下,插件就解密请求并展示解密版本(响应同理)。要是在支持修改的工具里(比如Repeater或Intercept),你还能修改解密后的内容,插件自动重新加密再发出去。这样既保留了原始流量,又像没有加密层一样工作。实际效果长这样:

从第一部分的Hello World框架开始:

package org.fd.montoyatutorial;

import burp.api.montoya.BurpExtension; import burp.api.montoya.MontoyaApi; import burp.api.montoya.logging.Logging;

public class HttpRequestResponseEditorExample implements BurpExtension {

    MontoyaApi api;     Logging logging;

    @Override     public void initialize(MontoyaApi api) {         this.api = api;         this.logging = api.logging();         api.extension().setName(“Montoya API tutorial – HttpRequestResponseEditorExample”);         this.logging.logToOutput(“*** Montoya API tutorial – HttpRequestResponseEditorExample loaded ***”);         // TODO – 注册监听器     } }

扩展需要注册两个监听器,一个处理请求,一个处理响应。看文档:

每个监听器需要实现特定接口的对象:

两个接口都很简单,每个只需要一个方法。我打算用一个Java类同时实现两个接口,当然你也可以分开写。

package org.fd.montoyatutorial;

import burp.api.montoya.MontoyaApi; import burp.api.montoya.ui.editor.extension.*;

public class CustomHttpRequestResponseEditor implements HttpRequestEditorProvider, HttpResponseEditorProvider {

    MontoyaApi api;

    public CustomHttpRequestResponseEditor(MontoyaApi api) {         this.api = api;     }

    @Override     public ExtensionProvidedHttpRequestEditor provideHttpRequestEditor(EditorCreationContext creationContext) {         // TODO     }

    @Override     public ExtensionProvidedHttpResponseEditor provideHttpResponseEditor(EditorCreationContext creationContext) {         // TODO     } }

这两个方法分别返回ExtensionProvidedHttpRequestEditor和ExtensionProvidedHttpResponseEditor对象。看名字你就知道,它们负责创建和返回一个图形选项卡。好消息是Burp API提供了创建选项卡的方法,不用自己跟Java图形库死磕(谢天谢地)。我们只需要用API创建选项卡,在里面实现加解密逻辑。

回到provideHttpRequestEditor和provideHttpResponseEditor,它俩的参数都是EditorCreationContext,包含当前请求/响应的上下文信息(哪个Burp工具生成的,是否可编辑):

下面看怎么创建ExtensionProvidedHttpRequestEditor对象(只讲请求部分,响应完全一样,代码在GitHub仓库里都有)。

需要实现一个接口,定义以下方法:

  • caption:返回自定义选项卡的名字(示例里叫”Decrypted”)。
  • isEnabledFor:根据当前请求和上下文决定是否显示这个选项卡。
  • uiComponent:返回实际的UI组件。不用写Swing代码,Burp有现成方法生成选项卡。
  • setRequestResponse:在这里生成选项卡的内容(比如解密请求体放进去)。
  • isModified:用户有没有修改过选项卡内容。
  • getRequest:当用户离开自定义选项卡回到默认选项卡,或者发送请求时调用。如果用户修改了内容,在这里把编辑后的内容加密,构建新请求。
  • selectedData:返回用户在选项卡中选中的数据(如果有)。Burp API生成的选项卡自带方法处理这个,我们这个插件不需要让用户选中部分请求。

先写个骨架,把简单方法实现,然后处理setRequestResponse和getRequest——这里有加解密逻辑。

先写构造函数:

public class CustomHttpRequestEditorTab implements ExtensionProvidedHttpRequestEditor {

    static String keyHex = “eeb27c55483270a92682dab01b85fdea”;     static String ivHex = “ecbc1312cfdc2a0e1027b1eaf577dce8”;

    MontoyaApi api;     Logging logging;     EditorCreationContext creationContext;     RawEditor requestEditorTab;     Base64Utils base64Utils;

    public CustomHttpRequestEditorTab(MontoyaApi api, EditorCreationContext creationContext) {         this.api = api;         this.creationContext = creationContext;         this.logging = api.logging();         this.base64Utils = api.utilities().base64Utils();         if (creationContext.editorMode() == EditorMode.READ_ONLY) {             requestEditorTab = api.userInterface().createRawEditor(EditorOptions.READ_ONLY);         } else {             requestEditorTab = api.userInterface().createRawEditor();         }     }     […]

构造函数传入MontoyaApi和上下文,保存一些工具对象。然后创建RawEditor对象——这就是我们的图形选项卡。根据当前请求是否只读(比如History里只读,Repeater或Intercept里可读写),创建只读或可读写编辑器。RawEditor提供了很多方法,后面会用到。

现在实现CustomHttpRequestEditorTab的其他方法:

[…] @Override public boolean isEnabledFor(HttpRequestResponse requestResponse) {     // 始终启用     return true; }

@Override public String caption() {     return “Decrypted”; }

@Override public Component uiComponent() {     return requestEditorTab.uiComponent(); }

@Override public Selection selectedData() {     if(requestEditorTab.selection().isPresent()) {         return requestEditorTab.selection().get();     } else {         return null;     } }

@Override public boolean isModified() {     return requestEditorTab.isModified(); } […]

这些方法都很简单,因为RawEditor已经实现了底层逻辑。

下面实现setRequestResponse,负责解密内容并放进选项卡:

static String keyHex = “eeb27c55483270a92682dab01b85fdea”; static String ivHex = “ecbc1312cfdc2a0e1027b1eaf577dce8”; HttpRequestResponse currentRequestResponse; […] @Override public void setRequestResponse(HttpRequestResponse requestResponse) {     HttpRequest request = requestResponse.request();     ByteArray body = request.body();     // Base64解码     ByteArray decodedBody = this.base64Utils.decode(body);     this.currentRequestResponse = requestResponse;     try {         byte[] iv = HexFormat.of().parseHex(this.ivHex);         IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);         byte[] key = HexFormat.of().parseHex(this.keyHex);         SecretKey SecKey = new SecretKeySpec(key, 0, key.length, “AES”);         Cipher aesCipher = Cipher.getInstance(“AES/CBC/PKCS5Padding”);         aesCipher.init(Cipher.DECRYPT_MODE, SecKey, ivParameterSpec);         byte[] decryptedBody = aesCipher.doFinal(decodedBody.getBytes());         this.requestEditorTab.setContents(ByteArray.byteArray(decryptedBody));     } catch (Exception e) {         this.logging.logToError(e);     } } […]

方法很简单:取出请求体,Base64解码,再用固定Key和IV解密,最后把结果set到选项卡里。同时保存HttpRequestResponse对象,后面修改时需要用它重建请求。

最后是getRequest,用户修改解密内容后,负责重新加密并构建新请求:

[…] @Override public HttpRequest getRequest() {     if(isModified()) {         ByteArray newBody = requestEditorTab.getContents();         try {             byte[] iv = HexFormat.of().parseHex(this.ivHex);             IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);             byte[] key = HexFormat.of().parseHex(this.keyHex);             SecretKey SecKey = new SecretKeySpec(key, 0, key.length, “AES”);             Cipher aesCipher = Cipher.getInstance(“AES/CBC/PKCS5Padding”);             aesCipher.init(Cipher.ENCRYPT_MODE, SecKey, ivParameterSpec);             byte[] encryptedBody = aesCipher.doFinal(newBody.getBytes());             ByteArray encodedBody = this.base64Utils.encode(ByteArray.byteArray(encryptedBody));             HttpRequest oldRequest = this.currentRequestResponse.request();             HttpRequest newRequest = oldRequest.withBody(encodedBody);             return newRequest;         } catch (Exception e) {             this.logging.logToError(e);             return this.currentRequestResponse.request();         }     } else {         return this.currentRequestResponse.request();     } } […]

代码跟之前类似,方向相反:取出修改后的内容,加密并Base64编码,替换到原始请求体里。

响应的CustomHttpResponseEditorTab几乎一模一样,就不贴代码了,GitHub仓库里有完整版。唯一的区别是我用了拷贝粘贴来保持示例清晰,实际写代码时最好避免重复。

最后,把前面写的类注册到插件里:

public class CustomHttpRequestResponseEditor implements HttpRequestEditorProvider, HttpResponseEditorProvider {     MontoyaApi api;     public CustomHttpRequestResponseEditor(MontoyaApi api) {         this.api = api;     }     @Override     public ExtensionProvidedHttpRequestEditor provideHttpRequestEditor(EditorCreationContext creationContext) {         return new CustomHttpRequestEditorTab(api, creationContext);     }     @Override     public ExtensionProvidedHttpResponseEditor provideHttpResponseEditor(EditorCreationContext creationContext) {         return new CustomHttpResponseEditorTab(api, creationContext);     } }

public class HttpRequestResponseEditorExample implements BurpExtension {     […]     @Override     public void initialize(MontoyaApi api) {         […]         CustomHttpRequestResponseEditor customHttpRequestResponseEditor = new CustomHttpRequestResponseEditor(api);         api.userInterface().registerHttpRequestEditorProvider(customHttpRequestResponseEditor);         api.userInterface().registerHttpResponseEditorProvider(customHttpResponseEditorProvider);     } }

搞定!编译运行插件,在请求和响应里都会多一个”Decrypted”选项卡,点开就能看到解密后的内容:

你可以在里面修改解密后的内容,然后点击”Send”或者切回Raw选项卡,插件会自动帮你重新加密:

这样,手动测试时既能保留原始请求/响应记录,又能像没加密一样工作。再加上HttpListener插件处理Scanner和Intruder,完美。

下一章咱们用ContextMenuItem插件来处理同样的场景,换个玩法。


参考资料

[1] https://hnsecurity.it/blog/extending-burp-suite-for-fun-and-profit-the-montoya-way-part-4/


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:幻泉之洲 《扩展Burp Suite:用Montoya API给你的工具加个”解密”标签(第四部分)》

评论:0   参与:  0