Skip to content

Memory leak oracle

İkili, gözlemlenebilir bir program davranışını (crash mı survive mı, askıda mı kalıyor yanıt mı veriyor, hızlı mı yavaş mı) byte-başına bir teste dönüştür; böylece herhangi bir doğrudan read primitive olmadan gizli belleği — canary'ler, pointer'lar, kod — yeniden inşa et.

Mechanism

Suistimal edilen invariant

Bir leak oracle, dolaylı bir sinyali information disclosure'a çevirir. Kanonik örnek, Bittau ve diğerlerinin "Hacking Blind" (IEEE S&P / Oakland 2014, BROP saldırısı) çalışmasındaki stack-reading crash oracle'ıdır. Her connection için taze bir worker fork'layan bir network servisi, child process'ler arasında aynı stack canary'yi ve aynı kod/library layout'unu korur. Bir gizli değere kadar (örn. stack canary) overflow yapabilen bir saldırgan, onu her seferinde bir byte overwrite eder ve oracle'ı gözlemler:

  • tahmin edilen byte doğru → değer eşleşir, function normal döner, servis ayakta kalır;
  • tahmin edilen byte yanlış__stack_chk_fail / corruption → worker crash olur, connection düşer.

Her pozisyonun yalnızca 256 olasılığı olduğu ve yanlış bir tahmin doğru olandan ayırt edilebildiği için, b-byte'lık bir değer için secret beklenen 128 × b request'te kurtarılır (üstel değil, lineer). Aynı oracle saklı return adreslerini ve frame pointer'ları okur; 64-bit hedeflerde hiçbir information-leak bug'ı ve binary'nin bir kopyası olmadan ASLR'ı ve stack canary'leri etkisiz bırakır.

Walkthrough

Bilinen bir overflow'a sahip fork'layan bir servise karşı stack-reading loop'u:

def leak_byte(known_prefix):
    for guess in range(256):
        payload = b"A"*PAD + known_prefix + bytes([guess])
        conn = connect(target)
        conn.send(payload)
        if survives(conn):          # oracle: still responsive == correct
            return guess
        # crash (connection reset) == wrong guess, try next
    raise RuntimeError("no byte survived")

secret = b""
for _ in range(SECRET_LEN):         # e.g. 8 bytes of canary
    secret += bytes([leak_byte(secret)])

survives() predicate'i oracle'dır. BROP'ta bu "TCP connection açık kaldı mı?" demektir; başka ortamlarda bir timing farkı, bir error string'i, bir exception tipi ya da farklı bir yanıt boyutu olabilir.

Oracle sinyal biçimleri
crash oracle      : worker dies  -> RST/EOF        (BROP stack reading)
timing oracle     : branch taken -> measurable dt  (e.g. memcmp early-exit)
error oracle      : distinct error code / page     (e.g. login differential)
fault oracle      : SIGSEGV vs SIGFPE distinguishes mapped vs unmapped

Tuzaklar / önkoşullar

  • Hedef, yanlış bir tahminden sonra deterministik olarak toparlanmalı ve denemeler boyunca secret'ı korumalı — tipik olarak request başına fork() yapan bir sunucu (child, parent'ın canary/layout'unu miras alır). Request başına exec() yapan ya da yeniden randomize eden bir sunucu oracle'ı kırar.
  • Sinyal temiz olmalı: gürültülü ağlar, rate limit'ler ya da crash-restart gecikmeleri request sayılarını şişirir ve predicate'i ters çevirebilir. Belirsiz byte'larda tekrarla-ve-oyla.
  • Saklı RIP'ye ulaşmadan önce canary'yi ilk okumak zorunludur — canary'nin ötesini yanlış değerle overwrite edersen return pointer'ına ulaşmadan crash olursun.
  • Byte sırası önemlidir: bilinen prefix'i overflow'un büyüdüğü yönde uzatırsın.

Detection

Crash-loop izleme en yüksek sinyalli savunmadır: tek bir peer'dan yüzlerce kez __stack_chk_fail abort'u ya da SIGSEGV saçan fork'layan bir servis anormaldir. Canary başarısızlıklarının connection başına loglanması, rate limiting ve anormal child-crash oranlarında alarm verilmesi brute force'u açığa çıkarır.

Mitigation

Fork'lar arası kalıtım olmadan secret'ları process başına yeniden randomize et (re-exec ya da request başına canary/ASLR yeniden seed'leme), yalnızca worker yerine corruption'da tüm-servisi-sonlandır ve crash throttling ekle ki oracle'ın request bütçesi patlasın. Altta yatan overflow'u kaldırmak oracle'ı tümüyle ortadan kaldırır.

References