Pentesting Meteor Applications with Burp Suite

AttachmentSize
Plain text icon Websocket proxy script3.92 KB

The following post will cover some techniques to test Meteor applications with Burp Suite. This can also be applied to other protocols that run over WebSockets.
To test this yourself, the example Meteor application “Todos” can be downloaded here.

Test Fallback to XHR

Meteor applications use Distributed Data Protocol (DDP) over a WebSocket connection. Websockets have limited support by Burp. We can view, intercept and modify the WebSocket messages in Burp, but we cannot use modules like Repeater, Intruder or Active Scanner. Burp extensions also cannot easily access WebSocket traffic, so writing a custom extension to deal with this is not an option.
A lot of applications that use WebSockets allow a fallback to XMLHttpRequest (XHR) when WebSockets are not supported. So this should be your first test. If a fallback is supported, it makes life easier since we can just run active scanner, do all authorization and business logic tests via XHR which is easy to do with Burp.
Testing for fallback is pretty easy, the following methods can be used:

  • disable WebSockets in the browser: never seemed to work in my case, despite changing multiple settings in the about:config in both FireFox and Chrome
  • tamper with the WebSocket upgrade response: When initializing a WebSocket connection, the browser will send an upgrade request. The server will respond with an “HTTP/1.1 101 Switching Protocols” header. Using Burp, you can use a match and replace rule in the proxy settings to change the response to a HTTP 500. This will trick the client into believing that the WebSocket connection is not supported and force it to fall back to XHR.
  • tamper with JavaScript code or other requests: When looking at the JavaScript code, a lot of the times a check can be identified, for example websocket=true or something similar. Tampering with his can force a fallback to XHR.

Sending DDP WebSocket Traffic to Burp Suite

In case no fallback to XHR can be forced, we can create one ourselves.
To send the DDP traffic to Burp, we can hook the JavaScript function that is used by Meteor. This is based on a technique which is described in the following post.
In our setup, we send the DDP message as the body of an XHR request to our Burp instance. We can do this in the browser console, but this means it will need to be done for every page reload. An easier way is to use a Tampermonkey script. An additional advantage of this is that we can use the GM_xmlhttpRequest function which allows cross-domain XHR requests.
We can use the following Tampermonkey script to do this for all requests.

// ==UserScript==
// @name         SendDDPToBurp
// @namespace    sdr
// @version      0.2
// @description  Send Meteor DDP traffic to Burp
// @author       sean@gremwell.com
// @match        http://*/*
// @match        https://*/*
// @require      https://code.jquery.com/jquery-3.1.1.min.js
// @require      https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js
// @connect      *
// @grant        GM_xmlhttpRequest
// ==/UserScript==
$(document).ready(function() {
  self.Meteor = Package.meteor.Meteor;
  var oldSend = Meteor.connection._stream.send;
  self.Meteor.connection._stream.send = function() {
      var msg = arguments[0];
      console.log("DDPLog:"+msg);
      GM_xmlhttpRequest({
      method: "POST",
      url: "http://localhost:9999/",
      data: msg
      });
  oldSend.apply(this, arguments);
  };
});

The script will send all outgoing DDP requests to our Burp instance in the body of an XHR request. The script will also send it to the original endpoint via WebSockets, so a response can be received from the server.
This allows us to log everything in Burp where we can easily use Repeater, Intruder and Active Scanner on these payloads.

Proxying XHR back to WebSocket

From Burp, we can then configure an outgoing connection to our proxy script. This script will start a HTTP server to listen for incoming connections from Burp. It will also establish a WebSocket connection to the target. It will link the two together and send data from incoming XHR requests as WebSocket messages to the target, and return the response over HTTP back to Burp.
An example script can be found as an attachment to this post.
To connect to our script, we use the “Request Handling tab” in Burp. We listen on port 9999 and redirect all traffic to port 8888 on localhost, where our script is listening.