Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    86398746

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

本文會詳細分析Windows即將推出的最大安全功能——智能應用控制(Smart App Control,SAC)。

“智能應用控制”功能是什麼,為什麼我認為它是Windows 中最牛的安全功能之一。首先,SAC 會嵌入在操作系統中,啟用後將阻止惡意或不受信任的應用程序。這與AppLocker 非常相似。

Smart App Control 預計將與Windows 22H2 一起發布,Windows 22H2 應該會在今年9 月下旬發布。

SAC 具有三種可能的狀態,其中只有一種會執行這些操作:

強制:將強制阻止惡意或不受信任的應用程序,我們將此狀態標記為1。

評估:在此模式下,該功能將繼續評估你的系統是否適合強制模式下的執行,我們將此狀態標記為2。

關:該功能被禁用。一旦禁用,除非你重新安裝操作系統,否則無法再次激活它,我們將此狀態標記為0。

SAC 安裝了解如何安裝它。此功能需要全新安裝才能激活。如果我們為Build 22621 安裝ISO 並通過install.wim 導航到包含註冊表配置單元的文件夾,那麼我們可以將SYSTEM Hive 加載到註冊表編輯器中。在CI\Policy 項中,我們可以找到值VerifiedAndReputablePolicyState設置為2(評估狀態)。

1.png

同樣在CI 項中,我們有SubKey Protected,我們可以在其中找到以下值VerifiedAndReputablePolicyStateMinValueSeen 也設置為2。

2.png

稍後我們將進一步了解如何使用這些項來控制SAC的實際狀態,我們還將了解如何保護Protected SubKey下的值以避免被篡改。

為了在升級時強制執行此操作,我們可以看到安裝ISO 在CI 的替換清單中有以下代碼:[ISO]\sources\replacementmanifests\codeintegrity-repl.man。

3.png

升級操作系統時,這段代碼將檢查註冊表值HKLM\SYSTEM\CurrentControlSet\Control\CI\Policy\VerifiedAndReputablePolicyState 是否存在,如果不存在,它將以SAC 狀態0(關閉狀態)創建。

除了這兩個新的註冊表值之外,操作系統還將在System32\CodeIntegrity\CiPolicies 文件夾中附帶兩個新的系統完整性策略文件(.cip)。

PolicyGUID:{0283AC0F-FFF1-49AE-ADA1-8A933130CAD6}強制SAC策略,當SAC狀態設置為Enforce(1)時激活;

PolicyGUID:{1283AC0F-FFF1-49AE-ADA1-8A933130CAD6} 評估SAC 策略,當SAC 狀態設置為evaluation (2)時激活;

使用WDACTools 中的CIPolicyParser 腳本,我們將兩個.cip 文件轉換為它們的.xml 表示形式。我們可以從XML 中獲取策略規則來了解這些策略的選項。設置了以下規則,兩個XML 文件都可以在附錄中找到。

啟用:UMCI;

啟用:智能安全圖授權;

啟用:開發者模式動態代碼信任;

啟用:允許補充策略;

啟用:已撤銷已過期未簽名;

啟用:繼承默認策略;

啟用:未簽名的系統完整性策略;

啟用:高級啟動選項菜單;

禁用:腳本執行;

啟用:更新策略不重啟;

啟用:有條件的Windows 鎖定策略;

啟用:審核模式(僅在SAC 評估策略中);

最後,我們可以在System32 文件夾中搜索使用前面提到的註冊表值的二進製文件/模塊。

4.png

SAC 初始化我們將這個部分分為兩個階段。第一階段我們將在Windows加載程序中討論SAC。第二階段我們將在OS初始化期間討論SAC。重要的是要了解加載程序和操作系統都在啟用SAC 中發揮作用。最後,我將添加一個部分來解釋SubKey CI\Protected 下的值保護是如何工作的。

SAC 初始化流程圖如下所示。

5.jpg

Winload 期間的SAC在本節中,我們將討論如何選擇活動SAC 狀態的SAC 策略、Winload 如何強制執行RegKey 之間的持久性和一致性以及如何將SAC 策略傳遞給內核。

6.jpg

SAC初始化的第一步在操作系統加載程序過程中提前完成。更具體地說,在準備目標(OslPrepareTarget) 期間加載SystemHive 之後。為了處理系統完整性策略,將調用函數OslpProcessSIPolicy。在此函數中,將對條件策略(SKU、EMode、SAC Enforce、SAC Evaluation)進行評估,以查看是否應該忽略或解鎖它們。 Microsoft 認為這四個策略是有條件的,因為它們可以被忽略/解鎖,這與始終適用的“MS Windows 驅動程序策略”等其他策略不同。條件策略的策略GUID 存儲在由符號g_SiConditionalPolicies 定義的全局數組中。

忽略和解鎖之間的區別非常微妙。解鎖標誌將一直被選中。另一方面,忽略標誌只會在沒有設置“Enabled:Unsigned System Integrity Policy”的策略中被選中。

要確定是否應為Enforce 或Evaluation 啟用SAC,使用以下兩個函數。

OslpShouldIgnoreUnlockableNightsWatchDesktopEnforcePolicy

OslpShouldIgnoreUnlockableNightsWatchDesktopEvalPolicy這是我們第一次看到引用Nights Watch 來表示SAC,這似乎是微軟的內部名稱。

這兩個函數的行為方式相同,唯一的區別是它們為內部評估函數提供了不同的PolicyGUID:

7.png

此函數使用PolicyGUID 參數來確定要檢查的SAC 狀態。它調用OslpGetNightsWatchDesktopRegKeyState,它返回設備中的實際SAC 狀態。如果實際SAC 狀態與正在評估的狀態匹配,則認為該策略是活動的,這過於簡化了。如果設備是WinPE 或者是否需要簽名策略,則需要進行更多檢查。即使註冊表指示SAC 處於活動狀態,這些檢查也可以使函數返回Ignore 和Unlockable。

OslpGetNightsWatchDesktopRegKeyState 的行為值得一看。此例程負責在重新啟動後保持啟用SAC 並保持兩個註冊表值之間的一致性。此例程有四種可能的情況:

VerifiedAndReputablePolicyState==VerifiedAndReputablePolicyStateMinValueSeen:值是相同的,所以直接返回值。

VerifiedAndReputablePolicyState VerifiedAndReputablePolicyStateMinValueSeen:在上一個啟動會話期間,SAC 狀態被修改。我們從VerifiedAndReputablePolicyState 返回值,並在Protected SubKey下更新該值。

VerifiedAndReputablePolicyState VerifiedAndReputablePolicyStateMinValueSeen:這是一個極端情況,因為VerifiedAndReputablePolicyState 永遠不應大於受保護項下的值。如果有人手動編輯值VerifiedAndReputablePolicyState,我相信這是為了保持兩個值之間的一致性。

值為3或3以上:表示無效狀態轉換並且函數將失敗。

偽代碼總結如下。

8.png

當使用安全應用程序發生SAC 狀態更改時。操作系統將寫入VerifiedAndReputablePolicyState。用戶重新啟動後,此狀態將持續存在於設備中。這意味著在SAC 狀態轉換之後,仍然可以編輯VerifiedAndReputablePolicyState,並且轉換不會在下一次重新啟動後持續存在。這讓我認為微軟只有在安裝更新時才會觸發評估模式的轉換,或者他們會要求重新啟動。顯然,在會話期間發生SAC狀態轉換時,活動策略將被更新。

一旦檢查了所有的條件策略,看看它們是可解鎖的還是應該被忽略。從每個函數獲得的值將寫入以下兩個全局變量:

g_SIPolicyConditionalPolicyConditionUnlockHasBeenMet

g_SIPolicyConditionalPolicyConditionIgnoreHasBeenMet

寫入這些全局變量的值是一個四字節數組,可以用以下結構表示

9.png

在此之後,加載程序將嘗試解析策略文件。首先將每個.cip 文件中的序列化數據加載到內存中(參見BlSIPolicyGetAllPolicyFiles)。然後從SIPolicyParsePolicyData 中的每個文件解析數據,如果有人對細節感興趣,請檢查SIPolicyInitialize 以了解如何將策略的每個部分解析為一個結構。

解析策略後,將檢查忽略和解鎖條件以查看它們是否滿足。如果滿足某個條件,則該策略將被放棄。如果不滿足任何條件,則將使用函數SIPolicySetAndUpdateActivePolicy 將策略設置為活動。

如果設置了策略選項“已啟用:未簽名的系統完整性策略”,則PolicyVersion 和PolicySignersData 將從EFI SecureBoot 私有命名空間中刪除。被刪除的變量名將由PolicyGUID和PolicyVersion/policyysignersdata字符串連接組成,只有當PolicyOptions禁用了“Enabled:Unsigned System Integrity Policy”時,才會創建這些EFI變量。

在下面的輸出中,我們可以看到SetVariable 是如何以大小0 被調用的,這將導致如果找到該變量將被刪除。

10.png

對於這兩個SAC 策略,任何EFI 變量都將被清除。之後,將通過調用SIPolicySetActivePolicy 將策略設置為活動。此調用會將策略添加到將鏈接到全局變量g_SiPolicyCtx 的節點中。 g_NumberOfSiPolicies 將相應地遞增,並且新策略的句柄將存儲在g_SiPolicyHandles 中,此變量是一個包含32 個句柄的數組,因為WDAC 一次在設備上支持多達32 個活動策略。

保存在g_SiPolicyCtx 中的SI_POLICY_CTX 結構的原型如下:

11.png

下圖顯示了三個全局變量。在我的示例中,有三個活動策略,其中一個是SAC 強制執行策略的補充策略,補充策略有助於擴展基本策略以增加策略的信任。

12.png

有了這些信息,加載程序將能夠在加載程序參數塊內構建CI 結構。這是在函數OslBuildCodeIntegrityLoaderBlock 內完成的。這個例程,除其他外,將在函數SIPolicyGetSerializedPoliciesSize 的幫助下獲得序列化SI 策略的大小。該代碼將使用全局變量g_NumberOfSiPolicies 和g_SiPolicyHandles,並且大小將存儲在LOADER_PARAMETER_CI_EXTENSION 的CodeIntegrityPolicySize 字段中。之後,將通過函數SIPolicyGetSerializedPolicies 複製序列化的數據。此數據的偏移量將存儲在字段CodeIntegrityPolicyOffset 中。此信息以及其他CI 信息將存儲在LOADER_PARAMETER_EXTENSION 的CodeIntegrityDataSize 和CodeIntegrityData 字段中,當加載程序轉換到操作系統時,加載程序參數塊作為參數傳遞。

只有序列化的有效負載會被複製。我猜之前所做的所有策略解析主要是檢查策略是否有效,如果無效則觸發SYSTEM_INTEGRITY_POLICY錯誤。還可能使用來自認證或EFI變量策略的值。

這幾乎就是我們將在winload 期間看到的SAC 初始化的全部內容。

下面的捕獲顯示了在轉換到操作系統之前如何設置這些數據。

13.png

操作系統初始化期間的SAC看看內核如何初始化CI。在此之後,我們將了解CI 如何初始化Winload 提供的策略。最後,再看看它如何從這些策略中確定SAC 是否能夠相應地採取行動。

14.jpg

在操作系統初始化期間,更具體地說是在階段1。內核將調用方法CiInitialize(由ci.dll 導出)。該函數主要用於內核和CI 交換API。內核接收SeCiCallbacks,其中包含內核將用於與CI 交互的函數指針。另一方面,CI DLL 接收SeCiPrivateApis,其中包含VSL HVCI 接口等內核函數,因此CI 可以在進行任何HVCI 驗證時通過內核觸發Hypercall。內核還將傳遞初始的CodeIntegrity 選項。這些選項由Windows 加載程序構建並存儲在LOADER_PARAMETER_CI_EXTENSION 中。這些選項最初將包含諸如CodeIntegrity BCD 選項(DisableIntegrityChecks、AllowPrereleaseSignatures、AllowFlightSignatures)和WHQL 設置之類的內容。 CI 選項存儲在全局變量g_CiOptions 中,CI 還將根據從操作系統和策略中檢索到的信息更新它們。

仍然在操作系統的第1 階段期間,內核將在整個CI 回調中調用CiInitializePolicy。該例程將接收LOADER_PARAMETER_CI_EXTENSION 作為第一個參數。該例程將調用它的私有對應項CipInitializeSiPolicy。該函數將調用SIPolicyInitializeFromSerializedPolicies 來驗證、解析和加載來自加載程序參數CI 擴展的序列化策略。與winload 相同,如果策略解析正確,則策略將添加到g_SiPolicyHandles 和g_SiPolicyCtx。更重要的是,如果正確解析了序列化策略,則將調用函數CipUpdateCiSettingsFromPolicies。此方法根據每個策略的PolicyRules 更新全局CI 設置。在此函數中,CI 將通過調用SIPolicyNightsWatchEnabled 檢查是否啟用了SAC。

15.png

這個函數很有趣,我們終於可以開始研究SI 策略結構了。該函數將調用SIPolicyQueryOneSecurityPolicy。該例程具有以下原型:

16.png

這種方法在處理SI策略時經常出現。 Since用於檢查/獲取策略中設置的SecureSettings。策略結構(我個人將該結構命名為SI_POLICY)有以下兩個成員:SecureSettingsCount 和SecureSettingsData。

17.png

解析序列化策略時,所有安全設置所需的內存將被分配並存儲在SecureSettingsData 指針中。每當CI 必須查詢安全設置時,它會調用SIPolicyQueryOneSecurityPolicy 並使用它需要查找的Provider、Key 和ValueName。在內部,該函數會將這三個值存儲在一個結構中,該結構將用作bsearch 函數中的Key。搜索的基礎將設置為策略的SecureSettingsData。 CompareFunction 設置為SIPolicySecureSettingSearchCompare。 CompareFunction 將嘗試將SECURE_SETTINGS_DATA 中的Provider、Key 和ValueName 正在查詢的內容進行匹配。每個值的比較是使用RtlCompareUnicodeString 完成的。

在我們的示例中,當查看是否啟用了SAC(在SIPolicyNightsWatchEnabled 內部)時,傳遞給查詢函數的值如下:

Provider:Microsoft

Key:WindowsLockdownPolicySettings

ValueName:VerifiedAndReputableTrustMode如果在策略中找到安全設置,則認為SAC 已啟用,並將在g_CiPolicyState 中設置值NW_ENABLED (0x4000)。

這些值也以策略的XML 格式顯示。如果你檢查附錄中的實施和評估XML,你將看到此安全設置在兩者中都設置為true。

僅僅為了完成,PolicyState是一個位字段,它可以取以下值(有些值沒有),這些值大多取自函數CiInstrumentSiPolicyInfo的ETW事件元數據。

18.png

下圖顯示了在SIPolicyNightsWatchEnabled 中調用SIPolicyQueryOneSecurityPolicy 之前的狀態,其中SAC 強制策略用於查詢。