Bana Her Şey SOP'u Hatırlatıyor: DNS Rebinding ile Ethereum'larınız Çalınabilir!

Invicti Security Team - 05 Şubat 2018 -

PHP Composer modülü bağımlılıkları yönetmek için ne kadar ideal olsa da güvenlik perspektifi olarak ele aldığımızda onunda kullanımı noktasında dikkat edilmesi gereken hususlar var. Ethereum geth Client'ı üzerinde DNS rebinding ile nasıl hack saldırısı yapabileceğine değiniyoruz ve SOP'u bir kez daha anıyoruz.

Bana Her Şey SOP'u Hatırlatıyor: DNS Rebinding ile Ethereum'larınız Çalınabilir!

PHP Composer security

Composer, PHP'de uygulama geliştirirken, uygulamamızın bağımlı olduğu bileşenleri yönetmemizi sağlayan bir kütüphane. Uygulamamızın bağımlı olduğu bileşenleri yükleme ve güncelleme işlemleri composer marifetiyle kolaylıkla gerçekleştirilebiliyor.

composer.json dosyası JSON tipinde bir dosya olduğu için, public dizinde bırakıldığı takdirde uygulamamıza dair pek çok ipucu verecektir:

  • Uygulamamız hangi bileşenlere bağımlı.
  • Bu uygulama bileşenlerini güncel sürümde mi, hangi sürümü kullanıyor.

Elde edilen uygulama ve sürüm bilgisi ile pek tabii ki bir saldırgan Vulnerability Mapping dediğimiz yönteme başvurarak uygulama ve sürüm bilgisinden yola çıkarak, bilinen bir zafiyet içerip içermediği kontrolünü yapabilir. Şayet kullanılan sürüm, bilinen bir zafiyet içeriyorsa, oyun daha baştan bitmiş demektir…

Fakat bu kadar da değil, composer.json dosyası pek çok nüans içeriyor. Dikkatli kullanılmadığı takdirde uygulamamızı zayıflatabilir.

Bunun peşinen önünü alabilmek için, public dizin dışında olsa bile, geliştiriciler composer.json dosyalarında bir dizi kontrolü yapmalıdır.

Örnek bir senaryo ile inceleyelim. Aşağıdaki gibi bir composer.json dosyasına sahip olduğumuzu düşünelim:

# composer.json: insecure
{
  "name": "my/awesome_project",
  "require": {
    "php": "~7.1.7",
    "ext-mysqli": "*",
    "symfony/validator": "^4.0",
    "guzzlehttp/guzzle": "^6.3",
    "phpunit/phpunit": "^6.5",
    "squizlabs/PHP_CodeSniffer": "^3.2"
  },
  "autoload": {
    "psr-4": {
      "AwesomeProject\\": "app/",
      "AwesomeProject\\Tests\\": "tests/"
    }
  }
}

composer.json dosyasının require alanına bakıldığı takdirde burada bir dizi uygulamanın listelendiği görülecektir. Require alanı uygulamanın çalışması için olmazsa olmaz paketlerin listelendiği node'dur. Fakat burada dikkatimizi iki ekstra paket çekiyor:

  • symfony/validator
  • phpunit/phpunit

Yine autoload kısmı, hem kaynak kodları, hem de PHP unit testleri için class ve dizin eşleşmesinin yapıldığı kısım.

En az yetki prensibi

Daha çok kullanıcı rolleri ile karşımıza çıkan en az yetki prensibini uygulamanın tamamına sindirmek, sadece rollerde değil uygulama fonksiyonlarında da bu ayrımı gözetmek, önerilen güvenli geliştirme ilkelerindendir. Nitekim OWASP Proactive Controls C6 maddesi yani Implement Access Controls maddesi bir alt başlık olarak bu hususa vurgu yapıyor.

Özetle, bir uygulamanın doğru bir biçimde çalışmak için gereksindiği minimal ihtiyaçlarla, yetkilerle donatılması.

Peki bunun konumuz ile alakası ne?

Yukarıdaki composer.json örneği tüm ortamlar (environment) için aynı bağımlılıkları yüklüyor. Oysa dikkatli okurun fark edebileceği gibi, ekstra kütüphaneler örneğin phpunit/phpunit kütüphanesi production ortamında ihtiyaç duyulabilecek bir bağımlılık değil.

E peki nasıl yapacağız?

Bunun farkında olan composer.json geliştiricileri, farklı ortamlar için, composer.json dosyasında farklı node'lar tanımlamaya imkan veriyor.

Örneğin phpunit/phpunit kütüphanesi require-dev isimli bir başta node'da tanımlanıp, production dışındaki ortamlarda yüklenmesi sağlanabilirdi.

Peki bunu composer nasıl gerçekleştiriyor?

composer ile beraber --no-dev parametresini kullanarak, require-dev ve autoload-dev gibi production ortamlarda gerekli olmayan bağımlılık ve eşleştirmelerin yüklenmemesini sağlayarak.

Yukarıdaki hatırlatmalardan sonra güvensiz olarak işaretlediğimiz composer.json'un daha güvenli hali aşağıdaki gibi olacaktır:

# composer.json: autoload-dev section
{
  "name": "my/awesome_project",
  "require": {
    "php": "~7.1.7",
    "ext-mysqli": "*",
    "symfony/validator": "^4.0",
    "guzzlehttp/guzzle": "^6.3"
  },
  "require-dev": {
    "phpunit/phpunit": "^6.5",
    "squizlabs/PHP_CodeSniffer": "^3.2"
  },
  "autoload": {
    "psr-4": {
      "AwesomeProject\\": "app/",
    }
  },
  "autoload-dev": {
    "psr-4": {
      "AwesomeProject\\Tests\\": "tests/"
    }
  }
}

Şimdi örneğin production ortamı için gerekli bağımlılıkların yüklenmesini istiyorsak, aşağıdaki komutu kullanabiliriz:

composer install --no-dev

PHP Composer güvenliği hakkında ayrıntılı bilgi için lütfen tıklayınız.

Bana Her Şey SOP'u Hatırlatıyor: DNS Rebinding ile Ethereum'larınız Nasıl Çalınabilir?

Haftanın Hackleri'nde gündemimize aldığımız ve yolu SOP (Same Origin Policy) ile çakışmayan çok az konu var. Sebebi bariz, istemci taraflı güvenliğin kalbinin SOP olması.

Jazzy isimli araştırmacı, DNS Rebinding ile Ethereum'larımızın nasıl çalabileceğini anlattığı yazısında, ister istemez SOP'a da değiniyor. Biz de baş tacı edip, yazımızda tekrar hatırlatıyoruz, hemen ardından ise ayrıntılara değineceğiz.

Coin fiyatlarındaki artışlar hem yatırımcıların, hem de saldırganların iştahını kabartıyor. Yavaş yavaş cüzdan güvenliği de gündemlerimize girdi. Tavis Ormandy'nin JSON-RPC 'lere yetkisiz erişimler ile coinlerin çalınabileceği senaryosundan sonra, güvensiz CORS impelemtasyonları senaryolarını da konuşmuş idik. Bu noktalardaki zaaflar da pek çok geliştirici tarafından onarıldı. Şimdi yepyeni bir senaryo var: DNS Rebinding.

Pek çok Ethereum client'ı JSON-RPC kullanıyor. Bu servis 8545 numaralı portu yerel makinede kullanıyor. Yerel makinede olduğu için, dışarıdan bir saldırganın doğrudan erişebilmesi mümkün değil.

Yukarıda CORS headerlarının yanlış implementasyonlarının bir saldırı vektörü olarak kullanıldığından söz etmiş idik. Jazzy'nin yazısına konu ettiği Geth isimli Ethereum client'ı CORS kullanmıyor. Kısa bir süreliğine güvenli addedilebiir. Fakat çok geçmeden Jazzynin aklına DNS Rebinding vektörü geliyor.

DNS, bilindiği üzere domain adlarını IP adreslerine çeviren bir protokol. DNS sunucular, DNS sorgularına yanıt döndürdüklerinde, bu yanıtın ne kadar süre ile istekte bulunan tarafta saklanacağını TTL değerleri ile belirtiyorlar. TTL değeri zaman aşımına uğradıktan sonra istemci, bu domaine bir istek yapıldığında, tekrar bir DNS sorgusu yapmaya ihtiyaç duyuyor.

Şimdi Jazzy'nin dilinin altındaki senaryoyu bir de bu nazarla gözden geçirelim. Saldırgan sizi kendi kontrolündeki bir siteye girmeye zorlandı: www.attacker.com. Browser bu siteye gireceği esnada IP adresini çözümlemek için bir DNS isteği yaptı. Saldırgan DNS sunucusundan çok düşük bir TTL ile yanıt verdi. Sizi uzun süre sitede kalmaya ikna eden saldırgan arka taraftan örneğin XHR (Ajax) ile kendi kontrolündeki domain'e (www.attacker.com) istek yaptı. İstemcinin sakladığı DNS yanıtının yaşam süresi dolduğu için tekrar DNS sunucusuna istek yaparak IP çözümlemesi yapmak istedi. Bu esnada saldırgan DNS sunucusundan, 127.0.0.1 değerinin dönmesini sağladı.

Peki bu durumda ne olacak?

Artık saldırgan, www.attacker.com 'a döndüğü 127.0.0.1 ile, istemci makinesinden servis edilen bu kaynağa erişebilecek. Hani SOP vardı? Evet hala var, ama burada artık her iki kaynak da attacker'ın kontrol ettiği domain üzerinden servis edildiği için, birbirlerinin DOM'larına erişebilecekler. Çünkü SOP iki kaynağın birbirlerine erişimi için şart koştuğu şema, domain ve port uyumu burada sağlanıyor olacak.

Jazzy 5 saniyeden daha düşük bir TTL değeri verdiğini ancak değer ne olursa olsun, modern browserlarda TTL süresinin minimum 60 saniye olarak set edilebileceğini belirtiyor. 60 saniye bir kullanıcıyı site geziniminde oyalamak için uzun olmasa gerek.

Ethereum client'ı Geth'in 8545 numaralı porttan hizmet verdiğini söylemiş idik. Ee SOP hani port'u da eşleşmeye dahil ediyordu? Elbette ediyor. Bu sebeple Jazzy kullanıcı kendi kontrolündeki domaine 8545 numaralı porttan girmeye ikna etmek istiyor. Ama bu durum şüpheleri üzerine çekebilir. İframe yoluna başvurarak bu engeli de aşacağını düşüyor. Kendi kontrolündeki siteyi hem 80, hem de 8545 numaralı porttan hizmet verecek şekilde ayarlıyor. 8545 ile erişimi, iframe üzerinden yapıyor:

<iframe src="http://www.attacker.com:8545"></iframe>

Jazzy burada çok ilginç bir sorunla karşılaşıyor. İlk istekte kendi sunucusunun IP'sini döndürmeli, ikinci istekte local IP'yi döndürmeli. Hangi istek ilk istek, hangisi takip eden istek buna nasıl karar verecek? Üstelik birden fazla kullanıcıyı hedeflediğinde bu takibatı nasıl yapacak? Jazzy'nin imdadına subdomainler yetişiyor.

Bu noktaya kadarki tüm adımları özetleyelim:

  • Hedef kullanıcı attacker.com 'u browserında açıyor.
  • İlk olarak kullanıcı bilgisayarı attacker.com 'un IP'sini öğrenmek için bir DNS sorgusu yapıyor. attacker.com 'un gerçek IP'sini elde ediyor: 87.87.87.87
  • attacker.com kullanıcı browserında açıldı. Hidden (gizli) bir iframe üzerinden, randomrsub.attacker.com:8545 adresine istek yaptı.
  • Bu isteğe mukabil, tarayıcı yeni bir DNS isteği yaparak randomsub.attacker.com:8545 domainine ait IP'yi aldı: 87.87.87.87.
  • randomsub.attacker.com:8545 üzerindeki Javascript 60 saniye bekledi. 60 saniye sonunda randomsub.attacker.com:8545/test kaynağına bir XHR (Ajax) isteği yaptı.
  • Cachelenen DNS yanıtının süresi dolduğundan, browser randomsub.attacker.com:8545 için yeni bir DNS sorgusu yaptı. Saldırgan kontrolündeki DNS sunucusu bu defa yanıt olarak, 127.0.0.1 adresini döndürdü.
  • Bu defa randomsub.attacker.com:8545/test yapılan istek esasen 127.0.0.1:8545/test adresine yapılmış olacağı için, yanıt okunabilecek.

Jazzy, hazırladığı PoC 'u http://rebinddns.ml/ adresi üzerinden de paylaşıyor. Yukarıdaki saldırı senaryosu aynı zamanda Stored XSS zafiyeti ile de birleştirilebilir.

Araştırmanın ayrıntılarına erişmek için lütfen tıklayınız.