CommBuffer / SMM pointer not-in-SMRAM (confused deputy)¶
CommBuffer'ı — ya da içindeki nested bir pointer'ı — SMRAM'e karşı doğrulamayan bir SMI handler, ring-0 kodun handler'ı kendi korunan belleğini okumaya ya da yazmaya zorlamasına izin verir; bu da SMM'i bir confused deputy'ye dönüştürür.
Mechanism¶
Neden çalışır — SMM, saldırgan kontrolündeki bellekte yaşayan bir pointer'a güvenir
System Management Mode (SMM), en privilege'li x86 execution context'idir. Kodu ve verisi SMRAM'de oturur, OS'tan kilitlenmiş halde. OS'tan iş kabul etmek için bir SMI handler, tasarım gereği SMRAM dışında, OS'un erişebildiği bellekte yaşayan paylaşımlı bir Communication Buffer (CommBuffer) okur.
Platform invariant'ı şudur: bir SMI handler, SMRAM'e yönelen OS-tarafından-verilmiş bir pointer'ı asla dereference etmemelidir. EDK2 bunu zorunlu kılmak için SmmIsBufferOutsideSmmValid(Address, Size) sağlar. Klasik confused-deputy bug'ı şudur: handler dış CommBuffer'ı doğrular ama sonra içindeki bir alanı pointer olarak ele alır (bir nested pointer, örn. CommBuffer->BufferSize uzunluğundaki CommBuffer->BufferPtr) ve o pointer'ı SMRAM'e karşı doğrulamadan dereference eder. Saldırgan o alanı basitçe SMRAM'in içine gösterir. Handler "çıktısını yazdığında" SMRAM'in üzerine yazar — en privilege'li context'e arbitrary write — ve "girdisini okuduğunda" SMRAM içeriğini geri sızdırır.
Açıklanmış bug'larda iki incelik görülür. Birincisi, mevcut bir kontrol bile yanlış olabilir: bir size alanını (beklenen sabit bir değer yerine) yalnızca sıfıra karşı doğrulamak hâlâ oversized write'lara izin verir. İkincisi, doğrulama dış buffer'ı değil, alanların local copy'sini kullanmalıdır: bir TOCTOU varyantında saldırgan, kontrol ile kullanım arasında CommBuffer'ı mutate eder. Defansif kural aynıdır: copy in, kopyayı doğrula, sonra yalnızca kopya üzerinde işlem yap.
Walkthrough¶
Aşağıdaki örnekleyici pattern, public EDK2/firmware analizini (NCC Group, SentinelOne) izler. Doğru kontrolü ve temsili bir eksik nested kontrolü gösterir — silahlandırılmış bir handler değildir.
Bir handler önce dış buffer'ı doğrular:
// Correct: outer CommBuffer is checked before any use
if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) {
return EFI_SUCCESS; // refuse: buffer overlaps SMRAM
}
Bug, bir nested pointer kontrolsüz dereference edildiğinde ortaya çıkar:
typedef struct {
UINTN OpCode;
UINT8 *BufferPtr; // attacker-controlled, points anywhere
UINTN BufferSize; // attacker-controlled
} SMM_REQUEST;
SMM_REQUEST *Req = (SMM_REQUEST *)CommBuffer; // outer buffer was validated
// MISSING: SmmIsBufferOutsideSmmValid((UINTN)Req->BufferPtr, Req->BufferSize)
CopyMem (Req->BufferPtr, InternalSmramSecret, Req->BufferSize); // leaks/overwrites SMRAM
Beklenen saldırgan sonucu: BufferPtr'ı SMRAM'in içine ayarlayarak CopyMem, kopya yönüne bağlı olarak arbitrary bir SMRAM read ya da write primitive'ine dönüşür — yani ring 0'dan SMM privilege escalation.
Sağlam biçim, her pointer'ı beklenen size ile doğrular ve local bir snapshot üzerinde çalışır:
SMM_REQUEST Local;
CopyMem (&Local, CommBuffer, sizeof (Local)); // snapshot
if (!SmmIsBufferOutsideSmmValid ((UINTN)Local.BufferPtr, Local.BufferSize)) {
return EFI_SUCCESS; // refuse nested overlap
}
if (Local.BufferSize != EXPECTED_SIZE) return EFI_SUCCESS; // size must match, not just != 0
Detection¶
- Firmware'ın static analizi. SMM driver'larını disassemble et (örn. IDA + HexRaysCodeXplorer / HexRaysPyTools ile) ve bir CommBuffer alanını, o nested pointer üzerinde karşılık gelen bir
SmmIsBufferOutsideSmmValid()çağrısı olmadan pointer olarak dereference eden SMI handler'ları işaretle. - CHIPSEC. SMRAM/TSEG DMA korumasını doğrulamak için
smm_dmamodülünü; SMI pointer argümanlarını fuzz'layıp olmaması gereken yerde fault üreten ya da yazan handler'ları yüzeye çıkarmak içinsmm/smm_ptrmodüllerini kullan. - WSMT flag'i. Windows SMM Security Mitigation Table'ında
COMM_BUFFER_NESTED_PTR_PROTECTION'ı kontrol et; yokluğunu bir risk sinyali olarak değerlendir (varlığı tek başına bir garanti değildir).
Mitigation¶
Her pointer'ın local copy'sini, beklenen size ile doğrula
- Herhangi bir dereference'tan önce, kontrol ettiğin bir uzunluk kullanarak dış CommBuffer ve her nested pointer üzerinde
SmmIsBufferOutsideSmmValid()çağır. - Önce CommBuffer'ı SMRAM'e kopyala, sonra yalnızca local copy'yi doğrula ve kullan — bu hem nested-pointer hem de TOCTOU pencerelerini kapatır.
- Size alanlarını yalnızca sıfıra karşı değil, beklenen sabit değere karşı karşılaştır.
- AMI firmware'ında, tutarlı kontroller için platform'un
AMI_SMM_BUFFER_VALIDATION_PROTOCOL'ünü tercih et; vendor BIOS güncellemelerini uygula (örn. ilgili SMM CommBuffer düzeltmeleri için INTEL-SA-00688 / CVE-2022-21198 gibi Intel advisory'leri).
Ayrıca bkz.: komşu SMM trust-boundary hataları için Binarly SMM callout class.