SSTエンジニアブログ

SSTのエンジニアによるWebセキュリティの技術を中心としたエンジニアブログです。

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

はじめに

3大登山ゲーの一つと言われるポゴスタックをやっていると、Garminから「高ストレスを感じています!」というアラートが出て、それにイラッとしてしまい仕事のやる気が起きない岩間です。 今回は先月発表された、Burp Suiteの新機能「Bambda」について使い方を紹介したいと思います。

Bambdaとは?

Bambdaとは、HTTP historyの検索フィルタリングにJavaのスニペットが使えるようになる高度なフィルタリング機能で、2023.10.3のアーリーアダプター版でリリースされました。

techblog.securesky-tech.com

高度なフィルタリングですが、通常フィルターと比べると使いどころが難しいかと思いますので、今回は「こんな使い方ができるよ!」といったBambdaの紹介をしたいと思います。

間違ったContent-type形式のJSONレスポンスを見つける

JSONをレスポンスとして返す際、本来であれば Content-Typeapplication/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-Typetext/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 スニペット

if (!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)などの情報に転がってるスニペットをそのままコピーする場合は、スニペットの中身をよく確認しましょう!

参考リンク