原文鏈接:http://drops.wooyun.org/tips/1067。
在測試時發現了一些問題,于是把解決方案與一些結論共享出來,以供需要。
原文提供的腳本直接導入執行會拋出“數據無效”的錯誤,測試系統在查詢分析器中獲取到的密文為:
[ 0x01, 0x00, 0x00, 0x00, 0xD0, 0x8C, 0x9D, 0xDF, 0x01, 0x15, 0xD1, 0x11, 0x8C, 0x7A, 0x00, 0xC0, 0x4F, 0xC2, 0x97, 0xEB, 0x01, 0x00, 0x00, 0x00, 0xCD, 0x2A, 0x0B, 0x54, 0x64, 0x6A, 0xBC, 0x4B, 0xB0, 0x99, 0xF1, 0xE6, 0x3D, 0x93, 0x9E, 0x6E, 0x04, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x5F, 0x00, 0x53, 0x00, 0x51, 0x00, 0x4C, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x39, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x20, 0x00, 0x4D, 0x00, 0x61, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x4B, 0x00, 0x65, 0x00, 0x79, 0x00, 0x20, 0x00, 0x45, 0x00, 0x6E, 0x00, 0x63, 0x00, 0x72, 0x00, 0x79, 0x00, 0x70, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x03, 0x66, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3F, 0xDA, 0x14, 0xC2, 0xE9, 0x3E, 0xC7, 0xDE, 0x3A, 0x5C, 0xC2, 0xAE, 0x0F, 0x27, 0xAB, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4C, 0xED, 0x01, 0x57, 0x33, 0x68, 0x06, 0x7E, 0x81, 0x4D, 0xB9, 0xF7, 0x13, 0xC6, 0x30, 0x34, 0x18, 0x00, 0x00, 0x00, 0xE9, 0x20, 0xD6, 0x91, 0xCD, 0x70, 0xF2, 0xC4, 0x0F, 0xB4, 0x97, 0xF7, 0xBB, 0x7C, 0x68, 0x76, 0x4D, 0x1F, 0x9D, 0x6D, 0xC8, 0x56, 0xBF, 0x04, 0x14, 0x00, 0x00, 0x00, 0xEA, 0x60, 0x44, 0xBC, 0xAC, 0xA3, 0x92, 0x1D, 0x04, 0xCC, 0x5E, 0x6C, 0x47, 0xE5, 0x1B, 0x0D, 0xE1, 0xF9, 0x29, 0xD8]
長度為236字節。而在powershell中獲取到的密文為:
[ 0x01, 0x00, 0x00, 0x00, 0xD0, 0x8C, 0x9D, 0xDF, 0x01, 0x15, 0xD1, 0x11, 0x8C, 0x7A, 0x00, 0xC0, 0x4F, 0xC2, 0x97, 0xEB, 0x01, 0x00, 0x00, 0x00, 0xCD, 0x2A, 0x0B, 0x54, 0x64, 0x6A, 0xBC, 0x4B, 0xB0, 0x99, 0xF1, 0xE6, 0x3D, 0x93, 0x9E, 0x6E, 0x04, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x5F, 0x00, 0x53, 0x00, 0x51, 0x00, 0x4C, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x39, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x20, 0x00, 0x4D, 0x00, 0x61, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x4B, 0x00, 0x65, 0x00, 0x79, 0x00, 0x20, 0x00, 0x45, 0x00, 0x6E, 0x00, 0x63, 0x00, 0x72, 0x00, 0x79, 0x00, 0x70, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x03, 0x66, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3F, 0xDA, 0x14, 0xC2, 0xE9, 0x3E, 0xC7, 0xDE, 0x3A, 0x5C, 0xC2, 0xAE, 0x0F, 0x27, 0xAB, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4C, 0xED, 0x01, 0x57, 0x33, 0x68, 0x06, 0x7E, 0x81, 0x4D, 0xB9, 0xF7, 0x13, 0xC6, 0x30, 0x34, 0x18, 0x00, 0x00, 0x00, 0xE9, 0x20, 0xD6, 0x91, 0xCD, 0x70, 0xF2, 0xC4]
長度為196字節,少了40字節。經過多次測試發現問題處于查詢語句中的len函數。
msdn(http://technet.microsoft.com/zh-cn/library/ms190329.aspx)上對len函數的說明是:返回指定字符串表達式的字符數,其中不包含尾隨空格。
由于函數返回的是字符數,自然要涉及編碼。老外的操作系統是英文,默認使用IBM437 (OEM United States) 編碼,這個編碼是單字節字符集(sbcs),0x00-0xff都被認為是一個字符,所以獲取的字符長度與字節長度相等。而中文操作系統默認使用gb2312編碼,屬于雙字節字符集(dbcs),大于0x7f的字節都將與其后的一個字節合并作為一個字符,于是在處理含有大于0x7f的字節數組時,len函數獲取的長度與字節數并不相等,最終會由于密文不完整而出錯。統計完整密文中大于0x7f的字節總數并排除連續兩個的情況,正好是40.
將原腳本第74行、第100行共計三個len函數換為datalength函數(http://technet.microsoft.com/zh-cn/library/ms173486.aspx)。
原文中有兩個錯誤的定論,而這兩個定論在某些條件下恰好可以組合成為一個新的獲取途徑。
第一個錯誤的定論是存放熵的注冊表鍵所屬項權限,原文:“再次提醒,訪問此表項需要本地系統的管理員權限。”的說法是完全錯誤的。
可以想象,如果sqlserver以低權限運行時不能訪問這個注冊表項,那么執行sp_addlinkedsrvlogin
必然會拋出錯誤,而實際上無論sqlserver的權限是什么,這個鍵總可以被sqlserver所訪問。
在默認情況下,此鍵所在項(HKLM\SOFTWARE\Microsoft\Microsoft SQL Server\[instancename]\Security
)有以下四個權限:
system:完全控制,子項繼承
administrators:完全控制,子項繼承
creator owner:完全控制
SQLServerMSSQLUser$[機器名]$[實例名]:查詢,修改,創建子項,枚舉子項,通知,讀取DAC
前兩個權限不必解釋,第三個權限代表所有者,一般為system/administrator
。
第四項代表sqlserver實例用戶組,每個實例對應一個。在修改sqlserver某個實例的服務登錄帳戶時,會自動將這個賬戶加入此組。
所以當某個程序的運行賬戶與sqlserver某實例服務賬戶相同,那么這個程序可以打開注冊表并讀取此實例的Entropy Key。實際上,真正起到驗證作用的實際上只有第四項。
第二個錯誤的定論是DAC連接的條件,原文:“打開專用管理員連接有兩個條件:一是需要有sqlserver的Sysadmin權限,二是本地服務器的管理員權限。”。
而實際上,打開DAC鏈接只需要屬于sysadmin固定服務器角色成員,DAC是支持遠程連接的。
于是有以下推論:
1.當某個程序的運行賬戶與sqlserver某實例服務賬戶相同,那么這個程序可以打開注冊表并讀取此實例的Entropy Key。
2.由于實例服務賬戶所在用戶組SQLServerMSSQLUser$[機器名]$[實例名]存在于sqlserver登陸名列表中并屬于sysadmin固定服務器角色成員,于是這個憑據可用來打開DAC鏈接。
3.由于實例服務賬戶屬于本地組成員,所以可以打開MachineKey進行加密解密。
4.連接服務器密碼解密只依賴于Entropy Key,MachineKey和DAC鏈接
最后得出結論:
當某個程序的運行賬戶與sqlserver某實例服務賬戶相同,則此程序可以獲取到此sqlserver實例中所有鏈接服務器的密碼。
在實際利用上,一個典型例子就是iis。眾所周知2003的iis默認應用程序池權限是Network Service,而一些管理員對sqlserver降權的做法也是將sqlserver實例服務賬戶更改為Network Service。于是在上述情況下,任何asp.net應用程序都可以通過windows驗證方式直接作為sa登陸,同時解密并導出鏈接服務器密碼。對于asp等由于使用了來賓帳戶作為默認憑據,則需要執行額外的程序來獲取。
如圖:當sqlserver實例服務賬戶配置為network service運行時,默認iis應用程序池賬戶network service具有讀取注冊表的權限:
于是可以通過工具直接獲取到鏈接服務器密碼:
有了以上結論與原始的Powershell腳本,工具也就不難寫了,Powershell腳本實際上就是.net類庫的調用,略作修改即可。
lspwd.exe為一鍵獲取工具,lspwd.js為源碼,編譯命令行:
jsc /r:system.xml.dll lspwd.js
在asp.net中利用的工具以AspxSpy插件形式提供,GetMSSQLLinkedServerPasswordPlugin.cs為源碼,編譯命令行:
csc /t:library GetMSSQLLinkedServerPasswordPlugin.cs
GetMSSQLLinkedServerPasswordPlugin.dll
和GetMSSQLLinkedServerPasswordPlugin.dll.Deflated
分別為未壓縮和壓縮后的插件。
插件信息如下:
TypeName:Zcg.Test.AspxSpyPlugins.GetMSSQLLinkedServerPasswordPlugin
MethodName:Run
HTML Result:true
Params:null
輸出:當前服務器所有能成功連接的sqlserver實例中所有鏈接數據庫信息。
調用結果如圖: