👻

OWASP ZAPでOAuth2.0 クライアントクレデンシャルズフローの認可を突破する

2021/09/06に公開

やりたいこと

OAuth2.0クライアントクレデンシャルズフローの認可がかかっているAPIに対して、OWASP ZAPから脆弱性診断をする。

OWASP ZAPのバージョンは2.10.0です。

方針

HTTP SenderというHTTPリクエストとHTTPレスポンスを操作できる機能がOWASP ZAPにはあるのでこれを使用します。
具体的には以下を行います。

  1. OWASP ZAPによるAPI実行前に、認可APIを実行をしてトークンを取得する
  2. 1で取得したトークンをHTTPリクエストのAuthorizationヘッダに設定する

やり方

1

OWASP ZAPの画面左上の「スクリプトタブ」にある「HTTP Sender」を右クリックして新規スクリプトを押す。

2

Script名を任意の名称、Script engineECMAScript : 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.jsECMAScript : 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

https://groups.google.com/g/zaproxy-users/c/HJZ8gxk17M8/m/5WQuD7t3AAAJ

https://securitymemo.blogspot.com/2016/06/owasp-zap-part3.html

Discussion