サイト内の現在位置

トレーニングコンテンツ:脆弱なAPIラボ環境「vAPI」の紹介

NECセキュリティブログ

2022年4月15日

NECサイバーセキュリティ戦略統括部セキュリティ技術センターの中島です。
本記事では、APIの脆弱性について学べるラボ環境「vAPI」をご紹介します。

vAPI とは

vAPI(Vulnerable Adversely Programmed Interface)new window[1]は、Tushar Kulkarni氏が作成した、APIセキュリティについて学ぶためのオープンソースラボ環境です。APIセキュリティとは、API特有の脆弱性とセキュリティリスクです。本環境には、「OWASP API Security Top 10」new window[2]が提示する10項目のAPIセキュリティに対する攻撃シナリオが教材として用意されています。

  1. Broken Object Level Authorization
  2. Broken User Authentication
  3. Excessive Data Exposure
  4. Lack of Resources & Rate Limiting
  5. Broken Function Level Authorization
  6. Mass Assignment
  7. Security Misconfiguration
  8. Injection
  9. Improper Assets Management
  10. Insufficient Logging & Monitoring

各シナリオは独立した教材になっています。また、環境内にはflagとよばれる情報が隠されており、シナリオに沿って脆弱性をつくことでflagを探します。

vAPI のセットアップ

まず、vAPIのセットアップを行います。vAPIのセットアップには、Dockerを用いてvAPIやデータベースのコンテナ一式を立ち上げる方法と、vAPIやデータベースを自身が用意した環境に手動でインストールするマニュアルインストールの2種類の方法が用意されています。詳しくはgithubのREADMEをご確認ください。今回はDocker Composeを使い、以下のコマンドでセットアップを行いました。

$ git clone https://github.com/roottusk/vapi.git
$ cd vapi
$ docker-compose up -d

vAPIのセットアップが完了し、環境が起動したら、ブラウザから http://<ipaddress>/vapi にアクセスし、vAPIに用意されたAPIの仕様を確認します。

次に、各シナリオに取り組む前の準備として、API開発用のプラットフォームであるPostmanとローカルプロキシツールであるBurpSuiteの設定を行います。なお、本記事ではこれらのツールのインストール方法や使い方の詳細については、割愛します。

vAPIには、Postman用のJSONファイルが用意されているので、vapi/postman/ 配下に用意された2つのJSONファイルをPostmanにインポートします。

  • vAPI.postman_collection.json(事前設定済みのリクエストをまとめたコレクション用ファイル)
  • vAPI_ENV.postman_environment.json(環境設定用ファイル)

インポートが成功すると、以下のように、Collectionsに反映されていることが確認できます。あわせて、任意のAPIを選択し、Postmanの画面右上の環境設定の箇所で先ほどインポートした環境設定「vAPI_ENV」を適用しておきます。

次に、BurpSuiteを通して通信を行えるようにするため、Postman側で設定を行います。Postmanで送信したリクエストをBurpSuite経由で送るためには、Postman側でBurpSuiteをプロキシとして設定する必要があります。Postmanの画面右上の歯車のボタンからSettingsを開き、Proxyタブでプロキシの設定を追加します。なお、お手元で再現される場合には、ポート番号を環境に合わせて設定してください。

これで、環境の準備は完了です。

Postman上で任意のAPIを選択してsendボタンを押すと、リクエストが送信されます。そしてそのレスポンスをBurpSuite上で確認することができます。

vAPIのシナリオにチャレンジ

vAPIには10個のシナリオが用意されています。/vapi にはAPIの仕様とともに「OWASP API top 10」のどのAPIセキュリティに該当するかの情報、そして、簡単な問題文が記載されています。
本記事では以下の5つのシナリオを取り上げてご紹介いたします。

  1. API1: Broken Object Level Authorization
  2. API2: Broken User Authentication
  3. API5; Broken Function Level Authorization
  4. API6: Mass Assignment
  5. API9: Improper Assets Management

1.API1: Broken Object Level Authorization

問題文

You can register yourself as a User , Thats it ....or is there something more?

APIの仕様

パス メソッド 認可制御 備考
/vapi/api1/user POST ユーザ作成
/vapi/api1/user/{api1_id} GET ユーザ情報の取得
/vapi/api1/user/{api1_id} PUT ユーザ情報の更新

このシナリオでは、ユーザ作成、ユーザの情報を取得、更新することができるAPIが用意されているようです。まずは、それぞれのAPIの挙動を確認するため、ユーザ「api1testuser」を作成します。

レスポンスで 201 Created が返され、idが「5」のユーザとして登録されたことを確認しました。

では続いてユーザ情報の取得APIを実行してみます。/vapi にアクセスし、vAPIに用意されたAPIの仕様を確認すると、ユーザ情報の取得リクエストを送る際には、認可用のヘッダとして「Authorization-Token」が求められることが分かります。これは、ユーザのIDとパスワードをコロン(:)でつないで、base64 でエンコードした値になります。Postmanを使用している場合、自動で付与されるように設定されており、以下の通り、ヘッダと環境変数から値を確認することができます。

「api1testuser」の情報を得るために、idに「5」を指定してユーザ情報を確認します。

先ほど作成した 「api1testuser」の情報が返されることを確認しました。ユーザ情報の更新APIについてもユーザ情報の取得APIと同様に、idを指定することでユーザ情報を更新することができました。

では、これらのAPIのどこに脆弱性があるのでしょうか。このシナリオのタイトルから、認可制御に問題があることはわかっているので、ユーザが他のユーザの情報を取得・編集することができるのではないかと考えます。まずは、検証のため、「api1testuser2」というユーザを作成します。

レスポンスで 201 Created が返され、idが「6」のユーザとして登録されたことを確認できました。つづいて、「api1testuser2」として「api1testuser」の情報にアクセスできるかを確認します。本記事の手順でPostmanを使用している場合、ヘッダ「Authorization-Token」には「api1testuser2」の情報が自動で設定されているはずです。したがって、このユーザとして idに「5」を指定し、ユーザ情報の取得APIを実行してみます。

「api1testuser」の情報が返されることを確認しました。このことから、ユーザ情報の取得APIでは、認可制御が適切に行われていないことがわかります。では、さらに他のユーザの情報を取得することが可能かを確認します。「api1testuser」と「api1testuser2」のidは「5」「6」と連続した番号でしたので、ユーザのidは1,2,3...というように連番で振られていることが予想できます。実際の脆弱性診断などでも、このように複数のアカウントを作成し、idの生成規則を確認することがあります。

「api1testuser2」として idに「1」を指定し、ユーザ情報の取得APIを実行してみます。

id「1」に紐づくユーザ「michaels」の情報とともにflagが返されることを確認しました。
なお、本シナリオには、ユーザ情報の更新APIにも情報取得のAPIと同様に認可制御の不備があることが分かりました。このため、任意のユーザのidを指定することでユーザ情報を更新することも可能でした。

本シナリオでは、ユーザのidに基づいてデータを返していますが、この際に適切にアクセス権限の検証が行われていないため、任意のユーザの情報を取得することが可能です。本シナリオのように、アプリケーションがユーザの入力値を使用してオブジェクトに直接アクセスするときに発生する脆弱性を Insecure direct object references(IDOR)、あるいは安全でない直接オブジェクト参照と呼びます。このような脆弱性を防ぐためには、APIを意図的に公開していない場合、デフォルトでアクセスを拒否するようにして、適切なアクセス制御を実装する必要があります。new window[3]

2.API2: Broken User Authentication

問題文

We don't seem to have credentials for this , How do we login? (There's something in the Resources Folder given to you )

APIの情報

パス メソッド 認可制御 備考
/vapi/api2/user/login POST ユーザログイン
/vapi/api2/user/details GET ユーザ情報の取得

資格情報を持っていない状態で、どうやってログインできるか?という問題です。このシナリオでは、使用できる情報が用意されているので、まずは問題文に従ってResourcesフォルダを確認します。

メールアドレスとパスワードの組み合わせが1000件ほど入っているリストがありました。このリストを用いてクレデンシャルスタッフィング攻撃ができそうです。まずは、リストの一行目の資格情報を用いてログインを試みます。

ログインに失敗し、「401 Unauthorized」が返されることを確認しました。残りの与えられた資格情報についてもBurpSuiteのIntruder機能(カスタマイズした攻撃を自動化するツール)を用いてログイン可能かを確認しましょう。

emailとpasswordのそれぞれに対してResourcesフォルダのリストをペイロードとして設定し、Intruderを実行します。すると、レスポンスの多くが「401 Unauthorized」を返してくる中で、「200 OK」を返している通信を3つ見つけることができました。そのうちのひとつのレスポンスを確認してみます。

ログインに成功し、トークンが払い出されていることを確認しました。このトークンを使って、ユーザ情報を取得するAPIを実行します。

ユーザ情報とともにflagが返されることを確認しました。

本シナリオでは、リクエスト数の制限やアクセス元の制限がなかったので、メールアドレスとパスワードの組み合わせを連続的に試す攻撃を容易に実行することができました。クレデンシャルスタッフィング攻撃の対策の一つとして、多要素認証を導入することが挙げられます。多要素認証の実装が難しい場合は、認証時に追加のセキュリティ情報を要求する、自動ログイン試行の制限やアクセス元の制限を行う、などの方法でリスクの低減を検討する必要があります。new window[4]

3.API5: Broken Function Level Authorization

問題文

You can register yourself as a User. Thats it or is there something more? (I heard admin logins often but uses different route)

APIの情報

パス メソッド 認可制御 備考
/vapi/api5/user POST ユーザ作成
/vapi/api5/user/{{api5_id}} GET ユーザ情報の取得

ユーザ登録を行うことができ、そのユーザの情報を取得することができるようです。まずは、サンプルリクエストを用いてユーザを作成します。

ユーザ情報の取得APIを実行し、作成したユーザの情報を確認します。

API1: Broken Object Level Authorization のシナリオと同様に考えて、 ユーザのidを変更して他のアカウントの情報を取得できないかを試します。

「Authorization-Token」ヘッダの値が検証されているため、他のユーザの情報にはアクセスができないことがわかりました。次に、問題文の「管理者が異なるルートを使用している」という情報について考えます。/vapi/api5/user/{user_id} というAPIエンドポイントでユーザの情報を取得できることから、全ユーザの情報を取得するための似たようなAPIエンドポイントがある可能性を考えます。そこで、GET /vapi/api5/users というAPIエンドポイントがあることを予想し、リクエストを送ります。

全ユーザの情報とflagを取得することができました。

本シナリオでは、本来非公開とするべき管理者用のエンドポイントに対して正当なAPI呼び出しを実行することでアクセスが可能な状態となっていました。エンドポイントの情報は公開されていなくても存在することを推測し、アクセスされる可能性があります。意図的に公開をしていない場合、デフォルトでは全てのアクセスを拒否するようにして、必要に応じて特定のロールに対してのみアクセス許可を与えるように設定や実装をする必要があります。new window[5]

4.API6: Mass Assignment

問題文

Welcome to our store , We will give you credits if you behave nicely. Our credit management is super secure

APIの情報

パス メソッド 認可制御 備考
/vapi/api6/user POST ユーザ作成
/vapi/api6/user/me GET ユーザ情報の取得

問題文から、脆弱性を突くことで所持しているクレジットを増やすことができるのかと考えます。まずはAPIの挙動を確認するため、「api6testuser」というユーザを作成します。

作成した「api6testuser」ユーザの情報を取得します。

ユーザ作成時には指定していない「credit」というパラメーターがあることがわかります。ユーザ作成時のパラメーターとして仕様書にはなかった情報ですが、問題文から、何らかの方法でこのクレジットを増やすことができそうです。本シナリオには、ユーザ作成とユーザ情報取得の2つのAPIがあるので、ユーザ作成時に「credit」を意図的に指定することで、ユーザ側で任意の値を設定できないかと考えます。そこで、「api6testuser2」というユーザを作成する際にパラメーターに「credit」というキーを追加し、 値に「10000」を設定してAPIを実行します。

「api6testuser2」ユーザが登録されたことを確認しました。続いて、ユーザ情報取得APIを実行します。

「credit」の値が作成時に指定した通り「10000」となっていることを確認し、flagを取得することができました。

オブジェクトのプロパティには、ユーザの入力値を受け取って更新されることを想定されているものと、想定されていないものがあります。本シナリオの場合、「credit」は本来内部的に設定するべき情報であり、ユーザから値を受け取り更新してはなりません。しかし、オブジェクトプロパティにアクセスして書き換えができるようになっていました。ユーザが更新する必要があるプロパティのみを許可リストに登録する、ユーザの入力をコード変数、または内部オブジェクトに自動的にバインドする関数の使用は避けるなどして、プロパティがユーザにより更新されないように実装する必要があります。new window[6]

5.API9: Improper Assets Management

問題文

Hey Good News!!!!! We just launched our v2 API :)

APIの情報

パス メソッド 認可制御 備考
/vapi/api9/v2/user/login POST ユーザログイン

問題文では、v2をローンチしました、という情報のみが与えられます。サンプルリクエストをみると、与えられたユーザログインAPIを使って、「richardbranson」というユーザとしてログインすることが求められているようです。まずは、サンプルのリクエストを送信してAPIの挙動を確認します。

200 OK が返されますが、ボディ部分には何も情報がありません。ログインするためには、”****”の部分に対して0000~9999までを入力してPINを特定する必要がありそうだと考えます。しかし、レスポンスのヘッダを確認すると、「X-RateLimit-Limit」「X-RateLimit-Remaining」があり、リクエスト数が制限されているように見えます。試しにリクエストを再送してみます。

「X-RateLimit-Remaining」の値が減っていくことがわかりました。繰り返しアクセスし、「X-RateLimit-Remaining」の値が 0 になると、以降「500 Internal Server Error」が返され、リクエスト数が制限されていることを確認しました。この制限があるため、PINの値を繰り返し入力して割り出すことは困難であるとわかります。ここで、v2 をローンチしたという問題文を思い出します。脆弱なバージョンのv1にアクセスできないかと考え、パスのv2をv1に変更してリクエストを送信します。

200 OKが返されました。ボディ部分に何も情報がないのはv2と同じです。しかし、レスポンスのヘッダにv2のときにあった「X-RateLimit-Limit」「X-RateLimit-Remaining」がないため、v1ではリクエスト数が制限されていない可能性があると推測できます。リクエストをBurpSuiteのIntruderに送り、0000~9999でPINの繰り返し入力を行います。

v2とは異なり、リクエスト数によりエラーが返されることはないようです。ログインに失敗した際にはボディが返されません。ログインに成功したものをみつけるために、Length が大きいレスポンスに注目してソートを行います。

PINに「1655」を送付した際にログインすることができ、flagを取得することができました。

本シナリオでは、脆弱な古いAPIバージョンに対してアクセス可能になっていたため、容易に繰り返し入力、すなわちブルートフォース攻撃を行うことができました。本番用データを非本番用APIのデプロイメントで使用しないようにする、古いバージョンは速やかに削除し、最新バージョンに移行するといった運用が必要です。new window[7]

おわりに

本記事では、APIセキュリティについて学ぶためのオープンソースラボ環境vAPIを紹介しました。vAPIはシナリオがわかりやすく、準備も容易で取り組みやすいラボ環境です。APIにおいてよく作りこまれてしまいがちな脆弱性について、手を動かして楽しく学べるので、ぜひ取り組んでみてください。

参考情報

執筆者プロフィール

中島 春香なかしま はるか
リスクハンティング・システムグループ

ペネトレーションテスト、脆弱性診断を通じたセキュア開発支援、社内CTF運営に従事。
CISSP Associate、GCPN、認定Webアプリケーション脆弱性診断士を保持。
CTF for GIRLS副代表として主にWeb分野の問題作成やワークショップ企画設計を担当。「Hardening II SU」 MVV(Most Valuable vendor)賞受賞。

執筆者の他の記事を読む

アクセスランキング