歡迎您光臨本站 註冊首頁

Linux 和 Unix 安全編程:環境變數

←手機掃碼閱讀     火星人 @ 2014-03-26 , reply:0

環境變數
預設情況下,環境變數從進程的父進程繼承而來。但是,在程序執行另一個程序時,調用程序可以把環境變數設置為任意值。這對setuid/setgid程序而言很危險,因為其入侵者可以完全控制它們得到的環境變數。由於環境變數一般是繼承來的,同樣可以傳遞使用;安全程序可能調用某些其它程序,在沒有特殊措施的情況下,這會把有潛在危險的環境變數值傳遞給調用的程序。

有些環境變數是危險的
有些環境變數是危險的,因為很多庫和程序被環境變數以某些隱含、模糊或未公開的方式所控制。例如,sh和bash shell使用IFS變數來決定哪個字元被用來分隔命令行參數。由於shell是被若干底層調用(如C中的system(3)和popen(3),或Perl中的back-tick算符)執行的,把IFS設置為不尋常的值就會攪亂那些看起來安全的調用。該行為在bash和sh里有說明,但不引人注目;許多長時間的用戶知道IFS,只不過是因為了解IFS可用來破壞安全性,而不是因為有意經常使用的緣故。更糟的是,不是所有的環境變數都有文檔說明,而且即使有,其它的程序也可以改變和增加危險的環境變數。所以唯一的真正解決方案(下文有描述)是只選擇所需要的環境變數,不理會其餘的環境變數。

環境變數的存儲格式是危險的
一般來說,程序應該使用標準的訪問常式來訪問環境變數。例如,在C里應使用getenv(3)獲取環境變數的值,使用POSIX標準常式putenv(3)或BSD擴展setenv(3)來設置環境變數的值,使用unsetenv(3)來清除環境變數。需要說明的是,Linux下也實現了setenv(3)。但黑客不會這麼善良;黑客可以用execve(2)直接控制傳遞給程序的環境變數數據區。這就可能進行一些骯髒的攻擊,只有那些了解環境變數工作實質的人才能理解這些攻擊。在Linux下可以閱讀environ(5)來了解環境變數工作實質的概要。簡而言之,環境變數在內部作為一個指向字元的指針數組的指針來存儲;該數組按順序存儲並以NULL指針結尾(這樣就可以知道何時數組結束)。指向字元的指針每個都依次指向一個形式為「NAME=value」的以NIL結尾的字元串值。這包含若干意義,例如,環境變數名不能包含等號,而且name或value都不能含有NIL字元。但是,這種格式有一個很危險的含義,就是允許多個入口使用同一個變數名而值不同(如SHELL有多個值)。雖然典型的命令shell禁止這麼做,本地操作的黑客可以使用execve(2)製造出這樣的情況來。

這種存儲格式(以及設置方式)的問題在於程序可能會檢查某個值(看看是否合法)而實際上使用的卻是另一個不同的值。在Linux下,GNU的glibc庫試圖保護程序免受此影響:在實現glibc 2.1的getenv時,總是獲取第一個匹配的入口,setenv和putenv總是設置第一個匹配的入口,而unsetenv實際上會清除所有匹配入口的設置(應該祝賀GNU的glibc實現者如此實現unsetenv!)。但是,有些程序直接訪問環境變數,重複遍歷所有環境變數;在這種情況下,它們可能會使用最後一個匹配的入口,而不是第一個。其結果就是如果檢查的是第一個匹配的入口,但實際使用的是最後一個匹配的入口,黑客就可以以此來繞過保護常式。

解決方案 -- 提取和清除
對於安全的setuid/setgid程序,應該小心提取需要作為輸入(如果需要的話)的簡短的環境變數列表。然後應該清除整個環境,再重新設置一小組必需的環境變數作為安全的值。如果調用了下一級的程序,這實際上並不是什麼更好的辦法;因為沒有可行的辦法來列出「所有的危險值」。即使對直接或間接調用的每一個程序的源碼都進行了仔細檢閱,還是有人可以在你編寫完代碼后加入新的未公開的環境變數,其中就可能有一個可利用的環境變數。

清除環境的簡單方式是把全局變數environ設置為NULL。全局變數environ在中定義,C/C++用戶需要#include該頭文件。在產生線程前需要處理該值,但這幾乎不成問題,因為在程序執行的開始階段就需要進行這些處理。另一個清除環境的方式是使用未公開的函數clearenv()。clearenv()有個奇怪的歷史;有人建議在POSIX.1中定義它,但不知什麼原因它沒有進入標準。儘管如此,POSIX.9(綁定POSIX的Fortran 77)中定義了clearenv(),所以它具有了半官方的地位。clearenv()定義在,但在使用#include包含它之前,必須確定__USE_MISC已經#defined。

一個幾乎可以確定會不斷添加的值是PATH,一個查找程序的目錄列表;PATH應該不包括當前目錄,一般應該像「/bin:/usr/bin」那樣簡單。一般還會設置IFS(其預設值為「 \t\n」)和TZ(時區)。如果不提供IFS或TZ,Linux也不會死機,但沒有TZ值時有些基於System V的系統會出問題,而且據傳言某些shell需要IFS值被設置。在Linux下,參見environ(5)以了解可能需要設置的通用環境變數列表。

如果確實需要用戶提供的值,首先要檢查這些值(以保證這些值與合法值的模式相匹配,而且在某些合理的最大長度之內)。理想情況是在/etc下有些標準的可信賴文件,包含「標準的安全環境變數值」的信息,但是現在沒有為此目的定義的標準文件。與此相似,可能需要在那些具有PAM模塊的系統里檢查PAM模塊的pam_env。

如果採用不允許直接重新設置環境的語言編寫setuid/setgid程序,一個方法是建立一個「包裹」程序。包裹程序把環境程序設置為安全值,然後調用其它程序。注意:確定包裹程序會實際執行預期的程序;如果它是個解釋程序,要確定不會出現可能的競爭狀態,使得解釋器能夠載入另一個與授予了特殊的setuid/setgid許可權的程序不同的程序。

[火星人 ] Linux 和 Unix 安全編程:環境變數已經有649次圍觀

http://coctec.com/docs/linux/show-post-190361.html