security research & pentest
Nasıl Başarılı Bir Şekilde Başarısız Olunur?

Merhabalar, bu blog yazısında yakın arkadaşım Ünsal ile birlikte bir zafiyeti araştırırken yaşadığımız süreci baştan sona anlatıyorum. Bu süreç, başlangıçta izlediğimiz yol ile ulaştığımız sonucun birbirinden oldukça farklı olduğu; zaman zaman başarısız olduğumuzu ve yanlış ilerlediğimizi düşündüğümüz, ancak tüm bunlara rağmen devam ederek başarıya ulaştığımız bir yolculuğun hikâyesi.

Bu yolculuk boyunca önce zafiyet kapsamını tek bir bakış açısıyla değerlendirdik. Daha sonra, zafiyeti ilk tespit eden Gareth Heyes ile gerçekleştirdiğimiz yazışmalar sayesinde farklı bir bakış açısının varlığını fark ettik ve araştırma yöntemimizi buna göre değiştirdik.

Tüm bunların sonunda, GitHub üzerinde 23 bin yıldıza sahip bir uygulamada keşfettiğimiz ve CVE ile sonuçlanan bu çalışma, araştırma boyunca yaptığımız hatalara rağmen doğru sonuca ulaşabildiğimizi gösterdi.

Blog yazısı bir zafiyeti 2 farklı bakış açısıyla ele almakta bu yüzden zafiyetin ayrıntılarını tam olarak anlayabilmeniz için sonuna kadar okumanızı tavsiye ederim.

Gareth Heyes blogunda bu konu ile ilgili farklı şekillerde de exploitler ve senaryolar üzerinde durmuştur bu yüzden onu da okumanızı şiddetle tavsiye ederim.

Başlangıç

Bütün bu sürecin başlangıcı Gareth Heyes'in makalesini okuduktan sonra gerçekten inanılmaz bir şekilde araştırmanın derinliği ve işleyişi hakkında etkilenmemin ardından daha derinlemesine bir araştırma yapmak istediğim için şu mesaj ile başladı;

Telegram mesajı - Araştırma başlangıcı
Zafiyetin Analizi

Gareth Heyes'in makalesinde bahsettiği şeyleri anlamak için öncelikle SMTP mesajlarının örnek bir şekilde nasıl iletildiğini anlayabilmek gerekir;

SMTP isteklerinde mail RCPT TO başlığından sonra gelen mail adresine iletilir, Data içerisinde bulunan "From" veya "To" mail adresi mailin gerçekten geldiği adresi veya gerçek iletilen mail adresini belirtmemektedir.

SMTP istek örneği

Örneğin anonim "Prank" mail gönderim sistemleri "Data" değerinin ardından gelen "From" ve "To" adreslerini değiştirir. Örnek bir prank sitesinin SMTP isteğini inceleyelim;

Prank sitesi SMTP isteği

Yukarıda görüntülenen istekte aslında maili ileten adresin "Mail From" adresini ile "From" başlığında bulunan değerler farklıdır ve bu maili "Outlook" gibi mail görüntüleme ekranlarında görüntülediğinizde göndericinin mail adresi "[email protected]" şeklinde görüntülenecektir.

Çünkü SMTP iki ayrı katmanda adres bilgilerini taşımaktadır ve bu katmanların kullanım amaçları birbirlerinden bağımsızdır.

KatmanBaşlık AlanlarıAmacı
Envelope (Zarf)MAIL FROM, RCPT TOGerçek yönlendirmelerin yapıldığı adresler, MTA'ların baktığı başlık değerleri
Header (Başlık)From:, To:, CC:Görüntüleme amaçlı mail istemcisinin kullanıcılara gösterdiği bilgi

Yani mail her zaman RCPT TO üzerindeki mail adresine gider, bu konudan bahsetmemin sebebi ise ilerleyen kısımlarda mailin iletildiği kişi dediğimizde nereye bakmamız gerektiğini şimdiden anlamamız.

Gareth Heyes tarafından da makalede belirtildiği gibi RFC2047 dokümanındaki açıklamaya göre SMTP istekleri üzerinde "encode" yapıda veriler kullanılabilmekte;

RFC2047 açıklaması

Peki bu ne demek? Örneğin [email protected] adresine mail göndermek istediğimizde mail adresini Q encoding ile =?utf-8?q?saadetelif=40lzzapsecurity.com?= şeklinde

Q Encoding örneği

Veya base64 ile =?utf-8?b?c2FhZGV0ZWxpZkBsenphcHNlY3VyaXR5LmNvbQ==?= şeklinde yazabilirim.

Base64 Encoding örneği

Peki burada zafiyet ne durumda ortaya çıkıyor? Eğer bir mail kütüphanesi RFC2047 özelinde mail adreslerini kabul ediyorsa ve bu kütüphaneyi kullanan bir uygulama MTA konfigürasyonunu RFC2047 şeklinde decode etmeyi kabul ediyorsa bu durumda ortaya şu senaryo çıkmaktadır;

Bir uygulama, kayıt veya e-posta gönderimi gibi işlemlerde yalnızca belirli domainlere izin veriyor ve bu doğrulamayı e-posta adresindeki `@` karakterinden sonraki kısmı kontrol ederek yapıyorsa, bu yaklaşım çeşitli bypass senaryolarına açık olabileceğinden potansiyel bir güvenlik zafiyeti olarak değerlendirilmelidir.

Neden?

Aşağıdaki mail adresini örnek alalım;

=?utf-8?q?saadetelif=40lzzapsecurity=3e=00=0A=0D?=@alloweddomain.com

@ işaretinin ardında alloweddomain.com olduğu için uygulamanın domain kontrolünü geçecektir. Peki uygulama gerçekten alloweddomain.com adresine mi mail atacaktır?

RCPT TO decode sonucu

Konuştuğumuz senaryo üzerinde hayır, yukarıdaki istekte görebileceğiniz gibi aslında biz payloadı verdiğimizde RCPT TO değeri <saadetelif@lzzapsecurity> adresine gitmektedir, girdinin geri kalan kısmı ise SMTP'ye bağlı olarak hata fırlatabilmekte veya ikinci bölüm görmezden gelinebilmektedir.

Bu durum gerçeklendiğinde exploit adımları aşağıdaki sırayla oluşur;

Kütüphane RFC2047'ye uygun encoded email adresini alır => MTA RFC2047 özelinde decode eder => Email hedef email adresine değil farklı bir email adresine iletilir.

Biz de bu aşamaya kadar olan durumu test etmek için github üzerinden bulabildiğimiz çoğu email işleme kütüphanesini inceledik ve bildirmek için rapor template'i hazırladık, kütüphane keşfi ve rapor otomizasyonu için AI kullandık.

Akşam 20.00'da başlayan araştırma serüvenimiz böylelikle 04.00'a kadar sürdü ve bu süreçte uzun bir süre telegramın UDP portlarını meşgul ettik ;

Telegram araştırma süreci

Bu aşamada hedef aldığımız kütüphanelerinin yüksek bir çoğunluğunun encoded verileri kabul ettiğini gördük;

Kütüphane test sonuçları - 23 kütüphanenin 22'si etkilendi

Makalede eğitim amaçlı olduğu ve kütüphaneler ile bir anlaşma sağlamadığımız için isimlerini vermek istemedik ancak 23 kütüphanenin 22'sinde bu durumun bulunduğunu görebiliyoruz.

Burada kütüphane sahiplerine raporlamak için örnek uygulamalar hazırlayarak bu uygulamalar üzerinde hangi domainin kabul edildiği gibi aşamaları kütüphanelere ait fonksiyonları kullanarak kanıtlamak istedik;

Örnek uygulama ile domain bypass gösterimi

Burada yaptığımız hatalardan biri, farklı bir domaine ait e-posta adreslerinin kabul edildiğini göstermek amacıyla SMTP'ye ulaşan isteği doğrudan paylaşmamızdı. Aslında, MTA'ların bu isteği nasıl decode ettiğini göstererek zafiyetin bu aşamada ortaya çıktığını açıklamamız gerekiyordu. Bunun yerine, isteğin kütüphaneden çıktıktan sonraki hâlini iletmeyi tercih ettik.

Bu yaklaşım sayesinde kütüphanelerin =? yapıları içeren girdileri kabul ettiğini kanıtlamış olsak da, zafiyetin hatalı MTA yapılandırmalarıyla nasıl istismar edilebileceğini göstermediğimiz için riskin boyutunu yeterince ortaya koyamadık.

Örneğin, aşağıdaki isteğin hatalı yapılandırılmış bir MTA tarafından decode edilip iletildiği şekli gösterseydik, bulgunun etkisi çok daha net ortaya konabilirdi.

MTA konfigürasyon örneği

Bazı kütüphane sahipleri sorumluluğun kendilerinde değil MTA'larda olduğunu düşündüklerini dile getirdiler ve karşılıklı konuşmalarda durumu sadece bildirmek istediğimizi dile getirdik.

Bazı kütüphanelerde ise bulgunun ardından düzeltme çalışmaları yapıldı. Örneğin, Govalidator kütüphanesi için gerçekleştirdiğimiz bildirim sürecinin ardından, kütüphane sahibi aşağıdaki commit ile birlikte ilgili düzenlemenin uygulandığını tarafımıza iletti:

Govalidator Güncelleme Commit'i

Bazılarının yaklaşımı ise aşağıdaki gibiydi;

Kütüphane sahibi yanıtı

Bu sürecin ardından aldığımız bazı tepkiler ve retlerden sonra, süreçte bir hata yapıp yapmadığımızı anlamak istedik ve Gareth Heyes ile iletişim kurduk. Yaptığımız çalışmaların da makalede ele alınan senaryolar kapsamında olduğunu belirtti. Ancak ek olarak — ve bizim gözden kaçırdığımız nokta da buydu — bazı kütüphanelerin, e-posta henüz SMTP veya MTA katmanına ulaşmadan önce RFC 2047 formatında encode edilmiş alanları decode ederek işlediğini ifade etti. Böylece, bizim yalnızca MTA davranışlarına bağlı olduğunu düşündüğümüz etkinin, aslında doğrudan kütüphanelerin kendi işleme mantığından da kaynaklanabildiğini ve herhangi bir MTA'ya ihtiyaç duymadan tetiklenebildiğini göstermiş oldu.

Biz de bu aşamada araştırmamızı bu yöne çektik ancak artık kütüphaneler özelinde değil bu kütüphaneleri kullanan uygulamalar özelinde ilerlemek istedik. Hedef alacağımız uygulamaları listemize eklerken ise aşağıdaki durumları göz önünde bulundurduk;

- Bypass edilebilmesi gereken bir domain allow list veya domain black list bulunması
- Register adımında bu allow ve black listlerin kullanılması
- Ve tabii ki RFC2047

Bu çerçevede incelediğimiz uygulamalarda en çok dikkatimizi çeken ve zafiyeti tetikleyebileceğimizi düşündüğümüz uygulama ise Github üzerinde 22.7K yıldızı olan Forem uygulamasıydı.

Öncelikli olarak uygulamayı kurup incelediğimizde allow list oluşturabildiğimizi gördük. Lzzapsecurity.com adresi olmak üzere bir allowlist oluşturduk ve aşağıdaki payloadı verdik:

=?utf-8?q?test1234=40v2ycjxkm94pldktx9tv8tu3ef5l19rxg.oastify.com=3e?=@lzzapsecurity.com
Forem register ekranı

Uygulamanın Backend'i üzerinde özel karakterlerin engellenmediğini ve bu mail adresinin kabul edildiğini gördük;

Backend mail adresini kabul etti

Mail adresinin SMTP üzerinde görünüşü ise RFC2047 özelinde decode edilmiş haliydi;

SMTP üzerinde decode edilmiş payload

Bu aşamada SMTP üzerinde hata fırlatılması ihtimalinde aşağıda kullanılabilecek farklı payloadları da ilettik;

[email protected]

[email protected]

[email protected]

=?utf-8?q?test=40attacker.com=3e=0A=0D?=RCPTTO:[email protected]

Bütün bu süreçlerin ardından Forem tarafından zafiyetin kabulü ve Github üzerinden CVE istenmesi aşamasına ilerledik ve başarılı bir şekilde CVE'mizi elde ettik.

Forem Security Advisory

Kapanış

Açıkçası tüm bu süreç, araştırmalarımızı nasıl ilerletmemiz gerektiğine dair kendi içimizde yaşadığımız güçlü bir geri bildirim deneyimi gibiydi.

Bir zafiyeti anlamaya çalışırken deneme-yanılma sürecinin kaçınılmaz olduğunu, motivasyonumuzu kaybetmeden ilerlememiz gerektiğini fark ettik. Tüm bunlar bize sadece teknik anlamda değil, düşünce olarak da gelişim sağlayan önemli bir deneyim oldu.

Yeniden böyle bir macera ile görüşmek dileğiyle.