經(jīng)常討論一些您發(fā)現(xiàn)的少量錯(cuò)誤并讓人們知道它們是很有益的。在本月的專(zhuān)欄中,討論兩個(gè)主題:
交互式服務(wù)
調(diào)用 _alloca()
安全性、服務(wù)和交互式桌面
與 Unix 守護(hù)程序類(lèi)似,服務(wù)是 Microsoft Windows NT 的中樞,可以向操作系統(tǒng)和用戶(hù)提供重要功能而無(wú)需用戶(hù)的參與。創(chuàng)建服務(wù)時(shí)幸恍┪侍廡枰⒁狻?/p>
Microsoft Windows 中的服務(wù)通常是控制臺(tái)應(yīng)用程序,它們的運(yùn)行無(wú)需用戶(hù)參與,也沒(méi)有用戶(hù)界面。但在某些實(shí)例中,服務(wù)可能需要與用戶(hù)進(jìn)行交互。運(yùn)行在較高安全環(huán)境中的服務(wù)(如 SYSTEM)不應(yīng)作為交互式服務(wù)運(yùn)行。在 Windows 用戶(hù)界面中,桌面是安全邊界,在交互式桌面上運(yùn)行的任何應(yīng)用程序可以與交互式桌面上的任何窗口交互,即使窗口并不可見(jiàn)。無(wú)論創(chuàng)建窗口的應(yīng)用程序的安全環(huán)境和應(yīng)用程序的安全環(huán)境如何,都是這樣。
由于這些設(shè)計(jì)特點(diǎn),任何在交互式桌面上打開(kāi)窗口的服務(wù)都會(huì)向登錄用戶(hù)所執(zhí)行的應(yīng)用程序公開(kāi)。如果服務(wù)試圖使用窗口消息控制其功能,則登錄用戶(hù)可以通過(guò)使用惡意消息來(lái)干擾該功能。
將服務(wù)作為 SYSTEM 運(yùn)行的做法(即,服務(wù)通過(guò)調(diào)用 OpenWindowStation 和 GetThreadDesktop 來(lái)支持交互式桌面)十分不可取。請(qǐng)注意,Windows 將來(lái)的版本可能會(huì)完全取消對(duì)交互式服務(wù)的支持。
我們建議服務(wù)編寫(xiě)人員使用客戶(hù)端/服務(wù)器技術(shù)(例如 RPC、套接字、命名管道或 COM)實(shí)現(xiàn)與來(lái)自某個(gè)服務(wù)的登錄用戶(hù)的交互,使用帶 MB_SERVICE_NOTIFICATION 的 MessageBox 顯示簡(jiǎn)單的狀態(tài)。如果您的服務(wù)代碼具有以下任何屬性,請(qǐng)?zhí)岣呔瑁?BR> 作為 LocalSystem 運(yùn)行,并且服務(wù)在安全配置管理器中進(jìn)行了標(biāo)記(“登錄為”->“允許服務(wù)與桌面交互”),或注冊(cè)表項(xiàng) -> HKLMCCSServicesMyServiceType & 0x0100 == 0x0100)
CreateService,并且 dwServiceType & SERVICE_INTERACTIVE_PROCESS == SERVICE_INTERACTIVE_PROCESS
調(diào)用 MessageBox(),其中 uType and (MB_DEFAULT_DESK_ONLY | MB_SERVICE_NOTIFICATION | MB_SERVICE_NOTIFICATION_NT3X) != 0
調(diào)用 OpenDesktop("winsta0",...) 并在該桌面上創(chuàng)建用Ы緱?/p>
在 OpenDesktop 上調(diào)用 LoadLibrary/GetProcAddress
小心 _alloca
_alloca 函數(shù)可以在堆棧中分配動(dòng)態(tài)內(nèi)存。分配的空間將在調(diào)用函數(shù)退出時(shí)自動(dòng)釋放,而不只是在分配超出范圍時(shí)釋放。下面是使用 _alloca 的示例代碼:
void function(char *szData) {
PVOID p = _alloca(lstrlen(szData));
// 使用 p
}
如果攻擊者提供一個(gè)比堆棧大小還要長(zhǎng)的 szData,_alloca 會(huì)引發(fā)一個(gè)異常并導(dǎo)致應(yīng)用程序停止。如果該代碼位于服務(wù)器中,則情況會(huì)更糟。處理這種錯(cuò)誤情況的正確方法是將對(duì) _alloca 的調(diào)用打包在異常處理程序中,并在出現(xiàn)錯(cuò)誤時(shí)重置堆棧。
void function(char *szData) {
__try {
PVOID p = _alloca(lstrlen(szData));
// 使用 p
} __except ((EXCEPTION_STACK_OVERFLOW == GetExceptionCode()) ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH) {
_resetstkoflw();
}
}
相關(guān)問(wèn)題:ATL 轉(zhuǎn)換宏
您還應(yīng)當(dāng)小心某些調(diào)用 _alloca 的 ATL 字符串轉(zhuǎn)換宏。這些宏包括 A2W、W2A 和 CW2CT 等。如果您的代碼是服務(wù)器代碼,則調(diào)用其中任何轉(zhuǎn)換函數(shù)時(shí)都必須考慮數(shù)據(jù)的長(zhǎng)度。這是不要輕易相信輸入的又一個(gè)示例。如果攻擊者向您的代碼提供一個(gè) 10 MB 的字符串,便會(huì)摧毀堆棧并引發(fā)異常;或者如果未引發(fā)異常,則導(dǎo)致失敗。所以千萬(wàn)不要這樣做!
發(fā)現(xiàn)缺陷
沒(méi)有人看出上星期的代碼中的錯(cuò)誤,但很多人已接近目標(biāo)。其中的問(wèn)題是為明文和密文使用了相同的緩沖區(qū)。您永遠(yuǎn)都不能這樣做。
乍看起來(lái),使用相同的緩沖區(qū)存儲(chǔ)明文,然后加密明文產(chǎn)生的密文似乎很好。在大多數(shù)情況下也是這樣。但在多線程環(huán)境中就不是這樣了。設(shè)想一下,您的代碼中出現(xiàn)了一個(gè)“競(jìng)爭(zhēng)狀態(tài)”,而您卻并不知道。(競(jìng)爭(zhēng)狀態(tài)是由對(duì)軟件中的事件的相對(duì)時(shí)間產(chǎn)生意外的嚴(yán)格依賴(lài)而引起的。它們通常與同步錯(cuò)誤一起出現(xiàn)。)坦白地說(shuō),您永遠(yuǎn)不會(huì)知道存在著嚴(yán)重的競(jìng)爭(zhēng)狀態(tài),等知道時(shí)已經(jīng)太晚了。請(qǐng)?jiān)倏紤]一下,您的應(yīng)用程序的正常流程如下所示:
使用明文加載緩沖區(qū)。
加密緩沖區(qū)。
將緩沖區(qū)內(nèi)容發(fā)送給接收者。
這看起來(lái)很正常。但是,設(shè)想您有一個(gè)多線程應(yīng)用程序,由于某種原因,最后兩個(gè)步驟由于競(jìng)爭(zhēng)狀態(tài)而被交換:
使用明文加載緩沖區(qū)。
將緩沖區(qū)環(huán)境發(fā)送給接收者。
加密緩沖區(qū)。
接收者只接收到一些明文!這是在 Internet Information Server 4.0 中修復(fù)的一個(gè)錯(cuò)誤。在非常特殊的負(fù)載和極少數(shù)情況下,當(dāng)使用安全套接字層 (SSL) 保護(hù)從服務(wù)器至用戶(hù)的數(shù)據(jù)通道時(shí),服務(wù)器可能會(huì)遵循此模式,并將一個(gè)未加密的數(shù)據(jù)信息包發(fā)送給用戶(hù)。這種潛在問(wèn)題的破壞很小;只向用戶(hù)(或者可能是一個(gè)攻擊者)發(fā)送了一個(gè)信息包。并且當(dāng)用戶(hù)接收到信息包時(shí),客戶(hù)端軟件將斷開(kāi)連接。據(jù)說(shuō)該問(wèn)題已被 Microsoft 修復(fù)了。有關(guān)該缺陷的詳細(xì)信息,請(qǐng)?jiān)L問(wèn) http://www.microsoft.com/technet/treeview/default.asp?url=/technet/security/bulletin/ms99-053.asp(英文)。
修復(fù)的方法是使用兩個(gè)緩沖區(qū)。一個(gè)緩沖區(qū)用于明文,另一個(gè)用于密文,并確保密文在執(zhí)行不同調(diào)用時(shí)已被清空。
您能指出此代碼中的錯(cuò)誤嗎?
void ShuffleAndUpdate(char *szName, char *szPwd,
DWORD index,
DWORD d) {
DWORD dwArray[32];
ZeroMemory(dwArray,sizeof(dwArray));
BOOL fAllowAccess = FALSE;
if (IsValidUser(szName,szPwd)) {
fAllowAccess = TRUE;
ShuffleArray(dwArray,szName);
}
dwArray[index]= d;
if (fAllowAccess) {
// 執(zhí)行某些敏感的操作
}
}
Michael Howard 是 Microsoft 的 Secure Windows Initiative 組的安全程序經(jīng)理,是 Writing Secure Code 的作者之一,也是 Designing Secure Web-based for Applications for Windows 2000 的主要作者。他的主要工作就是確保人們?cè)O(shè)計(jì)、構(gòu)建、測(cè)試和記錄無(wú)缺陷的安全系統(tǒng)。他最喜歡的話(huà)是“尺有所短,寸有所長(zhǎng)”。
交互式服務(wù)
調(diào)用 _alloca()
安全性、服務(wù)和交互式桌面
與 Unix 守護(hù)程序類(lèi)似,服務(wù)是 Microsoft Windows NT 的中樞,可以向操作系統(tǒng)和用戶(hù)提供重要功能而無(wú)需用戶(hù)的參與。創(chuàng)建服務(wù)時(shí)幸恍┪侍廡枰⒁狻?/p>
Microsoft Windows 中的服務(wù)通常是控制臺(tái)應(yīng)用程序,它們的運(yùn)行無(wú)需用戶(hù)參與,也沒(méi)有用戶(hù)界面。但在某些實(shí)例中,服務(wù)可能需要與用戶(hù)進(jìn)行交互。運(yùn)行在較高安全環(huán)境中的服務(wù)(如 SYSTEM)不應(yīng)作為交互式服務(wù)運(yùn)行。在 Windows 用戶(hù)界面中,桌面是安全邊界,在交互式桌面上運(yùn)行的任何應(yīng)用程序可以與交互式桌面上的任何窗口交互,即使窗口并不可見(jiàn)。無(wú)論創(chuàng)建窗口的應(yīng)用程序的安全環(huán)境和應(yīng)用程序的安全環(huán)境如何,都是這樣。
由于這些設(shè)計(jì)特點(diǎn),任何在交互式桌面上打開(kāi)窗口的服務(wù)都會(huì)向登錄用戶(hù)所執(zhí)行的應(yīng)用程序公開(kāi)。如果服務(wù)試圖使用窗口消息控制其功能,則登錄用戶(hù)可以通過(guò)使用惡意消息來(lái)干擾該功能。
將服務(wù)作為 SYSTEM 運(yùn)行的做法(即,服務(wù)通過(guò)調(diào)用 OpenWindowStation 和 GetThreadDesktop 來(lái)支持交互式桌面)十分不可取。請(qǐng)注意,Windows 將來(lái)的版本可能會(huì)完全取消對(duì)交互式服務(wù)的支持。
我們建議服務(wù)編寫(xiě)人員使用客戶(hù)端/服務(wù)器技術(shù)(例如 RPC、套接字、命名管道或 COM)實(shí)現(xiàn)與來(lái)自某個(gè)服務(wù)的登錄用戶(hù)的交互,使用帶 MB_SERVICE_NOTIFICATION 的 MessageBox 顯示簡(jiǎn)單的狀態(tài)。如果您的服務(wù)代碼具有以下任何屬性,請(qǐng)?zhí)岣呔瑁?BR> 作為 LocalSystem 運(yùn)行,并且服務(wù)在安全配置管理器中進(jìn)行了標(biāo)記(“登錄為”->“允許服務(wù)與桌面交互”),或注冊(cè)表項(xiàng) -> HKLMCCSServicesMyServiceType & 0x0100 == 0x0100)
CreateService,并且 dwServiceType & SERVICE_INTERACTIVE_PROCESS == SERVICE_INTERACTIVE_PROCESS
調(diào)用 MessageBox(),其中 uType and (MB_DEFAULT_DESK_ONLY | MB_SERVICE_NOTIFICATION | MB_SERVICE_NOTIFICATION_NT3X) != 0
調(diào)用 OpenDesktop("winsta0",...) 并在該桌面上創(chuàng)建用Ы緱?/p>
在 OpenDesktop 上調(diào)用 LoadLibrary/GetProcAddress
小心 _alloca
_alloca 函數(shù)可以在堆棧中分配動(dòng)態(tài)內(nèi)存。分配的空間將在調(diào)用函數(shù)退出時(shí)自動(dòng)釋放,而不只是在分配超出范圍時(shí)釋放。下面是使用 _alloca 的示例代碼:
void function(char *szData) {
PVOID p = _alloca(lstrlen(szData));
// 使用 p
}
如果攻擊者提供一個(gè)比堆棧大小還要長(zhǎng)的 szData,_alloca 會(huì)引發(fā)一個(gè)異常并導(dǎo)致應(yīng)用程序停止。如果該代碼位于服務(wù)器中,則情況會(huì)更糟。處理這種錯(cuò)誤情況的正確方法是將對(duì) _alloca 的調(diào)用打包在異常處理程序中,并在出現(xiàn)錯(cuò)誤時(shí)重置堆棧。
void function(char *szData) {
__try {
PVOID p = _alloca(lstrlen(szData));
// 使用 p
} __except ((EXCEPTION_STACK_OVERFLOW == GetExceptionCode()) ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH) {
_resetstkoflw();
}
}
相關(guān)問(wèn)題:ATL 轉(zhuǎn)換宏
您還應(yīng)當(dāng)小心某些調(diào)用 _alloca 的 ATL 字符串轉(zhuǎn)換宏。這些宏包括 A2W、W2A 和 CW2CT 等。如果您的代碼是服務(wù)器代碼,則調(diào)用其中任何轉(zhuǎn)換函數(shù)時(shí)都必須考慮數(shù)據(jù)的長(zhǎng)度。這是不要輕易相信輸入的又一個(gè)示例。如果攻擊者向您的代碼提供一個(gè) 10 MB 的字符串,便會(huì)摧毀堆棧并引發(fā)異常;或者如果未引發(fā)異常,則導(dǎo)致失敗。所以千萬(wàn)不要這樣做!
發(fā)現(xiàn)缺陷
沒(méi)有人看出上星期的代碼中的錯(cuò)誤,但很多人已接近目標(biāo)。其中的問(wèn)題是為明文和密文使用了相同的緩沖區(qū)。您永遠(yuǎn)都不能這樣做。
乍看起來(lái),使用相同的緩沖區(qū)存儲(chǔ)明文,然后加密明文產(chǎn)生的密文似乎很好。在大多數(shù)情況下也是這樣。但在多線程環(huán)境中就不是這樣了。設(shè)想一下,您的代碼中出現(xiàn)了一個(gè)“競(jìng)爭(zhēng)狀態(tài)”,而您卻并不知道。(競(jìng)爭(zhēng)狀態(tài)是由對(duì)軟件中的事件的相對(duì)時(shí)間產(chǎn)生意外的嚴(yán)格依賴(lài)而引起的。它們通常與同步錯(cuò)誤一起出現(xiàn)。)坦白地說(shuō),您永遠(yuǎn)不會(huì)知道存在著嚴(yán)重的競(jìng)爭(zhēng)狀態(tài),等知道時(shí)已經(jīng)太晚了。請(qǐng)?jiān)倏紤]一下,您的應(yīng)用程序的正常流程如下所示:
使用明文加載緩沖區(qū)。
加密緩沖區(qū)。
將緩沖區(qū)內(nèi)容發(fā)送給接收者。
這看起來(lái)很正常。但是,設(shè)想您有一個(gè)多線程應(yīng)用程序,由于某種原因,最后兩個(gè)步驟由于競(jìng)爭(zhēng)狀態(tài)而被交換:
使用明文加載緩沖區(qū)。
將緩沖區(qū)環(huán)境發(fā)送給接收者。
加密緩沖區(qū)。
接收者只接收到一些明文!這是在 Internet Information Server 4.0 中修復(fù)的一個(gè)錯(cuò)誤。在非常特殊的負(fù)載和極少數(shù)情況下,當(dāng)使用安全套接字層 (SSL) 保護(hù)從服務(wù)器至用戶(hù)的數(shù)據(jù)通道時(shí),服務(wù)器可能會(huì)遵循此模式,并將一個(gè)未加密的數(shù)據(jù)信息包發(fā)送給用戶(hù)。這種潛在問(wèn)題的破壞很小;只向用戶(hù)(或者可能是一個(gè)攻擊者)發(fā)送了一個(gè)信息包。并且當(dāng)用戶(hù)接收到信息包時(shí),客戶(hù)端軟件將斷開(kāi)連接。據(jù)說(shuō)該問(wèn)題已被 Microsoft 修復(fù)了。有關(guān)該缺陷的詳細(xì)信息,請(qǐng)?jiān)L問(wèn) http://www.microsoft.com/technet/treeview/default.asp?url=/technet/security/bulletin/ms99-053.asp(英文)。
修復(fù)的方法是使用兩個(gè)緩沖區(qū)。一個(gè)緩沖區(qū)用于明文,另一個(gè)用于密文,并確保密文在執(zhí)行不同調(diào)用時(shí)已被清空。
您能指出此代碼中的錯(cuò)誤嗎?
void ShuffleAndUpdate(char *szName, char *szPwd,
DWORD index,
DWORD d) {
DWORD dwArray[32];
ZeroMemory(dwArray,sizeof(dwArray));
BOOL fAllowAccess = FALSE;
if (IsValidUser(szName,szPwd)) {
fAllowAccess = TRUE;
ShuffleArray(dwArray,szName);
}
dwArray[index]= d;
if (fAllowAccess) {
// 執(zhí)行某些敏感的操作
}
}
Michael Howard 是 Microsoft 的 Secure Windows Initiative 組的安全程序經(jīng)理,是 Writing Secure Code 的作者之一,也是 Designing Secure Web-based for Applications for Windows 2000 的主要作者。他的主要工作就是確保人們?cè)O(shè)計(jì)、構(gòu)建、測(cè)試和記錄無(wú)缺陷的安全系統(tǒng)。他最喜歡的話(huà)是“尺有所短,寸有所長(zhǎng)”。