帳號:
密碼:
最新動態
產業快訊
CTIMES / 文章 /
安全系統的設計與實作相關課題
 

【作者: 梁泰華,黃士殷】   2000年02月01日 星期二

瀏覽人次:【4735】

前言

1988年11月,一個具有透過網路進行自我複製能力的程式被釋放至網際網路中的某一台主機上;而在網際網路的協助之下,它沒有花費太多的時間便成功地滲透了網路上數以千計的主機,迫使許多網站不得不停機等待修補漏洞。該程式主要由一名大學生Robert T. Morris 所撰寫,因為它在網站間傳遞與複製的特性,也被人稱之為「The Internet Worm」(網蟲)。


後人分析(註一)發現:這隻網蟲之所以能輕易地攻進許多主機,除了靠網際網路的推波助瀾之外,被攻擊主機上一些服務程式有Bugs,才是主要的關鍵。


開發過軟體的人應該都會認同一句話:「天下沒有零Bug的程式。」特別是當程式愈寫愈大時,這句話簡直是不變的鐵則。即使你/妳只是一個單純的應用軟體使用者,想必也一定有軟體使用到一半便莫名其妙當掉的經驗,先別急著責怪自己使用不當,這通常是應用程式設計或撰寫上的疏失所導致。


Bug所造成的影響

一個因為程式寫作上的錯誤而產生的Bug,究竟會造成多大的影響呢?實際上,根據該程式應用地點與錯誤程度的不同,造成的傷害也不一樣:輕者導致當機,重者甚至可以傷人性命!以下所列舉的,都是因為軟體上的疏失而導致人員傷亡的真實案例:


1.1985至1987年間,一種用以對癌症病人實施放射治療的醫療用機器Therac-25,因為控制軟體(以PDP組合語言寫成)中沒有對負責劑量的變數值作溢位(Overflow)檢查,使得在某些條件下,該變數可能溢位而造成照射劑量超過安全上限(註二);造成至少兩名患者因照射劑量過多而死亡。


2.1991年波斯灣戰爭期間,一枚飛毛腿飛彈穿過愛國者反飛彈的防護網,擊中了位於Saudi Arabia, Dhahran附近的一處軍營,造成28名美國人死亡、98人受傷。原因是當時愛國者飛彈的控制軟體中,潛藏了一個時序(Timing)上的錯誤,製造商提出的暫時解決方法是要求操作人員每隔數小時之後就必須重置(Reset)系統時鐘,但由於戰事緊張,Dhahran的愛國者飛彈在完全沒有定時重置時鐘的狀況下連續運作了一百多個小時,使得該時序錯誤足以累積至影響飛彈攔截的正確時機。雖然以色列軍方在取得愛國者飛彈八小時之後發現了這個軟體上的錯誤,並立刻回報美國的製造商,美方也以最快的速度修正了這個Bug,但很不幸地,更新版的軟體一直到事發後隔天才送達(註三)。


網站上的應用程式有Bug,雖然不至於造成人員的傷亡,但其所帶來的影響,卻也不可小覷;相信許多人對前一陣子海峽兩岸的許多政府、非政府單位網站首頁紛紛被非法修改的事件應該還記憶猶新。根據事後調查(註四),入侵者應該是利用rpc.cmsd這個服務程式的Bug,透過Buffer Overflow的攻擊方式取得權限,進而修改該網站的首頁內容。


事實上,根據被攻擊程式所執行的權限之不同,入侵者所能造成的破壞也不一樣,有時候雖然不至於整個網站的檔案都被砍掉,但僅僅是一個首頁被更改就夠令人頭痛了。因此,為了避免不安全的程式危害整個系統的安全,對一個程式設計者而言,瞭解並避免一些可能的程式設計錯誤是絕對必要的。以下我們將說明一些可能危害程式完整性(Integrity)與系統安全的錯誤,並提出一些解決方案。


常見的程式寫作錯誤

不當的環境變數(Environment Variables):

所謂的環境變數,通常是命令解譯器(Shell Interpreter,比方說︰DOS下的command.com或是UNIX下的sh等)於執行時所需要參考的一些資訊(例如︰PATH),但有時候這些變數設定上的疏失,卻可能帶來一些意想不到的後果。以PATH這個環境變數來說,命令解譯器在接受使用者的命令之後,若該命令並非內建指令(Builtin Command,如echo),則會依據PATH 所指的路徑搜尋同名稱的可執行檔。如果某一位使用者的PATH優先搜尋順序設定為:.、/bin、/sbin、/usr/bin、/usr/sbin、/usr/local/bin、/usr/local/sbin (其中的 . 符號表示搜尋目前路徑),並且在其目前的工作目錄(Working Directory)中包含了下列名為「ls」的命令稿(shell script):



[文字框標示起始]
#!/bin/sh
echo "hello world!"
[文字框標示結束]



則該使用者一旦執行了「ls」這個常見的命令,由於PATH搜尋順序的影響,使得該目錄下的ls命令稿最先被執行,因此結果將出現「hello world!」而非原來預期的檔案列表。


除了PATH之外,另外還有一個環境變數「IFS」(Internal Field Separator)也經常出現在與命令稿相關的程式設計中。它主要被用來指定一個區隔輸入字串的字元;比方說,如果當IFS為 “/” 時,”/bin/ls”這個字串就會被分割為 “bin ls”。配合前面的PATH變數,若使用者的目錄下有一個名為「bin」的執行檔,那原本在命令稿中欲以絕對路徑執行的/bin/ls,在IFS等於 “/” 的影響下,反而是「bin」這個程式被執行了,而如果bin這個程式是用來刪除所有使用者檔案的話...。


很明顯地,在某些不當的環境變數交互影響之下,使得一些其他的攻擊方式(如特洛依木馬)得以奏效,也因此在「橙皮書」(註五)中有定義到一項稱為「可信路徑」(Trusted Path)的安全規範。


要注意的一點:這個問題主要發生在使用命令解譯器時,這代表著即使是非命令稿的程式也可能被環境變數所影響;比方說在C程式中使用類似下列的呼叫:



[文字框標示起始]
.
.
.
system("/bin/ls -la");
.
.
.
[文字框標示結束]



UNIX系統下的檔案保護位元(Umask)與符號鏈結(Symbolic Link):

檔案保護位元(Umask)是UNIX系統下新建檔案時的預設存取權限,預設值通常為022(表示僅允許該檔案擁有者寫入;其他使用者只能讀取);但是,這對一些有特殊需求的應用程式而言是不夠的。比方說有一個專門負責修改使用者密碼的應用程式,它的作法乃是先將系統密碼檔(/etc/shadow)複製一份到暫存檔(/etc/shadow~)中,待使用者改好暫存檔中的密碼後,再將暫存檔改名為原來的密碼檔。但是,如果使用預設的檔案存取權限,將使得原來被保護的密碼檔(僅系統管理者可讀寫)被存成沒有被保護(其他人皆可讀取)的密碼暫存檔;也就是說,在這段時間攻擊者就有機可乘了。因此,針對一些安全層級要求較為嚴格的應用程式而言,程式設計人員必須注意檔案保護位元是否適當地被設定。


符號鏈結(Symbolic Link)是UNIX檔案系統中的一種特殊檔案,以(圖一)來說,/usr/sbin/sendmail這個程式即是一個符號鏈結檔,連到/home/qmail/bin/sendmail(注意最前面lrwxr-xr-x中那個「l」的屬性),也就是說,使用者在執行/usr/sbin/sendmail時,事實上執行的是/home/qmail/ bin/sendmail這個程式;同樣地,假設使用者寫入資料到/usr/sbin/sendmail,事實上該筆資料是被寫入至/home/qmail/bin/sendmail這個檔案中。



《圖一 》
《圖一 》

以下舉一個例子說明應用程式中不當的檔案保護位元再配合上不完善的符號鏈結檢查所造成的攻擊方式:


大約在1996 年,有一個在X Window上使用,稱為WorkMan的CD播放程式被發現因為缺乏完善的檔案檢查而造成一些安全上的顧慮(註六),使得在某些情況下可以讓攻擊者修改系統上任意檔案。這裡簡單介紹一下相關的背景:首先,WorkMan在啟動後會在/tmp的目錄下開啟一個用以存放該行程行程代碼(PID)的暫存檔,但很不幸的,該暫存檔的讀寫權限被設定為0666(也就是所有人都可以讀取與寫入這個檔案)。無巧不巧,在某些系統上,為了讓WorkMan具有讀取CD-ROM的能力,通常會給予它系統管理者的權限,換句話說,WorkMan此時便能以系統管理者的權限建立或覆蓋該暫存檔。再加上WorkMan並不會檢查暫存檔是否為符號鏈結,使得有心人可以綜合以上幾項特性,有技巧地覆蓋掉系統上的任意檔案;想想看:如果被覆蓋的檔案是/etc/passwd或/etc/shadow之類的重要檔案,後果將有多麼地嚴重!


Race Conditions

指兩個以上的行程,因為同時讀寫同一份沒有被保護好的共用資源,造成該資源出現最終狀態不穩定的情形;所謂的「最終狀態不穩定」,乃是指隨著最後對該資源修改動作的行程之不同,最終的資源內容也不一樣。以下舉一個多執行緒(Multi-threaded)的程式說明:



[文字框標示起始]
.
.
.
int	global = 0;

Thread1()
{
global = global + 3;
}
Thread2()
{
printf("global = %d\n", global);
}
Thread3()
{
global = global - 1;
}
.
.
.
[文字框標示結束]



若Thread1、Thread2與Thread3同時執行,由於無法得知這三者的執行先後次序,造成 Thread2可能印出四種不同的值:0、3、-1或2。這種不穩定的情形在一個程式中通常是不被允許的,因為可能導致時序上或其他方面的錯誤;最簡單的解決方式是採用諸如Semaphore或Critical Section之類的資源鎖定(Resource Locking)機制,由於行程間同步這個課題已經超過本篇範疇,在此不多加討論。


或許有些人會覺得奇怪:我的程式又沒有用到多執行緒,怎麼還會有Race Condition的顧慮呢?別忘了,程式中不僅在存取共用變數時,即使存取到程式以外的系統資源(如檔案),都有可能發生因為沒有做好保護動作,而導致最後結果不定的情形發生。以前面檔案保護位元所舉的「密碼修改程式」例子來說,如果改成:



[文字框標示起始]
.
.
.
開啟暫存檔"/etc/passwd.tmp";
將"/etc/passwd.tmp"的讀寫權限改為0600;
.
.
.
[文字框標示結束]



也就是開啟暫存檔之後,「立刻」把該檔案設為「僅有擁有者可讀寫」,是不是就保證不會被第三者得知暫存檔的內容了呢?很不幸地,在開啟檔案與修改檔案權限的兩個系統呼叫之間,該行程仍有可能被作業系統切換(Context Switch)掉,換另一個行程B執行,如果行程B正好是「開啟檔案/etc/passwd.tmp」,那麼行程B仍然有能力在該檔案讀寫權力被修改後讀取該檔的內容(因為檔案讀寫權限的檢查是在它被開啟的時候做的)。


Failed on Boundary Conditions

程式執行的過程中,不免有一些例外的狀況出現,比方說磁碟空間不足、印表機沒紙或記憶體不夠了;也因此,以C語言來說,在許多的系統函式庫提供的呼叫中,都包含了一個稱為「傳回值」(Return Value)的輸出結果,通常表示該呼叫是否執行成功,比方說當使用 malloc()配置記憶體時,若malloc()傳回的指標為NULL,表示系統無法配置到所需的記憶體空間。一般程式一旦呼叫函數失敗,應該立刻採取適當的處置,比方說終止程式的執行,或要求使用者提供協助。


但有許多不太細心的程式開發人員,卻往往忽略了傳回值,使得程式執行時一旦碰到突發狀況,就產生無法預期的後果;更廣泛地來說,UNIX系統中的信號處理(Signal Handling)也應該視為和回傳值同等地重要,特別針對一些在背景執行的程式(如daemon),如果沒有特別處理一些訊號,有幾個訊號預設是產生coredump(也就是結束程式之前,先將該程式所使用的記憶體及當時的執行資訊寫入一個稱為「core」的檔案中,以利後來偵錯工作的進行),某些作業系統在寫入core檔時並不會檢查目的檔是否為符號鏈結,如此一來將造成攻擊者越權覆蓋系統上其他檔案的一個機會。


根據過去的經驗,有一些邊界狀況是出在型態轉換上,比方說C語言中的signed與 unsigned兩種型態,舉例來說:



[文字框標示起始]
void corrupt(int idx)
{
char	string[1024];

if (idx < 1024)
string[idx] ='\0';
.
.
.
}
[文字框標示結束]



上例中的idx,預設是整數型態,也就是包含負數,而corrupt()中用來做邊界檢查的if條件式卻沒有考慮到負整數的情況,造成下一行的程式碼可能將 \0\ 填入string[]以外的地方。這一類的錯誤也引出了我們下一個主題:Buffer Overflow。


Buffer overflow

這個在1988年Morris的網蟲中所使用到的攻擊技術之一,一直到現在都還被人拿來使用,據估計(註七),目前約有三分之二的系統安全漏洞都是採Buffer Overflow 的途徑達成。簡單來說,這個技術可以讓一個程式去執行「自己程式碼以外的程式碼」。對一般應用程式而言,這並不會有很大的影響,但對於一些具有特權的程式,攻擊者可以讓特定的程式碼被這些特權程式以超級使用者的身份執行,進而達到取得系統管理者權限或破壞系統的目的。聽起來相當神奇,竟然可以執行原先編譯進去的程式碼以外的指令,這究竟是如何達到的?


一般說來,目前常見的電腦架構都是以堆疊(Stack)來存放副程式參數、區域變數(Local Variable)、傳回值與返回位址等相關資訊,我們先看看一般程式語言在處理副程式呼叫時,堆疊長什麼樣子:


如(圖二)所示,當sub()被呼叫時,參數a的位址會先被推入堆疊,然後是sub()的返回位址(Return Address),而sub()中的區域變數buf則待進入該函式之後再被配置於堆疊上方。


《圖二 》
《圖二 》

如(圖三)所示,strcpy()執行之後,因為*a的內容(“this is a test”包含結束字元共15Bytes)已經超過了buf的大小(只有8Bytes),因此多出來的幾個字元就會蓋掉鄰近buf的返回位址與參數位址。


《圖三 》
《圖三 》

事實上,一旦sub()結束,開始返回主程式main()時,程式的流程就已經被破壞了(在本例中是回到了一個可能當掉此程式的位址)。換句話說,只要攻擊者能算出該回到哪一個位址,並將想執行的指令以機器語言的形式放入該位址(在本例中可以放入buf),便能執行任何想執行的指令。


結論︰安全程式設計守則

綜合以上幾點,我們歸納出幾項設計或撰寫安全關鍵程式時應該注意的事項:


1.環境變數

不要相信任何外在環境輸入的資訊,不論是使用者輸入的資訊、系統環境變數、抑或副程式彼此之間傳遞的參數,設計者都有責任對所有外在環境輸入的資訊做嚴格地檢查。


2.型態轉換(Type Casting)

對於一些非使用強制型態(Strong Typing)的程式語言(如C),型態轉換是相當危險的一個動作,在它便利的功能背後往往隱藏著許多潛在的問題,小小一個char轉int的動作都可能因為指標(pointer)處理不慎而造成大災難。


3.邊界狀態檢查

千萬別假設所有的系統函式永遠會正確地被執行,使用任何函式前除了完全瞭解它的行為之外,對於它在錯誤下會產生何種結果,也必須一併考量,同時做出正確的處置。


為了避免Buffer Overflow的產生,程式中除了必須對所有用到陣列或類似型態的資料結構詳加檢查之外,在處理字串時,也應該避免使用一些「具有潛在危險」的函式,如strcpy()、sprintf()、strcat ()或gets()等等,因為它們提供的邊界檢查能力相當限(有些甚至完全沒有邊界檢查),而最好以一些較為安全的函式如:strncpy()、snprintf()、strncat()或fgets()取代之。


4.共用資源

謹慎處理程式中每一項可能用到的資源,不論是記憶體、目錄架構、周邊設備、暫存檔或檔案描述子(File Descriptor),在沒有做好鎖定的動作前,永遠不要相信它是安全的。以前面的密碼暫存檔一例而言,最好便是在開啟檔案之前,先以umask()設定保護權限;再不然,在open()時也可以設定暫存檔的讀寫權限。


此外,在開啟暫存檔之類的檔案時,多作一些額外檢查以應付諸如符號鏈結等特殊檔案是絕對有必要的(想想WorkMan的例子吧)。


5.資源與權限

對許多程式設計人員而言,他/她們都會希望自己的程式在最充足的系統資源與最大的權限之下執行。這其實是相當危險的想法,想想看,一旦一個應用程式攫取的系統中所有的資源與最高的權限,任何一個小小的程式錯誤,都可能傷害整個系統,所謂「爬得越高,摔得越重」,為了系統整體的安全,任何程式在執行時都必須限制資源的使用與遵循「最小權限原則」(Least Privilege Principle,註五)。


很多程式上的安全問題均是在原設計者未預期的狀況下產生的,正如一開始我們所提到,幾乎不大可能設計出一個完全沒有缺陷的程式,因此,我們除了提供幾項安全程式設計守則之外,也只能以一句話奉勸程式開發人員:「當你撰寫軟體的時候,千萬得仔細考慮每一個環節,以免造成難以彌補的損失:小心、小心、小心!」


(作者梁泰華、黃士殷分別就學、任教於元智大學資工系)


備註

註一:Eichin, Mark W., and Rochlis, Jon A., “With Microscope and Tweezers:


An Analysis of the Inter -net Virus of November 1988,” in Proceedings,


1989 IEEE Computer Society Symposium on Security and Privacy page


326-343, 1-3 May 1989, Oakland, California.


註二:N. G. Leveson and C. S. Turner, “An Investigation of the Therac-25


Accidents,” IEEE Computer 26(July 1993), pp. 18-41.


註三:P. Mellor, “CAD: Computer -Aided Disaster,”Techni- cal Report, Centre


for Software Reliability, City University, London, UK, July 1994.


註四:TWCERT, http://www.cert. org.tw/chi/index.html


註五:”Trusted Computer System Evaluation Criteria,” Department of Defense


Standard, DoD 5200.28-STD, December 1985.


註六:”Vulnerability in Work- Man,” CERT Advisory CA-96.23, October 1996.


http://www.cert.org/advisories/CA-96.23.workman_ vul.html


註七:Crispin Cowan, Perry Wagle , Calton Pu, Steve Beattie , and Jonathan


Walpole, “Buffer Over-flows: Attacks and Defe-nses for the Vulnerability


of the Decade,” Procee- dings of DARPA Information Survivability


Conference and Expo, vol 2, pp. 119-130, 1999.


相關文章
大型可撓曲式電漿電視
零組件科技論壇─「電池能源管理與新一代電池技術」研討會實錄
淺談奈米平面顯示器
未來兩年是OLED市場發展的關鍵期
數位電視時代的電漿顯示器展望
comments powered by Disqus
相關討論
  相關新聞
» 數智創新大賽助力產學接軌 鼎新培育未來AI智客
» VicOne深植車用資安DNA再報喜 獲TISAX AL3最高等級認證
» 勤業眾信獻策5方針 解決GenAI創新3大常見風險
» Fortinet整合SASE突破組織分散管理困境 重塑雲端安全的混合未來
» UD Trucks選用VicOne解決方案 利用情境化攻擊情報洞察風險


刊登廣告 新聞信箱 讀者信箱 著作權聲明 隱私權聲明 本站介紹

Copyright ©1999-2024 遠播資訊股份有限公司版權所有 Powered by O3  v3.20.2048.162.158.79.70
地址:台北數位產業園區(digiBlock Taipei) 103台北市大同區承德路三段287-2號A棟204室
電話 (02)2585-5526 #0 轉接至總機 /  E-Mail: [email protected]