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:
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.