Post

Kernel'deki Gölgeler: Windows 11 Yönetici Korumasını (Administrator Protection) Atlatmak

🇹🇷 Windows 11'in son sürümü olan 25H2 ile sunulan en önemli özelliklerden biri Yönetici Koruması (Administrator Protection). Bu özelliğin amacı, Kullanıcı Hesabı Denetimi'ni (UAC) daha sağlam ve en önemlisi, yerel bir kullanıcının yönetici ayrıcalıklarına yalnızca ihtiyaç duyulduğunda erişmesine izin veren güvenli bir sistemle değiştirmektir.

Kernel'deki Gölgeler: Windows 11 Yönetici Korumasını (Administrator Protection) Atlatmak

Bu yazı, Project Zero blogunda yayınlanan orijinal yazının çevirisidir.

Windows 11’in son sürümü olan 25H2 ile tanıtılan en önemli özelliklerden biri Yönetici Korumasıdır (Administrator Protection). Bu özelliğin amacı, Kullanıcı Hesabı Denetimi’ni (User Account Control - UAC) daha sağlam ve en önemlisi güvenliği sağlanabilir (securable) bir sistemle değiştirerek, yerel bir kullanıcının yönetici haklarına yalnızca gerektiğinde erişmesine izin vermektir.

Bu blog yazısında yeni özelliğe, nasıl çalıştığına ve UAC’den farklarına kısa bir genel bakış sunacağım. Ardından, Windows 11’in Insider Preview sürümlerinde bu özellik üzerinde yaptığım güvenlik araştırmalarından bahsedeceğim. Son olarak, bu özelliği sessizce atlatarak (bypass) tam yönetici ayrıcalıkları elde etmek için bulduğum dokuz ayrı zafiyetten birini detaylandıracağım. Microsoft’a bildirdiğim tüm sorunlar, ya özellik resmi olarak yayınlanmadan önce (isteğe bağlı KB5067036 güncellemesiyle) ya da sonraki güvenlik bültenleriyle giderildi.

Not: 1 Aralık 2025 itibarıyla Microsoft, bir uygulama uyumluluğu sorunu nedeniyle Yönetici Koruması özelliğini devre dışı bıraktı. Sorunun bu yazıda anlatılanlarla ilgili olması pek olası görünmediği için analiz değişmeden kalmıştır.

Yönetici Korumasının Çözmeye Çalıştığı Sorun

UAC, Windows Vista ile birlikte, kullanıcının işlemlerinin çoğu sınırlı ayrıcalıklarla çalışırken, kullanıcıya geçici olarak yönetici ayrıcalıkları verilmesini kolaylaştırmak amacıyla tanıtılmıştı. Ne yazık ki tasarım şekli nedeniyle kısa sürede bunun sert bir güvenlik sınırı (security boundary) temsil etmediği anlaşıldı ve Microsoft bunu bir “güvenlik özelliği” (security feature) seviyesine indirdi. Bu önemli bir değişiklikti çünkü artık sınırlı bir işlemin sessizce yönetici ayrıcalıkları kazanmasına izin veren UAC atlatma yöntemlerini düzeltmek bir öncelik olmaktan çıktı.

UAC tasarımındaki temel sorun, hem sınırlı kullanıcının hem de yönetici kullanıcının teknik olarak aynı hesap olması, sadece farklı grup ve ayrıcalık setlerine sahip olmasıydı. Bu, kullanıcı dizini ve kayıt defteri kovanı (registry hive) gibi profil kaynaklarını paylaştıkları anlamına geliyordu. Ayrıca bir yöneticinin işlemine ait erişim belirtecini (access token) açıp onu taklit etmek (impersonate) de mümkündü; çünkü taklit (impersonation) izni kontrolleri başlangıçta belirtecin “yükseltilmiş” (elevated) olup olmadığını değil, sadece kullanıcıyı ve bütünlük seviyesini (integrity level) dikkate alıyordu.

Yine de Vista’da sessizce yönetici hakları elde etmek o kadar kolay değildi, çünkü çoğu rota kullanıcıya bir onay istemi (prompt) gösteriyordu. Ne yazık ki Microsoft, sistem yapılandırmasını değiştirirken kullanıcının göreceği yükseltme uyarılarının sayısını azaltmaya karar verdi ve Windows 7’de “otomatik yükseltme” (auto-elevation) özelliğini tanıttı. Belirli Microsoft ikili dosyaları (binaries) otomatik olarak yükseltilecek şekilde seçilebiliyordu. Ancak bu, bazı durumlarda bu dosyaları kötüye kullanarak sessizce yönetici ayrıcalıkları kazanmanın mümkün olduğu anlamına geliyordu. UAC’yi her zaman uyarı gösterecek şekilde yapılandırmak mümkündü, ancak çok az kişinin değiştirdiği varsayılan ayarlar otomatik yükseltmeye izin veriyordu.

Bilinen atlatma yöntemlerinin iyi bir deposu, şu anda yönetici ayrıcalıkları elde etmek için 81 ayrı teknik listeleyen UACMe aracıdır. Microsoft, bir UAC atlatmasının ne zaman düzeltildiğini resmi olarak asla kabul etmese de, bu tekniklerin bir kısmı işletim sistemine gelen büyük güncellemelerle düzeltilmiştir. Ancak, Windows 11’in en son sürümünü etkileyen ve hala düzeltilmemiş sessiz atlatma yöntemleri mevcuttur.

Zararlı yazılımların yönetici ayrıcalıkları kazanmak için düzenli olarak bilinen atlatma yöntemlerini kullanıyor olması, Yönetici Koruması’nın çözmeyi hedeflediği şeydir. Eğer UAC’deki zayıflıklar azaltılabilirse, UAC atlatılması sadece daha fazla çalışma gerektiren değil, aynı zamanda uygulamadaki herhangi bir zafiyetin güvenlik sorunu olarak düzeltilebileceği güvenli bir sınır (secure boundary) haline getirilebilir.

Aslında UAC’nin kullanabileceği ve “yönetici onaylı” (admin approval) yükseltmenin yaşadığı sorunların çoğundan etkilenmeyen daha güvenli bir mekanizma zaten mevcut. Bu mekanizma, kullanıcı Administrators grubunun üyesi olmadığında kullanılır ve “omuz üstü” (over-the-shoulder) yükseltme olarak adlandırılır. Bu mekanizma, kullanıcının UAC yükseltme istemine girmesi gereken bir yerel yönetici kullanıcısının kimlik bilgilerini bilmesini gerektirir. Şu nedenlerle “yönetici onaylı” yükseltmeden daha güvenlidir:

  • Profil verileri artık paylaşılmaz, bu da sınırlı kullanıcının yükseltilmiş bir yönetici işlemi tarafından kullanılabilecek dosyaları veya kayıt defteri anahtarlarını değiştirmesini engeller.
  • Sınırlı kullanıcılar diğer kullanıcı hesaplarını taklit edemediği için, yönetici kullanıcısı için bir erişim belirteci alıp onu taklit etmek (impersonate) artık mümkün değildir.
  • Microsoft dosyalarının otomatik yükseltilmesi desteklenmez; tüm yükseltme istekleri bir onay istemi aracılığıyla onay gerektirir.

Ne yazık ki, bu mekanizmayı pratikte güvenli bir şekilde kullanmak zordur çünkü kimlik bilgilerini başka bir yerel yönetici hesabıyla paylaşmak büyük bir risk teşkil eder. Bu nedenle, öncelikle bir sistem yöneticisinin kullanıcının omuzu üzerinden kimlik bilgilerini yazdığı teknik destek durumlarında kullanışlıdır.

Yönetici Koruması, UAC servisi tarafından otomatik olarak yapılandırılan ayrı bir gölge yönetici (shadow administrator) hesabı kullanarak “omuz üstü” yükseltmeyi geliştirir. Bu, omuz üstü yükseltmenin tüm avantajlarına ek olarak şunları sağlar:

  • Gölge yönetici için herhangi bir kimlik bilgisi (parola) olmadığı için kullanıcının bunları bilmesine gerek yoktur. Bunun yerine UAC, istenirse biyometri dahil olmak üzere sınırlı kullanıcının kendi kimlik bilgilerini sorması için yapılandırılabilir.
  • Ayrı bir yerel yönetici hesabına gerek yoktur; sadece kullanıcının yöneticiler grubunun bir üyesi olarak yapılandırılması yeterlidir, bu da dağıtımı kolaylaştırır.

Microsoft, Yönetici Koruması’nı ayrı bir özellik olarak adlandırsa da, yükseltme işlemini gerçekleştirmek için aynı altyapıyı ve kodu (bazı ince ayarlarla) kullandığından aslında üçüncü bir UAC mekanizması olarak kabul edilebilir. Ancak bu özellik, yönetici onay modunun (admin-approval mode) yerini alır; yani “eski” modu ve Yönetici Korumasını aynı anda kullanamazsınız. Etkinleştirmek isterseniz şu anda buna yönelik bir kullanıcı arayüzü (UI) yok, ancak etkinleştirmek için yerel güvenlik politikasını değiştirebilirsiniz.

Büyük soru şu: Bu, UAC’yi güvenli bir sınır haline getirecek mi, böylece malware’ler artık bu kadar kolay at koşturamayacak mı? Gelin bir göz atalım ve öğrenelim.

Yönetici Korumasını Araştırmak

Genellikle yeni Windows özelliklerini yayınlanmadan önce araştırmaktan kaçınırım. Geçmişte, Insider Preview aşamalarında yeni bir özellikte güvenlik sorunu bulduğumda, bu hatanın daha sonra kaldırılan geçici kodlardan kaynaklandığı görüldüğünden bu her zaman iyi bir zaman kullanımı olmamıştır. Ayrıca güvenlik sorunları Insider Preview aşamasında düzeltilirse güvenlik bülteni yayınlanmaz, bu da bir şeyin ne zaman düzeltildiğini takip etmeyi zorlaştırır. Bu nedenle, özellikler yayınlanana kadar onları araştırmaya yönelik pek bir motivasyon yoktur; ancak o zaman bulunan hataların gerçek güvenlik sorunları olduğundan ve zamanında düzeltileceğinden emin olabilirim.

Bu durum biraz farklıydı; Microsoft, Insider Preview aşamasında uygulamanın içindeki sorunları bulmalarına yardımcı olup olmak istemediğimi sormak için bana ulaştı. Kuşkusuz bana ulaşmalarının bir nedeni, karmaşık mantıksal UAC atlatmaları bulma konusundaki geçmişimdi. Ayrıca, özelliğe zaten kısaca göz atmıştım ve loopback Kerberos istismarım gibi bazı iyi bilinen halka açık atlatmalara karşı özelliğin hala savunmasız olduğunu fark etmiştim.

Tam bir “pentest” yapmadan bir tasarım dökümanını incelemeyi ve geri bildirim sağlamayı kabul ettim. Ancak, Yönetici Korumasının güvenli bir sınır olması hedeflendiği için, herhangi bir sorun bulursam bunların bir bülten aracılığıyla düzeltileceğine veya en azından özelliğin final sürümünden önce giderileceğine dair güvence aldım.

Microsoft’un sağladığı belge genel bir bakış sunuyordu ancak tüm tasarım detaylarını içermiyordu. Örneğin, geliştiricilerin güvenlik sınırını ne olarak gördükleri konusunda bir sorum vardı. Otomatik yükseltmenin kaldırılmasına paralel olarak, sınırı aşmanın şunlardan bir veya daha fazlasını gerektireceği varsayımında bulundum:

  • Gölge yönetici profilini tehlikeye atmak (arbitrary dosya veya kayıt defteri anahtarı yazmak gibi).
  • Gölge yönetici olarak çalışan mevcut bir işlemi (process) ele geçirmek (hijacking).
  • Bir işlemi, onay istemi gösterilmeden yönetici olarak çalıştırmak.

Onay isteminin (prompt) bir sınır olması önemlidir. Yönetici Korumasında hala çalışacak olan, yükseltilmiş COM nesnelerine dayananlar gibi bir dizi UAC atlatma yöntemi vardır. Ancak otomatik yükseltmeye artık izin verilmediğinden, bunlar her zaman bir onay istemi gösterecektir; bu nedenle bunlar atlatma (bypass) sayılmaz. Tabii ki, onay isteminde gösterilenler (yükseltilen çalıştırılabilir dosya gibi), yönetici haklarıyla gerçekleştirilmek üzere olan işlemle her zaman birebir örtüşmeyebilir.

Belgede, UI Access işlemleri (bu serinin 2. bölümünde tartışılacaktır) gibi bazı ilişkili UAC özelliklerinin pek dikkate alınmadığı görülüyordu; ama yine de bazı açıklamalar dikkatimi çekti. Bu nedenle kendimi tutamadım ve Insider Preview’ın Canary sürümündeki mevcut uygulamaya en azından bir göz atmaya karar verdim. Bu araştırma, appinfo.dll içindeki UAC servis kodunun tersine mühendisliği ve davranışsal analizin bir karışımıydı.

Araştırmanın sonunda, özelliği atlatmanın ve sessizce yönetici ayrıcalıkları elde etmenin 9 ayrı yolunu buldum. Bazı atlatmalar, halka açık test senaryoları olan uzun süreli UAC sorunlarıydı. Diğerleri ise özelliğin kendi uygulama kusurlarından kaynaklanıyordu. Ancak en ilginç hata sınıfı, işletim sisteminin geri kalanı devreye girene kadar aslında bir hata olmayan durumdaydı.

Gelin araştırmada tespit ettiğim bu en ilginç atlatma yöntemine dalalım. Eğer ilerisini okumak isterseniz, tüm detayları issue tracker üzerinden okuyabilirsiniz. Bu konu ilginç, çünkü sadece korumayı atlatmama izin vermekle kalmadı, aynı zamanda uzun yıllardır bildiğim ancak sadece bu özelliğin tanıtılmasıyla pratik olarak istismar edilebilir hale gelen potansiyel bir UAC atlatma yöntemiydi.

Oturum Açma Oturumları (Logon Sessions)

Zafiyeti anlamak için öncelikle biraz temel bilgi. Bir kullanıcı Windows sisteminde başarılı bir şekilde kimlik doğrulaması yaptığında, kendisine benzersiz bir oturum açma oturumu (logon session) atanır. Bu oturum, kullanıcı hakkındaki bilgileri kontrol etmek için kullanılır; örneğin, ağ kimlik doğrulaması için kullanılabilecek kullanıcı kimlik bilgilerinin bir kopyasını tutar.

Oturum açma oturumu, oturum açma işlemi sırasında oluşturulan erişim belirtecine (access token) bir referans olarak eklenir, böylece belirteç kullanılarak yapılan herhangi bir kernel işlemi sırasında kolayca erişilebilir. Şunları yaparak oturum için benzersiz 64-bit kimlik doğrulama kimliğini (authentication ID) bulabilirsiniz: NtQueryInformationToken sistem çağrısını kullanarak belirteci sorgulamak. UAC’de, aşağıdaki betikte gözlemleyebileceğiniz gibi sınırlı (limited) ve bağlı yönetici (linked administrator) erişim belirteçlerine ayrı oturum açma oturumları atanır; sınırlı belirteç ve bağlı belirteç farklı LUID değerlerine sahiptir:

1
2
3
4
5
6
7
8
9
10
11
12
# Mevcut belirtecin kimlik doğrulama kimliğini al
PS> Get-NtTokenId -Authentication
LUID
----
00000000-11457F17

# Bağlı yönetici belirtecini sorgula ve kimlik doğrulama kimliğini al
PS> $t = Get-NtToken -Linked
PS> Get-NtTokenId -Authentication -Token $t
LUID
----
00000000-11457E9E

Oturum açma oturumunun kernel tarafından referans alındığı önemli bir yer, DOS sürücü harflerine bakıldığı zamandır. Kernel perspektifinden sürücü harfleri özel bir nesne dizine olan \?? içinde saklanır. Bu yol kernel tarafından arandığında, kernel önce kontrol edilecek oturuma özel bir dizin olup olmadığına bakar; bu, \Sessions\0\DosDevices\X-Y yolu altında tutulur (burada X-Y, oturumun kimlik doğrulama kimliğinin hexadecimal temsilidir). Sürücü harfi sembolik bağı (symbolic link) o dizinde bulunamazsa, kernel \GLOBAL?? dizinini kontrol etmeye geçer. Bu davranışı, NtOpenDirectoryObject sistem çağrısını kullanarak \?? nesne dizinini açarak gözlemleyebilirsiniz:

1
2
3
PS> $d = Get-NtDirectory "\??"
PS> $d.FullPath
\Sessions\0\DosDevices\00000000-11457f17

Bilindiği üzere, bir DOS aygıt nesne dizinine sembolik bağ yazabiliyorsanız, o oturumdaki o erişim belirteciyle çalışan herhangi bir işlemin C: sürücüsünü ele geçirebilirsiniz (hijack). C: sürücüsü global nesne dizininde tanımlanmış olsa bile, önce oturuma özel dizin kontrol edildiği için geçersiz kılınabilir (override).

Eğer bir kullanıcı başka bir oturum açma oturumunun DOS aygıt nesne dizinine yazabilirse, sistem sürücüsüne yapılan her türlü dosya erişimini yönlendirebilir. Örneğin, o oturum bağlamında çalışan bir işlemin bağlamında rastgele kod çalışmaya zorlamak için sistem DLL yüklemesini yönlendirebilirsiniz. UAC durumunda bu bir sorun teşkil etmez çünkü ayrı DOS aygıt nesne dizinleri farklı erişim kontrollerine (ACL) sahiptir ve bu nedenle sınırlı kullanıcı bir yönetici işleminin C: sürücüsünü ele geçiremez. Yöneticinin DOS aygıt nesne dizini için erişim kontrolü aşağıda gösterilmiştir:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PS> Get-NtTokenSid

Name           Sid
----           ---
DOMAIN\user    S-1-5-21-5242245-89012345-3239842-1001

PS> $d = Get-NtDirectory "\??"
PS> Format-NtSecurityDescriptor $d -Summary

<Owner> : BUILTIN\Administrators
<Group> : DOMAIN\Domain Users
<DACL>
NT AUTHORITY\SYSTEM: (Allowed)(ObjectInherit, ContainerInherit)(Full Access)
BUILTIN\Administrators: (Allowed)(ObjectInherit, ContainerInherit)(Full Access)
BUILTIN\Administrators: (Allowed)(None)(Full Access)
CREATOR OWNER: (Allowed)(ObjectInherit, ContainerInherit, InheritOnly)(GenericAll)

DOS Aygıt Nesne Dizini Oluşturma

Aklınıza gelebilecek bir soru, bu DOS aygıt nesne dizinini kimin oluşturduğudur? Görünüşe göre kernel, dizine ilk kez erişildiğinde onu talep üzerine (on demand) oluşturuyor. Oluşturma işlemini yapan kod SeGetTokenDeviceMap içindedir ve kabaca şuna benzer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
NTSTATUS SeGetTokenDeviceMap(PTOKEN Token, PDEVICE_MAP *ppDeviceMap) {
  *ppDeviceMap = Token->LogonSession->pDeviceMap;
  if (*ppDeviceMap) {
    return STATUS_SUCCESS;
  }

  WCHAR path[64];  
  swprintf_s(
      path,
      64,
      L"\\Sessions\\0\\DosDevices\\%08x-%08x",
      Token->AuthenticationId.HighPart,
      Token->AuthenticationId.LowPart);

  PUNICODE_STRING PathString;
  RtlInitUnicodeString(&PathString, path);
  OBJECT_ATTRIBUTES ObjectAttributes;
  InitializeObjectAttributes(&ObjectAttributes,
                               &PathString,
                               OBJ_CASE_INSENSITIVE |
                              OBJ_OPENIF |
                              OBJ_KERNEL_HANDLE |
                              OBJ_PERMANENT, 0, NULL);

  HANDLE Handle;
  NTSTATUS status = ZwCreateDirectoryObject(&Handle,
                                             0xF000F,
                                             &ObjectAttributes);
  if (NT_ERROR(status)) {
    return status;
  }

  status = ObpSetDeviceMap(Token->LogonSession, Handle);
  if (NT_ERROR(status)) {
    return status;
  }

  *ppDeviceMap = Token->LogonSession->pDeviceMap;
  return STATUS_SUCCESS;
}

Fark edebileceğiniz bir şey, nesne dizininin ZwCreateDirectoryObject sistem çağrısı kullanılarak oluşturulmasıdır. Kernel’da bir Zw sistem çağrısı kullanmanın önemli bir güvenlik detayı, OBJECT_ATTRIBUTES içinde isteğe bağlı OBJ_FORCE_ACCESS_CHECK bayrağı ayarlanmadıkça -ki burada durum böyle değil- güvenlik erişim kontrolünü devre dışı bırakmasıdır.

Bu kodun doğru çalışması için erişim kontrolünün (access check) atlanması gereklidir; gelin \Sessions\0\DosDevices dizininin erişim kontrolüne bakalım.

1
2
3
4
5
6
7
8
PS> Format-NtSecurityDescriptor -Path \Sessions\0\DosDevices -Summary

<Owner> : BUILTIN\Administrators
<Group> : NT AUTHORITY\SYSTEM
<DACL>
NT AUTHORITY\SYSTEM: (Allowed)(ObjectInherit, ContainerInherit)(Full Access)
BUILTIN\Administrators: (Allowed)(ObjectInherit, ContainerInherit)(Full Access)
CREATOR OWNER: (Allowed)(ObjectInherit, ContainerInherit, InheritOnly)(GenericAll)

Dizin, yönetici olmayan bir kullanıcı tarafından yazılamaz, ancak bu kod kullanıcının güvenlik bağlamında çağrıldığından, kullanıcının yönetici olduğundan emin olamadığı için dizini oluştururken erişim kontrolünü devre dışı bırakması gerekir. Önemli bir nokta, dizinin erişim kontrolünün tam erişim sağlayan özel CREATOR OWNER grubu için kalıtılabilir (inheritable) bir kurala sahip olmasıdır. Bu, nesne oluşturma sırasında kullanılan erişim belirtecinin atanan sahibiyle otomatik olarak değiştirilir.

Bu nedenle, erişim kontrolü devre dışı bırakılmış olsa bile, oluşturulan son dizine çağıran tarafından erişilebilir. Bu, UAC yönetici DOS aygıt nesne dizininin sınırlı kullanıcıya erişimi nasıl engellediğini açıklar. Yönetici belirteci, yerel yöneticiler grubu sahibi olarak ayarlanmış halde oluşturulur ve CREATOR OWNER grubu bununla değiştirilir. Ancak sınırlı kullanıcı sadece kendi SID’sini sahip olarak ayarlayabilir ve bu da sadece kullanıcıya erişim hakkı tanır.

Bu nasıl yararlı olabilir? Uzun zaman önce bu davranışın potansiyel bir UAC atlatma yöntemi olduğunu fark etmiştim (aslında bir Yerel Ayrıcalık Yükseltme - EoP potansiyeliydi, ama UAC atlatma en olası sonuçtu). Özellikle, NtQueryInformationToken çağrısını TokenLinkedToken bilgi sınıfıyla yaparak yönetici kullanıcısına ait erişim belirteci için bir handle elde etmek mümkündür. Güvenlik nedenleriyle bu belirteç SecurityIdentification taklit seviyesiyle (impersonation level) sınırlıdır, bu nedenle herhangi bir kaynağa erişim sağlamak için kullanılamaz.

Buna rağmen, belirteci taklit eder (impersonate) ve \?? dizinini açarsanız, kernel identification (tanımlama) belirtecini kullanarak SeGetTokenDeviceMap çağrısı yapacaktır ve eğer dizin henüz oluşturulmamışsa, DOS aygıt nesne dizinini oluşturmak için ZwCreateDirectoryObject kullanacaktır. Erişim kontrolü devre dışı bırakıldığı için oluşturma işlemi yine de başarılı olacaktır; ancak oluşturulduktan sonra kernel dizinin kendisi için bir erişim kontrolü yapacak ve identification belirteci taklit edildiği için başarısız olacaktır.

Bu bize pek bir şey kazandırmıyor gibi görünebilir; dizin oluşturulurken identification belirtecindeki sahibi kullanacaktır ki bu da yerel yöneticiler grubu olurdu. Ancak taklit işleminden önce belirtecin sahip SID’sini kullanıcının SID’si ile değiştirebiliriz; çünkü bu izin verilen bir işlemdir. Artık nihai DOS aygıt nesne dizini kullanıcıya ait olacak ve üzerine yazılabilecektir. UAC’nin yönetici tarafı için tek bir oturum açma oturumu kullanıldığından, yükseltilmiş herhangi bir işlemin C: dizini artık ele geçirilebilir.

Bunun bir UAC atlatma yöntemi olmasında tek bir sorun var: Sınırlı kullanıcının, herhangi bir yönetici işlemi oluşturulmadan önce kod çalıştırdığı bir senaryo bulamadım. İşlem oluşturulup çalışmaya başladığında, bir kodun illa ki bir dosya açması ve dolayısıyla \?? dizinine erişmesi neredeyse kesin bir ihtimaldir. Sınırlı kullanıcı kontrolü devraldığında, DOS aygıt nesne dizini zaten oluşturulmuş ve beklenen erişim kontrolü atanmış olur. Yine de UAC bir güvenlik sınırı olmadığı için bunu raporlamanın bir anlamı yoktu; bu yüzden bu davranışı bir gün ilgili olması durumunda kullanmak üzere bir kenara not ettim.

Yönetici Korumasını Atlatmak

Bugüne dönelim ve karşımıza Yönetici Koruması çıksın. Uyumluluk nedenleriyle Microsoft, NtQueryInformationToken çağrısının TokenLinkedToken ile yapılmasının hala yönetici belirtecine bir identification handle’ı döndürmesini sağladı. Ancak bu durumda bu, kullanıcının yönetici versiyonu yerine gölge yöneticinin belirtecidir. Kritik bir fark ise, UAC için bu belirtecin her seferinde aynı olması, Yönetici Korumasında ise kernel’ın LSA’yı çağırması ve gölge yöneticinin yeni bir örneğini doğrulamasıdır. Bu, TokenLinkedToken üzerinden döndürülen her belirtecin benzersiz bir oturum açma oturumuna sahip olmasıyla sonuçlanır ve bu nedenle aşağıda görülebileceği gibi şu anda DOS aygıt nesne dizini henüz oluşturulmamıştır:

1
2
3
4
5
6
7
8
9
10
PS> $t = Get-NtToken -Linked
PS> $auth_id = Get-NtTokenId -Authentication -Token $t
PS> $auth_id

LUID
----
00000000-01C23BB3

PS> Get-NtDirectory "\Sessions\0\DosDevices\$auth_id"
Get-NtDirectory : (0xC0000034) - Object Name not found.

Teoride artık DOS aygıt nesne dizinini oluşturmaya zorlayabilsek de ne yazık ki bu bize pek yardımcı olmuyor. UAC servisi de yeni işlemi oluşturacağı belirteci almak için TokenLinkedToken kullandığından, şu anda çalışan veya gelecekte çalışacak olan her yönetici işlemi oturum açma oturumlarını paylaşmaz; dolayısıyla aynı DOS aygıt nesne dizinlerini de paylaşmazlar ve biz de kendi işlemimizde sorguladığımız belirteci kullanarak onların C: sürücülerini ele geçiremeyiz.

Bunu istismar etmek için halihazırda çalışan gerçek bir işlemin belirtecini kullanmamız gerekir. Bu mümkündür çünkü yükseltilmiş bir işlem oluştururken askıya alınmış (suspended) olarak başlatılabilir. Bu askıya alınmış işlemle, okuma için işlem belirtecini açabilir, onu bir identification belirteci olarak kopyalayabilir ve ardından onu taklit ederken DOS aygıt nesne dizini oluşturabiliriz. İşlem daha sonra ele geçirilmiş C: sürücüsüyle devam ettirilebilir.

Bunun bir atlatma olmasında iki sorun var; birincisi, askıya alınmış bir yükseltilmiş işlem oluşturmak için bir yükseltme uyarısına tıklanması gerekecektir. Otomatik yükseltmeli UAC için bu bir sorun değildi, ancak Yönetici Koruması için her zaman uyarı verecektir ve bir uyarı göstermek güvenlik sınırını aşmak olarak kabul edilmez. Bunun etrafından dolanmanın yolları vardır; örneğin, UAC servisi yükseltilmiş bir ikili dosyayı sessizce çalıştıracak olan RAiProcessRunOnce API’sini sunar. Tek sorun, işlemin askıya alınmış olmamasıdır; bu nedenle işlemi açmak ve herhangi bir kod çalışmadan önce atlatmayı gerçekleştirmek için bir yarış durumunu (race condition) kazanmanız gerekir. Bu, yeni işlemin ana iş parçacığının zamanlanmasını önlemek için iş parçacığı öncelikleriyle (thread priorities) oynayarak yapılabilecek bir şeydir.

İkinci sorun daha ciddi görünüyor. Bir erişim belirteci için sahibini ayarlarken, sadece belirtecin kullanıcı SID’sini veya SE_GROUP_OWNER bayrağı ayarlanmış bir üye grubunu ayarlamanıza izin verir. Sahip bayrağına sahip tek grup yerel yöneticiler grubudur ve tabii ki gölge yöneticinin SID’si sınırlı kullanıcınınkinden farklıdır. Bu nedenle, oluşturma işleminden sonra dizine erişim söz konusu olduğunda bu SID’lerden herhangi birini sahip olarak ayarlamak bize yardımcı olmaz.

Görünüşe göre bu bir sorun değil çünkü sahip atama süreci hakkında tam doğruyu söylemiyordum. Yeni bir nesne için erişim kontrolü oluştururken, kernel identification (tanımlama) seviyesindeki taklit belirtecine güvenmez. Bunun iyi bir güvenlik nedeni vardır; bir identification belirtecinin erişim kontrolü kararları vermek için kullanılması beklenmez, bu nedenle nesneyi oluştururken sahibini atamak mantıklı değildir. Bunun yerine kernel, bu kararı vermek için işlemin birincil belirtecini (primary token) kullanır ve bu nedenle atanan sahip sınırlı kullanıcının SID’isidir. Aslında UAC atlatması için sahip SID’sini ayarlamak hiçbir zaman gerekli değildi, hiç kullanılmadı. İsimsiz bir nesne oluşturarak bu davranışı doğrulayabilirsiniz; böylece bir identification belirtecini taklit ederken oluşturulabilir ve atanan sahip SID’sini kontrol edebilirsiniz:

1
2
3
4
5
6
7
8
9
10
11
12
13
PS> $t = Get-NtToken -Anonymous

# Anonim belirteci taklit et ve dizin oluştur
PS> $d = Invoke-NtToken $t { New-NtDirectory }
PS> $d.SecurityDescriptor.Owner.Sid.Name
NT AUTHORITY\ANONYMOUS LOGON

# Identification seviyesinde taklit et
PS> $d = Invoke-NtToken $t -ImpersonationLevel Identification {
      New-NtDirectory
}
PS> $d.SecurityDescriptor.Owner.Sid.Name
DOMAIN\user

Aklınıza gelebilecek son bir soru, gölge yöneticinin belirteciyle bir işlem oluşturmanın, o kullanıcı olarak bir DOS sürücüsünün dosya kaynağına erişilmesine ve dolayısıyla DOS aygıt nesne dizininin oluşturulmasına neden nasıl olmadığıdır. CreateProcessAsUser API’sinin uygulanması, hangi erişim belirteci atanırsa atansın tüm kodunu çağıranın güvenlik bağlamında çalıştırır, bu nedenle varsayılan olarak yeni oturum açma oturumu altında bir dosya açmaz.

Bununla birlikte, bir sistem servisinde güvenli bir şekilde nasıl işlem oluşturulacağını biliyorsanız, kullanıcının erişemeyeceği bir çalıştırılabilir dosya için işlem oluşturmasına izin vermediğinizden emin olmak için CreateProcessAsUser çağrısı üzerinden yeni belirteci taklit etmeniz (impersonate) gerektiğini bekleyebilirsiniz. UAC servisi bunu doğru yapıyor, bu yüzden işlemi oluşturmak için bir sürücüye erişmiş olmalı ve DOS aygıt nesne dizini oluşturulmuş olmalıydı, neden olmadı?

Küçük bir ironi olarak, gerçekleşen şey şudur: UAC servisi, sistem servisinde düşük ayrıcalıklı bir kullanıcıyı taklit ederken C: sürücüsünün ele geçirilmesini önlemek için yakın zamanda tanıtılan bir güvenlik önlemine takılıyor. Bu önlem, bir sistem çağrısını yapan SYSTEM kullanıcısıysa ve C: sürücüsüne erişmeye çalışıyorsa devreye girer. Bu, Microsoft tarafından manifest dosyası ayrıştırmasındaki birden fazla zafiyete yanıt olarak eklendi; genel bir bakış isterseniz işte bir video, Maddie Stone ile OffensiveCon 23’te saldırı yüzeyini anlattığımız konuşma.

İlginç bir şekilde, UAC servisi de SYSTEM olarak çalışıyor ve yükseltilmiş çalıştırılabilir dosya C: sürücüsünde olduğu sürece (ki bu çok muhtemel), önlem taklit edilen belirtecin DOS aygıt nesne dizinini tamamen görmezden geliyor. Böylece SeGetTokenDeviceMap hiçbir zaman çağrılmaz ve dolayısıyla oturum açma oturumu altında bir dosyaya ilk kez erişilmesi ancak işlem çalışmaya başladığında gerçekleşir. Yeni işlem bir dosyaya dokunmadan önce istismarı gerçekleştirebildiğimiz sürece DOS aygıt nesne dizini oluşturabilir ve işlemin C: sürücüsünü yönlendirebiliriz.

Sonuç olarak, bu atlatmayı istismar etme adımları şöyledir:

  1. C: sürücüsünden runonce.exeyi çalıştıracak olan RAiProcessRunOnce aracılığıyla bir gölge yönetici işlemi başlatın.
  2. Yeni işlemi bir dosya kaynağına erişmeden önce açın ve birincil belirteci sorgulayın.
  3. Belirteci bir identification belirteci olarak kopyalayın (duplicate).
  4. Gölge yönetici belirtecini taklit ederken DOS aygıt nesne dizininin oluşturulmasını zorlayın. Bu, NtOpenDirectoryObject çağrısı yoluyla \?? açılarak yapılabilir.
  5. Sistem sürücüsünü ele geçirmek için yeni DOS aygıt dizininde bir C: sürücü sembolik bağı (symlink) oluşturun.
  6. İşlemin devam etmesine izin verin ve yönlendirilmiş bir DLL’in yüklenmesini bekleyin.

Son Düşünceler

Bu atlatma ilginçti çünkü ona neden olan spesifik bir hatayı göstermek zor. Zafiyet, 5 ayrı işletim sistemi davranışının bir sonucudur:

  • Yönetici Koruması özelliğinin TokenLinkedToken sorgusuna getirdiği değişiklikler, her gölge yönetici belirteci için yeni bir oturum açma oturumu oluşturur.
  • Belirteç başına DOS aygıt dizini her yeni oturum açma oturumu için geç başlatılır (lazy initialisation); yani bağlı belirteç ilk oluşturulduğunda dizin henüz mevcut değildir.
  • Kernel, DOS aygıt dizine erişildiğinde onu erişim kontrolünü devre dışı bırakan Zw fonksiyonlarını kullanarak oluşturur. Bu, sınırlı bir kullanıcının gölge yönetici belirtecini identification seviyesinde taklit etmesine ve \?? açarak dizini oluşturmasına olanak tanır.
  • Bir iş parçacığı identification seviyesinde bir belirteci taklit ederse, herhangi bir güvenlik tanımlayıcısı (security descriptor) ataması sahip SID’sini taklit edilen belirteçten değil, birincil belirteçten alır. Bu, sınırlı kullanıcıya gölge yönetici belirtecinin DOS aygıt nesne dizini üzerinde tam erişim verilmesiyle sonuçlanır.
  • Düşük ayrıcalıklı kullanıcı işlem belirtecine eriştiğinde DOS aygıt nesne dizini henüz oluşturulmamış olur; çünkü bir SYSTEM işleminde C: sürücüsünden dosya açarken taklit edilen DOS aygıt nesne dizinini devre dışı bırakan güvenlik önlemi devrededir.

Microsoft’u testler sırasında bu sorunu bulamadığı için suçlamıyorum. Birçok hareketli parçası olan karmaşık bir zafiyet. Muhtemelen sadece DOS aygıt nesne dizini oluşturulurkenki garip davranışı bildiğim için bunu bulabildim.

Microsoft’un uyguladığı düzeltme, bir gölge yönetici belirtecini identification seviyesinde taklit ederken DOS aygıt nesne dizininin oluşturulmasını engellemek oldu. Bu düzeltme, isteğe bağlı KB5067036 güncellemesinin bir parçası olarak yayınlanan nihai sürüme eklendiği için onunla ilişkili bir güvenlik bülteni bulunmuyor. Yönetici Koruması ekibine ve MSRC’ye (Microsoft Güvenlik Yanıt Merkezi) tüm sorunları düzeltmedeki hızlı yanıtları ve bu özelliğin bir güvenlik sınırı olarak ciddiye alınacağını gösterdikleri için teşekkür ederim. Ayrıca araştırma aşamasında yardımcı olan tasarım belgesi gibi ek bilgileri sağladıkları için de onlara teşekkür etmek isterim.

Bir özellik olarak Yönetici Koruması hakkındaki görüşlerime gelince; Microsoft’un olabileceği kadar cesur davranmadığını hissediyorum. UAC’de küçük ince ayarlar yapmak, özellikte güvenlik zafiyeti olarak kendini gösteren neredeyse 20 yıllık düzeltilmemiş atlatma yöntemlerini de beraberinde taşıdı. Görmek istediğim, belki de bir kullanıcının belirli görevler için belirli ek erişimlere sahip olabildiği düzgün bir sudo sürümü veya Linux yetenekleri (capabilities) gibi daha yapılandırılabilir ve kontrol edilebilir bir şeydi.

Sanırım uygulama uyumluluğu nihayetinde buradaki temel sorun; Windows böylesine radikal bir değişiklik için tasarlanmamış. Ayrıca bunun yönetici onayını tamamen değiştirmesi yerine ayrı bir yapılandırılabilir mod olarak sunulmasını da isterdim. Bu sayede, bir sistem yöneticisi herkesin bu modeli kullanmasını zorunlu kılmak yerine, insanların ne zaman yeni modele dahil edileceğini seçebilirdi.

Varsayılan olarak etkinleştirildiği varsayılırsa, yönetici onaylı UAC’ye göre güvenliği artırdığını düşünüyorum. Daha ciddi tasarım hataları bulunmadıkça savunulabilir olması gereken daha önemli bir güvenlik sınırı sunuyor. Malware’lerin, kullanıcıyı yükseltme uyarısını kabul etmeye zorlayarak bile olsa yönetici haklarını hala elde edebileceğini bekliyorum; ancak kullanabilecekleri herhangi bir sessiz atlatma yönteminin düzeltilmesi gerekecek ki bu, mevcut duruma göre önemli bir gelişme olacaktır. Tüm bunlardan bağımsız olarak, Windows’u kullanmanın en güvenli yolu, herhangi bir UAC sürümü olsun veya olmasın, asla yönetici haklarıyla (admin) çalışmamaktır. Ve ideal olan, makinenize malware bulaşmasından en baştan kaçınmaktır.

This post is licensed under CC BY 4.0 by the author.