撰文:叡揚資訊 資訊安全事業處
2009、2010、2011…睽違8年,CWE終於在2019年底推出最新Top 25常見軟體安全弱點。
CWE™:The Common Weakness Enumeration(常見弱點列舉),是美國國土安全部(DHS)網路與基礎設施安全局(CISA)所贊助,由非營利的研發機構MITRE負責管理的弱點列表。這些弱點列表提供評估軟體安全的共通語言,羅列各種軟體弱點、識別方法、緩解與預防工作的知識。
CWE Top 25是常見弱點中,前25名最嚴重的軟體弱點列表,這些弱點因為容易被發現與利用,且威力強大而上榜。通常這些弱點能讓攻擊者完全 接管軟體的執行、竊取資料或阻止軟體運行。該列表作為一種社群資源,可以幫助軟體開發員、軟體測試員、軟體使用者、軟體PM、安全研究者、教育工作者等人員了解軟體界中最普遍的安全威脅。
八年前的Top 25列表是CWE與SANS合作,透過調查及訪談多位資安專家來評估各項弱點的危險排名。2019年起,CWE團隊採用更客觀的方式:他們分析了Common Vulnerabilities and Exposures (CVE®)跟National Vulnerability Database (NVD)這幾年來上萬個安全漏洞出現的頻率,並參考Common Vulnerability Scoring System (CVSS, 常見漏洞評分系統)的威脅評分。透過一致的公式來計算每個CWE弱點的危險程度,並據此排名。未來CWE團隊也 會繼續利用公式化的評分方式,逐年更新列表。
2011 | 2019 | ID | Name | Score |
{3}* | [1] | CWE-119 | Improper Restriction of Operations within the Bounds of a Memory Buffer | 75.56 |
[4] | [2] | CWE-79 | Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') | 45.69 |
[3] | CWE-20 | Improper Input Validation | 43.61 | |
[4] | CWE-200 | Information Exposure | 32.12 | |
[5] | CWE-125 | Out-of-bounds Read | 26.53 | |
[1] | [6] | CWE-89 | Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') | 24.54 |
[7] | CWE-416 | Use After Free | 17.94 | |
[24] | [8] | CWE-190 | Integer Overflow or Wraparound | 17.35 |
[12] | [9] | CWE-352 | Cross-Site Request Forgery (CSRF) | 15.54 |
[13] | [10] | CWE-22 | Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') | 14.1 |
[2] | [11] | CWE-78 | Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') | 11.47 |
[12] | CWE-787 | Out-of-bounds Write | 11.08 | |
{5} | [13] | CWE-287 | Improper Authentication | 10.78 |
[14] | CWE-476 | NULL Pointer Dereference | 9.74 | |
[17] | [15] | CWE-732 | Incorrect Permission Assignment for Critical Resource | 6.33 |
[9] | [16] | CWE-434 | Unrestricted Upload of File with Dangerous Type | 5.5 |
[17] | CWE-611 | Improper Restriction of XML External Entity Reference | 5.48 | |
[18] | CWE-94 | Improper Control of Generation of Code ('Code Injection') | 5.36 | |
[7] | [19] | CWE-798 | Use of Hard-coded Credentials | 5.12 |
[20] | CWE-400 | Uncontrolled Resource Consumption | 5.04 | |
[21] | CWE-772 | Missing Release of Resource after Effective Lifetime | 5.04 | |
[22] | CWE-426 | Untrusted Search Path | 4.4 | |
[23] | CWE-502 | Deserialization of Untrusted Data | 4.3 | |
[24] | CWE-269 | Improper Privilege Management | 4.23 | |
[25] | CWE-295 | Improper Certificate Validation | 4.06 |
*{雖然之前未上榜但因其弱點的特性有相關}
今年上榜的弱點,以程式開發的角度細分為以下五類:記憶體操作不當、使用了未經驗證或處理的輸入資料、資源管理的疏忽、身分認證、其他常見弱點。
一、 記憶體操作不當
C/C++/Assembly 等語言可以直接存取/管理記憶體中的資料,開發者必須自行維護記憶體的使用情況,操作過程若不慎,程式就可能讀寫超出預期的記憶體位置。第1名的 (CWE-119)就是在描述這種情形。而第5名的越界讀取(CWE-125)與 第12名的越界寫入(CWE-787),也都屬於這個類型的弱點。
上面這段程式碼,第4行的a字元陣列在記憶體佔了5個byte的空間,位址假設是0x5~0x9。第5行的整數b可能接續在a的位置後,位址從0xa~0xd。
程式第7行接收一個外部輸入,輸入的資料會轉為字串,從a陣列所在的0x5開始寫入。
l 此時若接收的字串長度大於5 byte, 資料就會寫超出a陣列的範圍,覆蓋到整數b的資料。這就是CWE-119的延伸弱點:[#12] CWE-787 Out-of-bounds Write。
l 假設接收的字串是 "Hello" 長度剛好是5 byte,乍看之下沒有問題,但作為字串結尾的Bound '\0' 會被塞在0xa的位置,一樣有CWE-787的越界寫入問題。
l 接著第8行接收一串整數,存在整數b的記憶體位製(0xa~0xd), 這又會把剛才的Bound '\0'覆蓋掉。
l 最後在第10行列印a陣列內容的時候,系統因為沒遇到'\0'或 是空值NULL,所以會把整數b的資料也印出來,直到遇到'\0'或 是空值NULL為止。所以就會看到系統印出: Hello!;J^ 的結果。這就是CWE-119的另一延伸弱點:[#5] CWE-125 Out-of-bounds Read。
2011的[#3] CWE-120其 實就是CWE-119的延伸,描述的弱點是複製資料時沒有確認來源資料的長度。
二、 使用了未經驗證或處理的輸入資料
純真善良的開發者,相信使用者會按照預期輸入"正常"的資料。可惜這個世界太殘酷,使用未經驗證的輸入資料無異於引狼入室。
根據輸入資料使用的情境,會造成下列幾項不同的弱點:
l 未經處理的輸入,用於生成網頁:[#2] CWE-79 ('Cross-site Scripting')
l 未經處理的輸入,用於流程判斷:[#3] CWE-20 ('Improper Input Validation')
l 未經處理的輸入,用於SQL查 詢:[#6] CWE-89 ('SQL Injection')
l 未經處理的輸入,用於存取路徑:[#10] CWE-22 ('Path Traversal')
l 未經處理的輸入,用於OS指 令:[#11] CWE-78 ('OS Command Injection')
l 未經驗證,沒有限制的上傳檔案:[#16] CWE-434 ('Unrestricted Upload of File with Dangerous Type')
l 未經處理的輸入,用於XML解 析器:[#17] CWE-611 ('XXE')
l 未經處理的輸入,用於組成程式碼:[#18] CWE-94 ('Code Injection')
l 未經處理的輸入,用於反序列化:[#23] CWE-502 ('Deserialization of Untrusted Data')
外部輸入的資料千奇百怪,開發者必須根據不同的用途,細心的驗證,以免被趁虛而入。以生成網頁的[#2] CWE-79 XSS來說,根據輸出的類型(JavaScript/HTML/…)做相對應的Encode,就可以避免被XSS攻擊。用於流程判斷的[#3] CWE-20, 就要依據程式邏輯去調整。例如下面的購物車範例,雖然價格是寫死的,但是Quantity(數量)是來自使用者端,如果塞一個負值,系統可能會出問題。這邊的改法,就是對數量設定上下限,驗證輸入的數值是否合理,再繼續執行交易流程。
存在多年的[#6] CWE-89 ('SQL Injection'),只要正確的使用參數化查詢就能化解。[#10] CWE-22 ('Path Traversal')的問題,核心觀念就是限制程式可以存取的路徑,因此解法可以將外部字串先用Replace去除跳脫字元,接著串在白名單路徑後,或是直接使用白名單路徑。[#11] CWE-78 ('OS Command Injection')一樣是用白名單,或是為程式準備一個最小權限的帳號,只能執行特定幾種系統指令。
上傳檔案的[#16] CWE-434, 解法依據程式邏輯而定,可以把檔案儲存在Server外的地方,例如資料庫或DMS上,或是將該檔案標記為"不可執行"(non-executable),也可以先行驗證檔案的大小、副檔名、MIME類型等資訊是否合理,並為檔案重新命名,加上特定的副檔名等等。[#17] CWE-611 ('XXE')只要關閉XXE(XML External Entity外部實體)解析的功能就能防堵,如果業務需求不能關,就要對外部傳來的內容進行驗證,根據情況做encode或replace處理。
[#18] CWE-94 ('Code Injection')就比較棘手,建議盡量避免動態編譯並執行程式碼。如果真的有這種需求,盡可能的用白名單,搭配replace提高注入攻擊的難度。反序列化[#23] CWE-502的問題,只要在做反序列化前指定預期的物件/Class型別,不要用寬鬆的"Object"或是泛型<T>,就能縮小反序列化帶來的弱點。
程式開發的過程中,會使用到各種軟硬體"資源"(Resource)來提供服務。第一項提到的"記憶體"就是其中一種資源,另外還有CPU、儲存空間、通訊協定、各種Driver等等。這些資源,通常以物件(Object)的型態供應用程式使用。程式需要按照流程正確的取得資源使用權,使用完畢後也要歸還給系統。
[#7] CWE-416 Use After Free
今年上榜的名單中,就有幾個跟資源相關的弱點,其中得分最高的是[#7] CWE-416 Use After Free,這個弱點說的是C/C++這種可以直接存取記憶體的程式,在記憶體資源使用完畢並歸還後,沒有重新申請就直接存取已歸還的記憶體空間。這個弱點會使應用程式讀取一塊狀況不明或受其他程式控制的資料,或者是寫入其他程式正在使用的記憶體區塊,這有可能導致程式崩潰或執行異常。
[#14] CWE-476 NULL Pointer Dereference
另一種情況是對NULL指標進行反參照/解構。指標,可以用來指向某個變數或資源,有效利用記憶體空間並加速程式執行速度,物件導向程式中的物件也是一種指標。若指標沒有指向任何東西,就會以NULL表示。對NULL指標進行反參照/解構,會發生不可預期的情況。
以上圖為例,程式意圖使用cmd指令,若環境中的cmd遭移除,程式就會對NULL的cmd變數進行反參照,引發程式崩潰或退出。所以在取得資源(各種handle)時,務必檢查是否取得成功,避免留下[#14] CWE-476的漏洞。
[#22] CWE-426 Untrusted Search Path
除了資源取得失敗,還有一種特殊的情況:
這段程式的指令與DIR都是固定值,看似安全,但是程式貪圖方便,在ls前面省略了/bin/,攻擊者就可以在程式當前路徑下準備惡意的ls程式。當程式的第9行執行system()時,會優先選擇當前目錄下的ls,並以提升後的權限執行。這就是[#22] CWE-426路徑不可信的問題。
[#20] CWE-400 Uncontrolled Resource Consumption
使用資源的時候,還要注意數量的管理,避免資源耗盡的情況。尤其是在平行/非同步的程式中,更要注意沒有管理及限制程式能使用的資源上限 [#20] CWE-400所導致的問題。
上面這段程式中,利用一個無窮迴圈來等候socket連線。每當有新的連線進來,系統會就fork一個thread來服務之。但程式沒有控管socket連接數或fork thread的上限,所以攻擊者可以透過大量連接,使系統快速耗盡CPU、processes及記憶體,將服務癱瘓。
[#21] CWE-772 Missing Release of Resource after Effective Lifetime
另一種資源耗盡的情況是資源使用完畢後沒有釋放 [#21] CWE-772,例如:開啟某個資源的 handle,用完後沒有關閉或沒有歸還。上面的案例,在try catch中嘗試進行資料庫連線,一旦發生異常就會直接進入catch區塊,已經開啟的Connection handle就無法被關閉,亦無法再被使用。
應用系統若為不同的使用者提供相異的資料或服務。就要注意身分與權限的問題。這次上榜的身分問題有兩個,第一個是服務提供者必須確認使用者身分,另一個是確保使用者溝通的對象是真正的服務提供者。權限的部分,則有檔案存取的權限問題,以及程式執行權限的問題。
[#13] CWE-287 Improper Authentication
在網頁系統中,當使用者在cookie、URL、request中聲稱具有某身分,而網站端沒有進行充分的身份驗證,就直接給予相對應的權限,或是沒有限制登入嘗試的次數,就會存在[#13] CWE-287身分驗證不足的弱點。攻擊者如果掌握受害者的部分資訊,就能輕易的利用此弱點冒充受害者,竊取有價值的資訊或財產。CWE-287看似是新上榜的弱點,實際上在2011年就出現過,分別是CWE-863、CWE-862、CWE-306。而這三個弱點在2019分別排在第[#33]、[#34]、[#36]名。
有些網站會把使用者的身分及登入狀態記在cookie中,如果網站僅憑cookie來識別使用者,攻擊者甚至不需要知道受害者的資訊,直接利用「瀏覽器會自動帶入request目標網站的cookie」特性所引發的弱點:[#9] CWE-352 CSRF跨站請求偽造,就能在受害者不知情的情況下,用受害者的身分(瀏覽器&cookie)向網站提出請求。為了防範此弱點,程式在敏感操作前,務必充分驗證提交請求的使用者身分,例如:請使用者再輸入一次密碼,或是簡訊驗證碼、圖形驗證碼這一類的身分驗證。也可以由Server產生CSRF-Token,儲存在Session與Client端動態生成的的地方(cookie以外)。收到請求時,檢查Token是否相同,也是一種確認使用者本人的好方法。[#9] CWE-352 Cross-Site Request Forgery (CSRF/XSRF)
[#25] CWE-295 Improper Certificate Validation
另一個身分問題則好發在移動應用程式上,使用者會透過移動裝置上的應用程式,與遠端的服務提供者進行通訊。若應用程式沒有幫使用者確認對方的certificate(證書)是否正確且有效,就會存在[#25] CWE-295弱點,通訊的過程就可能被介入。下面這段程式碼中,雖然有檢查連接方的certificate,但由於certificate是自簽的,因此沒有外部機構可以證明對方主機的身分。與不明的主機進行通訊,DNS cache可能被破壞或遭受中間人攻擊。
[#15] CWE-732 Incorrect Permission Assignment for Critical Resource
確認完身分,再來要注意權限的問題。首先是檔案存取的權限,若程式建立檔案時沒有特別指定存取權限,該檔案就會以系統預設的檔案權限來建立。這就可能就會讓預料以外的人能夠讀取檔案內容。若很不幸的檔案內容是機敏資料或關鍵資源(如:檔案、config),那就存在[#15] CWE-732弱點。
[#24] CWE-269 Improper Privilege Management
第二種權限是執行的權限。一般來說,應用程式的執行身分會根據最小權限原則來分配,只有某些業務需求才會暫時提高權限。下面這段程式,先用raisePrivileges()提高權限,建完資料夾後再用lowerPriileges()降回原來的權限。但如果os.mkdir()裡面發生異常,則程式會直接跳過lowerPriileges(),保持特權提升的狀態繼續執行。這就是一個[#24] CWE-269的案例。
除了上述四類,還有幾個開發過程中常見的程式寫法,也有可能暗藏弱點。
[#4] CWE-200 Information Exposure
開發人員為了方便debug,常在Extension的處理上直接把log印出來。這個小小的行為,不僅能幫助開發人員了解錯誤原因,更能打造一個"駭客友善系統",幫助攻擊者快速掌握系統機敏資訊,好比說IP位址、路徑、帳號名稱等等。大部分的log功能,都要小心[#4] CWE-200資料洩漏這個議題。對了,不要認為log檔存在server端就沒有洩漏的問題,攻擊者可能利用其他系統弱點(例如'Path Traversal')來取得這些資訊。換句話說,這個弱點的嚴重程度取決於資訊的內容,而不是資訊輸出的地方,所以建議您在log輸出前,做個加密或編號化。
[#19] CWE-798 Use of Hard-coded Credentials
另一個開發常見的行為是"hard-coded"。"hard-coded"沒有不好,在程式開發過程中,作為概念驗證或流程測試,都是省時省力的好作法。但是在程式或產品發布時,機敏資訊仍是hard-coded的狀態,這就會有[#19] CWE-798的問題了。許多程式連接資料庫或與Server通訊時,使用的連線資訊或加密金鑰都是直接寫死在程式碼中。對於函式庫、API、mobile APP這類執行檔(二進位檔)在外流通的程式,hard-coded的資訊容易因反組譯而被揭露。相對的,被連接的Server程式也不能疏忽,對於連接者的資訊同樣不能寫死在程式中(例如為維修單位開的後門),應該要使用configuration檔或properties檔來記錄這些資訊,未來才能動態維護這些資訊。當然,機敏資訊寫在檔案中,一定要加密,才不會關了一扇CWE-798的門,又開了[#4] CWE-200、CWE-260、CWE-13的窗。
[#8] CWE-190 Integer Overflow or Wraparound
最後這個弱點是考驗開發者的細心度:[#8] CWE-190 整數溢出或循環,在計算機的世界中,不同大小的數字變數都有其能表示的上限與下限。如果超過這個限制,多數系統會自動忽略無法表示的部分,讓程式繼續執行下去。
以下面這個OpenSSH 3.3的程式碼為例,假設nresp的值為1073741824 ,在第3行乘上4之後,就會發生溢位,剛好變成0。因此系統會分配一塊長度為0的空間給程式,而程式也會繼續執行下去,衍生出記憶體操作不當的弱點。
其實應用系統中的弱點並不只有上述25項弱點,在這邊僅先提醒各位讀者這些重大的弱點需要優先處理,但仍不要忘了還是有其他的弱點需要我們的關注;建議應多使用SAST或IAST工具(如Checkmarx),檢查可能有缺陷的程式碼。如果沒有適當的應用程系安全測試流程,那麼很容易忽略由開發人員不小心引起的可利用漏洞。