證明 C Runtime 和 Windows API 對安全性的影響 (轉)

worldblog發表於2007-12-07
證明 C Runtime 和 Windows API 對安全性的影響 (轉)[@more@]

 應用性的一大進步:證明 C Runtime 和 對安全性的影響

摘要:本文將討論使用 C 和 C++ 進行時的常見錯誤及其安全隱患,並概括某些函式的正確使用方法。作為一種持續不斷的努力,在今後的幾個月中,我們將繼續展開討論,為更多的 API 提供安全性資訊。

簡介
在對 C 和 C++ 程式碼進行程式碼檢查以尋找安全薄弱環節時,我發現了在呼叫某些函式時的一些常見問題。儘管某種函式呼叫可能與安全性無關,但如果使用不當,仍會導致不易發覺的安全隱患。

本文將討論這些錯誤及其安全隱患,並將概述一些函式的正確使用方法。

對於在 MSDN 和 PlatfoSDK 中記載的一些 API 的安全性評價,我們已經展開了討論(這一討論仍在繼續)。在第一輪的討論中,我們概述了頂級函式呼叫,這些呼叫導致了 和非 Microsoft 產品的安全薄弱環節。

我們首先討論以下函式,這些函式在安全性方面尤為值得注意:

CopyMemory
CreateProcess、CreateProcesUser、CreateProcessWithLogonW
SetSecurityDescriptorDacl
模擬函式
memcpy
sprintf、swprintf
strcat、wcscat、_mbscat
strcpy、wcscpy、_mbscpy
strncat、wcsncat、_mbsncat
WinExec

CopyMemory
安全性評價
第一個引數 Destination 必須足以容納 count 個位元組的 組合大小,否則就可能發生緩衝區。這樣,當發生違規訪問時,應用程式就可能會遭到拒絕服務,或者更壞,可能會使攻擊者將可程式碼注入到您的程式中。如果 Destination 是基於堆疊的緩衝區,則尤為如此。要注意的是,最後一個引數 Length 是要複製到 Destination 的位元組數,而不是 Destination 的大小。

以下程式碼示例演示了安全使用 CopyMemory() 的方法:

void test(char *pbData, unsigned int cbData) {
  char buf[BUFFER_SIZE];
  CopyMemory(buf, pbData, min(cbData,BUFFER_SIZE));
}

CreateProcess、CreateProcessAsUser、CreateProcessWithLogonW
安全性評價
第一個引數 lpApplicationName 可以為 NULL。在這種情況下,可執行程式的名稱必須是 lpCommandLine 中第一個用空格分隔的字串。但是,如果可執行程式的名稱或路徑名中有空格,則存在一定的風險,因為如果空格處理不當,就可能會執行惡意的可執行程式。以下示例是危險的,因為該程式將試圖執行“Program.exe”(如果該程式存在),而不是“foo.exe”。

CreateProcess(NULL, "C:Program Filesfoo", ...)

如果惡意要在中建立名為“Program.exe”的特洛伊程式,那麼任何使用“Program Files”目錄不正確地呼叫 CreateProcess 的程式都將啟動特洛伊程式,而不是要呼叫的應用程式。

注意不要為 lpApplicationName 傳遞 NULL,以避免函式根據其執行時引數來分析並確定可執行路徑名。否則,如果 lpApplicationName 為 NULL,則用引號將 lpCommandLine 中的可執行路徑引起,如下例所示。

CreateProcess(NULL, ""C:Program Filesfoo.exe" -L -S", ...)

SetSecurityDescriptorDacl
安全性評價
最好不要建立具有 NULL DACL 的安全描述符(即:pDacl 為 NULL),因為這樣的 DACL 無法為提供安全性。實際上,攻擊者可在物件上設定一個 Everyone (Deny All Access) ACE,從而拒絕每個人(包括管理員)訪問該物件。NULL DACL 沒有為物件提供任何免受攻擊的保護。

模擬函式
安全性評價
如果對模擬函式的呼叫因任何原因而失敗,則不會對客戶端進行模擬,客戶端請求將在進行呼叫的程式所在的安全環境中進行。如果程式作為高度特權化的帳戶(如 LocalSystem)來執行,或作為管理組的成員來執行,則使用者可能可以執行在其他情況下不允許進行的操作。所以,您務必要始終檢查呼叫的返回值,如果該值未報出錯誤,則不要繼續執行客戶端請求。以下是一些示例:

RpcImpersonateClient
ImpersonateNamedPipeClient
SetThreatToken
ImpersonateSelf
CoImpersonateClient
ImpersonateDdeClientWindow
ImpersonateSecurityContext
ImpersonateLoggedOnUser

memcpy
安全性評價
第一個引數 dest 必須足以容納 count 個位元組的 src 組合大小,否則就可能發生緩衝區溢位。這樣,當發生違規訪問時,應用程式就可能會遭到拒絕服務攻擊,或者更壞,可能會使攻擊者將可執行程式碼注入到您的程式中。如果 dest 是基於堆疊的緩衝區,則尤為如此。要注意的是,最後一個引數 count 是要複製到 dest 的位元組數,而不是 dest 的大小。

以下程式碼示例演示了安全使用 memcpy() 的方法:

void test(char *pbData, unsigned int cbData) {
  char buf[BUFFER_SIZE];
  memcpy(buf, pbData, min(cbData,BUFFER_SIZE));
}

sprintf、swprintf
安全性評價
第一個引數 buffer 必須足以容納 format 的格式化版本和末尾的 NULL (') 字元,否則就可能發生緩衝區溢位。這樣,當發生違規訪問時,應用程式就可能會遭到拒絕服務攻擊,或者更壞,可能會使攻擊者將可執行程式碼注入到您的程式中。如果 buffer 是基於堆疊的緩衝區,則尤為如此。

另外,應注意使用者或應用程式將 format 提供為變數的危險。下例是危險的,因為攻擊者可能會將 szTemplate 設定為“%90s%10s”,這樣會建立一個 100 位元組的字串:

void test(char *szTemplate,char *szData1, char *szData2) {
  char buf[BUFFER_SIZE];
  sprintf(buf,szTemplate,szData1,szData2);
}

應考慮使用 _snprintf(英文)或 _snwprintf 來代替。

strcat、wcscat、_mbscat
安全性評價
第一個引數 strDestination 必須足以容納當前的 strDestination 和 strSource 組合以及一個末尾 ',否則就可能發生緩衝區溢位。這樣,當發生違規訪問時,應用程式就可能會遭到拒絕服務攻擊,或者更壞,可能會使攻擊者將可執行程式碼注入到您的程式中。如果 strDestination 是基於堆疊的緩衝區,則尤為如此。應考慮使用 strncat(英文)、wcsncat 或 _mbsncat。

strcpy、wcscpy、_mbscpy
安全性評價
第一個引數 strDestination 必須足以容納 strSource 和末尾的 ',否則就可能發生緩衝區溢位。這樣,當發生違規訪問時,應用程式就可能會遭到拒絕服務攻擊,或者更壞,可能會使攻擊者將可執行程式碼注入到您的程式中。如果 strDestination 是基於堆疊的緩衝區,則尤為如此。應考慮使用 strncpy(英文)、wcsncpy 或 _mbsncpy。

strncat、wcsncat、_mbsncat
安全性評價
第一個引數 strDestination 必須足以容納當前 strDestination 和 strSource 組合以及一個末尾 NULL ('),否則就可能發生緩衝區溢位。這樣,當發生違規訪問時,應用程式就可能會遭到拒絕服務攻擊,或者更壞,可能會使攻擊者將可執行程式碼注入到您的程式中。如果 strDestination 是基於堆疊的緩衝區,則尤為如此。要注意的是,最後一個引數 count 是要複製到 strDestination 的位元組數,而不是 strDestination 的大小。

還要注意,如果緩衝區 strDestination 中有剩餘的空間,則 strncat 僅新增末尾 NULL。

以下程式碼示例演示了安全使用 strncat 的方法:

void test(char *szs1, char *szWords2) {
  char buf[BUFFER_SIZE];
 
  strncpy(buf,szWords1,sizeof buf - 1);
  buf[BUFFER_SIZE - 1] = '; 
  unsigned int cRemaining = (sizeof buf - strlen(buf)) - 1;
  strncat(buf,szWords2,cRemaining);
}

WinExec
安全性評價
可執行名稱被視為 lpCmdLine 中的第一個用空格分隔的字串。 但是,如果可執行程式的名稱或路徑名中有空格,則存在一定的風險,因為如果空格處理不當,就可能會執行惡意的可執行程式。以下示例是危險的,因為該程式將試圖執行“Program.exe”(如果該程式存在),而不是“foo.exe”。

WinExec("C:Program Filesfoo", ...)
如果惡意使用者要在系統中建立名為“Program.exe”的特洛伊程式,那麼任何使用“Program Files”目錄不正確地呼叫 WinExec 的程式都將啟動特洛伊程式,而不是要呼叫的應用程式。

就安全性而言,我們強烈建議您使用 CreateProcess 而不是 WinExec。但是,如果您由於遺留問題而必須使用 WinExec,則務必要將應用程式名用引號引起來,如下例所示:

WinExec(""C:Program Filesfoo.exe" -L -S", ...)


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-989642/,如需轉載,請註明出處,否則將追究法律責任。

相關文章