1 puan yazan GN⁺ 2025-04-16 | Henüz yorum yok. | WhatsApp'ta paylaş
  • Üreticinin uygulamasına ve bulutuna bağlı ESP32 tabanlı bir hava temizleme cihazını Home Assistant üzerinden doğrudan kontrol etmek için uzaktan kontrol yolu tersine mühendislikle incelenip yerel bir sunucuyla değiştirildi
  • Uygulama analizi, DNS atlatma ve Wireshark yakalamalarıyla cihazın smartdeviceep.---.com:41014 adresine UDP paketleri gönderdiği ve standart DTLS yerine özel bir protokol kullandığı doğrulandı
  • UART bağlantısı ve 4 MB flash dökümü aracılığıyla dev_key.key, sertifikalar, sunucu ayarları ve WiFi ayarları elde edildi; Ghidra ve esp32knife ile firmware yapısı analiz edildi
  • Paketler 13 baytlık başlık ve sondaki 2 baytlık CRC-16, ECDH/HKDF anahtar üretimi, AES-128-CBC ve MessagePack serileştirmesini birleştiriyordu; firmware yamasıyla paylaşılan sır seri günlüğe yazdırılarak şifre çözme başarıldı
  • Nihai kurulum bir MITM proxy, yerel sunucu ve Mosquitto tabanlı MQTT köprüsünden oluştu; Home Assistant’ın MQTT Fan entegrasyonuyla güç ve fan hızı birkaç hafta boyunca kararlı şekilde kontrol edildi

Buluta bağımlı hava temizleme cihazını yerel kontrole dönüştürme

  • Amaç, yalnızca üreticinin mobil uygulamasına ve bulut hesabına bağlanan hava temizleme cihazını Home Assistant üzerinden kontrol etmekti
  • Telefonda Bluetooth, WiFi ve 5G açılıp kapatılarak yapılan kontrolde, uygulamanın cihazı yerel Bluetooth veya WiFi üzerinden değil, yalnızca internet bağlantısı üzerinden kontrol ettiği görüldü
  • Cihaz ile bulut sunucusu arasında bir yerde fan hızı gibi kontrol değerleri gidip geldiğinden, ağ bölümü temel saldırı noktası hâline geldi
    • Trafiği araya girip yakalayarak ve değerleri değiştirerek cihaz kontrol edilebilir
    • Sunucu yanıtları emüle edilirse internet ve üretici bulutu olmadan da çalıştırılabilir
  • Tersine mühendislik içeriği eğitim amaçlıdır; ürüne özgü hassas bilgiler olan özel anahtarlar, alan adları ve API uç noktaları bulanıklaştırılmış veya silinmiştir
  • Cihazda değişiklik yapmak garantiyi geçersiz kılabilir veya cihazda kalıcı hasara yol açabilir

Uygulama analizi ve UDP trafiği yakalama

  • Android uygulamasının .apk dosyası çıkarıldı; classes.dex, dex2jar ve jd-gui ile açılarak içeriği incelendi
  • MainActivity.class içinde uygulamanın React Native tabanlı olduğu doğrulandı; assets/index.android.bundle içinde güvenli WebSocket bağlantısı bulundu
    • Örnek kodda wss://smartdeviceapi.---.com bağlantısı yer alıyordu
  • Pi-hole’un DNS sorgusu görüntüleme özelliğiyle cihazın bağlandığı bulut sunucusu alan adı belirlendi
  • Pi-hole’un Local DNS özelliğiyle ilgili alan adı yerel iş istasyonu 192.168.0.10 adresine yönlendirildi ve Wireshark üzerinde cihaz IP’si 192.168.0.61 trafiği filtrelendi
  • Cihaz, iş istasyonunun 41014 portuna UDP paketleri gönderiyordu

Röle kurulumu ve özel protokole dair ipuçları

  • Yerel DNS bulut alan adını iş istasyonuna çözdüğü için gerçek sunucu IP’si Cloudflare DNS resolver 1.1.1.1 üzerinden sorgulandı
  • node-udp-forwarder kullanılarak iş istasyonunun cihaz ile bulut sunucusu arasında UDP rölesi rolü üstlenmesi sağlandı
  • Açılıştaki ilk paket ve sunucu yanıtı yakalandı, ancak okunabilir karakter dizisi olmadan rastgele baytlar gibi göründüğü için şifreleme olasılığı vardı
  • Wireshark paketi DTLS olarak tanımadı; DTLS spesifikasyonundaki başlık biçimi de yakalanan paketlerden farklıydı
  • Standart bir protokol olmadığı göründüğünden paket yapısı ve şifreleme yönteminin doğrudan tersine mühendislikle incelenmesi gerekti

ESP32 sökümü ve seri erişim

  • Cihaz söküldüğünde ana PCB, fan bağlantı portu ve ön kontrol paneli şerit kablosu görüldü
  • Ana kontrolcü ESP32-WROOM-32D olarak işaretlenmişti ve WiFi ile Bluetooth özelliklerine sahip ESP32 ailesinden bir mikrodenetleyiciydi
  • ESP32 tersine mühendisliğiyle ilgili materyaller için ESP32-reversing deposundan yararlanıldı
  • ESP32 veri sayfasında TXD0 ve RXD0 pinleri doğrulandı; PCB’deki hata ayıklama pin deliklerine bağlı izler takip edilerek seri bağlantı noktası bulundu
  • Flipper Zero’nun USB-UART Bridge özelliğiyle UART bağlantısı kuruldu
    • Flipper Zero TX, ESP32 RX pinine bağlandı
    • Flipper Zero RX, ESP32 TX pinine bağlandı
    • GND, GND pinine bağlandı
  • Putty’de COM7, 115200 hızında bağlanıldığında açılış günlükleri çıktı

Açılış günlüklerinde ortaya çıkan dosyalar ve sunucu ayarları

  • Seri günlükler ESP32’nin 2 CPU çekirdeğine, WiFi/BT/BLE’ye ve 4 MB harici flash’a sahip bir çip olduğunu yazdırdı
  • Uygulama factory bölümünden çalışıyordu
  • FAT dosya sistemi bağlandı; 122 KiB toplam alan ve 0 KiB kullanılabilir alan gösterildi
  • Uygulama şu dosyaları okuyordu
    • serial
    • dev_key.key
    • SmartDevice-root-ca.crt
    • SmartDevice-signer-ca.crt
    • server_config
  • Sunucu ayarlarında smartdeviceep.---.com:41014 yer alıyordu

Flash dökümü ve bölüm yapısı

  • ESP32’yi Download Boot modunda başlatmak için IO0 pini GND pinine bağlıyken cihaza güç verildi
  • esptool kullanılarak 4 MB flash’ın tamamının dökümü alındı
    • Komut: esptool -p COM7 -b 115200 read_flash 0 0x400000 flash.bin
  • Döküm birkaç kez alınarak okumanın doğru olduğu doğrulandı ve sorun çıkarsa yeniden flash’lanabilmesi için yedeklendi
  • Döküm esp32knife ile analiz edilerek partitions.csv elde edildi
  • Bölüm yapısında şu öğeler vardı
    • nvs: 16K anahtar-değer deposu
    • otadata: 8K OTA verisi
    • phy_init: 4K PHY verisi
    • factory: 768K uygulama bölümü
    • ota_0, ota_1: her biri 768K OTA uygulama bölümü
    • storage: 1M FAT veri bölümü
  • Okurdan gelen bir bilgiye göre bu flash dökümü, flash şifreleme etkin olsaydı korunabilirdi; ancak bu cihazda etkin değildi

Depolamada bulunan anahtarlar ve sertifikalar

  • nvs bölümünün en güncel durumunda WiFi SSID’si ve parolası vardı; geçmiş günlüklerinde daha önce kullanılan WiFi kimlik bilgileri de görünüyordu
  • FAT storage bölümü, OSFMount ile sanal disk gibi bağlanarak incelendi
  • Depolamada şu dosyalar vardı
    • dev_info
    • dev_key.key
    • serial
    • server_config
    • SmartDevice-root-ca.crt
    • SmartDevice-signer-ca.crt
    • wifi_config
  • dev_key.key, -----BEGIN EC PRIVATE KEY----- ile başlayan bir Elliptic Curve özel anahtarıydı ve openssl ec -in dev_key.key -text -noout ile doğrulandı
  • İki .crt dosyası -----BEGIN CERTIFICATE----- ile başlayan sertifikalardı ve openssl x509 ile doğrulandı
  • Sertifikalar ve cihaz anahtarı cihazda saklandığı için UDP paket verilerinin şifrelenmesinde kullanılıyor olma olasılığı arttı

Ghidra analiz ortamının kurulumu

  • Çalışan factory bölüm imajı Ghidra CodeBrowser’da açılıp analiz edildi
  • ESP32, Xtensa komut kümesini kullandığı için Tensilica Xtensa 32-bit little-endian dili seçildi
  • Ham bölüm imajı sanal bellek eşlemesini doğru yansıtmadığından, esp32knife ile part.3.factory.elf oluşturulup yeniden içe aktarıldı
  • RTC_DATA segmentini desteklemek için esp32knife üzerinde yapılan değişikliğin commit’i de yayımlandı
  • SVD-Loader-Ghidra ile ESP32 çevre birimi yapısı ve bellek haritası yüklendi
  • Ghidra’nın SymbolImportScript aracıyla ESP32 ROM işlev etiketleri içe aktarılarak printf gibi yaygın ROM işlevlerinin kolayca tanımlanması sağlandı

Dizelerden bulunan şifreleme ipuçları

  • Ghidra’daki Defined Strings içinde seri günlükte görülen dize ve çevresindeki dizeler izlendi
  • Çevredeki dizelerde şu ipuçları vardı
    • Message CRC error
    • Seed Error
    • PRNG fail
    • ECDH setup failed
    • mbedtls_ecdh_gen_public failed
    • mbedtls_ecdh_compute_shared failed
    • MBED HKDF failed
    • Write ECC conn packet
  • mbedtls, kriptografik ilkel işlevleri, X509 sertifika işlemlerini, SSL/TLS ve DTLS’yi uygulayan açık kaynaklı bir kütüphanedir
  • ECDH ve HKDF işlevlerinin doğrudan kullanılması, DTLS kullanılmaması dikkate alındığında, anahtar değişimi ve anahtar türetmenin özel bir protokol içinde uygulandığı analiz edildi
  • ECC conn packet dizesi, ilk bağlantı paketinin ECDH anahtar değişimi süreciyle ilişkili olduğunu gösterdi

Kontrol paneli bağımlılığını kaldıran firmware yaması

  • PCB’yi fan ve kontrol paneline bağlı tutarak analiz yapmak zor olduğundan kontrol paneli çıkarıldı, ancak açılış sırasında No Cap device found! günlüğüyle birlikte panic oluştu
  • No Cap device found! dizesinin çevresindeki işlev CapSense Init çıktısı verdiği için bunun ön panelin kapasitif giriş başlatma mantığı olduğu değerlendirildi
  • Ghidra’da ilgili işleve InitCapSense, onu çağıran servise ise StartCapSenseService adı verildi
  • StartCapSenseService çağrı komutu nop ile değiştirilerek kontrol paneli servisinin başlatılması kaldırıldı
  • Ham part.3.factory imajındaki baytlar değiştirilip 0x10000 ofsetine yeniden flashlandı, ancak ESP32 imaj checksum hatası nedeniyle açılmadı
  • esptool’un iç mantığı temel alınarak uygulama bölümü checksum’ını düzelten bir betik eklendi
  • Checksum’ı onarılan imaj flashlanınca cihaz kontrol paneli olmadan da normal çalıştı ve firmware değişikliği başarılı oldu

Paket başlığı ve CRC yapısı

  • Birden fazla açılışta paketler karşılaştırıldığında ilk 13 baytın benzer, geri kalanının ise şifrelenmiş gibi göründüğü anlaşıldı
  • Paket başlığı biçimi şöyleydi
    • 55: protokolü tanımlayan magic byte
    • 00 31: paket uzunluğu
    • 02: mesaj tanımlayıcı
    • 01 23 45 67 89 AB CD EF FF: 9 baytlık cihaz seri numarası
  • Mesaj ID deseni şöyleydi
    • 0x02: akıllı cihazın ilk gönderdiği paket
    • 0x82: bulut sunucusunun ilk gönderdiği yanıt
    • 0x01: akıllı cihazın daha sonra gönderdiği paket
    • 0x81: sunucunun daha sonra gönderdiği yanıt
  • Üst bit, istemci isteği ile sunucu yanıtını ayırıyor; alt bit ise ilk değişim ile sonraki paketleri ayırıyor
  • Message CRC error dizesine referans veren işlev izlenerek CRC doğrulama mantığı doğrulandı
  • Son 2 bayt, paketin geri kalanının tamamı için CRC-16 checksum’ıydı
    • Polinom 0x1021
    • Başlangıç değeri 0xFFFF
    • Birden fazla yakalanmış pakette aynı yöntemle doğrulandı

ECDH/HKDF anahtar üretim akışı

  • İlk anahtar değişimi gibi görünen pakette, başlıktaki 13 bayt ve CRC’deki 2 bayt çıkarıldığında veri 32 bayttı; bu da 256 bitlik açık anahtar boyutuyla eşleşiyordu
  • İstemci isteğinin başında 00 01 bulunuyordu ve bu değer her açılışta değişmediği için bir veri tanımlayıcı gibi ele alındı
  • Ghidra’da hata dizeleri izlenerek anahtar üretim işlevi bulundu ve mbedtls kaynak koduyla karşılaştırılarak sözde kod düzeyinde özetlendi
  • Anahtar üretim işlevi şu işlemleri yapıyordu
    • mbedtls_ecdh_gen_public ile ECDH anahtar çifti oluşturur
    • Oluşturulan anahtarın bellekteki başka bir anahtarla üzerine yazıldığı bir yapı görülür
    • Başka bir açık anahtar yükler
    • mbedtls_ecdh_compute_shared ile paylaşılan sırrı hesaplar
    • mbedtls_ctr_drbg_random ile 32 bayt rastgele değer üretir
    • mbedtls_hkdf ile nihai anahtarı türetir
  • HKDF ayarları şöyleydi
    • Hash: SHA-256
    • salt: ECDH paylaşılan sırrı
    • input: cihazın ürettiği 32 baytlık rastgele değer
    • info: 9 baytlık cihaz seri numarası
    • Çıkış anahtarı boyutu: 0x10, yani 16 bayt
  • Çağıran işlev, 00 01 sonuna 32 baytlık rastgele değeri ekleyerek 0x22 bayt gönderiyordu; bu da yakalanan ilk anahtar değişimi paketinin biçimiyle uyuşuyordu

Paylaşılan sırrın çıktılanması ve AES ile şifre çözme

  • Nihai şifre çözme anahtarını hesaplamak için ECDH paylaşılan sırrı gerekiyordu
  • JTAG hata ayıklama yerine, önceden devre dışı bırakılmış CapSense mantığının bulunduğu konuma özel bir işlev yazılarak firmware paylaşılan sırrı seri hatta yazdıracak şekilde yamalandı
  • GenerateNetworkKey içinde paylaşılan sır oluşturulduktan hemen sonra işlev çağrısı eklendi ve register’daki anahtar işaretçisi kullanılarak 32 bayt yazdırıldı
  • Açılışta Write ECC conn packet sonrasında paylaşılan sır onaltılık olarak çıktılanıyordu ve birkaç kez yeniden başlatmaya rağmen değer değişmiyordu
  • HKDF çıkış anahtarı da ayrı bir yamayla doğrulandı ve yakalanan paketlerde aynı anahtar üretim mantığı yeniden üretilebildi
  • Şifreleme işlevinin içinde 63 7C 77 7B F2 6B 6F C5 ile başlayan statik bir tablo bulundu ve bunun mbedtls’in AES Forward S-Box tablosuyla eşleştiği görüldü
  • Nihai şifreleme yöntemi AES-128-CBC idi ve paket içindeki 16 baytlık rastgele değer IV olarak kullanılıyordu
  • Şifresi çözülen paketlerde mirror_data_get, FAN_SPEED, BOOST, FILTER1, FILTER2 gibi okunabilir değerler doğrulandı

MITM proxy uygulaması

  • Cihaz özel anahtarı ve anahtar türetme mantığı elde edilmişti; gerekli dinamik veri ağda görünür olduğundan firmware yaması olmadan MITM proxy yazılabildi
  • Node.js betiği yerel UDP soketi ve bulut sunucusu için UDP soketi oluşturup paketleri iki yönde iletir
  • Akıllı cihazdan alınan paketler günlüğe yazıldıktan sonra bulut sunucusuna gönderilir; bulut sunucusundan alınan paketler de günlüğe yazıldıktan sonra akıllı cihaza gönderilir
  • messageId değeri 2 olan paket anahtar değişimi paketi olarak kabul edilir ve içindeki rastgele değer kullanılarak sonraki paketlerin AES anahtarı hesaplanır
  • Mobil uygulamayla cihaz kontrol edilirken MITM günlükleri biriktirilerek yerel sunucu uygulaması için gereken istek ve yanıt biçimleri doğrulandı

MessagePack mesaj yapısı

  • Şifresi çözülmüş veri hâlâ ikili bir serileştirme biçimindeydi
  • İç veri başlığı, little-endian biçiminde bir ID ve uzunluk gibi görünüyordu
    • 01 00: paket ID’si
    • 64 00: işlem ID’si
    • 29 00: serileştirilmiş veri uzunluğu
  • Serileştirme biçiminin bir kısmını kendim tersine mühendislikle çözmüştüm; ancak kontrol edince bunun MessagePack olduğu anlaşıldı
  • msgpackr gibi bir uygulama kullanıldığında ikili veri kolayca JSON biçiminde açılabiliyordu
  • Tespit edilen başlıca mesajlar şunlardı
    • Anahtar değişimi: cihaz, HKDF’de kullanılacak rastgele baytları sunucuya gönderir
    • mirror_data_get: açılış sırasında ilk durumu sunucudan alır
    • connect: mevcut firmware UUID’sini gönderir; sunucu da firmware, ayar, saat ve sunucu adresi bilgileriyle yanıt verir
    • mirror_data: sunucu cihaz durumunu değiştirir ya da cihaz değişen durumunu sunucuya bildirir
    • keep_alive: cihaz RSSI, RTT, paket kaybı, bağlantı sayısı, çalışma süresi gibi durum bilgilerini periyodik olarak gönderir

MQTT köprüsü ve Home Assistant entegrasyonu

  • Home Assistant ile özel sunucuyu bağlamak için MQTT kullanıldı
  • Home Assistant’ta açık kaynak MQTT broker’ı Mosquitto eklentisi yapılandırıldı
  • Bağlantı yapısı Home AssistantMQTT BrokerCustom ServerSmart Device şeklindedir
  • Özel sunucu şu şekilde çalışır
    • Cihaz mirror_data_get ile durum istediğinde MQTT broker’daki retained değeri kullanır veya varsayılan değerle yanıt verir
    • Home Assistant bir durum değiştirme komutunu MQTT topic’i olarak gönderdiğinde özel sunucu bunu cihaza iletir
    • Cihaz durumu herhangi bir nedenle değiştiğinde cihazın mirror_data paketini MQTT broker’a publish eder ve retain eder
  • Durum için source of truth her zaman cihazdır
    • Durum güncellemesi başarısız olursa MQTT broker’da güncellenmiş gibi gösterilmez
    • Durum fiziksel kontrol panelinden değişse bile MQTT broker’a yansıtılır
  • Hava temizleyiciyi fan cihazı olarak eşlemek için Home Assistant’ın MQTT Fan entegrasyonu kullanıldı
  • configuration.yaml içinde güç durumu topic’i, komut topic’i, fan hızı durumu topic’i, fan hızı komut topic’i ve 1~4 hız aralığı ayarlandı
  • Pi-hole yerel DNS, üreticinin bulut domain’ini özel sunucuya çözecek şekilde ayarlanarak yerel sunucunun cihazın sunucusu gibi davranması sağlandı

Güvenlik değerlendirmesi ve sonuçlar

  • Üretici, DTLS gibi standart protokoller yerine kendi protokolünü uygulamıştı
  • Her cihazda benzersiz bir özel anahtar olup olmadığı kesin değil; ancak her iki durumda da dezavantajlar var
    • Tüm cihazlar aynı firmware özel anahtarını paylaşıyorsa tek bir cihazı tersine mühendislikle analiz etmek bile diğer cihazlara MITM saldırısı denemeyi mümkün kılar
    • Her cihazda benzersiz bir özel anahtar varsa sunucunun seri numarası ile cihaz anahtarı eşlemesini tutması gerekir; bu verilerin kaybı durumunda sunucu cihaz iletişimine yanıt veremez hâle gelir
  • Firmware’de statik bir özel anahtar bulunduğu için saldırgan tek bir firmware dökümünden anahtarı elde edip MITM saldırısı gerçekleştirebilir
  • Uygulama güvenlik açısından tamamen kötü değil; saldırı için hâlâ fiziksel erişim gerekiyor
  • Özel uygulama ağ iletişimini opak hâle getirmiş olsa da Security through obscurity, standart uygulamaları hedefleyen genel saldırıları bir süre engelleme düzeyine yakındır ve saldırgan için aşılabilir bir engeldir
  • Nihai hedef olan Home Assistant entegrasyonu başarıldı ve hava temizleyici birkaç hafta boyunca sorunsuz çalıştı
  • Ayrı bir hava monitöründe PM2.5 veya VOC değerleri çok yükselirse hava temizleyiciyi belirli bir süre boost moduna alan bir otomasyon da yapılandırıldı

Henüz yorum yok.

Henüz yorum yok.