regreSSHion Anatomisi: CVE-2024-6387 OpenSSH Pre-Auth RCE Derinlemesine Analizi

OpenSSH sunucusunda kimlik doğrulama öncesi uzaktan kod yürütme zafiyeti (CVE-2024-6387): SIGALRM sinyali, glibc ptmalloc reentrancy yarışı, heap manipülasyonu ve 9.8p1 yama analizinin tam kaynak kod incelemesi.

9 dk okuma
ibrahimsql
1.786 kelime

regreSSHion Anatomisi: CVE-2024-6387 OpenSSH Pre-Auth RCE Derinlemesine Analizi#

Temmuz 2024'te Qualys Güvenlik Danışma Grubu tarafından açıklanan CVE-2024-6387 (regreSSHion), modern Linux güvenlik tarihindeki en öğretici ve kritik uzaktan kod yürütme (RCE) zafiyetlerinden biridir.

Açık, glibc tabanlı Linux sistemlerinde çalışan OpenSSH sunucu sürecinde (sshd) kimlik doğrulama gerçekleşmeden önce (pre-authentication) root ayrıcalıklarıyla keyfi kod yürütülmesine olanak tanımaktadır.

Zafiyeti benzersiz kılan üç temel unsur bulunmaktadır:

  1. Tarihsel Regresyon: Hata ilk olarak 2006 yılında (CVE-2006-5051) kapatılmış, ancak Ekim 2020'de OpenSSH 8.5p1 sürümünde yapılan bir kod temizliği sırasında yanlışlıkla yeniden ortaya çıkarılmıştır.
  2. Async-Signal-Safety İhlali: Linux çekirdeğinin zaman aşımı durumunda yolladığı SIGALRM sinyalinin, yeniden giriş güvenliği (reentrancy safety) olmayan glibc fonksiyonlarını tetiklemesi.
  3. Deterministik Heap Manipülasyonu: Çok çekirdekli sistemlerde dahi mikrosaniye seviyesindeki bir zamanlama penceresinin ağ üzerinden istismar edilebileceğinin kanıtlanması.

Bu makalede, OpenSSH süreç mimarisinden glibc ptmalloc iç yapılarına, zafiyetli C kaynak kodlarından 9.8p1 yamasının diff analizine kadar regreSSHion'un tam anatomisini inceliyoruz.


1. OpenSSH Süreç Mimarisi ve Ayrıcalık Ayrımı#

OpenSSH, güvenliği artırmak amacıyla Ayrıcalık Ayrımı (Privilege Separation / privsep) mimarisini kullanır. Bir SSH oturumunun yaşam döngüsü tek bir monolitik süreçte yürümez:

+-----------------------------------------------------------------------------+ | ANA DİNLEYİCİ (LISTENER) | | sshd (PID 1200, Kullanıcı: root) | | - Port 22'yi dinler, gelen TCP bağlantılarını kabul eder | +--------------------------------------+--------------------------------------+ | | fork() v +-----------------------------------------------------------------------------+ | KİMLİK DOĞRULAMA ÖNCESİ ÇOCUK SÜREÇ (PREAUTH CHILD) | | sshd [net] (PID 1201, Kullanıcı: sshd / chroot) | | | | - Ağ paketlerini çözer (Packet parsing) | | - Kriptografik anahtar değişimini (KEX) yönetir | | - Çekirdek SIGALRM zamanlayıcısı bu süreci hedefler (LoginGraceTime) | +--------------------------------------+--------------------------------------+ | | IPC (Unix Domain Socket / Pipe) v +-----------------------------------------------------------------------------+ | AYRICALIKLI İZLEYİCİ (MONITOR) | | sshd [priv] (PID 1202, Kullanıcı: root) | | | | - PAM kimlik doğrulamasını yürütür | | - Yalnızca preauth çocuk süreci kullanıcıyı doğruladıktan sonra devreye | | girer ve kullanıcı kabuğunu (shell) ayağa kaldırır | +-----------------------------------------------------------------------------+

Kritik Risk Noktası:#

Geleneksel privsep mimarisinde dahi, istemcinin kimliğini kanıtlaması için belirli bir süre tanınır: LoginGraceTime (varsayılan: 120 saniye).

Eğer istemci bu süre içinde kimlik doğrulamasını tamamlayamazsa, çekirdek çocuk sürece (PID 1201) bir SIGALRM sinyali iletir. Bu sinyali yakalayan sshd sinyal işleyicisi (signal handler), oturumu derhal sonlandırmakla görevlidir. Zafiyet tam olarak bu sinyal işleyicisinin çalışma anında meydana gelir.


2. POSIX Async-Signal-Safety ve glibc malloc Açmazı#

Linux ve POSIX standartlarında bir sinyal işleyicisi (signal handler), ana programın rastgele bir talimatı (instruction) icra ettiği sırada araya girer (asenkron kesme). Bu nedenle, sinyal işleyicisi içinde yalnızca async-signal-safe (asenkron sinyal güvenliğine sahip) fonksiyonlar çağrılabilir.

Async-Signal-Unsafe Nedir?#

Bir fonksiyon global durum değişkenleri, kilitler (mutex) veya dahili tamponlar kullanıyorsa, bu fonksiyon sinyal güvenli değildir.

POSIX standardında (man 7 signal-safety):

  • _exit(), write(), sigaction() gibi fonksiyonlar güvenlidir.
  • printf(), syslog(), malloc(), free(), localtime() gibi fonksiyonlar kesinlikle güvensizdir.
ANA PROGRAM DÖNGÜSÜ SİNYAL İŞLEYİCİSİ (SIGALRM) ------------------- -------------------------- 1. malloc(1024) çağrılır 2. arena->mutex kilitlenir 3. Free-list göstericileri düzenleniyor... [KRİTİK BÖLGE / YARIM GÜNCELLENMİŞ DURUM] | +======== KESME (SIGALRM) ========> 4. Sinyal geldi! 5. sigdie() çağrılır 6. logit() -> syslog() çağrılır 7. syslog dahili tampon için malloc(256) çağırır! 8. arena->mutex zaten kilitli! YA DEADLOCK YA DA BOZUK FREE-LIST ÜZERİNE YAZMA!

Eğer malloc() bir bellek öbeğini bölüp tcache (thread cache) veya fastbin bağlantılı listesini güncellerken tam o mikrosaniyede SIGALRM gelirse:

  1. syslog() içindeki malloc() çağrısı aynı bellek havuzuna yeniden girer (allocator reentrancy).
  2. Bağlantılı listenin fd (forward) göstericileri yarım güncellendiği için saldırganın ağ paketleriyle doldurduğu sahte bellek yapıları geçerli bir heap bloğu gibi algılanır.

3. Kaynak Kod Analizi: 20 Yıl Uyuyan Regresyon#

Hatanın kaynağı, OpenSSH kaynak kodunda sinyal işleyicisinin hata günlüğü yazmak için sigdie() fonksiyonunu çağırmasıdır.

1. Zafiyetli Sinyal İşleyicisi (sshd.c)#

/* sshd.c - LoginGraceTime dolduğunda tetiklenen fonksiyon */ static void grace_alarm_handler(int sig) { /* HATA: Sinyal işleyicisi içinde doğrudan sigdie çağrılıyor! */ sigdie("Timeout before authentication for %100s", rdomain); }

2. Çağrı Zinciri ve Güvensiz syslog (log.c)#

sigdie() fonksiyonu log.c içindeki logit() fonksiyonuna, o da vlog() üzerinden glibc'nin syslog() fonksiyonuna dallanır:

/* log.c */ void sigdie(const char *fmt, ...) { va_list args; va_start(args, fmt); /* logit -> do_log -> syslog() zincirini başlatır */ do_log(SYSLOG_LEVEL_FATAL, fmt, args); va_end(args); _exit(1); }

3. Kod Temizliğinde Kaybolan Güvenlik Kilidi (#ifdef)#

OpenSSH geliştiricileri 2006 yılında (CVE-2006-5051) bu sorunu çözmek için sigdie() içine bir koruma makrosu koymuştu:

/* 2006 Yılındaki Düzeltme: */ #if defined(DO_LOG_SAFE_IN_SIGHAND) /* Yalnızca doğrudan write(2) kullanan güvenli rutinleri çağır */ syslog_direct_write(fmt, args); #else /* syslog çağrısını engelle ve doğrudan çık */ _exit(1); #endif

Ekim 2020'de yapılan bir refactoring (OpenSSH 8.5p1 commit b404cb1) sırasında, bu makro blokları "artık ihtiyaç kalmadığı" düşünülerek temizlendi. Böylece sigdie() fonksiyonu sessizce tekrar syslog() çağırır hale geldi ve 2006'daki RCE zafiyeti 2020'de yeniden canlandı (regreSSHion adı buradan gelir).


4. İstismar Primitifleri (Exploitation Mechanics)#

Bir bellek tahsisatçısı yarışını (allocator race) uzaktan istismar etmek son derece zordur; ancak imkansız değildir. Qualys araştırmacıları bunu glibc'nin modern bellek korumalarını aşarak başarmıştır.

+-----------------------------------------------------------------------------+ | İSTİSMAR ZAMANLAMA PENCERESİ | +-----------------------------------------------------------------------------+ TCP Bağlantısı Başlatılır | v +-------------------------------+ | sshd child prosesi doğar | <--- LoginGraceTime sayacı başlar (T = 0s) +---------------+---------------+ | | [119.999 saniye boyunca] | Saldırgan sürekli genel anahtar (public key) paketleri, | şifreleme parametreleri ve geçersiz KEX paketleri yollayarak | child prosesin heap alanını belirli boyuttaki bloklarla doldurur | (Heap Grooming / Heap Spraying). | v +-------------------------------+ | T = 119.9992 saniye | <--- Saldırgan özel olarak boyutlandırılmış | (Alarmdan 800µs önce) | büyük bir paket yollar; child süreç bu | | paketi açmak için malloc() / free() döngüsüne girer. +---------------+---------------+ | | Tam malloc() kritik bölgesindeyken SIGALRM ateşlenir! v +-------------------------------+ | SIGALRM Handler Devreye Girer| <--- syslog() -> malloc() çağrısı tetiklenir. +---------------+---------------+ | v +-----------------------------------------------------------------------------+ | SONUÇ: glibc tcache/fastbin göstericisi saldırganın kontrolündeki | | adrese yönlendirilir. Bir sonraki tahsisatta RIP saldırgan koduna sıçrar. | +-----------------------------------------------------------------------------+

1. Heap Masajı (Heap Grooming)#

Saldırgan, kimlik doğrulama sürecini bilerek tamamlamaz; ancak sunucuya sürekli SSH protokol mesajları (SSH2_MSG_KEXINIT) gönderir. Bu paketler sunucunun packet_read_seqnr() rutininde dinamik bellek tahsis etmesine (malloc) ve serbest bırakmasına (free) yol açar. Saldırgan, heap düzenini öyle bir hizalar ki, syslog() tarafından istenecek bellek boyutu önceden hazırlanmış bir boşluğa (hole) denk gelir.

2. 800 Mikrosaniyelik Yarış Penceresi#

Zafiyetin tetiklenmesi için SIGALRM sinyalinin, child sürecin malloc() veya free() fonksiyonunu icra ettiği ~800 mikrosaniyelik bir zaman aralığında gelmesi gerekir. Ağ gecikmesini (jitter) telafi etmek için saldırgan, TCP zaman damgalarını (timestamps) ve TCP pencere boyutunu dinamik olarak kalibre eder.

3. ASLR Baypası: 32-bit vs. 64-bit Gerçeği#

MimariASLR EntropisiOrtalama İstismar SüresiUygulanabilirlik
Linux i386 (32-bit)16-bit (65.536 olasılık)~6 - 8 Saat (~10.000 deneme)Pratik olarak tam güvenilirlikle çalışır
Linux x86_64 (64-bit)28-32 bit entropiHaftalar / AylarBrute-force pratik değildir; ek bir bilgi sızıntısı (info leak) gerektirir

32-bit sistemlerde glibc libc.so kütüphanesinin ve heap alanının taban adresi sınırlı bir entropiye sahiptir. SSH bağlantısı her fork() edildiğinde çocuk süreç ana dinleyicinin bellek düzenini miras alır; ancak başarısız bir denemede çocuk süreç çökse dahi ana dinleyici çökmez. Bu durum saldırgana sunucuyu çökertmeden binlerce kez deneme yapma imkanı tanır.


5. Yama Analizi: OpenSSH 9.8p1 Değişiklikleri#

OpenSSH geliştiricileri, sorunu 9.8p1 sürümünde sinyal işleyicisinden tüm güvensiz fonksiyonları kaldırarak ve atomik bir bayrak (atomic flag) yapısına geçerek çözmüştür.

Resmi Yama Diff İncelemesi:#

--- a/sshd.c +++ b/sshd.c @@ -218,10 +218,17 @@ static void grace_alarm_handler(int sig) { + /* GÜVENLİ YAKLAŞIM: Sinyal işleyicisi içinde ASLA logit/syslog çağırma! */ + /* Yalnızca async-signal-safe olan tekil sistem çağrıları kullanılabilir */ + /* 1. Atomik olarak çıkış yap */ - sigdie("Timeout before authentication for %100s", rdomain); + _exit(255); }

Geliştiriciler ayrıca ayrıcalık ayrımı sınırlarını daha da daraltmış, ağ ayrıştırma kodunun çalıştığı çocuk sürecin yetkilerini daha katı seccomp filtreleri ile sınırlandırmıştır.


6. Tespit, Savunma ve Azaltma Stratejileri#

1. Geçici Azaltma Çözümü (Workaround)#

Eğer OpenSSH sürümünü derhal 9.8p1 veya üzerine yükseltemiyorsanız, /etc/ssh/sshd_config dosyasında şu yapılandırmayı uygulayın:

LoginGraceTime 0
  • Nasıl Çalışır? LoginGraceTime değeri 0 yapıldığında çekirdek alarmı (SIGALRM) hiç kurulmaz; dolayısıyla yarış durumunun tetiklenebileceği bir sinyal ortamı oluşmaz.
  • Yan Etkisi (Dikkat!): Bu ayar, kimlik doğrulaması yapmayan bağlantıların süresiz olarak açık kalmasına izin verir. Saldırgan binlerce açık TCP soketi tutarak sunucuda kaynak tükenmesine (Denial of Service) yol açabilir.

2. SIEM ve Log Tabanlı Tehdit Avcılığı (Threat Hunting)#

regreSSHion istismarı binlerce başarısız deneme gerektirir. Sistem günlüklerinde (/var/log/auth.log veya journalctl) şu desenlerin sıklığı aranmalıdır:

# Şüpheli istismar belirtileri: Timeout before authentication for [IP] Connection closed by [IP] port [PORT] [preauth] fatal: Timeout before authentication for ... [preauth]

Eğer tek bir IP adresinden veya aynı subnet'ten yüzlerce preauth zaman aşımı uyarısı geliyorsa, aktif bir regreSSHion istismarı yürütülüyor demektir.

3. Fail2ban ve Ağ Seviyesinde Sıkılaştırma#

# /etc/fail2ban/jail.d/sshd-regresshion.conf [sshd] enabled = true port = 22 filter = sshd maxretry = 5 findtime = 600 bantime = 86400

7. Özet ve Mühendislik Dersleri#

CVE-2024-6387 vakası, modern yazılım güvenliği için üç temel gerçeği hatırlatmıştır:

  1. Regresyon Testlerinin Önemi: Geçmişte kapatılmış bir zafiyetin (CVE-2006-5051) neden kapatıldığı test senaryolarına ve CI/CD süreçlerine açıkça kodlanmazsa, gelecekteki bir "kod sadeleştirme" hamlesi o zafiyeti sessizce geri getirebilir.
  2. Sinyal Güvenliği Ciddiye Alınmalıdır: Bir sinyal işleyicisi içinde tek bir zararsız görünen syslog() veya printf() çağrısı bile, altında çalışan bellek tahsisatçısının kilitlerini kırarak RCE kapısı aralayabilir.
  3. Mimarinin Rolü: OpenSSH'nin çok süreçli privsep yapısı olmasaydı, bu zafiyet tek bir denemede tam sistem ele geçirmeye dönüşebilirdi; privsep sayesinde istismar binlerce denemeye ve saatler süren hassas bir zamanlama mücadelesine hapsolmuştur.

İlgili İncelemeler ve Dahili Bağlantılar#


Tespit Fırsatları#

SinyalKaynakKullanım
Tek IP'den tekrar eden Timeout before authenticationsshd auth loguBağlantı açlığı denemesi
SIGALRM ağırlıklı sshd çocuklarıperf/auditAnormal sinyal oranı
Çok kaynak IP'den başarısız KEXnetflowSpray deseni
glibc sistemlerde sshd sürümü < 9.8p1envanterZafiyetli filo

CVE-2024-6387 uygulama katmanında iz bırakmaz. Tespit, payload imzası değil sürüm envanteri ve bağlantı deseni anomalisidir.

Sınırlılıklar#

  • İstismar çok deneme ve saatlerce zamanlama ister; bağlantı limiti olan üretim MTA'sında pratikte tamamlanamayabilir, ancak yama zorunludur.
  • glibc dışı sistemler (musl, BSD) kapsam dışıdır.
  • Ayrıcalık ayrımı pre-auth çocuğu yetkisiz tutar; etki yalnızca yarış kazanıldığında gelir, kısmi denemeler sıradan başarısız giriş gibi görünür.

Cikarilar#

  • Sinyal işleyicileri async-signal-safe kalır; içinden malloc, syslog veya printf çağırma.
  • 2006 düzeltmesinin 2020'de geri dönmesi, regresyon testlerinin yeniden düzenlemelerden uzun yaşaması gerektiğini gösterir.
  • Ayrıcalık ayrımı etkiyi sınırlar; hatayı engellemez.
  • Bu sınıf için tek güvenilir tespit sürüm envanteridir.
---
Bu yazıyı paylaş:
TwitterLinkedInFacebook

Ne düşünüyorsun?

Tepki bırakarak geri bildirim ver

İlgili Yazılar