使用污點分析檢查log4j問題

摘要:log4j問題的餘波還在繼續,為什麼這個問題潛伏了這麼長時間,大家一直沒有發現?這裡從靜態分析的角度談下log4j問題的發現。

本文分享自華為雲社區《使用污點分析檢查log4j問題》,作者: Uncle_Tom。

1. JNDI注入

這次log4j的問題主要是由於JNDI問題造成的,先介紹下JNDI。

1.1. JNDI

  • JNDI是介面服務
    Java Naming and Directory Interface(JNDI) 是一個應用程式編程介面 (API),它為使用 Java程式語言編寫的應用程式提供命名和目錄功能。各種目錄服務都可以以一種通用的、統一的介面方式訪問。
  • JNDI架構
    JNDI 架構由 API 和服務提供者介面 (service provider interface(SPI))組成。 Java 應用程式使用 JNDI API 來訪問各種命名和目錄服務。 SPI 可以透明地插入各種命名和目錄服務,從而允許使用 JNDI API 的 Java 應用程式訪問它們的服務。見下圖:

  • JNDI提供的服務
    JNDI 包含在 Java SE 平台中。要使用 JNDI,必須使用JNDI類和一個或多個服務提供者。 JDK 包括以下命名/目錄服務的服務提供者:
    • 輕量級目錄訪問協議 (Lightweight Directory Access Protocol (LDAP));
    • 域名服務 (Domain Name Service (DNS))。
    • 網路資訊服務(Network Information Service(NIS));
    • 名稱服務 Java 遠程方法調用(Java Remote Method Invocation (RMI));
    • 通用對象請求代理體系結構 (Common Object Request Broker Architecture (CORBA)) 通用對象服務 (Object Services (COS))。
  • JNDI的Java實現
    JNDI程式包:
    • javax.naming:包含了訪問命名服務的類和介面。例如,它定義了Context介面,這是命名服務執行查詢的入口。
    • javax.naming.directory:對命名包的擴充,提供了訪問目錄服務的類和介面。例如,它為屬性增加了新的類,提供了表示目錄上下文的DirContext介面,定義了檢查和更新目錄對象的屬性的方法。
    • javax.naming.event:提供了對訪問命名和目錄服務時的事件通知的支援。例如,定義了NamingEvent類,這個類用來表示命名/目錄服務產生的事件,定義了偵聽NamingEvents的NamingListener介面。
    • javax.naming.ldap:這個包提供了對LDAP 版本3擴充的操作和控制的支援,通用包javax.naming.directory沒有包含這些操作和控制。
    • javax.naming.spi:這個包提供了一個方法,通過javax.naming和有關包動態增加對訪問命名和目錄服務的支援。這個包是為有興趣創建服務提供者的開發者提供的。
  • JNDI上下文(javax.naming.Context)
    • javax.naming 包定義了一個 Context 介面,它是查找、綁定/解除綁定、重命名對象以及創建和銷毀子上下文的核心介面。
    • lookup: 最常用的操作是lookup()。通過lookup()查找對象的名稱,它返回綁定到該名稱的對象。
    • Bindings:listBindings() 返回名稱到對象綁定的枚舉。綁定是一個元組,包含綁定對象的名稱、對象類的名稱和對象本身。
    • list: list() 與 listBindings() 類似,不同之處在於它返回包含對象名稱和對象類名稱的名稱枚舉。 list() 對於諸如瀏覽器之類的應用程式很有用,這些應用程式希望發現有關綁定在上下文中的對象的資訊,但不需要所有實際對象。儘管 listBindings() 提供了所有相同的資訊,但它可能是一個更昂貴的操作。
    • Name: Name 是一個表示通用名稱的介面, 包含零個或多個組件的有序序列。命名系統使用此介面來定義遵循其約定的名稱。
  • JNDI初始化
    在JNDI中,所有命名和目錄操作都是相對於上下文執行的,沒有絕對的根路徑。因此,JNDI 定義了一個 InitialContext,它為命名和目錄操作提供了一個起點。通過初始的上下文,就可以使用它來查找其他上下文和對象。

1.2. JNDI注入

JNDI 通過InitialContext.lookup(String name)一個字元串參數進行初始化,如果該參數來自不受信任的源,則可能通過遠程類載入導致遠程程式碼執行。當請求對象的名稱由攻擊者控制時,可能會將受害者Java應用程式指向惡意RMI/LDAP/CORBA伺服器,並使用任意對象進行響應。

1.3. log4j的JDNI注入

本次log4j的JNDI注入問題,就是因為當log4j的日誌輸出為”${xxx}”的時候,log4j會認為這是個JNDI調用的標誌,並將xxx解析為JNDI資源後,載入運行。如果xxx為惡意服務地址的時候,危害也就發生了。

2. 污點分析

污點分析(Taint analysis)可以被看作是資訊流分析(Information Flow Analysis)的一種,主要是追蹤數據在程式中的走向。

在漏洞分析中,使用污點分析技術將所感興趣的數據(通常來自程式的外部輸入)標記為污點數據,然後通過跟蹤和污點數據相關的資訊的流向,可以知道它們是否會影響某些關鍵的程式操作,進而挖掘程式漏洞。即將程式是否存在外部輸入導致漏洞的問題,轉化為污點資訊是否會被 Sink 點上的操作所使用的問題。

污點分析技術目前主要有靜態和動態分析兩種方式。本文主要講述通過靜態分析工具所採用的靜態分析方法。

2.1. 污點分析的主要概念

通常外部輸入數據都被認為是一種可能引起安全風險的源頭,被稱為污染源。這些資訊被程式的接收或處理函數收到後,在程式內部通過程式內部的各個功能模組被加工、處理或調用某些外部介面執行。這些資訊如果未做有效的檢驗就可能造成各類安全風險。

下圖是污染分析在分析污染傳播的過程中所經歷的主要過程,這個過程被劃分為:污染源、污染傳播、污染清理、污染爆發。

2.1.1. 污染場景

  • 數據流入系統
    外部污染通常由信任域意外傳入信任域內,會導致系統運行的問題。主要有:
    • 外部輸入:用戶介面的輸入、外部發來的請求電文、資料庫讀取的資訊;
    • 環境和設置資訊:在不安全的環境中運行時,需要從環境中讀入的資訊。例如:環境變數、配置文件等;
  • 數據內部流轉
    在程式內部,會有一些因為設計或編碼過程中引入的數據範圍不當設置或判斷,最終導致安全問題。例如除零、數組越界等。
  • 數據流出系統
    信任區域內的關鍵或需要保密的資訊,被泄露到信任域外的場景一樣可以通過污點分析技術進行分析。例如:
    • 系統內部資訊:系統內部的資訊往往會暴漏系統的內部結構,這些資訊給外部攻擊提供了明確的嗅探目標。例如:日誌的輸出的包含堆棧資訊的異常資訊等;
    • 敏感資訊:敏感資訊是需要嚴格保護的,這些資訊的泄露也會給系統帶來巨大的損失。例如:個人身份資訊、個人財產資訊、個人健康資訊等。

2.1.2. 污染源

污染的資訊會通過應用軟體特定的函數被引入到應用系統中,這也是污染分析的起點。靜態分析工具中,通常採用下面的方法完成污染源的定義:

  • 通過配置文件完成不同外部污染源的定義,方便靜態分析工具在分析時,可配置的載入和分析;
  • 配置文件通過正則表達式或直接指定具體函數的方式定義污染源,通過這種方式匹配程式中使用的函數;
  • 污染源函數的匹配主要包括:命名空間、類名、函數名;
  • 污染源通過函數向下傳播的具體變數。通常被定義為匹配到函數的某個參數或返回值;
  • 對於面向對象的程式,需要考慮是否適用於這個匹配函數的繼承或派生函數;
  • 通常還會給每個定義加上不同的污染標記,以便在污染清理時,做出不同類型污染的清理判斷。

2.1.3. 污染傳播

污染傳播是污染分析中最複雜的一個階段,主要分為顯示分析和隱式分析。

  • 顯示分析:顯式分析就是分析污點標記如何隨程式中變數之間的數據依賴關係傳播。
    這個分析主要是通過依賴圖完成污染的在賦值操作、表達式處理、數組/結構體賦值等函數內的傳遞分析;再依託函數調用圖完成函數間的傳播。
  • 隱式分析:是分析污點標記如何隨程式中變數之間的控制依賴關係傳播,污點標記如何從條件指令傳播到其所控制的語句,即污點通過控制運行的分支,達到污染傳播的目的。

在顯示分析和隱式分析中,都不可避免的會遇到:內置函數、第三方函數,以及應用框架的問題。

  • 內置函數:由於內置函數沒有程式碼,當污染進入某個參數是,分析程式無法知道這個函數的行為,污染是被另一個參數傳遞出去了?還是被返回值傳遞出去了?
  • 第三方函數:應用程式通常是以模組的方式完成應用系統組合,特別是目前的應用系統大量引用第三方的模組。當第三方函數是以包的方式引入的時候,就會遇到和內置函數一樣的問題,分析程式不知道這些函數的行為。
  • 應用框架:為了降低應用系統開發的難度,通常我們會採用一些成熟的開發框架。這些框架為了應用的靈活性,往往也不直接實現程式碼間的調用,而是通過配置的方式。例如spring框架中事件的驅動方式,是通過配置完成了不同模組間的銜接,這些對於依賴源碼的靜態分析來說,無法適配彼此間的調用關係。

內置函數、第三方函數,以及應用框架都會造成分析中斷的問題,從而使污染分析不能有效的進行。為了解決這個問題,靜態分析軟體需要定義這些函數,幫助分析程式完成污染傳播的繼續分析。

這些函數的定義方式基本和污染源的定義方式相同,採用配置的方式匹配到這些函數,同時還需要明確這些函數污染傳入的參數和傳出的參數。以方便靜態分析軟體在碰到這些函數時,根據傳出參數繼續分析。

2.1.4. 污染清理

在編程的過程中,如果對於不安全的外部輸入,已經做了相應的處理,但如果靜態分析工具無法知道這些檢查和處理,讓然給出告警,將成為誤報,反而會增加分析的成本。所以在程式分析的過程中需要對污染清理做出分析,以便減少不必要的誤報,同時還可以提高分析效率。

污染清理主要分為兩種處理方式:

  • 有效的條件判斷:對輸入值的範圍做了條件判斷,以便剔除不安全的輸入;
  • 清理函數:對於一些複雜的判斷通過一些函數來完成。這包括:加密函數、轉義函數等;

對於清理函數的處理,同樣可以採用配置的方式完成。只要對適配到的函數,做污染標記清楚就可以了,使靜態分析工具在經過這些函數不再帶有繼續分析的標記,終止後續的分析。

2.1.5. 污染爆發

當污染經過傳播仍然進入到一些特定的函數或語句時,就會引發安全漏洞的發生。這些安全漏洞主要有:

  • 輸入驗證不當 – (20)

例如:輸入中指定數量的不正確驗證 – (1284); 輸入中指定索引、位置或偏移量的不正確驗證 – (1285)等。

  • 可索引資源的錯誤訪問(「範圍錯誤」) – (118)

例如:不檢查輸入大小的緩衝區複製(「經典緩衝區溢出」) – (120); 整數溢出導致緩衝區溢出 – (680)等。

  • 下游組件使用的輸出中特殊元素的轉義處理不恰當(「注入」) – (74)

例如:在命令中使用的特殊元素轉義處理不恰當(「命令注入」) – (77); SQL 命令中使用的特殊元素轉義處理不恰當(「SQL 注入」) – (89)等。

  • 動態管理程式碼資源控制不當 – (913)

例如:使用外部控制輸入來選擇類或程式碼(「不安全反射」) – (470);不可信數據的反序列化 – (502), 這次的log4j JNDI問題被NVD定義的CWE。

  • 資訊泄露
    • 將敏感資訊暴露給未經授權的使用者 – (200)

    • 敏感資訊的不安全存儲 – (922)

3. log4j的問題分析

3.1. 測試程式碼

  • 下載程式碼
$ git clone --branch rel/2.14.1 https://gitbox.apache.org/repos/asf/logging-log4j2.git
  • 一個測試程式碼
    log4j的debug、info、warn、error、fatal都是污染源,
package org.apache.logging;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class testJndi {
    private static final Logger LOG = LogManager.getLogger();
 
    public static void main (String[] args){
        LOG.error("${jndi:xxx}");
    }
}

3.2. 調用鏈

  • 污染分析設置:
    • 污染源: org.apache.logging.log4j.Logger.error函數,且第一個參數中包含”${” 和 「}」,加這個約束的目的是降低工具的分析難度;
    • 爆發點: JNDI主要的的調用函數:javax.naming.Context.lookup()函數,且污染進入這個函數的第一個參數,同時查找繼承和派生這個函數的函數;
    • 污染傳播:java的內置函數,例如這個問題里用到的String.substring() 等等;
  • 靜態分析工具完整的分析鏈如下:
    圖很長,並做了節選,拆成兩段:

圖1

圖2

4. 思考

  • 從分析鏈路來看,log4j問題經過了非常複雜的鏈路才傳到最後的污染爆發點,一共涉及了10幾個文件之間的調用,這裡還沒有放入更詳細的類圖之間的關係,可見是一個非常複雜的問題;
  • 污染傳播的鏈路中,任何的調用(跨函數、類繼承等)或內置函數的不適配,都將導致無法檢查出這個問題;
  • 通常情況下,工具不會將log做為外部輸入,將其設置成污染源;如果從外部輸入傳入log,將會使分析鏈路更長,分析的難度也會更大;
  • 第三方的軟體通常以包的形式引入開發工程,做靜態檢查的時候不會以源碼的方式鏈接到檢查程式碼中。對於靜態分析,也就很難發現其中的問題;
  • 這樣複雜和深度的檢查,是開源靜態檢查工具能力所無法到達的地方,即使是商用工具也都非常困難;
  • 隨著大家對軟體安全的重視,比較淺層的安全問題相對比較容易發現,剩下深層次的安全問題,就需要更加強大的靜態分析工具,特別需要加大在安全檢查能力上的布局。

5. 參考

 

點擊關注,第一時間了解華為雲新鮮技術~