HTTPリクエストと各要素を取得しよう。
初期設定
この記事を参考に、環境を整えてください。
この記事のゴール
Burp SuiteのExtentionコンソール内にHTTPリクエストの中の要素を表示させることです。
すべてを紹介するととんでもない長さになるので、基本的なものと脆弱性診断でよく使うであろう部分に分けて取得していきたいと思います。
公式サンプルコード
公式サンプルコードはとても優秀なので、公式のコードをオーバーライドする形で記載していきます。
JavaDoc
取得できるリクエスト・レスポンスの中身
リクエスト・レスポンスの中身は基本的に全て取得できると考えて良いです。
Bodyはもちろん、Headerやパラメータ、クエリなどおよそ取得したいと考えるものはすべて取得できると考えていいでしょう。
基本構成
ファイル構成や大まかな構造は公式コードをオーバーライドする形で進めます。基本的に各値を取得する際に変更が必要なのはrequestResponseHandler.javaの方のみなので、以降のコードはそちら側のみ記載します。
App.java
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package org.httpRequestResponseHandler;
import burp.api.montoya.BurpExtension;
import burp.api.montoya.MontoyaApi;
public class App implements BurpExtension{
@Override
public void initialize(MontoyaApi api) {
api.extension().setName("Httpリクエスト・レスポンスの各要素を取得するExtention");
// Burp Suiteを通過するHttpリクエスト・レスポンスを取得する。
api.http().registerHttpHandler(new httpRequestResponseHandler(api));
}
}
requestResponseHandler.java
こちら側にリクエストに関するコードを色々書いて、取得して改ざんしたりします。
次回説明しますが、レスポンスもここに記載します。
package org.httpRequestResponseHandler;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.Annotations;
import burp.api.montoya.http.handler.*;
import burp.api.montoya.logging.Logging;
import static burp.api.montoya.http.handler.RequestToBeSentAction.continueWith;
import static burp.api.montoya.http.handler.ResponseReceivedAction.continueWith;
class httpRequestResponseHandler implements HttpHandler {
private final Logging logging;
public httpRequestResponseHandler(MontoyaApi api) {
this.logging = api.logging();
}
@Override
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent) {
Annotations annotations = requestToBeSent.annotations();
// リクエストがスコープ内かどうかを判定する。
if(requestToBeSent.isInScope()){
// コンソール内に取得したリクエストを表示する。
logging.logToOutput(requestToBeSent.headers().toString());
}
return continueWith(requestToBeSent,annotations);
}
@Override
public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived responseReceived) {
Annotations annotations = responseReceived.annotations();
return continueWith(responseReceived,annotations);
}
}
リクエストを取得する前に
さて、ではリクエストを取得していきましょう。以降は記事の長さを短くするために handleHttpRequestToBeSent
メソッドのみの記載とします。その他は上のコードと同じです。
Scope範囲内であるかを判定する
Burp Suiteにはスコープという機能があります。これは脆弱性診断対象サイト以外へのサイトへのリクエスト・レスポンスの改ざんを防ぐためにあります。
今回からリクエストやレスポンスに関しての処理を記載していきます。この記事では改ざんはしませんが、今後はそういう処理を書いて脆弱性診断をすることがあるかと思います。その際に万が一にもスコープ外のサイトへのリクエスト・レスポンスの改ざんが発生しないようにする必要があります。
そこで isInScope()
を使用することで、Extention内でそのリクエストがスコープ内かどうかを判定してくれます。
基本的にスコープ内のリクエストに対してのみ、何かを行うような挙動をするようにしましょう。
@Override
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent) {
Annotations annotations = requestToBeSent.annotations();
// リクエストがスコープ内かどうかを判定する。
if(requestToBeSent.isInScope()){
// リクエストをどうのこうのする処理を記述する。
}
return continueWith(requestToBeSent);
}
リクエストヘッダを取得する
リクエストヘッダをログに出力する
前回の記事で説明した logToOutput()
を使用して出力します。
他に使用するMontoya APIは以下のです。
requestToBeSent.headers()
このExtentionがProxyしたHttpリクエストはrequestToBeSent
に入っているため、ここからはこのObjectの中身を詳細に見ていこうという形になります。
@Override
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent) {
Annotations annotations = requestToBeSent.annotations();
// リクエストがスコープ内かどうかを判定する。
if(requestToBeSent.isInScope()){
// コンソール内に取得したリクエストを表示する。
logging.logToOutput(requestToBeSent.headers().toString());
}
return continueWith(requestToBeSent);
}
これを使用して取得したヘッダは以下のように表示されます。
特定のリクエストヘッダとその値をログに出力する
今度は特定のリクエストヘッダを取得しましょう。試しにRefererヘッダの名前と値をそれぞれ取得してみます。
使用するMontoya APIは以下のとおりです。各APIはそれぞれの値を取得してくれます。hasParameters()
は、リクエスト内にそのパラメータが存在するかどうかを判定してくれます。
また、一番最後の2つのように、リクエストヘッダ名を文字列で指定してあげることもできます。
requestToBeSent.method()
- requestToBeSent.hasParameters(HttpParameterType.COOKIE)
- requestToBeSent.parameters(HttpParameterType.COOKIE)
requestToBeSent.contentType()
requestToBeSent.query()
requestToBeSent.pathWithoutQuery()
requestToBeSent.header("Referer").name()
-
requestToBeSent.header("Referer").value()
この他にもurl()
などがあるので、公式から確認してみてください。
@Override
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent) {
Annotations annotations = requestToBeSent.annotations();
// リクエストがスコープ内かどうかを判定する。
if (requestToBeSent.isInScope()) {
// コンソール内にメソッドを表示する。
logging.logToOutput(requestToBeSent.method());
// コンソール内にCookieがある場合、それを表示する。
if(requestToBeSent.hasParameters(HttpParameterType.COOKIE)){
logging.logToOutput(requestToBeSent.parameters(HttpParameterType.COOKIE).toString());
}
// コンソール内にContent-Typeを表示する。
logging.logToOutput(requestToBeSent.contentType());
// コンソール内にクエリを表示する。
logging.logToOutput(requestToBeSent.query());
// コンソール内にクエリを含めないPathを表示する。
logging.logToOutput(requestToBeSent.pathWithoutQuery());
// コンソール内にRefererヘッダ名(="Referer")を表示する。
logging.logToOutput(requestToBeSent.header("Referer").name());
// コンソール内にRefererヘッダの値を表示する。
logging.logToOutput(requestToBeSent.header("Referer").value());
}
return continueWith(requestToBeSent,annotations);
}
Burp Suite内で確認すると以下のように表示されます。今回はCookieがないので表示されていません…ごめんね…
リクエストボディを取得する
リクエストボディをログに出力する
最後にリクエストボディを取得して、その内容をログに出力します。
使用するMontoya APIは以下のとおりです。
requestToBeSent.bodyToString()
@Override
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent) {
Annotations annotations = requestToBeSent.annotations();
// リクエストがスコープ内かどうかを判定する。
if (requestToBeSent.isInScope()) {
// コンソール内にリクエストボディを表示する。
logging.logToOutput(requestToBeSent.bodyToString());
}
return continueWith(requestToBeSent,annotations);
}
Burp Suite内で確認すると以下のように表示されます。
リクエストボディのJSONをパースしてその値を表示する
Burp Suiteを使っている方々にとって一番やりたいことと言えば、JSONのパースでしょう。
Burp Suiteの仕組みだけでは、JSONの取り回しがしにくいです。他のExtentionでは正規表現で対応していることが多いです。
そんなJSONをパースして表示してみましょう。ちょうど上のリクエストボディの確認のときに使用したボディがJSONなので、これをパースして値を取り出してみます。
今回使用するMontoya APIは以下のとおりです。
-
JsonUtils
jsonUtils.isValidJson()
jsonUtils.read()
今回はリクエスト部分以外のところにも実装しているため、全体を乗せます。
JsonUtils
はInterfaceなので、Logging
と同様に宣言をします。その後、String型として取り出したリクエストボディがJSONとしてパース可能かをisValidJson()
で確認し、read()
を用いてKeyがuuid
の値を取得しています。
package org.httpRequestResponseHandler;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.Annotations;
import burp.api.montoya.http.handler.*;
import burp.api.montoya.logging.Logging;
+ import burp.api.montoya.utilities.json.JsonUtils;
import static burp.api.montoya.http.handler.RequestToBeSentAction.continueWith;
import static burp.api.montoya.http.handler.ResponseReceivedAction.continueWith;
class httpRequestResponseHandler implements HttpHandler {
private final Logging logging;
+ private final JsonUtils jsonUtils;
public httpRequestResponseHandler(MontoyaApi api) {
this.logging = api.logging();
+ this.jsonUtils = api.utilities().jsonUtils();
}
@Override
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent){
Annotations annotations = requestToBeSent.annotations();
// リクエストがスコープ内かどうかを判定する。
if (requestToBeSent.isInScope()) {
// コンソール内にリクエストボディを表示する。
logging.logToOutput(requestToBeSent.bodyToString());
+ if(jsonUtils.isValidJson(requestToBeSent.bodyToString())){
+ logging.logToOutput(jsonUtils.read(requestToBeSent.bodyToString(),"uuid"));
+ }
}
return continueWith(requestToBeSent, annotations);
}
@Override
public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived responseReceived) {
Annotations annotations = responseReceived.annotations();
return continueWith(responseReceived,annotations);
}
}
Burp Suiteで確認すると以下のように表示されます。
Jsonの取り扱いについては、また別記事として記載しようと思います。
まとめ
リクエストを取得、改ざんする前に、そのリクエストが自分が意図しているサイトへのリクエストであるか、つまりスコープ内であるかを判定する必要があります。
そのためには以下のAPIを使用して、スコープ内外を判定します。判定するためにはBurp Suite本体側で別途スコープの設定が必要です。
requestToBeSent.isInScope()
リクエストは基本的にすべての要素を取得することができます。
取得するために必要なAPIは大まかに以下のとおりです。
- リクエストヘッダ全体
requestToBeSent.headers()
- リクエストヘッダ個別
-
requestToBeSent.method()
- requestToBeSent.hasParameters(HttpParameterType.COOKIE)
- requestToBeSent.parameters(HttpParameterType.COOKIE)
requestToBeSent.contentType()
requestToBeSent.query()
requestToBeSent.pathWithoutQuery()
requestToBeSent.header("Referer").name()
requestToBeSent.header("Referer").value()
- などなど
-
- リクエストボディ全体
requestToBeSent.bodyToString()
- リクエストボディ内のJSONをパースして取得
jsonUtils.isValidJson(requestToBeSent.bodyToString())
jsonUtils.read(requestToBeSent.bodyToString(),"uuid")
Discussion