Yüksek Bütünlüklü (High-Integrity) UI Erişimini Kullanarak Yönetici Koruması (Administrator Protection) Atlatma
🇹🇷 UI Erişimi (UI Access) kötüye kullanımı yoluyla UAC ve Yönetici Koruması (Administrator Protection) atlatmalarının kök nedenlerini ve Microsoft'un bu uzun süreli güvenlik zorluklarını nasıl ele aldığını öğrenin.
Bu yazı, Project Zero blogunda yayınlanan makalenin çevirisidir.
Son blog yazımda, yeni Windows özelliği Yönetici Koruması’nı (Administrator Protection) ve UAC için daha önce var olmayan güvenli bir sınır oluşturma amacını tanıtmıştım. Özellik yayınlanmadan önce onu atlatmanın yollarından birini açıklamıştım. Araştırmam sırasında toplamda 9 adet atlatma (bypass) buldum ve bunların hepsi artık düzeltildi.
Bu blog yazısında, bu 9 sorundan 5’inin kök nedenini, özellikle de UI Erişimi (UI Access) uygulamasını, bunun UAC ile ilgili nasıl uzun süredir devam eden ve yeterince önemsenmeyen bir sorun olduğunu ve şimdi nasıl düzeltildiğini açıklamak istiyorum.
Bir Erişilebilirlik Meselesi
Windows Vista’dan önce, bir kullanıcının masaüstünde çalışan herhangi bir işlem (process), bir başkası tarafından oluşturulan herhangi bir pencereyi kontrol edebiliyordu; örneğin pencere mesajları (window messages) göndererek. Bu davranış, SYSTEM gibi ayrıcalıklı bir kullanıcı masaüstünde bir kullanıcı arayüzü (UI) görüntülerse kötüye kullanılabilirdi. Kısıtlı bir kullanıcı UI’ı kontrol edebilir ve potansiyel olarak ayrıcalıklarını yükseltebilirdi. Bu, Shatter Saldırısı olarak adlandırılıyordu ve genellikle kullanıcı arayüzü bileşenlerinin ayrıcalıklı kodlardan çıkarılmasıyla düzeltiliyordu.
UAC, aynı masaüstünde farklı ayrıcalık seviyelerinde işlemlerin yürütülmesini teşvik ettiğinden, Microsoft ek bir özellik olan Kullanıcı Arayüzü Gizlilik İzolasyonu’nu (User Interface Privacy Isolation - UIPI) tanıttı. Bu özellik, bir işlemin hangi pencerelerle etkileşime girebileceğini sınırlamak için UAC’deki Zorunlu Bütünlük Denetimi (Mandatory Integrity Control) özelliğini kullanıyordu. Bir işlemin bütünlük seviyesi (integrity level), bir pencereyi oluşturan işlemin seviyesinden düşükse, o pencereye mesaj göndermek gibi işlemlerden engelleniyordu. Ek bir koruma olarak Vista, kullanıcı işlemlerini artık “servis” masaüstünde çalıştırmıyordu; böylece UIPI yetersiz kalsa bile, bir servis işlemi tarafından sunulan kullanıcı arayüzü kısıtlı işlemlere erişilemiyordu.
Örnek vermek gerekirse, kısıtlı bir kullanıcı işleminin atanan bütünlük seviyesi “Orta” (Medium) iken, bir UAC yönetici işlemininki “Yüksek” (High) değerindedir. Bu durumda UIPI, açıkça izin verilen küçük bir mesaj seti hariç, kısıtlı kullanıcı işleminin yönetici işlemi tarafından oluşturulan herhangi bir pencereye mesaj göndermesini engelleyecektir. Ayrıca pencere kancaları (windows hooks) gibi diğer UI işlevlerini de engelleyecektir.
Bu durum, ekran okuyucular gibi erişilebilirlik teknolojilerine güvenen kullanıcılar için bir sorun teşkil ediyordu. Eğer erişilebilirlik işlemi kısıtlı kullanıcı olarak çalışıyorsa, masaüstünde oluşturulan yönetici işlemleriyle artık etkileşime giremiyordu. Hem pencerelerin içeriğini okuması hem de bir düğmeye tıklamak gibi işlemleri gerçekleştirmesi engelleniyordu. Bu kabul edilebilir bir taviz değildi, bu nedenle Vista’nın bu uygulamaların çalışmaya devam etmesine izin verecek bir yola ihtiyacı vardı.
Microsoft’un seçtiği çözüm, bir işlemin erişim belgesi (access token) için UI Access adlı bir bayrak (flag) tahsis etmekti. İşlemin erişim belgesi, Win32 alt sistemiyle bağlantısını başlattığında bu bayrağa sahipse, işleme UIPI tarafından dayatılan birçok kısıtlamayı atlamak için özel izinler veriliyordu. TokenUIAccess bilgi sınıfı ile bir NtSetInformationToken çağrısı yaparak bu bayrağı etkinleştirmek, SE_TCB_NAME ayrıcalığı kontrolüne tabiydi ve bu nedenle kısıtlı bir kullanıcı tarafından gerçekleştirilemezdi. Dolayısıyla, UI Access yeteneğine sahip bir işlem oluşturmak için bayrağı etkinleştirecek ve yeni işlemi oluşturacak bir sistem servisi gerekiyordu.
UAC zaten bir sistem servisine ihtiyaç duyuyordu, bu nedenle bir UI Access işlemi oluşturmak, RAiLaunchAdminProcess RPC çağrısı yoluyla bir yönetici işlemini başlatmak için kullanılan akışın bir parçası haline getirildi. Bir UI Access işlemi bu RPC çağrısı yoluyla oluşturulduğunda, yönetici yükseltmesinin aksine onay istemi (consent prompt) göstermez. Bu önemlidir; aksi takdirde kullanıcının, yükseltme için onay istemine tıklamak üzere ihtiyaç duyduğu erişilebilirlik uygulamasını oluşturamama riski vardı.
Kötü amaçlı yazılımların sadece kendilerini bir erişilebilirlik uygulamasıymış gibi göstermelerini önlemek için servis, yeni işlemde UI Access bayrağını etkinleştirmek için karşılanması gereken bazı ek kontroller getirdi:
- Yürütülebilir dosyanın içinde,
uiAccessözniteliğitrueolarak ayarlanmış gömülü bir manifest bulunmalıdır. - Yerel makine kök sertifika deposu tarafından güvenilen bir kod imzalama sertifikası ile imzalanmış olmalıdır. Sertifika için bunun dışında özel bir gereksinim yoktu (örneğin özel bir EKU veya Microsoft tarafından çapraz imzalama gerekmiyordu).
- Sistem sürücüsünde yalnızca yöneticilere özel bir konumda saklanmalıdır, örneğin:
Program FilesdiziniWindowsdizini (bilinen bazı yazılabilir konumlar hariç)System32dizini (bilinen bazı yazılabilir konumlar hariç)
Tüm kriterler karşılanırsa, işlem RAiLaunchAdminProcess aracılığıyla başlatıldığında, servis çağıranın erişim belgesinin bir kopyasını alacak, UI Access bayrağını etkinleştirecek ve çağıran kişiye bağlı olarak bütünlük seviyesini şu şekilde artıracaktır:
- Çağıran bir UAC yöneticisinin kısıtlı kullanıcısı ise, bütünlük seviyesini “Yüksek” (High) olarak ayarlar.
- Çağıran bir yönetici ise, bütünlük seviyesini “Yüksek” (High) olarak ayarlar (normalde bu bir işlem yapmaz).
- Çağıran normal bir kullanıcıysa, bütünlük seviyesi çağıranın bütünlüğü artı 16 olacak şekilde (maksimum Yüksek’e kadar) ayarlanır.
Yüksek bütünlük seviyesi, ayarlanmasına izin verilen mutlak maksimumdur; ancak servis işlemleri için ayrılmış daha yüksek bir seviye olan “Sistem” (System) de mevcuttur. Ayrıca, çağıran kişi zaten UI Access bayrağına sahipse belgenin bütünlük seviyesinin değiştirilmediğini unutmayın; bu sadece otomatik olarak Yüksek bütünlük seviyesine ulaşamayan normal kullanıcılar için önemlidir. Yükseltilmiş bir bütünlük seviyesi ayarlamanın bir faydası da, oluşturulan işlemin daha düşük bütünlüklü bir işlem tarafından okuma veya yazma erişimi için açılamamasıdır. Bu, kısıtlı bir kullanıcının yeni işleme kod enjekte etmesini ve dolayısıyla UI Access bayrağına erişmesini engeller.
Not: Erişim belgesi üzerindeki UI Access bayrağını TCB ayrıcalığı olmadan devre dışı bırakabilirsiniz. Normal kullanıcı olarak çalışan geçerli bir UI Access işlemi, kendi belgesindeki bayrağı temizleyip ardından UAC servisi aracılığıyla kendisinin başka bir kopyasını yeniden başlatarak kendisini Yüksek bütünlüğe “kademeli olarak” (ratchet) çıkarabilir. Orta ve Yüksek arasında 4096 seviye olduğu için bu, UAC servisini 255 kez çağırmayı gerektirir ki bu biraz gürültülü bir yöntemdir ancak çalışır.
Önemli bir nokta; UI Access bayrağı sadece diğer daha yüksek bütünlüklü işlemlere pencere mesajları göndermek gibi kısıtlı bir dizi işleme izin verir. Bir işleme kod enjeksiyonuna izin veren pencere kancaları (windows hooks) gibi şeylerin kullanılmasına izin vermez. Bu nedenle, bütünlük seviyesi Yüksek’ten az olan normal bir kullanıcı olarak çalışan bir UI Access işlemi, başlatılan bir yönetici işlemiyle mesajlar aracılığıyla etkileşime girebilir ancak pencere mesaj kuyruğuna kanca atmak (hooking) gibi daha istilacı bir şey yapamaz.
Ancak, kısıtlı bir kullanıcı bir UI Access işlemi oluşturursa, bu işlem Yüksek bütünlük seviyesinde çalışır ve penceresi olan herhangi bir yönetici işlemini ele geçirebilir. Sistem bütünlük seviyesindeki bir servis işlemiyle yalnızca pencere mesajları kullanılarak etkileşime girilebilir. Ancak yönetici ile sistem servisi arasında bir güvenlik sınırı yoktur, bu nedenle bu pratikte anlamsızdır.
Sonuç olarak, Yüksek bütünlük seviyesine sahip olmadan sadece UI Access bayrağına sahip olmak, bir yönetici işlemini kolayca tehlikeye atmak için yeterli değildir. Ayrıcalıklı kodun çalışmasını sağlamak için otomatikleştirilebilecek bir kullanıcı arayüzü sunan bir işleme ihtiyacınız olacaktır. Örneğin, yönetici komut istemine rastgele bir komut çalıştırmak için tuş vuruşları gönderilebilir.
Ancak, hedef işlemle aynı bütünlük seviyesine sahipseniz, UI Access bayrağı önemsizleşir ve bir DLL enjekte etmek için pencere kancalarını kullanarak en az bir penceresi olan işlemi doğrudan tehlikeye atabilirsiniz. Bu pencerenin bir kullanıcı arayüzü sunması gerekmez; hatta COM gibi teknolojiler, kullanıcıya hiçbir şey göstermeden işlemi tehlikeye atmak için kullanılebilecek, arka planda sadece mesaj amaçlı pencereler (message-only windows) kullanır.
Elbette tüm bunlar UAC’de böyle çalışıyordu; peki ya yeni ve geliştirilmiş Yönetici Koruması (Administrator Protection)? Mevcut yönetici onaylı UAC ile tamamen aynı. UI Access işlemi, bu durumda gölge yönetici değil kısıtlı kullanıcı olacak olan çağıranın belgesi altında çalışacaktır. İşlem, etkinleştirilmiş UI Access bayrağına ve Yüksek olarak ayarlanmış bütünlük seviyesine sahip olacaktır.
Bu bir sorundur; Yüksek bütünlük seviyesinde çalışan bir işleme sahip olmak, aynı masaüstünde o işlem farklı bir kullanıcı olarak çalışsa bile, o seviyede çalışan başka herhangi bir işlemi tehlikeye atmanıza olanak tanır. UI Access işlemi kısıtlı kullanıcı olarak çalıştığı için, Yönetici Koruması’ndaki temel iyileştirmelerden biri olan profil ayrımı söz konusu değildir.
Yüksek bütünlüğe yükseltme işlemi de sessizdir, dolayısıyla sömürülecek uygun bir yönetici işlemi olduğu varsayılırsa, kullanıcıya herhangi bir uyarı vermeden güvenlik sınırını aşmak en azından teorik olarak mümkün olacaktır. Şimdi tek ihtiyacımız olan, Yüksek bütünlüklü bir UI Access işleminde rastgele kod çalıştırmanın bir yoludur. Neyse ki bunu yapmanın pek çok yolu var.
Keyfi UI Erişimi (UI Access) Yürütmesi Sağlamak
Yıllar boyunca, Yüksek bütünlük seviyesindeki bir UI Access işlemi olarak rastgele kod çalıştırmanın pek çok yolu bulundu. Microsoft bunların düzeltilmesinin bir öncelik olmadığını açıkça belirtmiş olsa da, bazen düzeltildiler. Bunları geçmişe dair bazı detaylar ve son araştırmalarım eşliğinde birkaç kategoride inceleyelim.
Güvenli Uygulama Dizini Kontrolünü Atlatmak
Rastgele kod çalıştırmanın bir yolu, UAC servisindeki güvenli dizin konumu kontrolünü atlatmaktır. Eğer bu kontrolü atlatabilirseniz, ya kendi imzalı yürütülebilir dosyanızı ya da örneğin DLL zararlısı yerleştirme (DLL planting) yoluyla suistimal edilebilecek mevcut bir yürütülebilir dosyayı o konuma yerleştirebilirsiniz.
Bir yaklaşım, kontrolü uygulayan appinfo.dll içindeki AiCheckSecureApplicationDirectory yönteminde bir hata bulmaktır. Bu yöntem önce RAiLaunchAdminProcess aracılığıyla sağlanan dosya yolunu açar, ardından yolun güvenli olmayan bir konuma yönlendirilmediğinden emin olmak için tutamaç (handle) üzerinde GetFinalPathNameByHandle çağrısı yapar. Daha sonra yol üzerinde dahil edilen ve hariç tutulan dizinleri arayan basit bir dize kontrolü yapar. 2017 yılında bu kontrol için bir atlatma buldum ve MSRC’ye bildirdim. Kontrol, kısıtlı kullanıcının o dizine yazma erişimi varsa bir dizine NTFS adlandırılmış akışı (named stream) yazmanın mümkün olduğunu hesaba katmıyordu.
Örneğin C:\Windows\tracing dizini kısıtlı bir kullanıcı tarafından yazılabilir durumdadır, ancak tracing alt dizini kontrolde açıkça hariç tutulmuştur, böylece C:\Windows\tracing\file.exe güvenli kabul edilmez. Ancak, aynı erişimle dizin üzerinde adlandırılmış bir akış yazabilirsiniz, böylece C:\Windows\tracing:file.exe, C:\Windows dizini içinde ve dolayısıyla güvenli kabul edilecektir. Bu hata bir güvenlik bülteni olarak düzeltilmedi ancak Windows’un sonraki bir sürümünde nihayet çözüldü ve Windows 11 için geçerli değil.
Başka bir yaklaşım, güvenli bir konumda kontrolde açıkça hariç tutulmayan yazılabilir bir dosya veya dizin bulmaktır. Yazılabilir bir dosya bulursanız, UAC servisi tarafından kullanılan CreateProcessAsUser API’si yürütülebilir dosya için belirli bir dosya uzantısına ihtiyaç duymadığından, onu yürütülebilir dosyanızla değiştirebilirsiniz. Bir dizin bulursanız, yürütülebilir dosyayı o konuma kopyalamanız yeterlidir.
Varsayılan bir kurulumda kapsam dışında kalan herhangi bir yer yok gibi görünüyor. Araştırma sırasında bulduğum bir konum, bazen büyük Windows güncellemelerinde Tasks dizininin yedek olarak Tasks_Migrated dizinine kopyalanmasıdır. Bu yedek dizin orijinal Tasks gibi yazılabilirdir ve hariç tutulan dizinler listesinde yer almıyordu. Ancak bunun oluşturulmasını zorlamanın bilinen bir yolu yoktur ve ben buna dikkat çektiğimden beri Microsoft bunu hariç tutulacak dizinler listesine ekledi.
Not: Microsoft, Tasks_Migrated üzerindeki adlandırılmış akışlar (named streams) için bir kontrol eklemeyi unuttu, ancak dizin üzerindeki erişim kontrolü nedeniyle normal bir kullanıcı olarak sömürülmesi mümkün değildir.
Potansiyel adayları bulmak için aşağıdaki komutu kullanarak PowerShell araçlarımı kullanabilirsiniz. En iyi sonuçlar için bunu yönetici olarak çalıştırın ve <PID> kısmını kısıtlı bir kullanıcı işleminin işlem kimliğiyle değiştirin. Hariç tutulan dizinleri filtrelemez, bu yüzden kendiniz kontrol etmelisiniz.
1
2
3
PS> $paths = "C:\Windows","C:\Program Files","C:\Program Files (x86)"
PS> Get-AccessibleFile -Win32Path $paths -Access Execute,WriteData `
-DirectoryAccess AddFile -Recurse -ProcessId <PID>
Son bir yaklaşım ise, o dizinin erişim kontrolünü atlamayı gerektirmeyen ayrı bir mekanizma aracılığıyla mevcut bir güvenli konuma dosya yazmanın bir yolunu bulmaktır. Son araştırmamda tam da böyle bir sorun buldum. Windows yükleyicisi MSIX dosyalarını, kontrol tarafından hariç tutulmayan C:\Program Files\WindowsApps dizinine yükleyecektir. Windows 11, yönetici ayrıcalıklarına ihtiyaç duymadan imzalı MSIX dosyalarının yüklenmesine izin verecek şekilde varsayılan olarak yapılandırılmıştır.
Bu nedenle bir UI Access yürütülebilir dosyasını bir MSIX yükleyicisi içine paketleyebilir, yükleyiciyi rastgele bir sertifika ile imzalayabilirsiniz; yüklendiğinde yürütülebilir dosya güvenli bir konumda olacaktır. Elbette bunu yapmak için bir kod imzalama sertifikasına ihtiyacınız olacaktır ancak bu göründüğü kadar büyük bir zorluk değildir. Eğer niyetliyseniz, imzalı UI Access yürütülebilir dosyasını bir mağaza uygulamasına bile sızdırabilirsiniz. Ancak WindowsApps dizini de artık hariç tutulduğu için bu sorun düzeltildi.
İlginç bir şekilde, MSIX’i oluştururken manifest’e ekleyebileceğiniz ve paketlenmiş yürütülebilir dosyayı Yüksek bütünlüklü UI Access’e yükseltecek bir uiAccess kısıtlı yeteneği (restricted capability) vardır. Ancak bunu yaptığınızda, paketin yüklenmesi aşağıda gösterildiği gibi yönetici ayrıcalıkları gerektirir ve bu nedenle bir atlatma değildir.
Mevcut Güvenli Bir UI Access Dosyasını Farklı Amaçla Kullanmak
İkinci bir kategori, güvenli bir konumda bulunan ve zaten UI Access yeteneğine sahip olan yürütülebilir bir dosyanın içinde kötüye kullanılabilecek işlevler bulmaktır. UI Access işleminin komut satırı üzerinde tam kontrole sahipsiniz; belki de rastgele bir DLL yükleme seçeneği vardır?
Sömürülebilir bir davranış bulmadan önce, tersine mühendislik yapabileceğiniz aday yürütülebilir dosyaları bulmanız gerekir. uiAccess manifest seçeneği true olarak ayarlanmış yürütülebilir dosyaları bulmak için PowerShell araçlarımı kullanabilirsiniz.
1
2
3
4
5
PS> $paths = "C:\Windows","C:\Program Files","C:\Program Files (x86)"
PS> Get-ChildItem -Path $paths -Include *.exe -Recurse |
% {
Get-Win32ModuleManifest $_.FullName
} | Where-Object UiAccess | Select-Object -ExpandProperty FullPath
Aday listesiyle birlikte biraz tersine mühendislik yapmanız gerekecek. Bunu size bırakıyorum.
Paylaşılan Profil ve Ortam
Yönetici Koruması’ndaki büyük değişikliklerden biri, kısıtlı kullanıcı ile yönetici arasındaki paylaşılan profilin ayrılmasıydı. Amaç, disk üzerindeki kullanıcı profilini veya kayıt defterini (registry) değiştirerek ayrıcalık yükseltilmesini önlemekti. Maalesef, UI Access işlemleri UAC’de olduğu gibi kısıtlı kullanıcı temel alınarak oluşturulduğu için bu ayrım geçerli değildir ve bu davranışı sömürmenin yollarını bulabilirsiniz.
Potansiyel olarak kötüye kullanılabilecek davranışları incelemenin basit bir yolu Process Monitor’u çalıştırmak ve kısıtlı kullanıcının kayıt defteri kovanına (registry hive) veya profil dizinine erişen olayları yakalamaktır. Oturum açma oturumu kısıtlı kullanıcı ile onun UI Access işlemleri arasında aynı olduğu için kullanıcının C: sürücüsü eşlemesi gibi şeyleri ele geçirmek de mümkündür.
Bu, UI Access ve UAC ile ilgili bilinen bir sorundur, bu nedenle Yönetici Koruması’nda bulduğumda bunu bildirmeme pek gerek yoktu ama bildirmem gerektiğini hissettim. Uygun şekilde ele alındığından emin olmak için sömürülebilir belirli bir durum buldum ve beraberinde bir kavram kanıtlama (PoC) gönderdim. Bu durumda, Ekran Klavyesi’nin (On-Screen Keyboard), CommonProgramFiles ortam değişkenine dayalı bir yoldan bir DLL yüklediğini buldum. Kullanıcının kayıt defteri kovanında bu değişkeni geçersiz kılarak (overriding), DLL yüklemesini yeniden yönlendirebilir ve UI Access işleminde rastgele kod çalıştırabilirdim.
Araştırmam sırasında, başlangıçta UAC için olan ancak Yönetici Koruması ile hala çalışan herkese açık bir atlatmaya rastladım. Bu atlatma, Windows 11’de isteğe bağlı bir bileşen gibi görünen ancak varsayılan olarak yüklü gelen Hızlı Yardım (Quick Assist) uygulamasındaydı. HTML içeriğini görüntülemek için WebView2 API’lerini yüklemesi gerçeğini suistimal ediyordu. WebView2, kütüphanesini yüklemek için kullanıcının kovanında geçersiz kılınabilir bir kurulum konumu arıyordu; bunu kullanıcının kontrolü altındaki bir konuma yönlendirerek, UI Access işlemine bir DLL yüklenmesini zorlamak mümkündü.
Bu atlatmanın en ilginç yönlerinden biri, UI Access işleminde rastgele kod yürütmek üzere bir pencere oluşturan işlemin çekirdek tutamacını (kernel handle) almak için varlığından haberdar olmadığım bir API olan GetProcessHandleFromHwnd API’sini kullanmasıdır.
RAiLaunchAdminProcess’i Suistimal Etmek
Bir UI Access işlemini başlatmak için kabuk (shell), UAC servisindeki RAiLaunchAdminProcess RPC yöntemini çağırır. Doğrudan sunulmayan veya belgelenmeyen API’lerde yaygın olduğu gibi, bunlar sömürülebilir davranışlara yol açabilecek gizli işlevler barındırabilirler.
Bir UI Access işleminde rastgele kod çalıştırmama izin veren iki sorun bildirdim; bunlardan biri herkes tarafından bilinen bir atlatma iken diğeri yürütülebilir dosya yolunun işlenmesindeki bir TOCTOU (Time-of-Check to Time-of-Use) hatasıydı. Herkese açık atlatma, yerel RPC yöntemlerini çağırmak için PowerShell araçlarımı kullanma hakkındaki bir blog yazımda tarafımdan açıklanmıştı. Verdiğim örnek, RAiLaunchAdminProcess‘i çağırmak ve servisin işlem oluşturma bayraklarını (process creation flags) temizlememesi durumunu suistimal etmek hakkındaydı.
DEBUG_PROCESS bayrağını geçirebilir ve bu sayede oluşturulan işlem üzerinde tam kontrol sahibi olabilirdiniz. Blog yazısı bunu bir UAC atlatma bağlamında açıklamıştı, ancak MSRC’ye gönderdiğim raporda detaylandırdığım gibi, bu durum UI Access işlemleri için de aynı derecede geçerliydi. Bu, Yönetici Koruması’nın geliştirilmesi sırasında düzeltilmemiş olmasından endişe ettiğim hatalardan biriydi, ancak sadece bir UAC atlatma olduğu için açıkça gözden kaçmıştı.
İkinci sorun, yürütülebilir dosya yolunun işlenmesindedir ve bu bizim bir UI Access işlemini ele geçirmemize olanak tanır. RAiLaunchAdminProcess RPC yöntemi, yeni işlemi oluşturmak için nihayetinde çağrılan CreateProcessAsUser API’sine çok benzer bir parametre setine sahiptir. Bu, oluşturulacak yürütülebilir dosyanın yolunu temsil eden ayrı bir dize ile yeni işleme geçirilecek komut satırını içerir.
Güvenli uygulama dizini kontrolü bölümünde açıkladığım gibi, doğrulama kullanıcı tarafından sağlanan güvenilmeyen yol dizesi kullanılarak yapılmaz; bunun yerine dosya açılır ve karşılaştırma yapmak için nihai çözümlenmiş yol (final resolved path) çıkarılır. Ancak, bu çözümlenmiş yol yalnızca kontrol sırasında kullanılır; işlem oluşturma aşamasında CreateProcessAsUser‘ın lpApplicationName parametresine orijinal güvenilmeyen yol geçirilir.
Örneğin, yürütülebilir dosya olarak Z:\osk.exe yolunu geçirdiyseniz, servis o yolu açmaya ve ardından nihai adı çözmeye çalışacaktır. Eğer Z: sürücüsü C:\Windows\system32 dizinine eşlenmişse, yürütülebilir dosyayı izin verilen güvenli bir dizin olan C:\Windows\system32\osk.exe konumunda bulacaktır. Ancak, Z:\osk.exe daha sonra lpApplicationName olarak CreateProcessAsUser‘a geçirilecektir.
Bu ne işe yarar? Yeni işlemin yerel DLL yüklemelerini kontrol edeceği bir temel dizine ihtiyacı vardır ve CreateProcessAsUser API’si, yürütülebilir dosya adı çıkarılmış haliyle lpApplicationName parametresini bu temel dizin için kullanır. Bu, Z:\osk.exe yolunu kullanarak bir UI Access işlemi başlatabileceğiniz anlamına gelir; işlem bilinmeyen DLL’leri önce Z:\ dizininden yüklemeye çalışacaktır. Eğer işlem oluşturulduğu an ile DLL’leri yüklemeye çalıştığı an arasında Z: sürücüsünü güvenilmeyen bir konuma yeniden eşlerseniz, işleme güvenilmeyen bir DLL’in yüklenmesini zorlayabilir ve rastgele kod çalıştırabilirsiniz. Bu kolaydır, çünkü RAiLaunchAdminProcess çağrılırken CREATE_SUSPENDED bayrağı geçirilerek UI Access işlemi askıya alınmış (suspended) olarak oluşturulabilir, sürücü yeniden eşlenebilir ve ardından işlem devam ettirilebilir.
Erişim Belgesi (Access Token) Çalma
Bahsedeceğim son kategori erişim belgesi çalmadır. Bu diğerlerinden biraz farklıdır çünkü genellikle ondan Yüksek bütünlük seviyesi alamazsınız; bunun yerine, pencereli kancalar gibi şeyleri suistimal etmek yerine daha yüksek bütünlüklü UI’yı kontrol etmek için kullanılabilecek, UI Access bayrağı etkinleştirilmiş bir işlem elde edersiniz.
Eski bir blog yazımda açıkladığım gibi, bir UI Access işlemi oluşturursanız, işlemin erişim belgesini açabilir, çoğaltabilir, bütünlük seviyesini Orta’ya düşürebilir ve son olarak bu belgeyi kullanarak yeni bir işlem oluşturabilirsiniz. Bu sürecin hiçbir adımında belgedeki UI Access bayrağı devre dışı bırakılmamıştı.
Blog yazısından sonra Microsoft bir değişiklik yaptı. Artık bir belgenin bütünlük seviyesi NtSetInformationToken sistem çağrısı yoluyla düşürülürse, UI Access bayrağı da devre dışı bırakılmaktadır. Bütünlük seviyesini Orta’ya düşüremezseniz, belgeyi taklit etmek (impersonate) veya yeni bir işlem için kullanmak mümkün olmaz ve böylece sorun hafifletilmiş olur.
Ancak, çekirdekte (kernel) NtSetInformationToken üzerinden geçmeyen ve dolayısıyla UI Access bayrağını devre dışı bırakmayan bazı yerlerin bütünlük seviyesini düşürdüğünü fark ettim. Bir seçenek, NtCreateLowBoxToken sistem çağrısı yoluyla bir App Container belgesi oluşturmaktı. Bu, bütünlük seviyesini Düşük (Low) olarak ayarlayacak ve yeni belgenin bir işlem oluşturmak için kullanılmasına izin verecektir. İşlem daha sonra bir App Container kum havuzunda (sandbox) çalışacak olsa bile, daha ayrıcalıklı işlemlere rastgele pencere mesajları göndermek için hala yeterliydi.
Windows Yönetici Korumasını (Administrator Protection) Sessizce Atlatmak
Şimdi Yüksek bütünlük seviyesindeki bir UI Access işleminde kod çalıştırdığımızı varsayalım. Yönetici Korumasını atlatmak için bunu nasıl sömürürüz? Yürütülmesi sırasında bir pencere oluşturan, gölge yönetici (shadow administrator) olarak sessizce oluşturulacak bir işleme ihtiyacımız var. Buradan, yeni işleme rastgele bir DLL yüklenmesini zorlamak için SetWindowsHookEx API’sini kullanabiliriz.
Bulduğum en iyi vektör, zamanlanmış görevlerin (scheduled tasks) çalıştırıldığında yönetici ayrıcalıklarıyla çalışacak şekilde yapılandırılabileceği gerçeğiydi. Bu durum Yönetici Koruması etkinleştirildiğinde hala çalışır, sadece görev işlemi gölge yönetici olarak çalışır. Etkinleştirilmiş, kısıtlı kullanıcı tarafından başlatılabilen ve yönetici ayrıcalıklarıyla çalışan bir göreve ihtiyacız var. Bulmak için PowerShell araçlarımın Get-AccessibleScheduledTask komutunu kullanabiliriz:
1
2
3
4
5
6
7
8
9
10
PS> Get-AccessibleScheduledTask -Access Execute |
? { $_.AllowDemandStart -and $_.Enabled -and ($_.RunLevel -eq "Highest") } |
Select-Object Name
Name
----
\Microsoft\Windows\DiskCleanup\SilentCleanup
\Microsoft\Windows\Input\LocalUserSyncDataAvailable
\Microsoft\Windows\Input\MouseSyncDataAvailable
...
Listedeki ilk görev olan SilentCleanup tarafımdan iyi bilinmektedir. Yürütülebilir dosyayı bulmak için kısıtlı bir kullanıcının geçersiz kılabileceği ortam değişkenlerini kullanması gerçeğini suistimal etmek gibi UAC’yi defalarca atlatmak için kullanılmıştır. Beklenmedik bir şekilde, ortam değişkenlerini işleme sorunu test ettiğim Yönetici Koruması sürümünde düzeltilmemişti, bu yüzden bunu ayrı bir sorun olarak bildirdim.
Ortam değişkenlerini işleme konusundaki sorunu görmezden gelirsek, bu görevi suistimal edebiliriz çünkü işlem çalıştığında bir pencere oluşturacaktır; bu nedenle sadece bir kanca (hook) kurarız, görevi başlatırız, kanca DLL’inizin cleanmgr.exe işlemi tarafından yüklenmesini bekleriz ve işte yönetici korumasını atlattınız. Bu yaklaşımı kullanan tam bir PoC’yi burada bulabilirsiniz.
Not: Eğer QuickAssist herkese açık atlatmasında olduğu gibi GetProcessHandleFromHwnd API’sini kullanmak istiyorsanız, pencere oluşturan işlem ile onun sonlanması arasındaki yarışı muhtemelen kazanmanız gerekecektir. Örneğin QuickAssist, görev işleminin belirli bir dosyayı açtığında asılı kalmasını sağlamak için dosya oplock’ları kullanır. Eğer pencere kancaları yaklaşımını kullanırsanız bunun için endişelenmenize gerek kalmaz.
Sonuçlar
Bildirdiğim tüm sorunlar düzeltildi, ancak bu bulunacak hiçbir şey kalmadığı anlamına gelmiyor. Umarım artık bir şeyler bulmak eskisinden biraz daha zordur. Ancak, UI Access etkin olsun ya da olmasın, Yüksek bütünlük seviyesindeki bir işlemde kod yürütmeyi başarabilirseniz, bunu Yönetici Korumasını atlatmak için kullanabileceğiniz hala bir gerçektir. Umarım artık bir UI Access işleminde kod yürütmeye izin veren herhangi bir bulgu, servis edilebilir bir güvenlik açığı olarak kabul edilir ve düzeltilir.
Yönetici Koruması’nın orijinal tasarımına göre UI Access işlemlerindeki büyük bir değişiklik, artık kısıtlı kullanıcı olarak çalışmamalarıdır. Bu değişiklik 437868751 numaralı sorunu düzeltmek için yapıldı. Bunun yerine, gölge yönetici belgesinin filtrelenmiş bir kopyasıyla oluşturulurlar. Bu, paylaşılan profil sorunlarını ortadan kaldırarak yönetici işlemleri ile kısıtlı kullanıcı arasında çok daha net bir ayrım sağlar.
Zaman, Yönetici Koruması’nın bir güvenlik sınırı olarak başarılı olup olmayacağını gösterecek. Microsoft bunu ciddiye alıyor, ancak geliştirme sırasında daha titiz testler yapılsaydı, önceden var olan pek çok UAC atlatmasının gözden kaçması önlenebilirdi. Bu özellikle ilgilenen herkese, artık yayınlandığı ve önceden bilinen hataların düzeltildiği şu dönemde bir göz atmasını tavsiye ederim.


