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/