Burpscript adds dynamic scripting abilities to Burp Suite, allowing you to write scripts in Python or Javascript to manipulate HTTP requests and responses.
Features:
- Python 3 and JavaScript support
- Manipulate requests and responses from the Proxy or other tools such as the Repeater
- Conditionally drop requests & responses, or send them to the Intercept tab for manual inspection
- Hot reloading of scripts on file change
- Quickly enable/disable scripts
- Built-in cryptographic utilities
- Filter DSL for easily determining if the plugin's registered handlers should handle a request or response
The best way to build this project is to enter the nix
development environment and then use the build script.
$ nix develop
# Build with Python support
$ ./build.sh
# Build with both Python and JavaScript Support
$ ./build.sh --js --python
If you don't want to use nix
, you'll need to have gradle
installed and can just run ./build.sh
without it, but this may cause issues with different gradle
versions.
The resulting jar file will be in build/libs/burpscript-plugin-<version>.jar
, which you can then install into Burp Suite through the Extensions -> Add window. For more information, see Managing extensions.
Burpscript supports writing scripts in JavaScript or Python. When a script is added, Burpscript will call specially named handler functions defined in the script when a request or response is received, allowing scripts an opportunity to manipulate them as they pass through the proxy. Scripts can also define filter expressions using a Lisp-like DSL to determine which requests and responses they should be applied to.
References
- The examples directory
- The ScriptHttpRequest and ScriptHttpResponse classes. These define the API that scripts can use to modify requests and responses.
- Burp Montoya API Javadoc. In particular, HttpRequestToBeSent and HttpResponseReceived.
Python scripts look like this. Examples can be found in the examples directory, and for more information about how Python behaves when interacting with Java, see the GraalVM interop reference.
REQ_FILTER = """..."""
RES_FILTER = """..."""
def initialize():
print("Initialized Python script")
def cleanup():
print("Cleaning up Python script")
def on_request(req):
print(f"{req.method()} - {req.url()}")
return req.withBody("Modified")
def on_response(res):
print(f"{res.statusCode()} - {res.reasonPhrase()}")
Scripts can be written as either ES6 or CommonJS style modules. Examples can be found in the examples directory, and for more information about how JavaScript behaves when interacting with Java, see the GraalVM interop reference.
Scripts with the file extension .mjs
, are treated as ES6 modules, where exported handlers look like this:
export const RES_FILTER = "..."
export const REQ_FILTER = "..."
export function initialize() {
console.log("Initialized the JavaScript module");
}
export function cleanup() {
console.log("Cleaning up JavaScript");
}
export function onRequest(req) {
console.log(`${req.method()} - ${req.url()}`)
return req.withBody("Modified")
}
export function onResponse(res) {
console.log(`${res.statusCode()} - ${res.reasonPhrase()}`);
return res;
}
Scripts with the extension.js
, are treated as CommonJS modules, where handlers are exported with module.exports
:
module.exports = {
RES_FILTER: "...",
REQ_FILTER: "...",
initialize: function() {
...
},
cleanup: function() {
...
},
onRequest: function(req) {
...
},
onResponse: function(res) {
...
}
}
Scripts may also define handlers using an "Addon" style, similar to Mitmproxy. Each addon can define their own filter expressions and handlers. This is useful for organizing complex scripts or sharing addons between different scripts.
Python
class AddonOne:
REQ_FILTER = "..."
def on_request(self, req):
...
class AddonTwo:
RES_FILTER = "..."
def on_response(self, res):
...
addons = [AddonOne(), AddonTwo()]
JavaScript
class AddonOne {
// The methods must be declared this way
onRequest = function(req) {
...
}
}
class AddonTwo {
RES_FILTER = "..."
onResponse = function(res) {
...
}
}
export const addons = [new AddonOne(), new AddonTwo()]
Scripts have the following global parameters available:
api
- a MontoyaApi instancehelpers
- an instance of ScriptHelperslog
- an instance of ScriptLogger
Scripts can print messages to the Burpscript Extension tab using the log
object, or with console.log
in JavaScript, and print
in Python. Regular messages go to the Output tab, and errors and exceptions go to the Errors tab (see Managing extensions)
Python
log.info("This is an info message")
log.error("This is an error message", Exception("Oh no!")) # Goes to Errors tab
print("This is an info message")
JavaScript
log.info("This is an info message");
log.error("This is an exception", new Error("On no!")); // Goes to Errors tab
console.log("This is an info message");
console.error("This is an error message"); // Goes to Errors tab
Java classes can also be imported and used directly from scripts. In python the java
module can be imported. In JavaScript, the Java
global object is available. These can be used to import Java types and use them in scripts.
Python
import java
HttpParameter = java.type("burp.api.montoya.http.message.params.HttpParameter")
HttpParameterType = java.type("burp.api.montoya.http.message.params.HttpParameterType")
def on_request(req):
return req.withParameter(
HttpParameter.parameter("__carve", "injected", HttpParameterType.URL)
)
JavaScript
const HttpParameter = Java.type("burp.api.montoya.http.message.params.HttpParameter")
const HttpParameterType = Java.type("burp.api.montoya.http.message.params.HttpParameterType")
export function onRequest(req) {
return req.withParameter(
HttpParameter.parameter("__carve", "injected", HttpParameterType.URL)
)
}
Scripts can import other modules that reside in the same directory. Importing 3rd party modules is also supported, however, there are language-specific limitations.
Python
Here is an example of importing a utility module that resides in the same directory as the script module:
# ./myutils.py
def do_something():
...
# ./script.py
from myutils import do_something
Python standard library modules and 3rd party pypi modules can be imported as well. However, not all modules are supported. In particular, modules that depend on native code may fail to import. For the most reliable support, we recommend using GraalPy.
To allow BurpScript to resolve Python module imports, ensure that the Python interpreter executable, and Python path variables are set in the BurpScript configuration file.
$ python -m venv burpscript-env
$ source burpscript-env/bin/activate
(burpscript-env) $ pip install pyjwt
{
"python": {
"executablePath": "/path/to/burpscript-env/bin/python",
"pythonPath": "/path/to/burpscript-env/lib/python3.11/site-packages"
}
}
# script.py
import jwt
def on_request(req):
token = jwt.encode({"some": "payload"}, "secret", algorithm="HS256")
return req.withHeader("Authorization", f"Bearer {token}")
JavaScript (CommonJS)
CommonJS module style (.js
) scripts can import other modules using the require
function.
// ./myutils.js
module.exports = {
doSomething: function() {
...
}
}
// ./script.js
const { doSomething } = require('./myutils.js')
Limited support for 3rd party NPM modules is also available, however, modules that depend on Node.js built-ins, such as fs
, and buffer
, are not supported. See the GraalVM JavaScript documentation for more information about the limitations. To use 3rd party NPM modules, ensure that the node_modules
directory is present in the same directory as the script module.
$ npm i lodash
$ ls
node_modules/ package.json package-lock.json script.js
// script.js
const _ = require('lodash')
module.exports = {
onRequest: function(req) {
return req.withHeader("X-RAND", `${_.random(0, 100)}`)
}
}
JavaScript (ES6)
If the script is written using the ES6 module style (.mjs
), path-adjacent .mjs
files can be imported using the import
statement.
// ./myutils.mjs
export function doSomething() {
...
}
// ./script.mjs
import { doSomething } from './myutils.mjs'
There are some limitations with the polyglot API and how values are handled between the script and JVM. If you run into issues with this, it may be difficult to debug exactly what has gone wrong. We're working on helper functions to make these issues easier to deal with. Also, sometimes import
statements in Python don't work. If you run into such issues, sometimes it may be easier to use helpers.exec(...)
or helpers.execStdin(...)
.
Filter expressions are a Lisp-like DSL for selecting requests/responses that should be forwarded on to a script. See FILTER_EXPRESSIONS.md for documentation.
Configuration is available via the ${XDG_CONFIG_HOME:-$HOME/.config}/burpscript/conf.json
file. An example config is shown in the examples dir.