MiraclePtr UAF 漏洞利用緩解技術介紹

語言: CN / TW / HK

作者:墨雲科技VLab Team

原文連結: http://mp.weixin.qq.com/s/oJPYrY84yqEa0FrezG-QPw

2022年9月13日,Google安全團隊在其安全部落格中釋出了一篇關於MiraclePtr的文章,介紹了Google Chrome安全團隊在緩解UAF漏洞利用上的進展。由於MiraclePtr並不是單指某一種智慧指標技術,而是包含了Google安全團隊在緩解UAF利用上的多次實驗和嘗試,本文也僅針對其最新啟用的BackupRef方案做介紹,如有疏漏錯誤,敬請斧正,共同交流探討。

MiraclePtr

首先需要明確,MiraclePtr與unique_ptr、weak_ptr等C++中的原始智慧指標並不是同一概念,它是Google安全團隊在緩解由指標引起的記憶體破壞漏洞過程中,提出的多種方案集合,其本質是希望將原始指標遷移到帶緩解方案的智慧指標類,通過引用計數、指標標記等方式阻止攻擊者對記憶體破壞漏洞被利用,重點解決的是UAF型別漏洞的懸垂指標可被利用的問題。

如上圖,Google安全團隊認為攻擊者在針對Chrome的攻擊過程中,通常需要組合一個渲染器漏洞利用和一個沙箱逃逸漏洞來達到完整利用的目的,MiraclePtr可以通過緩解UAF漏洞利用,有效的阻止攻擊者針對瀏覽器主程序中UAF漏洞的利用(上圖藍色部分),讓攻擊者無法實現完整的利用鏈,從而降低漏洞危害。

在對Chrome歷史可利用漏洞統計中,UAF型別漏洞佔了幾乎一半,因此MiraclePtr也嘗試了包含BackupRefPtr、BorrowPtr、SafePtr、CheckedPtr、MTECheckedPtr、ViewPtr在內的多種方式來緩解UAF型別的漏洞利用,並在對比了各方案在效能開銷、記憶體開銷、安全保護、開發人員便利性上的優缺點後,近期在Windows和Android的Chrome 102穩定版中啟用了BackupRefPtr,下文只重點介紹BackupRefPtr,其他方案詳細資訊檢視參考連結中的內容。

BackupRefPtr方案

BackupRefPtr提出了依賴“確定性引用計數”的指標保護方案,主要借鑑了CheckedPtr2、SafePtr和BorrowPtr的思路,並且需要Chrome的堆記憶體分配器PartitionAlloc支援。在2020年,Google ProjectZero在部落格公佈的一篇採用CPU漏洞側通道攻擊來洩漏快取資料,從而實現Chrome沙箱逃逸的文章,證明了依賴指標標記的方案有潛在的被通過側通道攻擊的風險,出於安全性考慮,確定性引用計數的方案成了優先選擇。

PartitionAlloc是Chrome中自行實現的堆分配器,主要在分配效率、記憶體空間利用率和安全性上進行了優化。PartitionAlloc使用2MB大小的超級頁面作為普通資料桶,每個超級頁面被分割成多個分割槽。第一個和最後一個分割槽是永久不可訪問的,用來當作保護頁面,在第一個分割槽頁中間的一個系統頁面儲存了元資料(metadata),這些元資料提供了對記憶體物件的跟蹤能力,BackupRefPtr使用到的引用計數就儲存在metadata中。

在Chromium的原始碼實現中,BackupRefPtr是一個執行緒安全的引用計數指標類,可以非常簡單的替換原始指標,Chromium團隊在引入BackupRefPtr時也一次性替換了原始碼之中超過15000個原始指標。BackupRefPtr的引用計數儲存在PartitionAlloc元資料中(與CheckedPtr2方案使用同一標誌位),如果在銷燬一個物件時,它的引用計數不為零,則會將該物件標記為被汙染,此時程式不會真正的釋放該記憶體,而是在再次訪問被破壞的物件時,程式將發生主動崩潰。

該方案PoC程式碼如下,具體實現可參考Chromium原始碼raw_ptr.h中的BackupRefPtrImpl類:

template <typename T>
class BackupRefPtr {
  BackupRefPtr(T* ptr) : ptr_(ptr) {
    if (!isSupportedAllocation(ptr))
      return;

    atomic_int& ref_count = *(cast<atomic_int*>(ptr) - 1);
    CHECK(++ref_count);
  }

  ~BackupRefPtr() {
    if (!isSupportedAllocation(ptr_))
      return;

    atomic_int& ref_count = *(cast<atomic_int*>(ptr) - 1);
    if (--ref_count == 0) // needed in case the BackupRefPtr outlives
                          // its pointee and has to free the slot
      PartitionAlloc::ActuallyFree(ptr_);
  }

  T* operator->() { return ptr_; }

  T* ptr_;
};

void* Alloc(size_t size) {
  void* ptr = ActuallyAlloc(size);
  if (isSupportedAllocation(ptr)) {
    int& ref_count = *(cast<int*>(ptr) - 1);
    ref_count = 1; // We need to set the reference count to one initially
                   // otherwise |~BackupRefPtr| can trigger deallocation of
                   // an object that’s still alive.
  }
  return ptr;
}

void Free(void* ptr) {
  if (isSupportedAllocation(ptr)) {
    atomic_int& ref_count = *(cast<atomic_int*>(ptr) - 1);
    if (ref_count != 1)
      memset(ptr, 0xcc, getAllocationSize(ptr));
    if (--ref_count != 0)
      return;
  }

  ActuallyFree(ptr);
}

總結

BackupRefPtr通過上述機制,解決了懸垂指標(Dangling Pointer)被利用的問題,在該方案中,發生釋放操作但引用計數不為0的物件並沒有被真正釋放,攻擊者無法使用堆噴射等方式重新分配該物件的記憶體空間,並且在物件再次被訪問時,該記憶體區域被填充了汙染標誌或發生主動崩潰,UAF漏洞被緩解為記憶體洩漏、斷言失敗或空指標等無法利用的崩潰。

整體而言,該機制的引入進一步降低了Chrome中可利用漏洞的比例,一定程度上提高了Chrome的安全性。

參考連結

2、Pointer Safety Ideas [PUBLIC] - Comparison of Use-After-Free mitigation proposals

http://docs.google.com/document/d/1qsPh8Bcrma7S-5fobbCkBkXWaAijXOnorEqvIIGKzc0/edit

6、MDS: Microarchitectural Data Samplin

http://mdsattacks.com/

本文由 Seebug Paper 釋出,如需轉載請註明來源。本文地址: http://paper.seebug.org/1979/