DevTools üzerinden Chrome sandbox kaçışı olayı
(ading.dev)- Chromium’un WebUI yetki sınırı, kurumsal politika test özelliği ve DevTools uzantı API’sindeki kusur bir araya gelince, kötü amaçlı bir Chrome uzantısı çok az kullanıcı etkileşimiyle kabuk komutu çalıştırma noktasına kadar ulaşabildi
- Saldırı zinciri,
chrome://policyüzerinde belgelenmemiş politika test API’sini çağırarak kullanıcı politikalarını değiştirmeye ve Browser Switcher’ın alternatif tarayıcı yolu ile argümanlarını kabuk komutu olarak kötüye kullanmaya dayanıyordu chrome.devtools.inspectedWindow.reload()injectedScriptçalıştırılmasına izin veriyordu; ayrıca WebUI’ye geçiş anındaki erişim engellemesindeki gecikme veya çökmeden sonra kalanPage.reloadisteği nedeniyle ayrıcalıklı WebUI içinde kod çalıştırılabiliyordu- Google, açıkları P1/S1 olarak sınıflandırdı ve
Page.reloadiçinloaderIddoğrulaması,inspectedWindow.reload()için URL kontrolü ve WebUI işleyicisinde politika testinin etkin olup olmadığını denetleyen kontroller ekledi - İlgili açıklar CVE-2024-5836 ve CVE-2024-6778 olarak atandı; ikisi de CVSS 8.8 High aldı ve nihai ödül $20,000 oldu
Chromium WebUI ve sandbox sınırı
- Chromium, güvenilmeyen kodu sandbox içinde çalıştırır; Chrome uzantılarının JavaScript’i de yalnızca verilen izinler ve erişebildiği API’ler içinde çalışmalıdır
- Yalnızca uzantı izinleriyle bile oturum açma bilgileri veya tarayıcı geçmişi çalınabilir, ancak ilke olarak etki alanı tarayıcının içinde kalmalıdır
- Chromium’un GUI’sinin bir bölümü
chrome://settings,chrome://historygibi WebUI olarak uygulanır- WebUI, HTML, CSS ve JavaScript ile yazılır; ancak tarayıcı içi bilgileri göstermesi ve değiştirmesi gerektiğinden normal web sayfalarından daha yüksek ayrıcalıklara sahiptir
- WebUI ön yüzünün JavaScript’i, özel API’ler üzerinden tarayıcının yerel C++ koduyla iletişim kurabilir
- WebUI içinde kod çalıştırılabiliyorsa bu, Chromium sandbox’ını aşmaya kadar gidebilir; bu yüzden saldırganların
chrome://sayfalarında güvenilmeyen JavaScript çalıştıramaması önemlidir - Örneğin
chrome://downloadsiçinde bir.exeindirme öğesine tıklanırsa çalıştırılabilir dosya açılabilir; bu nedenle Chromium, dosya açma eyleminin gerçekten kullanıcı girdisinden gelip gelmediğini kontrol eder
Kurumsal politika test özelliğinin aşılması
- Açık araştırması, Chromium’un enterprise policy system bileşeninde başladı
- Bu sistem, şirket veya okul sahipli cihazlarda yöneticilerin belirli ayarları zorunlu olarak uygulaması için kullanılan bir özelliktir
- Politikalar genellikle Google hesabına bağlanır ve Google yönetim sunucularından indirilir
- Politikalar device policies ve user policies olarak ayrılır
- device policies, Chrome OS cihazının tamamındaki ayarları yönetir
- user policies, belirli bir kullanıcıya veya tarayıcı örneğine uygulanır ve tüm platformlarda kullanılabilir
- Linux’ta
/etc/opt/chrome/policiesiçine JSON dosyaları yerleştirerek Google Chrome örneğine user policies uygulanabilir, ancak bu dizine yazmak için root yetkisi gerekir
- Cihaza o anda uygulanan politikalar
chrome://policyWebUI’sinden görülebilir- Bu sayfa uygulanan politika listesini, politika hizmeti günlüklerini ve JSON dışa aktarma işlevini sunar
- Normalde bu sayfada politikaları düzenlemenin bir yolu yoktur
- Chrome v117 Chrome Enterprise sürüm notlarında,
chrome://policy/testsayfasının Beta, Dev ve Canary kanallarında politika testine izin verdiğine dair bir madde yer aldı- Bu özellik, ilgili sürüm notları dışında Chromium belgelerinde anılmıyordu
- Normal şekilde etkinleştirmek için belgelenmemiş
PolicyTestPageEnabledpolitikası gerekiyordu - Bu politika yoksa
chrome://policy/test,chrome://policysayfasına yönlendiriliyordu
setLocalTestPolicies doğrulama hatası
chrome://policy/testiçindeki JavaScript kodu, test politikalarını ayarlamak içinsendWithPromise('setLocalTestPolicies', ...)kullanıyordusendWithPromise(), WebUI’nin özel API’sichrome.send()için bir sarmalayıcıdır- Bu çağrı C++ işleyici fonksiyonuna istek gönderir ve işleyici tarayıcı içinde dahili işlemler gerçekleştirebilir
chrome://policykonsolundan doğrudansetLocalTestPoliciesçağrıldığında, ilk denemede tarayıcı çöktü ve günlükte politika dizisinin gerekli olduğuna dair bir mesaj kaldı- Politika dizisi biçimi düzeltilip
AllowDinosaurEasterEgggibi bir user policy gönderildiğinde, özellik açıkça etkinleştirilmemiş olmasına rağmen keyfi politikalar ayarlanabildi - C++ tarafındaki
HandleSetLocalTestPoliciesişleyicisi yalnızcalocal_test_providervar mı diye bakıyordu; politika test özelliğine gerçekten izin verilip verilmediğini kontrol etmiyordu LocalTestPolicyProvider::CreateIfAllowed(),IsPolicyTestingEnabled(nullptr, channel)çağrısı yapıyordu- İlk argüman olan
pref_servicenullolduğu içinPolicyTestPageEnabledkontrolü atlanıyordu - Geriye yalnızca release channel’ın
CANARYveyaDEFAULTolup olmadığı kontrolü kalıyordu
- İlk argüman olan
- markasız Chromium derlemelerinde
GOOGLE_CHROME_BRANDINGkodu derlenmediği için kanalUNKNOWNolarak kalıyordu- enum içinde
UNKNOWN = 0,DEFAULT = UNKNOWNolduğundan Chromium ve türev derlemelerde kanal kontrolü geçiliyordu - markalı Google Chrome stable derlemelerinde release channel doğru ayarlandığından bu hata stable Google Chrome’da çalışmıyordu
- enum içinde
Browser Switcher ile kabuk komutu çalıştırma
- Keyfi user policy ayarlanabilince, Chrome kurumsal politikalarındaki Legacy Browser Support modülü sandbox kaçış yolu haline geldi
- Legacy Browser Support, Browser Switcher olarak da bilinir; kullanıcı Chromium’da belirli bir URL’yi ziyaret ettiğinde alternatif bir tarayıcı başlatmak için tasarlanmıştır
- Özellik, Internet Explorer kullanıcı desteği için oluşturulmuştur
- Davranışı politikalarla kontrol edilir
AlternativeBrowserPathveAlternativeBrowserParameterspolitikaları birleştirildiğinde, Chromium “alternatif tarayıcı” olarak keyfi kabuk komutları çalıştırabiliyordu- Bu Browser Switcher politikaları yalnızca Linux, macOS ve Windows’ta vardır
- Örnek akış şöyledir
BrowserSwitcherEnableddeğeritrueyapılırBrowserSwitcherUrlListiçineexample.comeklenirAlternativeBrowserPathLinux’ta/bin/basholarak ayarlanırAlternativeBrowserParametersise["-c", "xcalc # ${url}"]gibi ayarlanır
- Tarayıcı
example.comadresine gittiğinde Browser Switcher devreye girer ve/bin/bash -c 'xcalc # https://example.com'biçiminde bir komut çalıştırılır${url}yerine geçen değer, kabuk yorumu olması için#sonrasına yerleştirilir
chrome://policyiçinde politikalar ayarlandıktan sonrawindow.open("https://example.com")çağrısı yapmak, yalnızca JavaScript ile keyfi kabuk komutu çalıştırılmasına kadar gidiyordu
DevTools uzantı API’sindeki atlatma yolu
- Yalnızca önceki adımlar kullanılırsa kurbanın
chrome://policyüzerinde tarayıcı konsoluna kötü amaçlı kod yapıştırması gerektiğinden pratik değildi - Otomatik yürütme yolu, kötü amaçlı bir Chrome uzantısı üzerinden bulundu
- Uzantılar sayfaya JavaScript enjekte edebilir, ancak ayrıcalıklı WebUI sayfalarında JavaScript çalıştıramamaları gerekir
- Uzantıların sayfada JavaScript çalıştırmak için kullandığı başlıca dört API vardı
chrome.scripting- Manifest v2’deki
chrome.tabs chrome.debuggerchrome.devtools.inspectedWindow
- İnceleme için, görece daha az sertleştirilmiş olduğu düşünülen
chrome.devtools.inspectedWindowseçildichrome.devtoolsAPI’sini kullanan uzantıların manifest içindedevtools_pagealanına sahip olması gerekir- Kullanıcı DevTools’u açtığında bu sayfa bir iframe olarak yüklenir ve içeride
chrome.devtoolsAPI’si kullanılabilir
- David Erceg’in önceki hata raporunda,
chrome.devtools.inspectedWindow.eval()kullanılarak WebUI içinde kod yürütmeye ulaşılan bir örnek vardı- Normalde incelenen sayfa WebUI’ye geçtiğinde DevTools API kullanımının devre dışı bırakılması gerekir
- Atlatmanın ana fikri, Chrome API’yi devre dışı bırakmadan önce eval isteğini gönderip isteğin WebUI sayfasına ulaşmasını sağlamaktı
inspectedWindow.reload() ve about:blank özelliği
chrome.devtools.inspectedWindow.reload()dainjectedScriptargümanını alırsa incelenen sayfada JavaScript çalıştırabilir- WebUI’nin açtığı bir
about:blanksayfasındainspectedWindow.reload()çağrıldığında, ayrıcalıklı sayfada JavaScript yürütülebiliyorduabout:blankURL olarak özel görünmese de kendisini açan sayfanın izinlerini ve origin’ini devralırchrome://settingstarafından açılanabout:blank,chrome://settingsorigin’ine sahip ayrıcalıklı bir sayfaydı
- DevTools API’yi devre dışı bırakan kod, incelenen hedefin yalnızca URL’sini kontrol ediyor, origin’i kontrol etmiyordu
- URL normal görünse bile origin ayrıcalıklı bir origin olabilir
- Yalnız
about:blankyolu,chrome://policyabout:blankaçılır penceresi oluşturmadığı için exploit zincirinde doğrudan kullanılamıyordu - Ancak
inspectedWindow.eval()başarısız olurken bileinspectedWindow.reload(),chrome://settingsiçinde JavaScript çalıştırabiliyordu- Bu,
eval()içinde origin kontrolüne denk bir denetim varkenreload()içinde aynı düzeyde bir kontrol olmadığını gösteriyordu
- Bu,
Yarış durumundan çökme tabanlı yönteme geçerek kararlılık sağlama
- İlk exploit zinciri,
inspectedWindow.reload()çağrısını tekrar tekrar yaparak, incelenen sayfanın WebUI’ye geçmesinden hemen sonra DevTools sayfası API’yi devre dışı bırakmadan önceki kısa pencereyi hedefliyordu- Bunun için incelenen sayfa ile DevTools sayfasının farklı süreçlerde çalışması gerekiyordu
chrome://policysayfasına geçiş anı ile DevTools API’sinin devre dışı bırakılması arasınareload()isteği girerse WebUI içinde kod çalışıyordu
- Bu yöntem işe yaradı ama güvenilirliği düşüktü
- Ayarlamalar sonrasında yaklaşık %70 başarı sağlanabildi
- Açık ciddi olsa da kararsızlık severity değerlendirmesini düşürebilirdi
- Daha sonra, David Erceg’in önceki yaklaşımındaki gibi, sekme çökmesinden sonra kalan debugger istekleri davranışının
inspectedWindow.reload()için de kullanılabilir olup olmadığı denendi debuggerifadesi art arda iki kez tetiklendiğinde sekme çöküyor ve kuyruğa alınmışPage.reloadisteği WebUI’ye geçildikten sonra çalışabiliyordu- Bu yöntem yarış durumunu gereksiz kılarak %100 güvenilir hale geldi
- Google, önceki hata yamasında çökme sonrasında bekleyen debugger isteklerini temizleyecek değişikliği yapmıştı, ancak
Page.reloadisteklerini istisna olarak bırakmıştıinspectedWindow.reload()dahili olarakPage.reloadisteği gönderdiği için bu istisnadan etkileniyordu- O dönemdeki yama,
Page.reloadkomutunun betik çalıştırabildiği gerçeğini engelleyememişti
- Sekme çökmesi,
debuggeryöntemi dışında bellek yetersizliği oluşturularak da tetiklenebiliyordu; ancak nihai PoC’de daha hızlı olandebuggerçökmesi kullanıldı
Nihai exploit zinciri ve kullanıcı etkileşimi
- Nihai PoC şu sırayla çalışıyordu
chrome.devtools.inspectedWindow.reload()açığı kullanılarakchrome://policyiçinde JavaScript payload çalıştırılır- payload, kullanıcı politikalarını ayarlamak için
sendWithPromise("setLocalTestPolicies", policy)çağrısı yapar BrowserSwitcherEnabled,BrowserSwitcherUrlList,AlternativeBrowserPath,AlternativeBrowserParametersayarlanırwindow.open()veya sayfa yönlendirmesiyle Browser Switcher tetiklenir ve kabuk komutu çalıştırılır
- PoC, işletim sistemine göre hesap makinesi açma komutlarını kullanıyordu
- Windows:
C:\Windows\System32\cmd.exevecalc.exe - Linux:
/bin/bashvexcalc - macOS:
/bin/bashveopen -na Calculator
- Windows:
- Kullanıcı etkileşimi, DevTools’un açılmasını sağlayacak kadarla sınırlıydı
- Örnek ekrandaki “extension install error” mesajı, kullanıcıyı DevTools açmaya kandırmak için kullanılan bir yöntemdi
- DevTools açıldığında sandbox kaçışına giden zincir başlıyordu
Google’ın düzeltmeleri ve CVE atamaları
- Google, rapordan sonra açıkları hızla doğruladı ve P1/S1 olarak sınıflandırdı
- P1/S1, yüksek öncelik ve yüksek ciddiyet anlamına gelir
- Sonraki birkaç hafta içinde üç ana düzeltme uygulandı
Page.reloadkomutunaloaderIdargümanı eklenmesi ve renderer tarafındaloaderIDkontrolü- Komutun yalnızca tek bir origin için geçerli olması ve istemeden ayrıcalıklı sayfaya ulaşsa bile çalışmaması sağlandı
inspectedWindow.reload()fonksiyonunda URL kontrolü- Böylece yalnızca uzantı API erişiminin geri alınmasına güvenilmemiş oldu
- WebUI işleyicisinde test politikasının etkin olup olmadığının kontrol edilmesi
- Böylece test politikaları tamamen engellenmiş oldu
- Yarış durumuyla ilgili açık CVE-2024-5836 olarak atandı
- CVSS severity score değeri 8.8 High idi
- İncelenen sayfanın çökmesiyle ilgili açık CVE-2024-6778 olarak atandı
- Bu açık da 8.8 CVSS severity score aldı
- Düzeltmeler sürüm dalına birleştirildikten sonra Chrome VRP paneli ödülü belirledi ve nihai ödül $20,000 oldu
Açıklanma takvimi ve kaynaklar
- Zaman çizelgesi şöyleydi
- 16 Nisan: test policies hatası bulundu
- 29 Nisan:
inspectedWindow.reload()yarış durumu hatası bulundu - 1 Mayıs: açıklar Google’a bildirildi
- 4 Mayıs: Google, açıkları P1/S1 olarak sınıflandırdı
- 5 Mayıs: incelenen sayfanın çökmesiyle ilgili hata bulunup rapor güncellendi
- 6 Mayıs: Google, zincirin her parçası için ayrı hata raporları istedi
- 8 Temmuz: hata raporları fixed olarak işaretlendi
- 13 Temmuz: ödül kararı için Chrome VRP paneline iletildi
- 17 Temmuz: VRP paneli $20,000 ödül kararı verdi
- 15 Ekim: tüm hata raporları kamuya açıldı
- İlgili özgün hata raporu crbug.com/338248595 adresinde görülebilir
- Açığın her parçasına ait PoC’ler GitHub deposunda yayımlandı
inspectedWindow.reloadhatası Chrome v45’e kadar geri giderek çalışıyordu- Belgelenmemiş, tamamlanmamış ve güvensiz özellikler tüm kullanıcılara dağıtıldığında, basit hatalar birleşerek yüksek ciddiyette açıklara dönüşebilir
1 yorum
Hacker News yorumları
Sayfa URL’sinin
${url}ile değiştirildiği ve komutu bozmaması için#işaretinden sonrasına koyulursa yorum satırı olacağı söylenmiş; peki bu politikada URL’ninAlternativeBrowserParametersiçinde bir yerlere aktarılması gerektiğini doğrulayan bir mantık var mı?Programlama, web geliştirme ve siber güvenlikle ilgilenen bir lise öğrencisi olması gerçekten etkileyici
Sorumlu açıklama sürecine uymasıyla gösterdiği meslek etiği de harika; ileride çok büyük işler yapacak biri gibi görünüyor
Harika bir yazı ve çalışmaydı; keşifler ilerledikçe heyecanın nasıl biriktiğini birlikte takip ediyormuş gibi hissettirdi
Ödülü de fazlasıyla hak ediyor
Açıkların birbirine bağlanma süreci de temiz, yazı da harika. Savunmasız kodun nasıl çalıştığını parçalara ayırıp göstermesi de çok iyiydi
“Yeniden denemek için F12’ye basın” gibi basit numaralar her gördüğümde hayran bırakıyor; gerçekten çok muzipçe
Eskiden aynı API ile Chrome OS’in
croshkabuğunda hata ayıklayıp işletim sistemi korumalarını aşarak geliştirici cihazında root erişimi elde ettiğim bir olayı hatırlattı. CVE-2014-3172 idiAncak bu yazının yazarı çok daha zor engelleri aşmak zorunda kalmış; gerçekten harika bir çalışma
Gece çok geç olduğu için WebUI doğrulamasında neyin bozulduğunu derinlemesine incelemek zor, ama sonuna kadar gidip bunu çözmüş olması hoşuma gitti
Dağıttığımız şeylerin araç zincirinden şüphe etmek ve ona güvenmemek oldukça standart bir tutum; buna karşılık Google veya Microsoft gibi büyük şirketlerin sihirli derecede kullanışlı geliştirme araçlarına fazlasıyla güveniyoruz. Sonuçta Chromium’un ya da VSCode’un içinde ne saklı olduğundan endişelenmektense kendi kodumu yazıp test etmek istiyoruz
Okuduklarım arasında açık ara en iyi yazılardan biri
Gerçekten çok zekice bir iz sürme çalışmasıydı
Tarayıcı kodunu kurcalayıp buraya kadar bulma çabası müthiş; yazı da çok ilginç ve ayrıntılı
Lise öğrencisiymiş, vay be gerçekten harika
Chromium projesi,
chrome://net-internals’ı fazla karmaşık olduğu gerekçesiyle kaldırıp yerine yarım yamalak JSON düzenleme desteği eklenmişchrome://policysayfasını koydu