周軒
(吉安職業技術學院,江西吉安 343000)
隨著網絡安全形勢日益嚴峻,Web網站正面臨各類網絡安全攻擊。根據OWASP在2021年發布的十大Web應用程序漏洞中指出,注入攻擊排在第三位,足以說明其威脅性非常大。常見的注入攻擊包括SQL注入和跨站腳本攻擊(XSS),也有代碼注入、命令注入、CCS注入等,遭受攻擊的主要原因是沒有對外部輸入的數據進行驗證和過濾、直接使用或連接惡意數據等,這類數據稱之為污點源。在Web網站開發時,如果程序員對可疑的外部輸入數據進行驗證,此類攻擊事件的發生概率將會非常小[1]。因此,對Web應用程序進行污點分析,找出所有未驗證的污點源是非常必要的。
程序變量的數據依賴關系在程序分析中起到重要的作用,許多編譯優化和數據流分析方法就需要使用經典的變量定義使用鏈(DU),DU鏈是一種方法內的數據依賴關系。基于需求的數據依賴圖是指程序中“有關”變量的所有數據依賴關系構成的一張圖,這里的“有關”變量由用戶輸入指定,當“有關”變量指程序中出現的所有變量時,即為程序中所有數據依賴關系[2]。
污點分析方法通常分為動態污點分析和靜態污點分析。動態污點分析是在程序的運行過程中對可疑的輸入源進行標記及跟蹤,觀察其在程序運行過程中的傳播路徑,判斷是否會對程序產生不良影響,以此判斷是否為污點輸入源[3]。靜態污點分析則是在不運行和不修改代碼的情況下,對程序進行語句分析,運用程序變量之間的數據依賴關系,找出污點源(Sources)和污點匯聚點(Sink)之間的傳播路徑,從而達到找出程序潛在漏洞隱患的目的,再通過驗證數據方法進行無害化處理(Sanitizer)[4]。
本文基于Soot字節碼分析工具將J2EE程序轉換成Jimple中間表示,運用其生成的函數調用關系圖(Call Graph)、數據依賴關系及指針分析工具,建立Sources和Sink的可達矩陣,從而得到污點傳播路徑,為漏洞修復提供參考依據。
Soot是一種開源的Java字節碼優化框架,其本身也是使用Java語言開發,可以將使用Java語言編寫的程序轉換為Soot的中間表示,其表示形式有Baf、Jimple、Shimple和Grimp,其中Jimple是Soot字節碼分析工具最主要使用的一種中間代碼,可以將Java程序中的復雜調用、計算等語句轉化為三地址的呈現方式,只保留15種不同語句類型,簡化程序表示方式,方便開展代碼優化及分析[5]。
Soot是一個開源框架,在轉譯J2EE程序為Jimple中間表示過程中,會根據代碼中不同類型元素生成相應的類與對象及函數調用關系圖(Call Graph)。根據靜態分析需求,此方法分析涉及語句主要有函數調用語句(帶返回值與不帶返回值)、函數的入口語句及返回語句、賦值語句,Sources、Sink均包含于函數調用語句。為增加污點分析精度,在分析數據依賴傳遞時加入域訪問變量數據的檢測,確保訪問對象內部變量時不遺漏數據依賴傳遞關系[6-8]。
定義1:字符串類型或對象為污點傳播類型,包括String、StringBuffer、StringBuilder、String[]、char[]等。
定義2:Sink語句為系統所有輸出類語句,輸出對象為污點傳播類型。
定義3:Source語句為系統所有輸入類語句。
定義4:當一個對象v出現在賦值語句s等號右邊或函數返回語句s返回對象時,則Use(s,v)成立。
定義5:當一個對象v出現在賦值語句t等號左邊或為函數入口語句t的形參,則Def(t,v)成立。
定義6:同一方法內,相同變量之間存在依賴關系。
定義7:當兩個不同對象v1和v2同時指向同一內存地址,則v1和v2之間存在依賴關系。
定義8:函數調用返回值與函數內返回語句返回對象存在依賴關系。
定義9:函數調用參數與函數內入口形參之間存在依賴關系。
定義10:語句s和t為兩條不同語句,Use(s,v1)、Def(t,v2)成立,且v1和v2存在依賴關系,則s和t之間存在一條數據傳遞路徑。
污點分析流程圖如圖1所示,具體步驟如下:
(1)使用Soot工具將要分析的J2EE源程序或其編譯的Class文件轉換為Jimple中間表示,在轉換的過程中加入指針分析,增加數據依賴分析精度。
(2)結合定義的Sink和Source語句特征找出程序中符合條件的所有語句,建立初始依賴圖。
(3)對所有Sink語句根據數據傳遞依賴關系,進行方法內和跨方法計算,建立數據依賴圖。
(4)根據數據依賴圖,建立圖中節點之間的可達矩陣,找出所有Sink與Source的可達性,從而得到污點傳播路徑,并進行漏洞修復。
根據定義1、2、3,Sink和Source定義的對象類型,在Soot進行字節碼轉換時,找出符合條件的對象和所在語句,分別添加至Sink和Source列表中,并將所有Sink語句添加至方法內計算隊列,后續分析將根據隊列中的Sink點進行依賴傳遞分析,找出Sink與Source的傳遞路徑。
根據定義7,對于不同方法中不同變量之間存在依賴關系,在初始化時將變量所在的語句直接建立依賴邊,可以減少數據分析的資源消耗。
從方法內計算隊列中取出一條語句s1及污點傳播對象v1,根據定義4、5、10,在語句方所在方法內部進行數據依賴傳遞分析。
在計算時,根據s1中的污點傳播對象v1,在方法內向上逐條掃描語句,當出現語句t1滿足Def(t1,v1)時,則建立一條語句s1到t1的依賴邊。如t1語句為普通賦值語句,將語句t1加入方法內計算隊列。如t1為函數調用語句,則將t1添加至跨方法計算隊列。
當方法內計算隊列所有語句全部處理完畢后,開始進行跨方法計算。
在跨方法計算隊列中取出一條語句s2及污點傳播對象v2,根據定義7、8、9,建立s2與調用方法內相關語句的依賴關系。
在計算時,如調用方法有返回值,則建立一條s2與返回值所在語句t2的依賴邊,并將t2添加至方法內計算隊列。如調用方法沒有返回值,則查找是否在數據依賴圖初始化時已經建立跨方法的數據依賴邊,如已建立,則將依賴邊t3取出,添加至方法內計算隊列,否則,結束當前語句依賴邊計算。
當調用方法為庫函數時,如進入庫方法進行計算,將花費大量的時間。為提高計算效率,對于庫方法調用時可直接建立依賴邊,并將語句涉及的所有變量均定義為五點傳播對象。此方法將導致后續進行污點路徑分析時有誤報情況,需進行人工核對,篩選出誤報路徑。
當跨方法計算隊列所有語句全部處理完畢時,返回執行方法內計算隊列進行依賴邊建立。
在對程序進行靜態分析的時候處理的種子變量主要為可變類型。由于Java中可變類的構造方法是將字符串保存在可變類中私有變量value[]數組中,在跨方法計算時往往迭代到可變類的方法中value[]的初始化,而不能返回到方法調用語句。例如sb.appand(s)語句,其中sb為StringBuffer類型,s為String類型,按照算法定義是sb被s修改,因此sb與s之間存在數據依賴關系。但是在算法實現中執行到該方法調用語句時,將進行跨方法計算,最終將計算到sb類的域變量value[],導致結果達不到預期的期望。對于這種情況,本文采取假定這些方法都是閉包,沒有任何副作用,它們不影響任何的全局變量,僅僅影響方法的參數和返回值。原迭代算法照常進行的基礎下,在處理方法調用時,若種子變量為可變類型,則認為其可被方法調用語句中其他可變類型參數修改。
當方法內計算與跨方法計算隊列均執行完畢時,完整的污點數據依賴圖完成建立。污點路徑查找最簡單的方法可以通過每個Sink語句,在數據依賴圖中進行深度優先搜索,當執行到最深處時,假如該節點為Source,則找到一條有序的污點路徑。
通過實驗分析,上述方法在執行小型web網站時可以取得很好的效果,但在分析大型網站所消耗的時間呈幾何增長。針對路徑過多的問題,可將路徑劃分為4部分:Sink、臨界點1、臨界點2、Source。臨界點定義為所在邊的兩條語句中一條為web程序代碼,另一條為庫方法中的語句,選取web程序代碼作為臨界點。臨界點1和臨界點2分別為離Sink和Source最近的進入庫方法語句和出庫方法語句。
為了找出路徑中的臨界點,可使用標準Warshall算法對數據依賴圖進行預處理,建立可達性矩陣,在矩陣中標記所有節點互相的可達性。為節省內存,可將int除去符號位拆分成31位,用來標記節點是否可達。
根據可達性矩陣找出Sink所在行,查看Sink的可達節點中是否有Source,有則說明Sink和Source之間存在至少一條污染路徑。由于Sink和Source之間路徑經過節點過多,使用遍歷的方法找出路徑當中離Sink和Source最近的臨界點(邊的兩個節點中一個節點為應用程序中的語句,另一個節點為庫方法中的語句),使用Sink、臨界點1、臨界點2、Source來表示一條大概的污點路徑。
找到路徑之后,可以在Source和Sink語句之間添加驗證函數來修復該漏洞。例如SQL注入攻擊,在輸入用戶名和密碼之后未對其進行驗證,系統將會進行報警,待驗證之后即修復了該潛在漏洞。
本文借助Soot字節碼分析工具提出了一種基于數據依賴的污點分析方法,以Sink開始計算,通過數據依賴關系及指針分析進行方法內及跨方法數據傳播依賴邊的建立,形成基于Sink的數據依賴圖,進而找出潛在的安全漏洞。下一步將完善Sink和Source定義,進一步優化算法,提高分析準確性,減少誤報率。