• XSS.stack #1 – первый литературный журнал от юзеров форума

Перехват управления в ядре при помощи Interrupt Stack Table (IST)

varwar

El Diff
Забанен
Регистрация
12.11.2020
Сообщения
1 383
Решения
5
Реакции
1 537
Пожалуйста, обратите внимание, что пользователь заблокирован
В общем пришла мне в голову идея с выполнением шеллкода в ядре через IST. По аналогии с модификацией стека в KTHREAD.StackBase.

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

x86_64 also has a feature which is not available on i386, the ability to
automatically switch to a new stack for designated events such as double fault
or NMI, which makes it easier to handle these unusual events on x86_64. This
feature is called the Interrupt Stack Table (IST). There can be up to 7 IST
entries per CPU. The IST code is an index into the Task State Segment (TSS). The
IST entries in the TSS point to dedicated stacks
; each stack can be a different
size.
An IST is selected by a non-zero value in the IST field of an interrupt-gate
descriptor. When an interrupt occurs and the hardware loads such a descriptor,
the hardware automatically sets the new stack pointer based on the IST value,
then invokes the interrupt handler
[1]

ist_1.png

Вкратце мысленный эксперимент заключался в том, чтобы:

1. Получить примитив записи при помощи эксплойта
2. Записать в KUSER_SHARED_DATA полезную нагрузку в виде ROP-цепочки.
3. Найти таблицу IST в ядре.
4. Запатчить адрес возврата в одной из записей в IST для одного из прерываний на адрес ROP-цепочки
5. Вызвать исключение, стригеррив обработчик прерывания в надежде, что тот будет каким-либо образом использовать подмененные данные и передаст управление на ROP.

Как это устроено в Windows? Судя по описанию, таблица IST находится в TSS. Точнее в структуре _KTSS64.

Код:
kd> dx -id 0,0,fffff8050cd48f40 -r1 ((ntkrnlmp!_KTSS64 *)0xfffff8050f9e7000)
((ntkrnlmp!_KTSS64 *)0xfffff8050f9e7000)                 : 0xfffff8050f9e7000 [Type: _KTSS64 *]
    [+0x000] Reserved0        : 0x0 [Type: unsigned long]
    [+0x004] Rsp0             : 0xfffff8050f9ea200 [Type: unsigned __int64]
    [+0x00c] Rsp1             : 0x0 [Type: unsigned __int64]
    [+0x014] Rsp2             : 0x0 [Type: unsigned __int64]
    [+0x01c] Ist              [Type: unsigned __int64 [8]]
    [+0x05c] Reserved1        : 0x0 [Type: unsigned __int64]
    [+0x064] Reserved2        : 0x0 [Type: unsigned short]
    [+0x066] IoMapBase        : 0x68 [Type: unsigned short]

Непосредственно IST.

Код:
kd> dx -id 0,0,fffff8050cd48f40 -r1 (*((ntkrnlmp!unsigned __int64 (*)[8])0xfffff8050f9e701c))
(*((ntkrnlmp!unsigned __int64 (*)[8])0xfffff8050f9e701c))                 [Type: unsigned __int64 [8]]
    [0]              : 0x0 [Type: unsigned __int64]
    [1]              : 0xfffff8050f9ea3d0 [Type: unsigned __int64]
    [2]              : 0xfffff8050f9ea5d0 [Type: unsigned __int64]
    [3]              : 0xfffff8050f9ea7d0 [Type: unsigned __int64]
    [4]              : 0xfffff8050f9ea9d0 [Type: unsigned __int64]
    [5]              : 0x0 [Type: unsigned __int64]
    [6]              : 0x0 [Type: unsigned __int64]
    [7]              : 0x0 [Type: unsigned __int64]

По описанию все так. 7 записей, нулевая запись не используется. Следующие четыре стека - это стеки для четырех прерываний.

Код:
0: kd> !idt
Dumping IDT: fffff8050f9e6000

01:    fffff8050cae5180 nt!KiDebugTrapOrFaultShadow    Stack = 0xFFFFF8050F9EA9D0
02:    fffff8050cae5240 nt!KiNmiInterruptShadow    Stack = 0xFFFFF8050F9EA7D0
08:    fffff8050cae5540 nt!KiDoubleFaultAbortShadow    Stack = 0xFFFFF8050F9EA3D0
12:    fffff8050cae59c0 nt!KiMcheckAbortShadow    Stack = 0xFFFFF8050F9EA5D0

Т.е. эти стеки предопределены. Каждый из этих стеков имеет права на запись, но когда стал умышленно корраптить их и пытаться вызвать исключение, то ничего не происходило. В ntoskrnl.exe я не нашел обращений к полю KPCR.KtssBase и такое чувство, что этот механизм не используется. Но на каком-то этапе KTSS инициализируется же. В общем здесь пока тупик. Может быть у кого есть идеи на этот счет?

P.S. В исходниках Windows XP я также не нашел обращений к полю Ist.

ktss64_2.png


P.P.S. Нашел обращение к KtssBase в KiExceptionDispatch.

1719522934137.png


Странно, ибо искал по этому паттерну и получил бублик.

1. https://android.googlesource.com/ke...8f4df9/Documentation/x86/x86_64/kernel-stacks
2. Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3 (3A, 3B, 3C, & 3D): System Programming Guide
4. https://www.kernel.org/doc/Documentation/x86/kernel-stacks
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Продолжаем копать. IST используется KVASом. Инициализация происходит в цепочке KiEnableKvaShadowing->KiInitializeDescriptorIst при загрузке системы.

Код:
0: kd> k
 # Child-SP          RetAddr               Call Site
00 ffff8986`c6006218 fffff802`16e7c2a1     nt!KiInitializeDescriptorIst
01 ffff8986`c6006220 fffff802`16c33b78     nt!KiEnableKvaShadowing+0x7d
02 ffff8986`c6006260 fffff802`16f40b73     nt!KiInitializeProcessorState+0x1cc
03 ffff8986`c60062b0 fffff802`16f2e285     nt!KeStartAllProcessors+0x29f
04 ffff8986`c6006a00 fffff802`16c2f473     nt!Phase1InitializationDiscard+0x579
05 ffff8986`c6006bb0 fffff802`167088b7     nt!Phase1Initialization+0x23
06 ffff8986`c6006bf0 fffff802`1682d324     nt!PspSystemThreadStartup+0x57
07 ffff8986`c6006c40 00000000`00000000     nt!KiStartSystemThread+0x34

Каждая запись IST описывается структурой KIST_BASE_FRAME, а каждый стек равен 0x200 байт.

Код:
kd> dt nt!_KIST_BASE_FRAME 0xFFFFF8021B47A9D0
   +0x000 KernelGsBase     : 0xfffff802`13dfd000 _KPCR
   +0x008 IstStack         : 0xfffff802`1b4b3fe0 _KIST_LINK_FRAME
   +0x010 PreviousGsBase   : 0xfffff802`13dfd000
   +0x018 PreviousCr3      : 0x1aa002
   +0x020 IstPad           : 0
   +0x028 Reserved         : 0

Пока не совсем понятно какие данные можно подделать, чтобы перехватить управление.

Когда скорраптил стек для KiMcheckAbortShadow, то после 10-15 секунд зависания система выдала bugcheck - KMODE_EXCEPTION_NOT_HANDLED

Код:
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

KMODE_EXCEPTION_NOT_HANDLED (1e)
This is a very common BugCheck.  Usually the exception address pinpoints
the driver/function that caused the problem.  Always note this address
as well as the link date of the driver/image that contains this address.
Arguments:
Arg1: ffffffffc0000005, The exception code that was not handled
Arg2: fffff8021676f66a, The address that the exception occurred at
Arg3: 0000000000000000, Parameter 0 of the exception
Arg4: ffffffffffffffff, Parameter 1 of the exception

3: kd> k
 # Child-SP          RetAddr               Call Site
00 ffffd881`a47308d8 fffff802`169587c2     nt!DbgBreakPointWithStatus
01 ffffd881`a47308e0 fffff802`16957eb3     nt!KiBugCheckDebugBreak+0x12
02 ffffd881`a4730940 fffff802`168284c7     nt!KeBugCheck2+0xba3
03 ffffd881`a47310b0 fffff802`1693946a     nt!KeBugCheckEx+0x107
04 ffffd881`a47310f0 fffff802`1683362f     nt!HvlpVtlCallExceptionHandler+0x22
05 ffffd881`a4731130 fffff802`1660c1b3     nt!RtlpExecuteHandlerForException+0xf
06 ffffd881`a4731160 fffff802`16610027     nt!RtlDispatchException+0x2f3
07 ffffd881`a47318d0 fffff802`16829582     nt!KiDispatchException+0x317
08 ffffd881`a4731fb0 fffff802`16829550     nt!KxExceptionDispatchOnExceptionStack+0x12
09 ffffd881`a4797a88 fffff802`1683dbf5     nt!KiExceptionDispatchOnExceptionStackContinue
0a ffffd881`a4797a90 fffff802`16838c83     nt!KiExceptionDispatch+0x135
0b ffffd881`a4797c70 fffff802`1676f66a     nt!KiGeneralProtectionFault+0x343
0c ffffd881`a4797e08 fffff802`1676f4e0     nt!KiRspInIstStack+0x3a
0d ffffd881`a4797e10 fffff802`16835d47     nt!KiMcheckFastForward+0x50
0e ffffd881`a4797e50 fffff802`16823fff     nt!KiNmiInterrupt+0x207
0f ffff8986`c6068558 fffff802`167ca5a4     nt!HalProcessorIdle+0xf
10 ffff8986`c6068560 fffff802`16627407     nt!PpmIdleDefaultExecute+0x14
11 ffff8986`c6068590 fffff802`1662626b     nt!PpmIdleExecuteTransition+0x1137
12 ffff8986`c6068a50 fffff802`1682d174     nt!PoIdle+0x67b
13 ffff8986`c6068c40 00000000`00000000     nt!KiIdleLoop+0x54

Если точнее, то падение происходит при разыменовывании стека в KiRspInIstStack.

C:
// In the KiMcheckFastForward
if ( KiRspInIstStack(2u, v5) { // 2 is the index of the KiMcheckAbortShadow stack
    ...
    }
// Skipped ...

C:
__int64 __fastcall KiRspInIstStack(unsigned int stack_idx, unsigned __int64 a2)
{
    unsigned int v2; // r8d
    __int64 v3; // r9
    _KIST_BASE_FRAME *stack; // rcx
    unsigned __int64 v5; // rcx

    v2 = 0;
    v3 = 0x6000i64;
    stack = KeGetPcr()->TssBase->Ist[stack_idx];
    if ( KiKvaShadow )
        v3 = 0x1D0i64;
    if ( a2 <= stack && a2 >= stack - v3 )
        return 1i64;
    if ( !KiKvaShadow )
        return 0i64;
    v5 = stack->IstStack;                       // Dereferencing stack memory leads to bugcheck
    if ( a2 > v5 )
        return 0i64;
    LOBYTE(v2) = a2 >= v5 - 0x5FE0;
    return v2;
}

В данном случае указатель стека был 0x4242424242424242.
В целом тут дохрена разных проверок, возможно их можно все обойти, подделав структуру стека _KIST_BASE_FRAME, но пока не вижу каких-либо интересных указателей в стеке.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх