Skip to content

Arbitrary read primitive

Bir "read-where": programın dereference edip açığa çıkardığı bir pointer'ı etkile; böylece bir read'i, attacker'ın seçtiği herhangi bir adresten okumaya çevir.

Mechanism

Note

Bir read primitive'i, hem dereference edilen bir adres'in hem de dereference edilen değerin output'unun attacker tarafından erişilebilir olduğu her yerde mevcuttur. Kanonik vektör bir format string'dir: variadic printf-ailesi fonksiyonlar argümanları pozisyonel olarak tüketir, ama printf kaç gerçek argümanın geçildiğini bilemez; bu yüzden fazla specifier'lar komşu memory'yi okur. %s argümanını bir pointer olarak ele alır ve o adresteki string'i yazdırır. Attacker stack üzerinde bir değeri de (kendi input buffer'ı) kontrol ediyorsa, oraya bir target adres yerleştirir ve %N$s'i ona nişanlar — map'lenmiş herhangi bir adresi okuyarak.

Invariant şu: dereference ettiği adres attacker-controlled olduğunda ve sonuç gözlemlenebilir olduğunda bir read arbitrary hale gelir. Diğer biçimler: bir struct içinde sonradan dereference edilen kontrollü bir index/pointer field'ı; bir out-of-bounds read; ya da basitçe resolve edilmiş bir libc symbol'ünü leak etmek için bir GOT entry'sini okumak (bir address leak).

Walkthrough

char buf[128];
fgets(buf, sizeof buf, stdin);
printf(buf);              // BUG: user input as format string
# Leak raw stack slots (direct param access at offset 6):
input:    %6$p
output:   0x7ffd1234abcd        # a stack/canary/libc pointer

# Arbitrary read: put target in our buffer, deref it with %s
# layout: ["%7$s" + padding][ p64(0x404018) ]   (0x404018 = a GOT entry)
input:    %7$s<pad>\x18\x40\x40\x00\x00\x00\x00\x00
output:   <8 bytes at 0x404018>    # resolved libc address of that function

Beklenen davranış: %N$p N'inci slot'u açığa çıkarır (canary/libc/PIE leak); %N$s kontrollü pointer'ı takip eder ve attacker'ın seçtiği bir adresteki memory'yi dump eder.

Warning

printf bir NULL byte'ta durur; bu yüzden payload'da format directive'lerini önce, ham target adresini sona yerleştir ve adres byte'larının getirdiği offset kaymasını hesaba kat.

Mitigation

  • Attacker input'unu asla format string olarak geçirme (printf("%s", buf)).
  • -Wformat -Wformat-security, printf(user)'ı compile time'da işaretler; FORTIFY_SOURCE bazı %n durumlarını kısıtlar ama read'leri değil.
  • Primitive bir disclosure yoluna bağlıdır — output'u kısıtlamak ve index'leri bounds-check etmek onu ortadan kaldırır.

References