利用靜態(tài)分析加固開源入侵檢測系統(tǒng)(IDS)的最佳實踐
譯文【51CTO.com快譯】入侵分析人員使用網(wǎng)絡安全監(jiān)控(NSM)的原理來保護計算機系統(tǒng)。NSM是“在對各種入侵進行檢測和響應過程中,所涉及到的標識和警告環(huán)節(jié)的收集、分析和問題升級”。NSM的核心功能包括入侵檢測系統(tǒng)(IDS),基于網(wǎng)絡的IDS(NIDS),主機入侵檢測系統(tǒng)(HIDS)和物理入侵檢測系統(tǒng)(物理IDS)。分析人員在部署之前應當評估諸如IDS和HIDS的軟件包。
我們可以用許多不同的方法來評估給定軟件包的安全水平,而其中的一種是使用Aberlarde安全系統(tǒng)工程法。這種方法在軟件開發(fā)生命周期(SDLC)的各個階段詳細評估了商業(yè)和開源軟件包的安全特性。檢查開源軟件的優(yōu)勢在于可以直接訪問它們的代碼。通過這種直接訪問的方式,開發(fā)人員可以使用諸如代碼檢查和靜態(tài)代碼分析的各種技術。
靜態(tài)代碼分析(SCA)是在不執(zhí)行軟件本身的情況下找出代碼問題的一種方式。為實現(xiàn)這一目的,SCA的相關工具通過使用各種可能的輸入數(shù)據(jù),來模擬代碼執(zhí)行的不同分支可能性。SCA工具能兼顧發(fā)現(xiàn)質量方面
(如COPY_PASTE_ERROR,F(xiàn)ORWARD_NULL,INCOMPATIBLE_CAST)和安全方面(如UNINIT,BUFFER_SIZE和USE_AFTER_FREE)的問題。SCA工具同時也能提供一些特定的修復,以便開發(fā)者應用到源代碼上,來減少軟件的缺陷密度。它是通過組件的大小(通常是代碼的指定行數(shù))除以缺陷的數(shù)量,來計算出缺陷的密度。在2014年,開源軟件的平均缺陷密度為0.61每一千行代碼或稱KLOC。相比之下,商業(yè)軟件的缺陷密度則為0.76每KLOC。
我們有許多源自OWASP的靜態(tài)分析工具可供選擇。自從Coverity掃描服務面世以來,該公司在過去的十年里備受矚目。開源開發(fā)者們免費將他們的代碼提交到Coverity基于云服務的掃描服務上,以進行分析和檢查。Coverity還可在客戶的本地環(huán)境中部署一個商業(yè)產品,以提供各種相同的分析工具。本文將介紹Coverity的靜態(tài)代碼分析是如何在不同部署場景中,被用來掃描那些組成安全洋蔥(Security Onion)發(fā)行版的軟件包。
安全洋蔥
安全洋蔥是由Doug Burks維護的一個Linux發(fā)行版,其中包括完整的數(shù)據(jù)包捕獲、NIDS、HIDS和一整套分析工具。這些工具包括:
- netsniff-ng:用于全量數(shù)據(jù)包的捕獲
- Snort、Suricata和Bro for NIDS
- OSSEC for HIDS
- Sguil、Squert、Snorby、and ELSA:用于數(shù)據(jù)分析
與單獨配置每個工具相比,使用安全洋蔥發(fā)行版可以節(jié)省時間。在著手使用該發(fā)行版進行開發(fā)之前,請遵循Burks 2016來安裝、配置和更新安全洋蔥。一旦完成之后,開發(fā)人員就可以檢查安全洋蔥軟件包的源代碼,以發(fā)現(xiàn)各種安全漏洞。
Coverity掃描
Coverity的首個部署選項是Coverity掃描。Coverity掃描是一種云服務,也是一個免費的開源社區(qū),已注冊的開源開發(fā)者可以上傳他們的源代碼用作分析。Coverity的靜態(tài)分析引擎隨即執(zhí)行對源代碼的分析。之后,開發(fā)們就可查看結果報告中的各種問題,并遵循給出的建議來解決問題,然后再重新提交源代碼。
Coverity掃描的實例:Wireshark
在使用Coverity掃描時,開發(fā)人員一般遵循四個步驟:構建、分析、提交缺陷和審查結果。在構建階段,原始的構建命令被作為參數(shù)傳遞給Coverity的命令行:cov-build工具。cov-build的指令運用帶有—dir的標記,在中間目錄下進行原始構建和存儲信息。讓我們以Wireshark為例,來看看Coverity的如下編譯命令:
- $ cov-build --encoding UTF-8 \
- --dir ~/cov-inter-wireshark make
在分析階段,中間目錄被手動、或一個連續(xù)集成系統(tǒng)(如 Travis-CI)上傳到Coverity的掃描處。代碼分析是在Coverity服務器上進行的,而并非開發(fā)人員的本地系統(tǒng)之上。Coverity將自動處理提交缺陷的階段。通過登錄到Coverity連接的網(wǎng)絡接口,各個缺陷將在源代碼的行內顯示出審查的結果。
Wireshark項目擁有著Coverity掃描的一批活躍用戶。自2006年以來,他們修復了數(shù)以千計的缺陷。如圖1所示,軟件的缺陷密度非常低,只有0.26每KLOC。
圖1:Coverity掃描:Wireshark (https://scan.coverity.com/projects/wireshark)
Coverity的本地分析
相對于Coverity掃描的云服務而言,開發(fā)者也可以選擇購買Coverity的商用產品。商業(yè)產品可以本地模式運行在他們的網(wǎng)絡之中。一個標準的Coverity部署要用到兩臺機器,來構成客戶機/服務器架構。
安全洋蔥一般作為本地開發(fā)主機,以客戶端的方式將其結果發(fā)送到Coverity的數(shù)據(jù)庫服務器上。默認情況下,安全洋蔥的軟件包以可執(zhí)行文件的形式被安裝。開發(fā)人員必須事先下載它,然后編譯并分析相應的源代碼。
開發(fā)人員在客戶端主機上執(zhí)行代碼分析,而并非使用Coverity掃描的服務器。存儲結果的數(shù)據(jù)庫是在本地網(wǎng)絡上,而不是在Coverity掃描的服務器上。如圖2所示,通過登錄到Coverity的Web服務器,并選擇合適的項目(如 Wireshark),便可瀏覽到各種不同的結果。
圖2:Coverity的項目菜單
如圖3所示,一旦選中了某個項目,就可以繼續(xù)選擇Coverity的菜單(三道線的圖標),并選擇“高危安全風險”。
圖3:高危安全風險過濾器
這張圖將所有的Coverity缺陷過濾到了一個僅包括安全問題的較小列表之中。
修復安全漏洞
在修復代碼之前,讓我們來看看如何對軟件使用“不傷害原則(do no harm rule)”,以及如何將編譯器的警告納入靜態(tài)分析的體系中。
不傷害
“學寫整潔的代碼并不容易”。在開始的時候,源代碼可能是整潔的,但隨著時間的推移它會變得“越來越雜亂”。對于一個優(yōu)秀的開發(fā)人員而言,既要會寫源代碼,也要會閱讀。如果不熟悉代碼的讀與寫,入侵分析人員將會面臨艱巨的挑戰(zhàn)。
“我們可以將美國童子軍的一個簡單規(guī)則運用到我們的專業(yè)領域:在離開營地時,將其打掃得比你發(fā)現(xiàn)它時更干凈。如果我們都能在代碼簽入時,使其比被簽出時更加整潔,那么代碼就會不朽了。”。
“不傷害原則”有兩個好處:開發(fā)人員能提高自己的編程技巧,同時原創(chuàng)作者也會認可開發(fā)者是負責任的披露(responsible disclosure)。
編譯器的警告
靜態(tài)代碼分析的另一個方面是編譯器的警告。人們常重視代碼是否能編譯通過,而忽視了編譯器的各種警告。我們以daq-2.0.6程序包為例,文件daq_afpacket.c的第859行聲明了一個變量rc:
- int rc
第866行包含了:
- rc = send(instance->peer->fd, NULL, 0, 0)
而編譯器的警告是:
- daq_afpacket.c:859:25: warning: variable ‘rc’ set but not used
- [-Wunused-but-set-variable] int rc
編譯器會告知開發(fā)人員:來自調用函數(shù)send()的返回值設置了變量rc,但是rc并沒有在后面的函數(shù)中被使用到。因此一種解決方案是:刪除第859行,并將第866行改為:
- (void) send(instance->peer->fd, NULL, 0, 0)
這種修改屏蔽了編譯器警告,并盡可能地貼近原始代碼。通過將send()的返回值調用分配給(void),目前的代碼就會忽略它了。另一種可能性的解決辦法是:在第866行后,添加額外的代碼,以檢查rc所有的返回值。這樣修改了程序的執(zhí)行,因此需要由維護人員進行審查。
編譯器也具有“視警告為錯誤”的能力。如果開啟了此功能,則會有益于在分階段的項目中引入編碼的規(guī)則。開發(fā)人員能夠一次只開啟一個警告,逐個修復,之后在時間允許的情況下,再打開額外的警告。比如:在Adobe Photoshop中,編譯器就具有開啟“視警告為錯誤”的選項,以使開發(fā)團隊提高整體發(fā)現(xiàn)能力。如果在構建系統(tǒng)時連續(xù)出現(xiàn)新的編譯器警告,并有構建的失敗,那么團隊就能迅速發(fā)現(xiàn)這些錯誤。開啟“視警告為錯誤”的另一個原因是:盡量減少各種靜態(tài)分析的缺陷,從而在添加其他工具之前,通過編譯器的幫助,更好地在代碼層面上消除那些缺陷。
Coverity的各種安全檢查
Coverity的7.7版本有著七十多種適用于C和C++的檢查,其中有十八項是注重安全問題的。本節(jié)將重點介紹UNINIT,BUFFER_SIZE和USE_AFTER_FREE。
1. UNINIT
在ANSI C語言中,“變量的初始內容是不確定的”。由于該語言允許各種變量在定義時不被初始化,因此經(jīng)常有大量的沒有顯式初始化的變量在C語言代碼中。一些代碼在變量聲明之后被立即賦值,因此完成了初始化。而有時,編譯器會自動將變量賦值為零。因此開發(fā)人員必須記住這些規(guī)則,這也就給軟件編程留下了安全隱患。雖然已有針對C語言該問題的解決辦法,但現(xiàn)如今,對于開發(fā)者來說還是需要記住這些規(guī)則的。
消除這些問題的一種方法是使用Coverity的安全檢查--UNINIT。UNINIT查找未初始化的堆棧變量,以及在堆上被動態(tài)分配的、可能會導致崩潰或安全問題的內存。在文件sf_bpf_filter.c的第222行中,daq-2.0.6程序包聲明了一個int32類型的、名為MEM的數(shù)組。
圖4:mem的聲明
第406行在未初始化的條件下使用mem。
圖5:mem的分配
如圖中的綠色代碼所示,Coverity通過循環(huán)執(zhí)行所有的代碼路徑來仿真運行。仿真發(fā)現(xiàn)了:在至少一種條件下,變量MEM在初始化之前被分配給了變量A。要解決此問題,需明確地將如下第222行的數(shù)組進行全零式的初始化。
- int32 mem[BPF_MEMWORDS] = {0}
2. BUFFER_SIZE
Michael Howard和David LeBlanc在《編寫安全代碼》一書中提到:“一個緩沖區(qū)溢出缺陷所需要的對應安全補丁的成本,有時會高達$100,000”。Coverity的安全檢查--BUFFER_SIZE能夠幫助開發(fā)人員找到,并修復他們C/C++代碼里所包含的各種緩沖區(qū)缺陷。我們以snort-2.9.8.0程序包為例,文件encode.c的第962行初始化了PROTO_ID的各種可能變量類型,直到PROTO_MAX。PROTO_MAX是PROTO_ID枚舉定義的最后一個元素:
- typedef enum {
- PROTO_TCP
- PROTO_UDP
- ..............
- PROTO_MAX } PROTO_ID;
如圖6所示,第960行定義了功能函數(shù)UDP_Encode。
圖6:越界讀取的示例
綠色的代碼顯示了Coverity所用到的執(zhí)行路徑。從NextEncoder函數(shù)返回的值被存放在PROTO_ID的下一個類型中。因此存在著如下的情況:其返回的值可能是PROTO_MAX、或22,這是枚舉的最后一個元素。因為數(shù)組的索引始于0而不是1,第992行所指定的下一個位置雖然超越了數(shù)組末尾,但是會被索引到編碼器數(shù)組之中。為了防止這種緩沖區(qū)溢出的可能,在它被索引到編碼器數(shù)組之前,我們可以用if/else語句將第992行“卷回來”,以檢查其下一個是否仍然小于PROTO_MAX。
3. USE_AFTER_FREE
定義各種變量時,一般為它們在內存中保留一個位置。當程序明確地應該釋放內存時,開發(fā)人員需要確保被釋放的內存不會再有被使用的可能。以不規(guī)范的方式使用內存,可能會導致不可預測的結果,和被利用的可能。
消除這些問題的一種方法是使用Coverity的安全檢查--USE_AFTER_FREE。我們以netsniff-ng-0.6.0程序包為例,在文件curvetun_client.c的第304行中,聲明一個指針去指向一個稱為“前導(ahead)”的數(shù)據(jù)結構。如圖7所示。
圖7:netsniff-ng – 前導聲明
如圖8所示,第339行將前導指針分配給ai。
圖8:前導指針賦值
Coverity在第358行發(fā)現(xiàn)前導指針已被釋放。第367行的goto語句將程序的執(zhí)行跳轉到第311行。下一次通過在第339行的循環(huán),指針在未被事先檢查為NULL的情況下,被分配給了ai。要解決此問題,應當添加以下代碼到第358行之后,將指針設置為NULL。
- ahead = NULL
負責任的披露
修復了各種漏洞之后,開發(fā)人員有責任向維護人員披露其程序代碼。對于像使用到GitHub的Wireshark之類的項目,各種修復項目文檔(如Wireshark開發(fā)者指南,2014),被以“git push”的命令予以提交。其他的項目也會有郵件列表,或缺陷跟蹤系統(tǒng)用于各種修復的提交。
未來的工作
在2016年1月,Coverity發(fā)布了靜態(tài)分析工具的8.0版本,其中一個主要的新功能是具有分析Python代碼的能力。安全洋蔥包含一種被稱為Scapy的數(shù)據(jù)包處理工具。Scapy正在被日益普及,尤其是在構建物聯(lián)網(wǎng)時,可被用來分析入侵和調查各種設備。未來的項目還會去檢查Scapy的靜態(tài)代碼分析結果。
結論
利用開源IDS加固計算機網(wǎng)絡,需要入侵分析人員了解系統(tǒng)里各種軟件包的安全特性。通過針對IDS的軟件靜態(tài)代碼分析,分析人員會對開源軟件所提供的安全特性更為了解。
id Software公司的聯(lián)合創(chuàng)始人John Carmack曾說:“作為一個程序員,近年來我所做的最重要的事情就是:積極地推進了靜態(tài)代碼分析。”這就是這位最有名的軟件開發(fā)者給大家的有關入侵分析的最佳實踐。
原文標題:Using Static Analysis to Harden Open Source Intrusion Detection Systems (IDS),作者::Jeff Sass
【51CTO譯稿,合作站點轉載請注明原文譯者和出處為51CTO.com】