フロントエンド開発にかかせないBurp
便利ツール
私たちは日々の開発の中で様々な便利ツールを使います。
その中でもBurpは多くの機能を持ち、エンジニアにとってかかせないツールとなっています。
今回の記事ではフロントエンド開発においてBurpがどのように役立つかを紹介します。
Burpとは
BurpはPortSwiggerにより提供されているツールで、Professional、Enterprise Editionなどの種類があります。中でもCommunity Editionは無料で提供されており、誰でも公式サイトからダウンロードすることができます。
では早速Burpの使い方を紹介していきます。
Proxy
ProxyはBurpを使う上で最もベーシックな機能になります。
準備
- Burpを立ち上げ、タブからProxyを選択
- Open browserをクリックし、ブラウザを立ち上げる
- 解析したいページを開く
上記のようにBurpで立ち上げた専用のブラウザで開くことでBurpがProxyサーバーのような役割をします。
リクエストを観察する
Burpの左上にあるIntercept Off
というボタンを押すとInterceptをONにでき、リクエストの詳細を一つずつ観察することができます。
例えば私のGitHubのプロフィールページを開いてみます。
最初のリクエストは以下のようになっており、この状態では上図の2.Burp
でリクエストが止まっています。Forward
ボタンを押すことでこのリクエストを実際に3.Origin Server
に投げることができます。
GET /fujiyamaorange HTTP/2
Host: github.com
Cookie: _octo=GH1.1.22222222222.3333333333; logged_in=no; _gh_sess=hoge; preferred_color_mode=dark; tz=Asia%2FTokyo
Pragma: no-cache
Cache-Control: no-cache
Sec-Ch-Ua: "Not;A=Brand";v="24", "Chromium";v="128"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
(略)
そしてこの後のリクエストによりページのJSやCSS、画像などが読み込まれていきます。
あるリクエストに対してDrop
ボタンを押すと2.Burp
でリクエストが破棄され、3.Origin Server
にリクエストが届かなくなります。
先ほどのページで最初のリクエスト以外の全てをDropすると以下のような画面になり、ブラウザのテーマや画像などが正しく読み込まれていないことがわかります。
レスポンスを書き換える
先ほどリクエストをDropする処理を紹介しましたが、Burpにはレスポンスを書き換える機能もあります。
- 対象のレスポンスを右クリックで選択
-
Do intercept
→Response to this request
を選択 - 編集画面でリクエストを書き換える
ここでは先ほどのGitHubページにランディングした際の最初のリクエストを書き換えてみます。
HTTP/2 200 OK
Server: GitHub.com
Date: Tue, 08 Oct 2024 13:39:03 GMT
Content-Type: text/html; charset=utf-8
Vary: X-Requested-With, X-PJAX-Container, Turbo-Frame, Turbo-Visit, Accept-Encoding, Accept, X-Requested-With
Etag: W/"542620f67bf562062bed456aff24a41a"
Cache-Control: max-age=0, private, must-revalidate
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
X-Frame-Options: deny
X-Content-Type-Options: nosniff
X-Xss-Protection: 0
Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
Content-Typeをtext/html
からapplication/json
に変更してみます。
HTTP/2 200 OK
Server: GitHub.com
Date: Tue, 08 Oct 2024 13:39:03 GMT
Content-Type: application/json; charset=utf-8
Vary: X-Requested-With, X-PJAX-Container, Turbo-Frame, Turbo-Visit, Accept-Encoding, Accept, X-Requested-With
Etag: W/"542620f67bf562062bed456aff24a41a"
Cache-Control: max-age=0, private, must-revalidate
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
X-Frame-Options: deny
X-Content-Type-Options: nosniff
X-Xss-Protection: 0
Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
上記のようにContent-Type
を書き換えるとレスポンス内容が正しく読み込まれず以下のような表示になります。
今回はContent-Type
を書き換えましたが、この機能はフロントエンドにおいて状態のエッジケースを実際のリクエストと共に確認したい時に非常に役に立ちます。
ぜひAPIレスポンスを書き換えて、エラーハンドリングを充実させましょう!
Repeater
上記に加え、BurpにはRepeaterという便利な機能があります。
Repeaterはその名の通り、特定のリクエストを何度も送ることができる機能です。しかし単に同じリクエストを送るのではなく、リクエストを編集しつつレスポンスの変化を観察できます。
今回は例としてGoogleのGitHub Organizationをフォローしてみようと思います。
Burpで立ち上げたブラウザに自分のGitHubアカウントでログインし、以下を実行します。
- InterceptをONにし、フォローボタンを押下
- 対象のリクエストを右クリックで
Send to Repeater
を選択
フォローするリクエストは以下のように構成されていました。
POST /users/follow?target=google HTTP/2
Host: github.com
Cookie: _octo=GH1.1.1883337129.1699597551; preferred_color_mode=dark; tz=Asia%2FTokyo; _device_id=<device_id>; saved_user_sessions=<saved_user_sessions>; user_session=<user_session>; __Host-user_session_same_site=<user_session_same_site>; tz=Asia%2FTokyo; color_mode=%7B%22color_mode%22%3A%22dark%22%2C%22light_theme%22%3A%7B%22name%22%3A%22light%22%2C%22color_mode%22%3A%22light%22%7D%2C%22dark_theme%22%3A%7B%22name%22%3A%22dark%22%2C%22color_mode%22%3A%22dark%22%7D%7D; logged_in=yes; dotcom_user=fujiyamaorange; _gh_sess=<hogehoge>
Content-Length: 334
Pragma: no-cache
Cache-Control: no-cache
(略)
パラメータを変更する
では上記のリクエストのクエリパラメータであるtarget
をmoneyforward
に変更してみます。
HTTP/2 200 OK
Server: GitHub.com
Date: Wed, 09 Oct 2024 10:53:43 GMT
Content-Type: application/json; charset=utf-8
Vary: X-PJAX, X-PJAX-Container, Turbo-Visit, Turbo-Frame
Etag: W/"3c2d8b0a6d72f80fa653b1ac0d6230b2"
Cache-Control: max-age=0, private, must-revalidate
Set-Cookie: _gh_sess=<_gh_sess=>; path=/; secure; HttpOnly; SameSite=Lax
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
X-Frame-Options: deny
X-Content-Type-Options: nosniff
(略)
そうするとなんと200 OK
が返ってきており別のブラウザで確認してもきちんとフォローできていることがわかります。
許可されないリクエストを送る
では先ほどのtarget
を自分のアカウントであるfujiyamaorange
に変更してリクエストを送ってみます。
HTTP/2 422 Unprocessable Entity
Server: GitHub.com
Date: Wed, 09 Oct 2024 10:59:11 GMT
Content-Type: application/json; charset=utf-8
Vary: X-PJAX, X-PJAX-Container, Turbo-Visit, Turbo-Frame
Cache-Control: no-cache
Set-Cookie: _gh_sess=<_gh_sess>; path=/; secure; HttpOnly; SameSite=Lax
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
X-Frame-Options: deny
X-Content-Type-Options: nosniff
(略)
すると今回は422 Unprocessable Entity
が返ってきました。おもしろいですね。
こういったように特定のフォーマットを編集したリクエストを送りそのレスポンスを調べることができます。
Intruder
では次にIntruderを紹介していきます。
今回は攻撃対象として、Burpの公式ドキュメントに載っているサイトを使います。
今まで行ってきたようにBurpからブラウザを立ち上げ以下のような専用のサイトを開きます。
https://<hogehoge>.web-security-academy.net/login
以下のようにしてログインのリクエストをIntruderに送ります。
- 適当な
Username
とPassword
を入力しログインを試みる - HTTP Historyからログインに使用しているリクエストを特定する
- 対象のリクエストを右クリック&
Sent to Intruder
を選択
今回対象となったリクエストです。
POST /login HTTP/2
Host: <hogehoge>.web-security-academy.net
Cookie: session=§<session>§
Content-Length: 31
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="129", "Not=A?Brand";v="8"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Accept-Language: ja
Origin: https://<hogehoge>.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.71 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://<hogehoge>.web-security-academy.net/login
Accept-Encoding: gzip, deflate, br
Priority: u=0, i
username=§hoge§&password=§password§
画面右にあるAuto §
を選択するとBurpがリクエストのパラメータになっているところを自動で判定してくれます。(§
で挟まれている部分)
上記の場合はCookie、bodyのusernameとpasswordがパラメータであると判定されました。
usernameを特定する
では早速Intruderを使ってサイトを攻撃してみます。
まずはユーザー名から判定したいので下のようにusername
のみをパラメータとします。
username=§hoge§&password=password
Payloadsタブへ移ると以下のようにパラメータに入れる値をセットできます。
今回はあらかじめ用意された以下のような辞書を使います。
carlos
root
admin
test
guest
info
adm
mysql
user
(略)
au
auction
austin
auth
auto
autodiscover
辞書がセットできたところで右上のStart attack
を押下すると全てのリクエストを走らせることができます。
Community Editionでは以下のような警告が出ますが、リクエストがthrottleされるだけで機能を使用することはできるので安心してください。
処理が終了すると以下のように結果が別ウィンドウで表示されます。
そして各リクエストに対してどんなレスポンスが返ってきたのかを1つずつ確認できます。
ここで注目したいのが、右にあるLength
です。全てが3248になっているように見えますが、ソートしてみると1つだけ3250のリクエストを見つけることができました。
もうお分かりだと思いますが、このリクエストだけ表示されるエラーメッセージが異なっています。
<p class=is-warning>Incorrect password</p>
つまりこのメッセージは対象のusername
は存在するということを意味しています。
たびたび警鐘されていることですが、このエラー文言は脆弱です。
本来は「ユーザー名またはパスワードが間違っています」というような文言にすべきです。
ここまででユーザー名を特定することができました。
passwordを特定する
上記によりユーザー名がarizona
であることがわかったので、今度はpassword
を特定していきます。
以下のようにpassword
がパラメータになるように設定し、あらかじめ用意した辞書を用いて再度攻撃を行います。
username=arizona&password=§password§
処理が終了すると以下のようになりました。こちらも明らかにLength
の異なるレスポンスがあることがわかります。
ということで無事、パスワードが12345678
であることがわかりました。
HTTP/2 302 Found
Location: /my-account?id=arizona
Set-Cookie: session=UB4T1xNxv2PzpIz9Hu9O7kQF7AW5ahqu; Secure; HttpOnly; SameSite=None
X-Frame-Options: SAMEORIGIN
Content-Length: 0
こうした一連のチェックをリリース前のサービスに行うことでシステムの安全性を高めることができます。
まとめ
いくつかBurpの機能を紹介してきましたが、これを見て便利だと感じてもらえたら嬉しいです。
まだまだ紹介しきれていない機能がたくさんあるので私も引き続き使い倒し、理解を高めていこうと思います。
ここまで読んでくださりありがとうございました。🎉
Discussion