NDIAS Automotive/IoT CTF 2026 — Gate to the Past Writeup
Pcap içindeki 20 yıllık bir IoT cihazının SSHv2 oturumu, AES-128-CBC ile şifreli. 2007 dönemi Debian OpenSSL zayıf PRNG'i (CVE-2008-0166), Diffie-Hellman private exponent'ini kurtarılabilir kılıyor; böylece tüm oturum deşifre edilip flag tel üzerinden okunuyor.
NDIAS Automotive/IoT CTF 2026 — Gate to the Past
20 yıllık bir SSH cihazı ve meşhur Debian OpenSSL felaketi üzerine kurulu bir network-forensics challenge'ı.
- Etkinlik: NDIAS Automotive/IoT CTF 2026
- Kategori: Network
- Artefakt:
dist-gate_to_the_past.zip→challenge.pcap - Flag formatı:
FLAG{...}
1. El sıkışmayı okumak
Capture tek bir TCP akışı: client:50398 → server:22. İki taraf da düz metin identification string'i ile açıyor:
SSH-2.0-OpenSSH_4.3p2 Debian-9etch3Bu string ipucunun ta kendisi. Debian-9etch3 derlemeyi Debian Etch dönemine (2007) tarihliyor — yani 2008-05-13'ten önce, CVE-2008-0166 penceresinin içinde. Debian OpenSSL PRNG felaketi. O sistemlerde RAND_bytes()'ın süreç ID'sinin ötesinde neredeyse hiç entropisi yoktu; OpenSSL'in ürettiği her "rastgele" değer küçük ve sayılabilir bir kümeden geliyordu.
Düz metin KEXINIT'lerden:
- KEX:
diffie-hellman-group-exchange-sha1 - Cipher:
aes128-cbc - MAC:
hmac-md5 - Compression:
none
GEX değişimi ihtiyacımız olan her şeyi açığa çıkarıyor:
min/n/max 1024 1024 8192
p bits 1024 g 2 (p bir safe prime)
istemci public e = g^x mod p
sunucu public f = g^y mod p
host key ssh-rsa, 2048-bit2. İstemcinin DH private exponent'ini kurtarmak
x, OpenSSL'in BN_rand yolundan RAND_bytes ile üretilir — yani bozuk PRNG'den. PRNG durumunu (seeding PID'i ve exponent çekilmeden önce üreteçten tüketilen az sayıda byte'ı) numaralandırmak, istemcinin public değerini birebir üreten tek bir x adayı verir. Capture'a karşı doğrulayalım:
x = 0xbd886dabb7951ae7495f31444753098d07db09b9357d8df2795f88012158a351
pow(2, x, p) == e # -> Truex belli olunca paylaşılan sır anında düşer:
K = pow(f, x, p)3. Exchange hash — padding'e dikkat
Group-exchange için SSH exchange hash'i; identification string'ler, iki SSH_MSG_KEXINIT payload'ı, host key, GEX parametreleri ve DH değerlerinden kurulur:
H = SHA1( str(V_C) + str(V_S) + str(I_C) + str(I_S) + str(K_S)
+ u32(min) + u32(n) + u32(max)
+ mpint(p) + mpint(g) + mpint(e) + mpint(f) + mpint(K) )Apaçık olmayan tek detay: I_C / I_S değerleri KEXINIT payload'larıdır; yani paketin 4-byte uzunluk, 1-byte padding uzunluğu ve sondaki padding'i çıkarılmış hâli. Padding'i dahil ederseniz hash yanlış çıkar. Doğru sınırlarla:
H = 3800bc5ef9859a61c28bfcc4fdd43b5bc9bcfdcaBu, sunucunun H üzerindeki RSA imzasıyla da doğrulanır.
4. Key türetimi ve deşifre
session_id = H ile RFC 4253 key derivation:
IV_c2s = SHA1(K || H || "A" || sid) key_c2s = SHA1(K || H || "C" || sid) mac_c2s = SHA1(K || H || "E" || sid)
IV_s2c = SHA1(K || H || "B" || sid) key_s2c = SHA1(K || H || "D" || sid) mac_s2c = SHA1(K || H || "F" || sid)AES-128-CBC ilk 16 byte'ı, IV'ler 16 byte'ı, HMAC-MD5 anahtarları 16 byte'ı alır. İki yönü paket paket deşifre edip her HMAC-MD5'i seq || plaintext üzerinden doğrulamak temiz ve tamamen kimliği doğrulanmış bir transcript verir — hiç MIC hatası yok.
Yakalanmaya değer bir paketleme detayı: sunucu SSH_MSG_GEX_REPLY (704 byte) ile minik SSH_MSG_NEWKEYS (16 byte) paketini tek TCP segmentine koymuş. Bu yüzden segment-başına ayrıştıran bir parser sıra numaralarını yanlış sayıp CBC IV zincirini bozar. Akışı TCP frame'i değil SSH frame'i olarak bölmek sorunu çözer.
5. İstemci aslında ne çalıştırdı
c2s #9 CHANNEL_REQUEST exec:
echo "flag: FLAG{n3v3r_f0rg3t_13g4cy_bug5}"
s2c #10 CHANNEL_DATA:
flag: FLAG{n3v3r_f0rg3t_13g4cy_bug5}6. Flag
FLAG{n3v3r_f0rg3t_13g4cy_bug5}Ders challeng'dan daha eski: 2008'den kalma bozuk tek bir RAND_bytes, "güçlü" 1024-bit Diffie-Hellman değişimini bir deşifre anahtarına dönüştürüyor. Bu cihazda sır parola değildi — PRNG'di.
Ne düşünüyorsun?
Tepki bırakarak geri bildirim ver