扩展BurpSuite——MontoyaAPI实战(第三篇):WebSocket消息处理

admin 2026-05-16 06:51:25 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细介绍了使用MontoyaAPI开发BurpSuite扩展处理WebSocket消息的方法,通过Flask-SocketIO示例演示了自动重新生成SHA-256哈希的插件开发流程。文章涵盖WebSocket协议特点、测试环境搭建、双重监听器注册机制以及消息篡改的具体实现,为渗透测试中WebSocket安全检测提供完整技术方案。 综合评分: 85 文章分类: WEB安全,安全工具,渗透测试,红队,安全开发


cover_image

扩展Burp Suite——Montoya API实战(第三篇):WebSocket消息处理

幻泉之洲

2026年5月8日 14:56 北京

在小说阅读器读本章

去阅读

本文介绍了如何使用Montoya API开发Burp Suite扩展,实现对WebSocket消息的检查和篡改,通过一个实际案例演示了自动重新生成SHA-256哈希的插件开发过程。

这是系列教程的第三篇。之前我们学了怎么搞HTTP处理器——渗透测试里最常见的Burp扩展。现在该看看WebSocket了。

WebSocket:以前是痛点,现在有解了

WebSocket是个有状态的全双工协议,现代浏览器都支持,一般用在需要实时更新的应用里。几年前Burp Suite对WebSocket的支持很有限,我测试时得用OWASP ZAP来补位。后来Burp改进了,现在WebSocket在Proxy和Repeater里整合得不错。

说到扩展,WebSocket支持是Montoya API才引入的——这是大家选择新API的另一个理由,尽管它还不支持Python和Ruby。这些年我碰到过很多用WebSocket的应用,想找个现成的扩展经常找不到(HTTP的倒是很多),这确实是个痛点。现在好了,我们可以像处理HTTP那样检查和篡改WebSocket消息了。

测试环境搭起来

老规矩,先搞个测试场景。我改了一个Flask-SocketIO的示例,用它跑个简单的聊天服务器,通信走WebSocket。

改过的代码放在我的GitHub仓库里。改动包括:

  • 把异步模式设为eventlet(走WebSocket的那种),加了必要模块
  • 去掉了ping/pong的来回发送,减少消息量
  • 给“Echo”功能加了哈希校验:前端JS计算SHA-256,后端验证,不对就返回错误

后端的核心逻辑就这几行:

@socketio.event def my_event(message):     session[‘receive_count’] = session.get(‘receive_count’, 0) + 1     if ‘hash’ in message:         calculated_hash = sha256(message[‘data’].encode(‘utf-8’)).hexdigest()         if(message[‘hash’] == calculated_hash):             emit(‘my_response’, {‘data’: message[‘data’], ‘count’: session[‘receive_count’]})         else:             emit(‘my_response’, {‘data’: ‘INVALID SIGNATURE’, ‘count’: session[‘receive_count’]})     else:         session[‘receive_count’] = session.get(‘receive_count’, 0) + 1         emit(‘my_response’, {‘data’: message[‘data’], ‘count’: session[‘receive_count’]})

跑起来很简单:

  1. (可选)创建并激活Python虚拟环境
  2. 安装依赖:pip install -r requirements.txt
  3. 启动:python app.py
  4. 访问http://localhost:5000/

在网页上发条消息试试:

Burp里可以看到浏览器发送了包含“test”字符串及其SHA256哈希的消息,也收到了同样的字符串回复。注意:WebSocket没有请求-响应的对应关系,只有双方互发的消息。

把消息发给Repeater,改了data字段的值但不重算签名,后端就会返回“INVALID SIGNATURE”。

好,场景有了。现在我们来写个Burp扩展,功能类似上一篇的HTTP处理器——自动重新生成所有从浏览器发往后端的WebSocket消息的哈希。这样就能拦截篡改消息,用Repeater时也不用手动算哈希了。

开发WebSocket扩展:两个监听器

从第一部分的Hello World项目起步,这次要用到MontoyaApi接口里的WebSocket相关方法。

像之前一样,我们需要注册一个监听器,等特定事件(这里是WebSocket创建)发生时触发。

WebSocket的处理比HTTP稍微复杂一点,需要注册两个监听器。HTTP场景下我们只有一个监听器,在请求/响应进出Burp时被通知。WebSocket不行,得先设一个监听器,等某个工具创建WebSocket时(通过特殊的HTTP Upgrade请求,详情看维基百科)被调用。当WebSocket创建后,我们的监听器拿到参数,再注册第二个监听器处理这条连接上的消息。

先搞第一个监听器。实现WebSocketCreatedHandler接口:

import burp.api.montoya.MontoyaApi; import burp.api.montoya.logging.Logging; import burp.api.montoya.websocket.WebSocketCreated;

public class CustomWebsocketCreatedHandler implements WebSocketCreatedHandler {     MontoyaApi api;     Logging logging;

    public CustomWebsocketCreatedHandler(MontoyaApi api) {         this.api = api;         this.logging = api.logging();     }

    @Override     public void handleWebSocketCreated(WebSocketCreated webSocketCreated) {         // 在这里做点啥     } }

传给handleWebSocketCreatedWebSocketCreated对象提供了三个方法:

  • toolSource()

    :哪个工具创建的WebSocket

  • upgradeRequest()

    :对应的Upgrade HTTP请求

  • webSocket()

    :获取实际的WebSocket引用,我们需要它来注册消息监听器

webSocket()返回的对象有个注册监听器的方法,看文档:

现在要创建第二个监听器,实现MessageHandler接口。它有两个方法(第三个可选):处理二进制消息和处理文本消息。

import burp.api.montoya.MontoyaApi; import burp.api.montoya.logging.Logging; import burp.api.montoya.websocket.*;

public class CustomWebsocketHandler implements MessageHandler {     MontoyaApi api;     Logging logging;

    public CustomWebsocketHandler(MontoyaApi api) {         this.api = api;         this.logging = api.logging();     }

    @Override     public TextMessageAction handleTextMessage(TextMessage textMessage) {         return TextMessageAction.continueWith(textMessage);     }

    @Override     public BinaryMessageAction handleBinaryMessage(BinaryMessage binaryMessage) {         return BinaryMessageAction.continueWith(binaryMessage);     } }

这两个方法分别返回TextMessageActionBinaryMessageAction。看文档,有几个静态方法可以创建这些对象:

现在把第二个监听器注册到第一个监听器里:

public class CustomWebsocketCreatedHandler implements WebSocketCreatedHandler {     …     @Override     public void handleWebSocketCreated(WebSocketCreated webSocketCreated) {         WebSocket websocket = webSocketCreated.webSocket();         websocket.registerMessageHandler(new CustomWebsocketHandler(api));     } }

然后在主类的initialize方法里注册创建监听器:

public class WebsocketExample implements BurpExtension {     …     @Override     public void initialize(MontoyaApi api) {         …         api.websockets().registerWebSocketCreatedHandler(new CustomWebsocketCreatedHandler(api));     } }

骨架好了。先写点调试代码,打印经过的消息:

@Override public TextMessageAction handleTextMessage(TextMessage textMessage) {     String payload = textMessage.payload();     Direction direction = textMessage.direction();     if(direction == Direction.CLIENT_TO_SERVER) {         logging.logToOutput(“T ==========>>”);     } else {         logging.logToOutput(“T <<==========”);     }     logging.logToOutput(payload);     logging.logToOutput(“”);     return TextMessageAction.continueWith(textMessage); }

@Override public BinaryMessageAction handleBinaryMessage(BinaryMessage binaryMessage) {     ByteArray payload = binaryMessage.payload();     Direction direction = binaryMessage.direction();     if(direction == Direction.CLIENT_TO_SERVER) {         logging.logToOutput(“B ==========>>”);     } else {         logging.logToOutput(“B <<==========”);     }     logging.logToOutput(base64Utils.encodeToString(payload));     logging.logToOutput(“”);     return BinaryMessageAction.continueWith(binaryMessage); }

编译加载插件(参考第一部分的流程),刷新测试页面,点“Echo”功能,看看输出:

截图里能看到我们的消息(内容“aaaa”)、哈希,还有后端的回复。都是文本消息,所以我们只在handleTextMessage里搞哈希逻辑。二进制消息流程一样。

逻辑跟上一篇类似,不过这次用正则提取数据:

  1. 只处理包含“my_event”且方向为客户端到服务器的消息
  2. 用正则提取data和hash字段
  3. 用Burp的加密工具重新计算SHA-256
  4. 替换旧哈希
  5. 打印改动后的消息(后面会解释为啥要打印)
  6. 把修改后的消息发往后端

@Override public TextMessageAction handleTextMessage(TextMessage textMessage) {     String payload = textMessage.payload();     Direction direction = textMessage.direction();

    if(payload.contains(“my_event”) && direction == Direction.CLIENT_TO_SERVER) {         Pattern p = Pattern.compile(“.*\\”data\\”\\:.*\\”([^\\”]+)\\”.*\\”hash\\”\\:.*\\”([^\\”]+)\\””);         Matcher m = p.matcher(payload);         if(m.find() && m.groupCount() == 2) {             ByteArray sha256hash = cryptoUtils.generateDigest(ByteArray.byteArray(m.group(1)), DigestAlgorithm.SHA_256);             String digest = String.format(“%064x”, new BigInteger(1, sha256hash.getBytes()));             String newMessage = payload.replaceAll(m.group(2), digest);             logging.logToOutput(“* Message with updated hash:”);             logging.logToOutput(newMessage);             return TextMessageAction.continueWith(newMessage);         } else {             logging.logToOutput(“Data and hash not found. Returning original message.”);             return TextMessageAction.continueWith(textMessage);         }     }     return TextMessageAction.continueWith(textMessage); }

构建插件,刷新页面,发“aaaa”,把消息丢Repeater里,改成“bbbb”但不算哈希,看看后端认不认:

后端返回“bbbb”而不是“INVALID SIGNATURE”,说明插件生效了。

和HTTP一样,插件在消息离开发送前修改了哈希,所以Repeater里看到的还是原始“aaaa”的哈希。HTTP场景下可以用Logger看实际发送的请求,但WebSocket目前没有Logger。所以我们在第5步打印了修改后的消息。在stdout里能看到实际发往后端的消息(包括没篡改的“aaaa”,因为插件重新算了所有哈希):

好,这一集就到这里。现在我们的扩展可以检查和篡改WebSocket消息了。代码和改过的Flask示例都在我的GitHub仓库里。

下一集我们聊聊怎么添加请求/响应消息编辑器标签页,用来处理自定义编码或加密——这跟之前的方法不太一样,是我这些年经常用的一种扩展类型。

下次见!


参考资料

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


免责声明:

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

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

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

本文转载自:幻泉之洲 《扩展Burp Suite——Montoya API实战(第三篇):WebSocket消息处理》

评论:0   参与:  0