Vault Policy の sudo を悪用してみる

## Vault Policy の sudo について

Vault は path ベースでデータを管理しており、そのパスに対するアクセス管理は、ポリシーを定義することで実現します。
ポリシーは設定を誤るとより強力な権限を持ったポリシーを適用することができます。
ポリシーは、path に対して `capability` と呼ばれる操作権限の可否を設定する必要があります。
今回は、capability の一つである `sudo` に注目しました。

`sudo` と聞くと、別のユーザや管理者権限で操作することを想起してしまいます。
Vault においては、特殊で `create`, `delete` などの `capability` とは全く機能が異なります。
`sudo` が付与されると、[Root protected API](https://developer.hashicorp.com/vault/tutorials/policies/policies#root-protected-api-endpoints) にアクセスできるようになります。
Root protected API 自体も Path があるので、アクセスしたい API に応じて、Path に対する capability を設定する必要があります。
とはいえ、ドキュメントを見るだけでは、`sudo` を使って何ができるのか、あまりイメージがわかなかったため、今回、`sudo` でどんな悪用ができるのか色々試してみました。

## 検証環境の準備

今回は Minikube を使い公式 Helm Chart にて検証環境を作っています。
手順は https://developer.hashicorp.com/vault/tutorials/kubernetes/kubernetes-minikube-consul です。
Unseal まで実施し、Pod `vault-0` に `exec /bin/sh` までしてください。

### User Bob と Alice を追加

最初は Root Token しかいないので、Bob と Alice のポリシー、ユーザを Root 権限で追加してください。

```
$ vault login
Token (will be hidden):
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                  Value
---                  -----
token                <SECRET>
token_accessor       AZSutG9aeFNmmW8haVLCGOvj
token_duration       ∞
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]
$ vault policy write bob bob.hcl
$ vault policy write alice alice.hcl
$ vault write auth/userpass/users/bob password=password policiy=bob
$ vault write auth/userpass/users/alice password=password policiy=alice
```

今回は `userpass` で認証します。

```
$ vault auth enable userpass

```

User Bob と Alice には、それぞれ以下のようなポリシーを用意し、ユーザとして追加します。
Bob と Alice は自分に割り当てられた Secret しかアクセスできないという設定です。

```:bob.hcl
path "secret/data/bob/*" {
  capabilities = ["create", "read", "delete", "update", "patch", "list"]
}
```

```:alice.hcl
path "secret/data/alice/*" {
  capabilities = ["create", "read", "delete", "update", "patch", "list"]
}
```

各ポリシーを Vault に登録します。

```
$ vault policy write bob bob.hcl
Success! Uploaded policy: bob
$ vault policy write alice alice.hcl
Success! Uploaded policy: alice
```

Bob と Alice をそれぞれのポリシーで追加します。

```
$ vault write auth/userpass/users//bob password=password policies=bob
Success! Data written to: auth/userpass/users//bob
$ vault write auth/userpass/users//alice password=password policies=alice
Success! Data written to: auth/userpass/users//alice
$ vault kv put secret/bob/foo name=bob
$ vault kv put secret/alice/foo name=alice
```

Bob でログインできるか試します。

```
$ vault login -method=userpass username=bob
Password (will be hidden):
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                    Value
---                    -----
token                  <SECRET>
token_accessor         JIIYb9PmF7QuUtqaxwWQqFb8
token_duration         768h
token_renewable        true
token_policies         ["bob" "default"]
identity_policies      []
policies               ["bob" "default"]
token_meta_username    bob
```

Bob の Policy は default と bob になっています。
Bob は bob ポリシーが適用されており、自分の Secret は読み書きできますが、Alice の Secret には何も操作できません。
以下のコマンドで確認します。

```
$ vault kv put secret/bob/foo name=bob
=== Secret Path ===
secret/data/bob/foo

======= Metadata =======
Key                Value
---                -----
created_time       2022-12-05T04:43:26.754224183Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            2

$ vault kv get secret/alice/foo
Error reading secret/data/alice/foo: Error making API request.

URL: GET http://127.0.0.1:8200/v1/secret/data/alice/foo
Code: 403. Errors:

* 1 error occurred:
	* permission denied

$ vault kv put secret/alice/foo name=bob
Error writing data to secret/data/alice/foo: Error making API request.

URL: PUT http://127.0.0.1:8200/v1/secret/data/alice/foo
Code: 403. Errors:

* 1 error occurred:
	* permission denied
```

## sudo 権限付与によって、任意のポリシーの Token を作る

Bob があるアプリケーションを構築しており、token を自由に払い出し、そのアプリケーションが Vault にアクセスする形にしたい、と Vault の管理者に要望を出しました。
管理者は良かれと思い、Bob に、Token 作成に必要な以下の Policy を追加しました。

```:token.hcl
path "auth/token/*" {
  capabilities = ["create", "update", "sudo"]
}
```

```
$ vault policy write token token.hcl
Success! Uploaded policy: token
$ vault write auth/userpass/users/bob password=password policies=bob policies=token
Success! Data written to: auth/userpass/users/bob
```

これで Bob は自由に Token が作成できます。が、これは結構まずい状況です。
早速 Bob の権限で Alice の権限に昇格してみます。
Bob は自身のポリシーでは、Alice のポリシーを見る・知ることはできませんが、ポリシー名さえわかれば Alice に適用されているポリシーを持つ Token を作成できます。

```
$ vault login -method=userpass username=bob

$ vault policy list
Error listing policies: Error making API request.

URL: GET http://127.0.0.1:8200/v1/sys/policies/acl?list=true
Code: 403. Errors:

* 1 error occurred:
	* permission denied

$ vault token create -policy=alice
...
token                <TOKEN>

$ vault login <TOKEN>
$ vault kv put secret/alice/foo name=bob
==== Secret Path ====
secret/data/alice/foo

======= Metadata =======
Key                Value
---                -----
created_time       2022-12-05T05:09:53.927799246Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            2

$ vault kv get secret/alice/foo
==== Secret Path ====
secret/data/alice/foo

======= Metadata =======
Key                Value
---                -----
created_time       2022-12-05T05:09:53.927799246Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            2

==== Data ====
Key     Value
---     -----
name    alice
```

Alice の Secret 領域に読み書きができました。

### Vault 管理者はどうすればよかったのか？

今回、Vault の管理者が Token 発行権限を Bob に与えたことが致命的でした。
任意の Policy を設定できてしまう Token 発行は必ず Vault の管理者が管理しましょう。

## 任意の認証手段の有効化と組み合わせ、任意のポリシーを付与する

あるシステムが Vault の認証に使えるということに Bob は気づきました。
Bob は検証のため、Vault の認証機能の有効無効を自由に変更したいと Vault の管理者に要望を出しました。
管理者は良かれと思い、Bob に以下のポリシーを追加してあげました。
その際、管理者は、有効無効を管理できるよう `sys/auth` の権限と、`auth/bob` 以下のパスを Bob 専用の認証情報専用に付与したと、Bob に伝えました。

```:auth.hcl
path "auth/bob/*" {
  capabilities = ["create"]
}

path "sys/auth/*" {
  capabilities = ["create", "update", "read", "sudo"]
}
```

```
$ vault policy write auth auth.hcl
Success! Uploaded policy: auth
$ vault write auth/userpass/users/bob password=password policies=bob policies=auth
Success! Data written to: auth/userpass/users/bob
```

Bob は任意の認証設定を Vault に追加し、Token 作成時と同様に、Alice のポリシーを持ったユーザを作成できます。
ここでは Bob 専用に割り当てられた path で管理される userpass を追加しています。

```
$ vault login -method=userpass username=bob

$ vault auth enable -path=bob/userpass userpass
Success! Enabled userpass auth method at: bob/userpass/
$ vault write auth/bob/userpass/users/fake-alice password=password policies=alice
Success! Data written to: auth/bob/userpass/users/fake-alice
$ vault login -method=userpass -path=bob/userpass username=fake-alice
Password (will be hidden):
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                    Value
---                    -----
token                  <SECRET>
token_accessor         7CiDeWp2XqvoiGyLBzfQpsRc
token_duration         768h
token_renewable        true
token_policies         ["alice" "default"]
identity_policies      []
policies               ["alice" "default"]
token_meta_username    fake-alice
```

### Vault 管理者はどうすればよかったのか？

今回、Vault の管理者が権限を `auth` 以下と `sys/auth` 以下に与えたのがよくありませんでした。
`sys/auth` に対して、本当に `sudo` が必要だったのでしょうか。Vault の認証方法は管理者が管理すべきではないでしょうか。
また、`auth/bob` に関しても、Bob に一任するのが適切だったのでしょうか？認証情報は管理者が管理するよう、`create` は提供せず、Bob は一覧表示できる程度でも良かったのではないでしょうか。

## まとめ

今回は `sudo` でどういったことができるかという実験をしました。結果として、ユーザが任意のポリシーの Token 作成, ユーザ追加をすることができました。
文中では、Alice というユーザの権限への昇格をしましたが、ポリシーはユーザの他に組織やシステム単位で設定されるのが一般的ですので、より大きな権限が付与されうることを意味します。

また、`sudo` を利用することで、今回検証していない API に対してもアクセスができます。
例えば、`pki/root` に `sudo` を付与することで PKI の秘密鍵を削除し、Vault PKI を使っているシステムに不具合を発生させたり、`sys/audit/:path` に `sudo` を付与することで監査を削除したり、都合のよいものにすり替えることもできそうです。

Vault の管理をされる方は、ユーザに `sudo` を設定しないことを基本とした上で、本当に必要かどうか考えるのがよいと思います。

## 余談

実は、本調査の前に社内で `sudo` が設定されているポリシーが事例が散見されました。
Issue や PR を見ても、なぜ `sudo` が追加されているのかわかりませんでした。
そもそも私も `sudo` の影響がわからないということもあったので、今回、影響調査を実施してみました。
本記事によってなんとなく付けている `sudo` 設定が減ったら幸いです。