企業資安防禦的關鍵點:暗網情資>>>立即了解>>>
技術分析

破解 NTLM 密碼:深入 SAM 結構的內幕

2024.08.13GSS & IR Team
Share:
By Sharon

不知道大家有沒有好奇過,像 mimikatz 和 impacket 這樣的工具是如何取出使用者的 NTLM 雜湊密碼 (NTLM Hash / NTHash)呢?若是想理解為什麼,我們就得深入從 windows registry 了解 SAM(安全帳戶管理器)結構。
首先,Windows 作業系統會將使用者的 NTLM 雜湊密碼 (NTHash) 透過 RC4 或是 AES「加密」演算後存放到 Registry 的 SAM 中,這當中會使用到包括其他 windows 的預設字串以及預設的加密演算法,如下所示:
Windows 的 預設字串:
  • !@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%
  • 0123456789012345678901234567890123456789
  • NTPASSWORD
  • 0x00 x 2

Windows 預設加密演算法:
  • DES → 用來當作第一層使用者密碼的混淆
  • MD5 → 用來製作鑰匙
image
微軟為了增加密碼安全性,會把上述加密後的資訊分散存放到以下四個註冊值:
  • User F : \HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\%RID%\F
  • User V : \HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\%RID%\V
  • Account F : \HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\F
  • System : \HKEY_LOCAL_MACHINE\\System\CurrentControlSet\Control\Lsa

image
fig 1. User F / V
image
fig 2. Domain Account F

接下來,我們將以「RC4 的加密方式」為例,一步一步剖析,如何透過擷取上述 Registry 個別的特定參數,說明解密的原理。
單元會依照此順序做介紹:
  1. User F 值
  2. User V 值
  3. Domain Account F 值
  4. SYSTEM 結構
  5. 整理以上取得的資訊
  6. 梳理加解密過程





1.User F值

(fixed length, 80)
\HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\%RID%\F
image
在這三個結構裡,User F 值 應該是最直覺的。他的大小永遠固定為 0x00 到 0x4F,也就是 80 bytes 的大小。 那在解密的過程中,我們 User F 值 唯一需要用到的資訊是「UserId」的值。 這裡所標示的 UserId 相同於我們的「相對識別碼 (RID)」。
image
RC4 加解密時會需要用到 RID 的值 (F4 01 00 00) 做為計算鑰匙的一部分。 DES 加解密時 RID 會被用來計算成 K1K2 這兩把鑰匙。

  • RC4_Encrypted (最終結果會存入 User V[14])
    = RC4( DES_Encrypted, MD5(SYSKEY+DWORD(RID) +"NTPASSWORD"+"\x00"))
  • DES_Encrypted
    = DES(NTHash, k1) + DES(NTHash, k2)

所以接下來,我們將從使用者 F 獲取的 RID ,跟著下面的步驟計算出我們 K1 和 K2 的值:
  1. RID DWORD (4 位元組 / 4 bytes) 格式=F4 01 00 00。K1 和 K2 各是 7 位元組 (byte),製作方式就是單純將 F4 01 00 00 依照順序先填滿 K1 再來接續填滿 K2。
    如下圖:
image
所以我們現在的結果如下:
K1 = F4 01 00 00 F4 01 00
K2 = 00 F4 01 00 00 F4 01

  1. 把 K1 和 K2 先轉換成二位元 (F4 = 1111010) image
  2. 把 K1 和 K2 從 7 位元組 (byte) 轉為 8 位元組,我們要先把所有位元組以連接在一起的方式列出來,然後在每 7 位元 (bit) 的分段開來後面 + 0,進行同位位元的計算。

K1 同位元計算
連接在一起呈現: 11110100000000010000000000000000111101000000000100000000
每 7 位元 (bit) 分段後 "+ 0" :
  • 1111010+0=F4
  • 0000000+0=00
  • 0100000+0=40
  • 0000000+0=00
  • 0000111+0=0E
  • 1010000+0=A0
  • 0000010+0=04
  • 0000000+0=00

K2 同位元計算
連接在一起呈現: 00000000111101000000000100000000000000001111010000000001
每 7 位元 (bit) 分段後 "+ 0" :
  • 0000000+0=00
  • 0111101+0=7A
  • 0000000+0=00
  • 0010000+0=20
  • 0000000+0=00
  • 0000011+0=06
  • 1101000+0=D0
  • 0000001+0=02
所以我們現在得到的結果如下:
> - K1 = F4 00 40 00 0E A0 04 00
> - K2 = 00 7A 00 20 00 06 D0 02


User F 的重點整理

User F 裡唯一取得的資訊為 RID。 此參數在 DES 會被用來運算成 K1 與 K2 的兩把鑰匙,也會被 RC4 作為製作密鑰運算中的一環。
RID = F4 01 00 00
K1 = F4 00 40 00 0E A0 04 00
K2 = 00 7A 00 20 00 06 D0 02

User F 取出的值與加密過程的對應:
1.DES_Encrypted
= DES(NTHash, k1)+DES(NTHash, k2)
2.SYSKEY
= RC4(EncKey, MD5((Salt + "!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%"+"\x00"+Bootkey+"0123456789012345678901234567890123456789"+`"\x00"))
3.RC4_Encrypted (最終結果會存入 User V[14])
= RC4(DES_Encrypted, MD5(SYSKEY+DWORD(RID)+"NTPASSWORD"+"\x00"))





2.User V 值

(varies in length, 80)
\HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\%RID%\V
image
User V 的第一部分:「SAMP_VARIABLE_LENGTH_ATTRIBUTE」有固定大小為: 0xCC (0~0xCB), 也就是 204 bytes 的大小。

image
它儲存了 UserV 值 中前 17 個陣列 (array) 的資訊摘要,包含自己。每個陣列長度為 12 位元組 (4+4+4)*17=0xCC ,而把這陣列切成 4 位元組的三等份,從左到右分別記載著「偏移量(Offset)」、「長度(Length)」以及「限定符(Qualifier)」,如下:
image
  • 資料的起點應計算為: 偏移量 + 0xCC → 所以 0xCC 是資料的開頭
  • 資料的終點則計算為: 偏移量 + 0xCC + 長度 → 而 0x1C0 是資料的結尾

如果想開發工具的話,理解此結構可以幫助我們辨別 UserV 值 內的資訊是否存在,若 Length 為 0 ,即為不存在。
那為了解密,我們需要取的是 UserV 值 中的第 13 (LMHash)第 14 (NTHash) 的陣列。請看下列圖例:
image
RC4 加密的 NTHash
(開頭 "User V 值圖例" 的 NTHash (SAMP_User_UNICODE_PWD) 使用的第二層加密為 AES,而非 RC4。所以請參考此 NTHash 為準)
image
以上兩者都屬於加密使用者密碼的方式,而自從 Windows Vista 和 Windows Server 2008 之後,微軟預設就禁止了 LMHash。所以這篇文章會以 NTHash 來做解密的運算。
SAMP_USER_UNICODE_PWD (User V[14]) = 01 00 01 00 92 A7 A7 EC 28 9E 26 60 DD F4 8F 54 C9 2D 3F 87

User V 的重點整理

User V 裡唯一取得的資訊是「最終加密版本的 NTHash」 (User V[14]) = RC4(RC4(DES(NTHash)))
User V[14] = SAMP_USER_UNICODE_PWD = 01 00 01 00 92 A7 A7 EC 28 9E 26 60 DD F4 8F 54 C9 2D 3F 87
image

User V 取出的值與加密過程的對應:
  1. DES_Encrypted
    = DES(NTHash, k1) + DES(NTHash, k2)
  2. SYSKEY
    = RC4(EncKey, MD5((Salt+"!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%"+"\x00"+Bootkey+"0123456789012345678901234567890123456789"+"\x00"))

  3. RC4_Encrypted (最終結果會存入 User V[14])
    = RC4(DES_Encrypted, MD5(SYSKEY+DWORD(RID)+"NTPASSWORD"+"\x00"))






3.Domain Account F 值(RC4 version)

(fixed length, 336)
\HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\F
image

Domain Account F 值 的結構在加密方法上會有所不同,上面所呈現的是使用 RC4 加密的結構示意圖。 先前提到加密會使用 RC4 與 AES 兩種方式,區分方法是看「Version」的值,如圖: image

而加密結構上的不同也只出於 0x68 後的資訊(如下圖所示)。 了解兩者加密運算法的話,便能得知後面結構的不同僅來自加密過程會用到的數值不同而已 (RC4 的 0x70, 0x80, 0x90 接固定為 16 bytes)。
image

Domain Account F 的重點整理

Domain Account F 裡取得的資訊為下:
  • 加密方式: 0x10002 → RC4
  • 加密/解密的資訊:
  • Salt = 0x70= 6F D9 76 B6 45 84 57 CB 9D 12 33 47 23 11 AC 9F
  • EncKey = 0x80 = 2C E6 69 B6 BD FB A6 D0 F7 2F 42 9F 6F 1A F2 08

Domain Account F 取出的值與加密過程的對應:
  1. DES_Encrypted*
    =DES(NTHash, k1) + DES(NTHash, k2)

  2. SYSKEY
    =RC4(EncKey, MD5((Salt+"!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%"+"\x00"+Bootkey+"0123456789012345678901234567890123456789"+"\x00"))

  3. RC4_Encrypted (最終結果會存入 User V[14])
    =RC4(DES_Encrypted, MD5(SYSKEY+DWORD(RID)+"NTPASSWORD"+"\x00"))






4.SYSTEM 結構

\HKEY_LOCAL_MACHINE\\System\CurrentControlSet\Control\Lsa\{Data, GBG, JD, Skew1}
使用 Registry Viewer (切記 windows 內建的 registry editor ) image

Windows 登入的本機帳號及密碼,會以 LM 或 NTLM hash 的型態存放在 SAM 登錄檔 (Registry 的 User V[14]) 裡。而自從 Windows NT 4.0 後,預設就開始使用 Syskey 來加密 SAM 裡面的資訊。這把 Syskey 會被做以下會講到的運算,分開來存入 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa 的四個機碼: "JD, Skew1, GBG, Data" 。真正數值可到機碼的 Class name 存取出來 (可參考以下圖)。
所以自從 SAM 的密碼開始被加密後,要解出密碼就需要連登錄檔的 SYSTEM 也一併取回才能解出 SysKey ,才有辦法解開使用者密碼的 Hash 值。
"JD, Skew1, GBG, Data" 這四個地方取得出 Class Name (字串值) 的資訊可以組成「bootkey(也稱 Syskey)」,這把鑰匙是 Windows 系統用來加密和保護系統關鍵數據的一部分。例圖: image

製作 bootkey 過程:
  1. 把四個值連接在一起: 假設 JD, Skew1, GBG, 和 Data 這四個值如下,將十六進制序列依序連接在一起,形成一個長的十六進制數字串。
     合併數據: JD + Skew1 + GBG + Data
                = "c2 a4 4d fa"  +  "a2 30 c4 ab"  +  "74 e4 48 2e"  +  "73 0b d8 57"
                = c2 a4 4d fa a2 30 c4 ab 74 e4 48 2e 73 0b d8 57
    最終得出 c2 a4 4d fa a2 30 c4 ab 74 e4 48 2e 73 0b d8 57
  2. 使用 ShiftArray 陣列演算法,將值進行重新排列,生成 bootkey
  3. ShiftKey 典型值 = [0x8, 0x5, 0x4, 0x2, 0xB, 0x9, 0xD, 0x3, 0xC, 0x0, 0x6, 0x1, 0xA, 0xE, 0xF, 0x7]
image
對應字節排序下來將是我們的 Bootkey。

SYSTEM 的重點整理

這裡取得的唯一資訊為:
  • Bootkey = 74 30 a2 4d 2e e4 0b fa 73 c2 c4 a4 48 d8 57 ab

SYSTEM 取出的值與加密過程的對應:
1.DES_Encrypted
= DES(NTHash, k1) + DES(NTHash, k2)
2.SYSKEY
=RC4(EncKey, MD5((Salt + "!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%"+"\x00"+Bootkey+"0123456789012345678901234567890123456789"+"\x00"))
3.RC4_Encrypted (最終結果會存入 User V[14])
= RC4(DES_Encrypted, MD5(SYSKEY+DWORD(RID)+"NTPASSWORD"`+`"\x00"))





5.整理以上取得的資訊

Windows 會用到的預設字串:
  • !@#$%^&()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(@&%
  • 0123456789012345678901234567890123456789
  • NTPASSWORD
  • 0x00 x 2
User F
  • RID = F4 01 00 00
  • K1 = F4 00 40 00 0E A0 04 00
  • K2 = 00 7A 00 20 00 06 D0 02
User V
  • SAMP_USER_UNICODE_PWD
= User V[14]
= 01 00 01 00 92 A7 A7 EC 28 9E 26 60 DD F4 8F 54 C9 2D 3F 87
image
Domain Account F
加密方式 = RC4
加密/解密的 資訊:
  • Salt = 0x70 = 6F D9 76 B6 45 84 57 CB 9D 12 33 47 23 11 AC 9F
  • EncKey = 0x80 = 2C E6 69 B6 BD FB A6 D0 F7 2F 42 9F 6F 1A F2 08

System
  • Bootkey =74 30 a2 4d 2e e4 0b fa 73 c2 c4 a4 48 d8 57 ab





6.梳理加解密過程

根據上述的「加密」過程,我們來對比一下「解密」的過程:

加密過程

  1. DES_Encrypted
    =DES(NTHash, k1)+DES(NTHash, k2)

  2. SYSKEY
    =RC4(EncKey, MD5((Salt + "!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%"+"\x00"+Bootkey+"0123456789012345678901234567890123456789"+"x00"))

  3. RC4_Encrypted (最終結果會存入 User V[14])
    =RC4(DES_Encrypted, MD5(SYSKEY+DWORD(RID)+"NTPASSWORD"+"\x00"))

DES_Encrypted
DES 的運算目的是給予 NTHash 第二層的加密保護,而使用 RID 作為 DES 中的 K1 和 K2 用意在於當兩個或多個使用者擁有相同密碼時可以進一步的混淆使用者的 NTHash。
SYSKEY
SYSKEY 會進行第一次的 RC4 加密運算,把 User F 值 的 EncKey(0x80) 作為 Data 加密起來。加密會使用到上述 User F 值 的 Salt (0x70) 、 Bootkey 、Windows 加密預設字串。
RC4_Encrypted
最後,我們將 SYSKEY 算出來的數字做為鑰匙,把 DES 加密後的 NTHash 作為 Data 再次進行 RC4 加密的動作。


解密過程 (含圖例)

  1. SYSKEY
    =RC4(EncKey, MD5((Salt + "!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%"+"\x00"+Bootkey+"0123456789012345678901234567890123456789"+"\x00"))

  2. RC4_Decrypted
    =RC4(User V[14], MD5(SYSKEY+DWORD(RID)+"NTPASSWORD"+"\x00"))

  3. DES_Decrypted
    = DES(RC4_Decrypted, k1)+DES(RC4_Decrypted, k2)

SYSKEY
解密過程的話,我們需要先從所有加密的「鑰匙」開始運算,第一步先拿最外層的鑰匙 SYSKEY 作解密。步驟跟上面的一樣,因為 RC4 是對稱式加密 (symmetric encryption)。
RC4_Decrypted
有了 SYSKEY 即可運算出下一把鑰匙: MD5(SYSKEY + RID +NTPASSWORD + 0x00)。拿取User V 值 的 第 14 個陣列 (SAMP_USER_UNICODE_PWD or User V[14]) 作為 Data ,使用鑰匙進行 RC4 的解密。
DES_Decrypted
現在我們剩下的就是被 DES 加密起來的 NTHash (DES(NTHash)),因 DES 也是對稱式的加密,用 K1 與 K2 重複相同的動作即可解出使用者的 NTHash。
image
2024.08.13GSS & IR Team
Share:
為提供您最佳的服務體驗,本網站使用 Cookies。當您使用本網站,即表示您同意 Cookies 技術支援。更多資訊請參閱隱私權與Cookies使用政策。