вторник, 25 апреля 2017 г.

Как заставить powershell загрузить CLR последней версии

Потребовалось мне получить некоторые имена функций из powershell, казалось бы качай pdb и дело в шляпе, ан нет.

Powershell это по сути фронтенд .NET Framework, со всеми вытекающими ввиде JIT и предварительно скомпилированными через Native Image Generator сборками. Сборки естественно генерятся под целевую платформу и логично, что символьные сервера микрософт никаким образом не могут содержать pdb для NI файлов.

Однако, в 4й версии фреймворка NGEN обзавелся параметром createpdb, и символы можно получить примерно так:

ngen.exe createpdb "C:\Windows\assembly\...\mscorlib.ni.dll" "C:\SymbolCache"

Соответственно, после получения символов задача должна быть решена. Но powershell подложил свинью, в виде загружаемого рантайма версии 2.0. А NGEN этой версии не имеет параметра для создания pdb. Далее пришлось раскапывать, как powershell узнает, какую версию рантайма грузить.

Недолгие поиски привели к реестру:

wmain => GetRegistryInfo(&NETversion) => RegQueryREG_SZValue(L"RuntimeVersion")

В реестре была видна версия 2.0:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine]
"ApplicationBase"="C:\\Windows\\System32\\WindowsPowerShell\\v1.0"
"PSCompatibleVersion"="1.0, 2.0"
"RuntimeVersion"="v2.0.50727"
"ConsoleHostAssemblyName"="Microsoft.PowerShell.ConsoleHost, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, ProcessorArchitecture=msil"
"ConsoleHostModuleName"="C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\Microsoft.PowerShell.ConsoleHost.dll"
"PowerShellVersion"="2.0"

Ради любопытства я посмотрел как именно грузится рантайм:

wmain => LaunchManagedMonad => CorBindToRuntimeEx(NETversion)

Ну и все, что оставалось сделать, это внести последнюю версию рантайма(v4.0.30319), и после небольшой возни с доступами к защищенной ветке powershell'а дело было сделано.

Меняем и сравниваем:

Было:  LoadLibraryExW(C:\Windows\assembly\NativeImages_v2.0.50727_32\mscorlib\fe70d777535c215f4fe9f9def2b4c815\mscorlib.ni.dll)
Стало: LoadLibraryExW(C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib\b7a12c4c0032847fcc6b9c710460456f\mscorlib.ni.dll)

Все имена функций прекрасно распознались:

Было:  0x792F123C (mscorlib.ni.dll)
Стало: System.AppDomain.SetupDomain(0x79A61212) (mscorlib.ni.dll)

четверг, 20 апреля 2017 г.

Another one EOP mitigation

В win10(10.0.15063) прикрыли еще одну возможность повышения привилегий, про первую я писал тут: http://kitrap08.blogspot.ru/2016/10/win10-securitydescriptor.html

Вторая заключается в обнулении не SecurityDescriptor'а, а флага Enabled в SEP_TOKEN_PRIVILEGES:

kd> dt nt!_TOKEN -r1
   +0x000 TokenSource      : _TOKEN_SOURCE
      +0x000 SourceName       : [8] Char
      +0x008 SourceIdentifier : _LUID
   +0x010 TokenId          : _LUID
      +0x000 LowPart          : Uint4B
      +0x004 HighPart         : Int4B
   +0x018 AuthenticationId : _LUID
      +0x000 LowPart          : Uint4B
      +0x004 HighPart         : Int4B
   +0x020 ParentTokenId    : _LUID
      +0x000 LowPart          : Uint4B
      +0x004 HighPart         : Int4B
   +0x028 ExpirationTime   : _LARGE_INTEGER
      +0x000 LowPart          : Uint4B
      +0x004 HighPart         : Int4B
      +0x000 u                : <unnamed-tag>
      +0x000 QuadPart         : Int8B
   +0x030 TokenLock        : Ptr32 _ERESOURCE
      +0x000 SystemResourcesList : _LIST_ENTRY
      +0x008 OwnerTable       : Ptr32 _OWNER_ENTRY
      +0x00c ActiveCount      : Int2B
      +0x00e Flag             : Uint2B
      +0x010 SharedWaiters    : Ptr32 _KSEMAPHORE
      +0x014 ExclusiveWaiters : Ptr32 _KEVENT
      +0x018 OwnerEntry       : _OWNER_ENTRY
      +0x020 ActiveEntries    : Uint4B
      +0x024 ContentionCount  : Uint4B
      +0x028 NumberOfSharedWaiters : Uint4B
      +0x02c NumberOfExclusiveWaiters : Uint4B
      +0x030 Address          : Ptr32 Void
      +0x030 CreatorBackTraceIndex : Uint4B
      +0x034 SpinLock         : Uint4B
   +0x034 ModifiedId       : _LUID
      +0x000 LowPart          : Uint4B
      +0x004 HighPart         : Int4B
   +0x040 Privileges       : _SEP_TOKEN_PRIVILEGES
      +0x000 Present          : Uint8B
      +0x008 Enabled          : Uint8B <===================== обнуляем, получаем полный доступ к процессу(EOP)
      +0x010 EnabledByDefault : Uint8B

Более подробный анализ тут: http://anti-reversing.com/Downloads/Sec_Research/ntoskrnl_v10.0.15063_nt!_SEP_TOKEN_PRIVILEGES-Single_Write_EoP_Protect.pdf

вторник, 31 января 2017 г.

Можно ли скрыть ключ в реестре изменив 1 бит?

Таким вопросом я задался когда-то давно, и тогда же получил ответ, а сейчас хочу его озвучить.

Как вообще скрывают ключи в реестре? Хуками, фильтрами и ... и похоже на этом моя фантазия исчерпалась. Но все это требует довольно много кода. Как это коррелирует с заголовком топика? Да никак.

Тем не менее, изменив 1 бит можно скрыть любой ключ реестра. Как это возможно?

Это возможно, если установить CM_KEY_CONTROL_BLOCK *keyCtrlBlock->TransKCBOwner не равным нулю, то есть достаточно установить 1 бит, чтобы сделать ключ реестра невидимым для regedit и системы в целом.

Как такое возможно? Все дело в транзакциях, именно так они работают.

NtOpenKey => CmpBuildHashStackAndLookupCache => CmpCacheLookup => CmRmIsKCBVisible => if (CM_TRANS *transOwner = keyCtrlBlock->TransKCBOwner ) != NULL ).

Собственно, в этом и кроется ответ, достаточно изменить один бит в keyCtrlBlock->TransKCBOwner и ключ реестра станет невидимым для любых существующих антивирусов и антируткитов.

Как это ловить? Да очень просто, перечисляем транзации, сравниваем их хендлы с keyCtrlBlock->TransKCBOwner, если не равно - руткит активность!

А можно ли обойти то, что описано выше? Естественно! Просто создаем валидную транзакцию, которая скрывает ключ реестра, но восстанавливает/удаляет ее когда нужно!

Что такое транзакции и как они работают я постараюсь описать в следующих постах.