Burp Suiteの新機能Bambdaを使った高度フィルタリング

はじめに
3大登山ゲーの一つと言われるポゴスタックをやっていると、Garminから「高ストレスを感じています!」というアラートが出て、それにイラッとしてしまい仕事のやる気が起きない岩間です。
今回は先月発表された、Burp Suiteの新機能「Bambda」について使い方を紹介したいと思います。
Bambdaとは?
Bambdaとは、HTTP historyの検索フィルタリングにJavaのスニペットが使えるようになる高度なフィルタリング機能で、2023.10.3のアーリーアダプター版でリリースされました。
高度なフィルタリングですが、通常フィルターと比べると使いどころが難しいかと思いますので、今回は「こんな使い方ができるよ!」といったBambdaの紹介をしたいと思います。

間違ったContent-type形式のJSONレスポンスを見つける
JSONをレスポンスとして返す際、本来であれば Content-Type に application/json を指定すべきですが、稀にtext/html を指定しているサイトに遭遇します。
application/json
HTTP/1.1 200 OK
Server: Werkzeug/2.2.3 Python/3.8.10
Date: Fri, 03 Nov 2023 02:16:58 GMT
Content-Type: application/json
Content-Length: 48
Connection: close
{"parameters":{"hoge":"\"><script>","url":"h"}}
html/text
HTTP/1.1 200 OK
Server: Werkzeug/2.2.3 Python/3.8.10
Date: Fri, 03 Nov 2023 02:16:00 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 51
Connection: close
{"parameters": {"url": "h", "hoge": "\"><script>"}}
Burpでは、どちらのレスポンスもMIME type JSON になってしまうため、レスポンス内の文字列検索以外では、上記のレスポンスの違いを見分けるフィルターは設定できません。
これをBambdaを使って、 Content-Type が text/html のJSONレスポンスに限定してフィルタリングすることができます。
Bambda スニペット
if (!requestResponse.request().isInScope()) {
return false;
}
if (requestResponse.response().hasHeader("Content-Type")) {
if (!requestResponse.response().headerValue("Content-Type").contains("text/html")) {
return false;
}
}
String body = requestResponse.response().bodyToString().trim();
boolean looksLikeJson = body.startsWith("{") || body.startsWith("[");
if (!looksLikeJson) {
return false;
}
return true;
Base64エンコードされる前の文字列を検索する
エンコードされる前の文字列を検索することは通常できませんが、Bambdaで使用できる Utilities インタフェースを使えば、検索することが可能です。
HTTPレスポンスをBase64デコードし、文字列 hoge を含む通信に限定してフィルタリングする
Base64 レスポンス
HTTP/1.1 200 OK
Server: Werkzeug/2.2.3 Python/3.8.10
Date: Fri, 03 Nov 2023 03:23:48 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 8
Connection: close
aG9nZQ==
Bambda スニペット
if (!requestResponse.request().isInScope()) {
return false;
}
String body = requestResponse.response().bodyToString().trim();
String decoded_body = utilities().base64Utils().decode(body).toString();
if (decoded_body.contains("hoge")) {
return true;
}
return false;
また、リクエストでも同様の方法でフィルタリングすることが可能です。
HTTPリクエストの data クエリパラメータをbase64デコードし、文字列 hoge を含む通信に限定してフィルタリングする
Base64パラメータを持つリクエスト
GET /?data=aG9nZQ== HTTP/1.1
Host: 127.0.0.1:5000
Connection: close
Bambda スニペット
if (!requestResponse.request().isInScope()) {
return false;
}
String data = requestResponse.request().parameter("data", HttpParameterType.URL).value();
String decoded_data = utilities().base64Utils().decode(data).toString();
if (decoded_data.contains("hoge")) {
return true;
}
return false;
他にも以下のようなデコードや変換による検索が可能です。
- Base64
- HTMLエンコード文字列をデコードする
- URLエンコード文字列をデコードする
- 文字列と16進数表記の変換
- 数字の進数変換
- バイト列から文字列の変換
など
Cookieの値を使って検索する
通常のフィルターは、リクエストもしくはレスポンス全体から文字列の検索が行われます。
設定は楽ですが、クッキーの値やPOSTパラメータやJSONなど、特定のパラメータだけを検索対象としたい場面があるかと思います。
リクエストのCookie fooの値が 6db771a4 を含む通信に限定してフィルタリングする
リクエスト
GET /?data=hoge HTTP/1.1
Host: 127.0.0.1:5000
Cookie: foo=6db771a4-7a28-11ee-8acf-c9e9a3365fa8
Bambda スニペット
if (!requestResponse.request().isInScope()) {
return false;
}
if (!requestResponse.request().hasParameter("foo", HttpParameterType.COOKIE)) {
return false;
}
String foo_cookie = requestResponse.request().parameter("foo", HttpParameterType.COOKIE).value();
if (foo_cookie.contains("6db771a4")) {
return true;
}
return false;
レスポンスのCookie fooの値が 6db771a4 を含む通信に限定してフィルタリングする
レスポンス
HTTP/1.1 200 OK
Server: Werkzeug/2.2.3 Python/3.8.10
Date: Fri, 03 Nov 2023 09:07:35 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 4
Set-Cookie: foo=6db771a4-7a28-11ee-8acf-c9e9a3365fa8; Expires=Fri, 03 Nov 2023 09:08:05 GMT; Path=/
Connection: close
hoge
Bambda スニペット
HTTP/1.1 200 OK
Server: Werkzeug/2.2.3 Python/3.8.10
Date: Fri, 03 Nov 2023 09:07:35 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 4
Set-Cookie: foo=6db771a4-7a28-11ee-8acf-c9e9a3365fa8; Expires=Fri, 03 Nov 2023 09:08:05 GMT; Path=/
Connection: close
hogeif (!requestResponse.request().isInScope()) {
return false;
}
if (!requestResponse.response().hasCookie("foo")) {
return false;
}
String foo_cookie = requestResponse.response().cookie("foo").value();
if (foo_cookie.contains("6db771a4")) {
return true;
}
return false;
リクエストの場合、 HttpParameterType のタイプを変えることで、検索箇所をクッキー以外にすることができます。
- BODY
- COOKIE
- JSON
- MULTIPART_ATTRIBUTE
- URL
- XML
- XML_ATTRIBUTE
レスポンスヘッダー Content-Length の値と実際の値が異なる通信を見つける
レスポンスヘッダーの Content-Length と実際のbodyの文字長が異なる通信に限定してフィルタリングします。
Bambda スニペット
if (!requestResponse.request().isInScope()) {
return false;
}
if(!requestResponse.hasResponse() || !requestResponse.response().hasHeader("Content-Length")) {
return false;
}
int declaredContentLength = Integer.parseInt(requestResponse.response().headerValue("Content-Length"));
int realContentLength = requestResponse.response().body().length();
return declaredContentLength != realContentLength;
今回の状況は非常に稀ですが、RFCに違反したレスポンスを返すサイトは、たまに見かけることがあります。通常のフィルターでは出来ませんが、Bambdaを駆使することで見落としてしまいそうな通信も漏れなく探せると良いですね。
番外: 電卓を呼び出す
「Javaのスニペットを使用して」という時点で、何となくお気付きの方もいると思いますが、任意のプロセス実行等も可能です。
電卓を表示する (historyにある件数分だけ電卓が起動します)
Bambda スニペット
new ProcessBuilder("cmd", "/c", "calc").start(); return true;
現時点では、自分で実行しない限り問題ありませんが、ネットの記事やX(旧Twitter)などの情報に転がってるスニペットをそのままコピーする場合は、スニペットの中身をよく確認しましょう!