はじめに
ペンテストをやっていると、こんなログイン画面に必ず出会います。
「安心してください。
ユーザー名とパスワードは JavaScript で暗号化してから送っています。」
……はい、それ、むしろ危険です。
本記事では、
Burp Suite の拡張(Montoya API)を使い、
クライアントサイド暗号化ログインを自動ブルートフォースで突破する方法
を、設計思想から実装まで解説します。
なぜ Intruder では突破できないのか?
今回のターゲットは以下のような構成でした。
const rawAesKey = window.crypto.getRandomValues(new Uint8Array(16));
const aesKey = await getSecretKey(rawAesKey);
let rawdata = "username=" + formDataObj["username"] + "&password=" + formDataObj["password"];
let data = window.btoa(String.fromCharCode(new Uint8Array(await encryptMessage(aesKey, enc.encode(rawdata).buffer))));
-
/loginに直接username/passwordを送らない -
送信前に JavaScript で:
- AES 鍵を生成
-
username=...&password=...を AES で暗号化 - Base64 エンコード
-
POST パラメータは:
mac=<base64(AES key)> data=<base64(encrypted payload)>
つまり…
| ツール | 結果 |
|---|---|
| Burp Intruder | ❌ 暗号を再現できない |
| Repeater | ❌ 毎回手動は無理 |
| curl / ffuf | ❌ 同様に詰む |
👉 暗号化ロジックを自分で再現しない限り、攻撃は不可能
Burp 拡張という選択肢
ここで初めて Burp 拡張が意味を持ちます。
Burp 拡張とは:
- Java(など)で書かれたプログラム
- Burp 内部 API(Montoya API)に直接アクセス
- HTTP リクエストを 自由に生成・送信・解析できる
言い換えると:
「Burp を“ツール”ではなく“フレームワーク”として使う」
という発想です。
攻撃方針(設計)
今回の突破戦略は極めてシンプルです。
- Java で AES 鍵を生成(JS と同等)
-
username+ 4桁passwordを組み立て - JS と同じ AES 暗号化方式で暗号化
- Base64 + URL Encode
- Burp API で
/loginに POST - レスポンスを解析
- 成功したら即停止
重要なのは 「Intruder 的な発想を捨てる」 こと。
Burp 拡張の最小構成
101Burp/
├── build.gradle
├── settings.gradle
└── src/main/java/
└── BruteForce.java
build.gradle では Montoya API を compileOnly で指定します。
dependencies {
compileOnly "net.portswigger.burp.extensions:montoya-api:2025.2"
}
👉 Burp 自身が API を持っているため、JAR に含めてはいけない
(ここで詰む人が本当に多い)
拡張のエントリポイント
public class BruteForce implements BurpExtension {
@Override
public void initialize(MontoyaApi api) {
api.extension().setName("Burp Password Brute-Forcer");
SwingUtilities.invokeLater(this::createUI);
}
}
-
initialize()は Burp 起動時に自動実行 - UI は Swing で作成
- 処理は別スレッドで実行(UI フリーズ防止)
Swing UI:人間が操作できる攻撃ツール
UI では最低限だけ入力させます。
- Username
- Server(host:port)
- Start Attack ボタン
Intruder と違い、攻撃ロジックはコード側に完全固定。
人間は「条件を与えるだけ」です。
ブルートフォース処理の核心
for (int i = 0; i <= 9999; i++) {
String password = String.format("%04d", i);
SecretKey aesKey = generateAesKey();
byte[] encrypted = encrypt(rawData, aesKey);
String body =
"mac=" + base64(aesKey.getEncoded()) +
"&data=" + base64(encrypted);
HttpResponse resp = api.http()
.sendRequest(request)
.response();
if (resp.statusCode() == 200 &&
resp.bodyToString().contains("result=")) {
// HIT
break;
}
}
ここでやっていることは:
- ブラウザがやっていることを 1:1 で再現
- ただし 9999 回、休まず、正確に
これが「自動化」の本質です。
結果:ログイン突破
数千回の試行の末、正しいレスポンスが返却。
- 正しい 4 桁パスワードを特定
- 復号された
resultデータを取得 - 実際にログイン可能であることを確認
クライアントサイド暗号化は、攻撃を一切防いでいなかった
ことが証明されました。
なぜこの設計は危険なのか?
この構成の致命的な問題は:
- 暗号鍵をクライアントで生成
- その鍵をサーバへ送信
- サーバ側で正当性を検証できない
これは暗号ではなく、
**「エンコード付き平文送信」**に過ぎません。
防御側への示唆
安全な設計をするなら:
- クライアント暗号化に依存しない
- TLS を信頼する
- 認証は:
- サーバ側で rate limit
- lockout
- challenge-response
- PAKE / SRP / FIDO2
「JS で暗号化してるから安全」は完全な幻想です。
まとめ:Burp 拡張は“突破力”を変える
- Intruder は万能ではない
- 非標準実装は 拡張で殴る
- Burp 拡張を書ける=
ツールに縛られないペンテスター
攻撃者はブラウザを信じない
防御者はクライアントを信じてはいけない