Skip to content

Information leak / memory disclosure

Hassas byte'ları — pointer'lar, sırlar ya da uninitialized memory — görmemesi gereken bir tarafa ifşa etmek; ASLR'ı yenip sonraki corruption'ın ihtiyaç duyduğu adres bilgisini sağlar.

Mechanism

Invariant

Bir program yalnızca kasıtlı olarak verdiği veriyi geri vermelidir. Memory disclosure bu niyeti bozar: byte'lar hiç geçmemeleri gereken bir trust boundary'yi geçer. CWE-200 üç rota tanımlar — explicit insertion (kod hassas veriyi temizlemeden erişilebilir bir kaynağa bilerek kopyalar), indirect insertion (başka bir güvenlik açığı, ör. fazla-uzun bir read, bitişik memory'yi çıktıya sürükler) ve unintentional access (ayrı bir kusur bir kaynağı ifşa eder). Low-level exploitation'da ödül neredeyse her zaman adres bilgisidir: tek bir canlı heap ya da code pointer'ı leak etmek o bölge için ASLR'ı çökertir ve "target nerede?" sorusunu bilinen bir offset'e çevirir. Bir info leak'in bu kadar sık iki-aşamalı bir exploit'in ilk yarısı olup ikinci yarıda bir write primitive ile eşleşmesinin nedeni budur.

Walkthrough

CWE-200, kök-neden hatasının — hassas veriyi yanlış işlemek, yanlış saklamak ya da temizlememek — güvenlik açığı olduğunu, "confidentiality loss"un ise yalnızca sonuç olduğunu vurgular. Ders kitabı niteliğinde bir explicit-insertion örneği, internal state'i yazdıran bir error handler'dır:

catch (Exception $e) {
    echo 'Caught exception: ', $e->getMessage(), '\n';
    // BUG: leaks an internal filesystem path to the client
    echo 'Check credentials in config file at: ', $Mysql_config_location, '\n';
}

C'deki eşit derecede yaygın indirect form, canlı veriyi aşıp onu takip eden her ne ise oraya kopyalayan bir length confusion'dır — Heartbleed-tarzı leak'lerin arkasındaki yapısal pattern:

#include <string.h>
#include <stdlib.h>

// Attacker controls req_len but the real payload is shorter.
char *echo_back(const char *payload, size_t payload_len, size_t req_len) {
    char *out = malloc(req_len);
    // BUG: trusts req_len, not the actual payload length -> over-read
    memcpy(out, payload, req_len);   // copies payload_len real bytes + slack
    return out;                      // slack = adjacent heap memory disclosed
}

req_len > payload_len ise, payload'dan sonraki byte'lar — muhtemelen key'ler ya da pointer'lar tutan başka allocation'lar — caller'a döndürülür. Kusur, disclosure'ı besleyen bir buffer over-read'dir.

Üçüncü bir rota uninitialized memory döndürmektir; bu, daha önce o byte'ları her ne işgal ediyorsa onu (çoğu zaman bayat bir stack/heap pointer'ı) leak eder:

struct msg { uint16_t type; uint16_t flags; uint32_t reserved; };

void build_reply(int fd) {
    struct msg m;
    m.type  = 1;
    m.flags = 0;
    // BUG: m.reserved never set; sends 4 bytes of prior stack contents
    write(fd, &m, sizeof m);
}
Leak edilen bir pointer neden önemli: byte'ları bir base adresine çevirmek

Leak'in, library base'inden statik offset'i 0x97678 olan bilinen bir symbol'e ait canlı bir libc pointer'ı 0x7f3a12345678 döndürdüğünü varsayalım. O zaman:

libc_base = 0x7f3a12345678 - 0x97678 = 0x7f3a122ae000
system    = libc_base + offsetof(system)
ASLR base'i rastgeleleştirdi, ama tek bir ifşa edilmiş pointer o modüldeki her diğer adresi hesaplanabilir kılar — leak, takip eden bir write'ı kullanılabilir yapan şeydir.

Structure padding'i her field set edilse bile leak eder

type, flags ve reserved'ı set etmek, compiler alignment için field'lar arasına padding byte'ları eklediyse yeterli değildir; write(fd, &m, sizeof m) o padding byte'larını uninitialized olarak gönderir. Field'ları doldurmadan önce tüm structure'ı sıfırla (memset(&m, 0, sizeof m)).

MITRE, CWE-200'ü somut CVE'lere eşlemeyi önermez

CWE-200 high-level bir class'tır. Belirli bir bug için, asıl hatayı adlandıran descendant'ı tercih et (CWE-209 error-message exposure, CWE-201 sent-data insertion, CWE-908 use of uninitialized resource). CWE-200'ü yalnızca soyut kategori için kullan.

Mitigation

  • Serialize etmeden önce buffer'ları/struct'ları (padding dahil) sıfırla; asla uninitialized memory yayma.
  • Over-read disclosure'ı önlemek için çıktıyı attacker-tarafından-sağlanan bir length ile değil, gerçek veri length'i ile sınırla.
  • Trust boundary'leri separation of privilege ve least privilege ile zorla; sırları error mesajlarından, log'lardan ve debug çıktısından uzak tut.

References