👻
OWASP ZAPでOAuth2.0 クライアントクレデンシャルズフローの認可を突破する
やりたいこと
OAuth2.0クライアントクレデンシャルズフローの認可がかかっているAPIに対して、OWASP ZAPから脆弱性診断をする。
OWASP ZAPのバージョンは2.10.0
です。
方針
HTTP Sender
というHTTPリクエストとHTTPレスポンスを操作できる機能がOWASP ZAPにはあるのでこれを使用します。
具体的には以下を行います。
- OWASP ZAPによるAPI実行前に、認可APIを実行をしてトークンを取得する
- 1で取得したトークンをHTTPリクエストのAuthorizationヘッダに設定する
やり方
1
OWASP ZAPの画面左上の「スクリプトタブ」にある「HTTP Sender」を右クリックして新規スクリプトを押す。
2
Script名
を任意の名称、Script engine
はECMAScript : Oracle Nashorn
にして保存を押す。
3
以下のソースコードを貼り付ける。
function sendingRequest(msg, initiator, helper) {
/*
* 設定値
*/
// URI
var uri = 'xxxxxx'
// 「ClientId + ':' + ClientSecret」をbase64エンコードした文字列(ZAP側で組み立てようとしたけど上手くいかなかったので)
var clientInfo = 'xxxxxx';
// スコープ
var scope = 'xxxxxx';
/**
* Javaのパッケージのインポート
*/
var TreeSet = Java.type('java.util.TreeSet');
var HttpRequestHeader = Java.type('org.parosproxy.paros.network.HttpRequestHeader');
var HttpRequestBody = Java.type('org.zaproxy.zap.network.HttpRequestBody');
var HttpMessage = Java.type('org.parosproxy.paros.network.HttpMessage');
// リクエストヘッダ
var requestHeader = new HttpRequestHeader();
requestHeader.setMethod('POST');
requestHeader.setURI(convertStrToURI(uri));
requestHeader.setHeader('Content-Type', 'application/x-www-form-urlencoded');
requestHeader.setHeader('Authorization', 'Basic ' + clientInfo);
// リクエストボディ
var ts = new TreeSet();
ts.add(generateHtmlParameter('grant_type', 'client_credentials'));
ts.add(generateHtmlParameter('scope', scope));
var requestBody = new HttpRequestBody();
requestBody.setFormParams(ts);
// リクエストヘッダとリクエストボディをまとめる
var my_msg = new HttpMessage(requestHeader, requestBody);
// リクエスト送信 & レスポンス受信
helper.getHttpSender().sendAndReceive(my_msg);
// アクセストークンを上書き
var tmp = JSON.parse(my_msg.getResponseBody().toString());
msg.getRequestHeader().setHeader('Authorization', 'Bearer ' + tmp.access_token);
}
function convertStrToURI(uristr) {
return new org.apache.commons.httpclient.URI(uristr);
}
function generateHtmlParameter(name, value) {
var form = org.parosproxy.paros.network.HtmlParameter.Type.valueOf('form');
return new org.parosproxy.paros.network.HtmlParameter(
form
, name
, value
);
}
function responseReceived(msg, initiator, helper) {
}
※letやconstを使用すると何故かエラーになったのでvarを使用しています。
4
スクリプトタブのツリー上で作成したスクリプトを右クリックして"スクリプトを有効にします。"を押す。
5
診断対象のAPIをOWASP ZAPに記憶させる。例えば以下のような方法があります。
- OpenAPIをimportする
- Postmanから実行する
6
動的スキャンを実施する。
備考
Script engine
にはECMAScript : Graal.js
とECMAScript : Oracle Nashorn
というものが使用できるようです。
上でOracle Nashorn
にしている理由は以下です。
最初はGraaljs
にしていたのですが、まれに以下のようなエラーが発生しました。
java.lang.IllegalStateException: Multi threaded access requested by thread Thread[ZAP-ActiveScanner-1,3,main] but is not allowed for language(s) js.java.lang.IllegalStateException: Multi threaded access requested by thread Thread[ZAP-ActiveScanner-1,3,main] but is not allowed for language(s) js.
エラー発生直後からHTTP Sender
が無効になることで、以降のAPI実行で認証突破ができなくなり想定通りに診断ができませんでした。
今のところNashorn
ではこのエラーが発生してないので、こっちを使うことにしています。
参考URL
Discussion