Skip to content

PartitionAlloc freelist pointer encoding bypass

PartitionAlloc'un encoded/shadowed freelist next pointer'larını bertaraf et, böylece transform ve IsWellFormed sağlamlık check'lerine rağmen bozulmuş bir free slot yine de attacker-seçimli bir allocation adresi verir.

Mechanism

Note

Free bir PartitionAlloc slot'u, singly-linked next pointer'ını düz memory olarak değil, slot body'sinde saklar. Değer geri çevrilebilir bir transform'dan (little-endian build'lerde, byte-order ters çevirme / pool-offset encoding) geçirilir ve shadow check etkin olduğunda, bir shadow field bitwise-ters çevrilmiş encoding'i tutar. Amaç iki yönlüdür: (a) olduğu gibi dereference edilen encoded bir pointer fault eder ve (b) low byte'ların partial lineer bir overwrite'ı encoding'i bozar, böylece artık geçerli bir slot'a decode olmaz ve bunu FreelistEntry::IsWellFormed() reddeder (FreelistCorruptionDetected()). Attacker'ın bunu bypass etmek için sağlaması gereken invariant bu yüzden rastgele bir cookie'nin gizliliği değil, iç tutarlılık'tır: encoding pointer'ın deterministik bir transform'udur (klasik build'lerde per-slot rastgele key yok), dolayısıyla tam encoded değeri — ve eşleşen bir shadow'u — yazabilen bir attacker, tüm check'leri geçen ve bir sonraki allocation'ı yönlendiren "well-formed" bir entry üretir.

Walkthrough

Kavramsal adımlar:

  1. Encoding'i geri kazan. Herhangi bir in-slot next değerini (örn. UAF read üzerinden) leak et ve bilinen transform'u ters çevir — little-endian build'lerde bu byte-swap'tir — böylece plaintext slot adresini, SuperPage base'ini ve offset'leri öğren.
  2. Tutarlı bir entry forge et. Transform(target)'ı hesapla ve build bir shadow kullanıyorsa, ters çevrilmiş shadow'u da hesapla, böylece IsWellFormed() kendi-içinde tutarlı bir çift görür.
    encoded = Transform(target_addr)        // byte-swap / pool-offset
    shadow  = ~encoded                       // shadow must match
    // write {encoded, shadow} into the freed slot body
    
  3. Sınır içinde kal. Check'ler ayrıca decode edilen next'in aynı SuperPage'de (thread-cache olmayan list'ler için) ve metadata'da olmamasını gerektirir; bu constraint'leri sağlayan bir target seç, ya da SuperPage sınırlarını aşabilen bir thread-cache list'i üzerinden pivot et.
  4. Yeniden allocate et. Bozulmuş entry bucket'ın freelist'ini besler, böylece sonraki aynı-boyutlu allocation target döndürür — bkz. partitionalloc-double-free-to-arbitrary-allocation.md.

Warning

Bu, tcache-poisoning.md / freelist-poisoning.md'in PartitionAlloc karşılığıdır: encoding çıtayı yükseltir (full overwrite + shadow + same-SuperPage), ama bu keyed bir MAC değil, obfuscation artı integrity check'leri'dir, dolayısıyla yeterince bilgili bir write geçerli bir entry'yi yeniden inşa eder.

Detection

  • Bir entry shadow, alignment, metadata ya da same-SuperPage check'lerini geçemediğinde instrumented/PA_DCHECK build'leri FreelistCorruptionDetected() / IsWellFormed()'de abort eder — uyarı verilmeye değer bir crash imzası.
  • Non-slot ya da metadata adreslerine decode olan renderer crash'leri ve free edilmiş slot'lar üzerindeki ASan raporları, freelist kurcalamasına işaret eder.

Mitigation

  • PA_CONFIG freelist shadow check'lerini ve *Scan'i etkin tut; mevcut olduğunda MTE/tagged build'ler gönder, böylece yeniden kullanılan slot'lar fault eder.
  • Deterministik transform'lar yerine keyed/randomize edilmiş freelist encoding'lerini tercih et ve BackupRefPtr (backuprefptr-bypass.md) quarantine ile birleştir, böylece free edilmiş slot'lar hemen yeniden kullanılabilir olmaz.
  • Metadata etrafındaki guard page'ler ve SuperPage-confinement check'leri, forge edilmiş bir pointer'ın nereye işaret edebileceğini sınırlar.

References