Command Injection(命令注入)
Abstract
執行不可信賴資源中的命令,或在不可信賴的環境中執行命令,都會導致程序以攻擊者的名義執行惡意命令。
Explanation
Command Injection 漏洞主要表現為以下兩種形式:
- 攻擊者能夠篡改程序執行的命令: 攻擊者直接控制了所執行的命令。
- 攻擊者能夠篡改命令的執行環境: 攻擊者間接地控制了所執行的命令。
在這種情況下,我們着重關注第一種情況,即攻擊者控制所執行命令的可能性。 這種形式的 Command Injection 漏洞在以下情況下發生:
- 數據從不可信賴的數據源進入應用程序。
- 數據被用作代表應用程序所執行命令的字符串,或字符串的一部分。
- 通過命令的執行,應用程序會授予攻擊者一種原本不該擁有的特權或能力。
例 1: 下面這段來自系統實用程序的代碼根據系統屬性 APPHOME 來決定其安裝目錄,然后根據指定目錄的相對路徑執行一個初始化腳本。
...
String home = System.getProperty("APPHOME");
String cmd = home + INITCMD;
java.lang.Runtime.getRuntime().exec(cmd);
...
例 1 中的代碼使得攻擊者可通過修改系統屬性 APPHOME 而指向一個包含惡意版本 INITCMD 的其他路徑,從而提高自己在應用程序中的權限,繼而隨心所欲地執行命令。 由於程序不會驗證從環境中讀取的值,所以如果攻擊者能夠控制系統屬性 APPHOME 的值,他們就能欺騙應用程序去運行惡意代碼從而取得系統控制權。例 2: 以下代碼來自一個管理 Web 應用程序,該程序旨在允許用戶通過使用圍繞 rman 實用程序的批處理文件包啟動 Oracle 數據庫備份,然后運行 cleanup.bat 腳本刪除一些臨時文件。 腳本文件 rmanDB.bat 接受一個命令行參數,其中指明了需要執行的備份類型。 由於訪問數據庫受限,所以應用程序執行備份需要具有較高權限的用戶。
...
String btype = request.getParameter("backuptype");
String cmd = new String("cmd.exe /K
\"c:\\util\\rmanDB.bat "+btype+"&&c:\\utl\\cleanup.bat\"")
System.Runtime.getRuntime().exec(cmd);
...
這里的問題是:程序沒有對讀取自用戶的 backuptype 參數做任何驗證。 通常情況下,一次調用Runtime.exec() 函數並不會執行多條命令,但是在本例中,程序首先運行了 cmd.exe 指令,進而在一次
調用 Runtime.exec() 后便可以運行多條命令了。 一旦調用了該 shell,它即會心甘情願地執行用兩個與號分隔的多條命令。 如果攻擊者傳遞了一個形式為 "&& del c:\dbms\." 的字符串,那么應用程序將會在執行其他程序指定的命令時執行這些命令。 由於該應用程序的特性,運行該應用程序需要具備與數據庫進行交互所需的權限,這就意味着攻擊者注入的任何命令都將通過這些權限得以運行。 例 3: 以下代碼來自一個 Web 應用程序。通過該應用程序,用戶可以訪問能夠更新其系統密碼的接口。 在特定的網絡環境中更新密碼時,其中的一個步驟就是在 /var/yp 目錄中運行 make 命令,下面顯示了此步驟的代碼。
...
System.Runtime.getRuntime().exec("make");
...
這里的問題在於程序沒有在它的構造中指定一個絕對路徑,並且沒能在執行Runtime.exec() 調用前清除它的環境變量。 如果攻擊者能夠修改 $PATH 變量,把它指向名為 make 惡意二進制代碼,程序就會在其指定的環境下執行,然后加載該惡意二進制代碼,而非原本期望的代碼。 由於應用程序自身的特性,運行該應用程序需要具備執行系統操作所需的權限,這意味着攻擊者會利用這些權限執行自己的 make,從而可能導致攻擊者完全控制系統。 此種類來源於 Cigital Java Rulepack。 http://www.cigital.com/securitypack/
Recommendation
應當禁止用戶直接控制由程序執行的命令。 在用戶的輸入會影響命令執行的情況下,應將用戶輸入限制為從預定的安全命令集合中進行選擇。 如果輸入中出現了惡意的內容,傳遞到命令執行函數的值將默認從安全命令集合中選擇,或者程序將拒絕執行任何命令。 在需要將用戶的輸入用作程序命令中的參數時,由於合法的參數集合實在很大,或是難以跟蹤,使得這個方法通常都不切實際。 開發者通常的做法是使用黑名單。 在輸入之前,黑名單會有選擇地拒絕或避免潛在的危險字符。 但是,任何一個定義不安全內容的列表都很可能是不完整的,並且會嚴重地依賴於執行命令的環境。 較好的方法是創建一份白名單,允許其中的字符出現在輸入中,並且只接受完全由這些經認可的字符組成的輸入。 攻擊者可以通過修改程序運行命令的環境來間接控制這些命令的執行。 我們不應當完全信賴環境,還需采取預防措施,防止攻擊者利用某些控制環境的手段進行攻擊。 無論何時,只要有可能,都應由應用程序來控制命令,並使用絕對路徑執行命令。 如果編譯時尚不了解路徑(如在跨平台應用程序中),應該在執行過程中利用可信賴的值構建一個絕對路徑。 應對照一系列定義有效值的常量,仔細地檢查從配置文件或者環境中讀取的命令值和路徑。 有時還可以執行其他檢驗,以檢查這些來源是否已被惡意篡改。 例如,如果一個配置文件為可寫,程序可能會拒絕運行。 如果能夠預先得知有關要執行的二進制代碼的信息,程序就會進行檢測,以檢驗這個二進制代碼的合法性。 如果一個二進制代碼始終屬於某個特定的用戶,或者被指定了一組特定的訪問權限,這些屬性就會在執行二進制代碼前通過程序進行檢驗。 盡管可能無法完全阻止強大的攻擊者為了控制程序執行的命令而對系統進行的攻擊,但只要程序執行外部命令,就務必使用最小授權原則: 不給予超過執行該命令所必需的權限。