Ad
Threats

Contagious Interview now ships malicious packages to npm, PyPI, Go, Rust, and PHP

Adam Bream
By Adam Bream , Tech Content Writer
Contagious Interview now ships malicious packages to npm, PyPI, Go, Rust, and PHP
Cover © Anonhaven

North Korea's Contagious Interview operation has expanded into five package registries in parallel. Socket disclosed on April 7 that a single threat actor cluster pushed staged loaders to npm, PyPI, Go Modules, crates.io, and Packagist. All twelve confirmed packages reused the same staging infrastructure and delivery patterns.

Socket's tracker for the broader Contagious Interview campaign now lists more than 1,700 malicious packages tied to the activity. This week's cluster is a small slice of that total. The cross-ecosystem coordination is the part that matters.

What was published, and where

The cluster published twelve confirmed malicious packages and two sleeper packages across five registries. They were uploaded under the GitHub aliases golangorg, aokisasakidev, and aokisasakidev1. A supporting persona network operated on maxcointech1010 and maxcointech0000.

npm. dev-log-core, pino-debugger, debug-fmt, and debug-glitz. Two additional packages, logkitx and logger-base, were sleepers — published under the same developer-tool disguise but not yet weaponized when Socket found them.

PyPI. logutilkit, apachelicense, fluxhttp, and license-utils-kit.

Go Modules. github.com/golangorg/formstash and github.com/aokisasakidev/mit-license-pkg.

crates.io. logtrace.

Packagist. golangorg/logkit.

Each package impersonated legitimate developer tooling. Socket lists debug, debug-logfmt, pino-debug, baraka, license, http, libprettylogger, and openlss/func-log as the libraries the cluster mimicked.

How the loaders work

Most packages follow the same loader workflow. They send an HTTP POST to https://apachelicense.vercel.app/getAddress?platform=<platform> and parse a JSON downloadUrl from the response. They then download a zip archive named ecw_update.zip. The archive extracts into a temp directory using the constant subdirectory name 410BB449A-72C6-4500-9765-ACD04JBV827V32V. Inside, the loader locates a platform-specific payload and executes it.

Payload filenames are chosen to blend with legitimate OS processes. Examples include com.apple.systemevents and systemd-resolved. The loaders also rewrite Google Drive sharing links into direct-download form via drive.usercontent.google.com.

The malicious entrypoint sits behind a routine-looking API. The threat actors did not generally rely on install-time execution. Instead the trigger hides inside a function that looks normal for the package's claimed purpose.

In logutilkit, the trigger sits inside the generic log() method of the logger class. It calls logutilkit_util.check_for_updates(level) before the actual logging call. In apachelicense and license-utils-kit, the trigger hides in find_by_key(). That is a believable helper for a license library, which on Windows launches a subprocess and on Linux/macOS hands execution to the shared loader.

The pattern repeats across languages with minor adjustments. In the Rust crate logtrace, the loader is concealed inside Logger::trace(i32), a method name that looks unremarkable inside a logging crate. In the Go module formstash, most of the code is a real multipart parser. It also exposes an unrelated CheckForUpdates(int) helper that fetches and executes the staged payload.

PHP gets the same treatment. In the Packagist package golangorg/logkit, real logging happens in write_log(), while the malicious path sits in an undocumented helper log_level($tag=1). On Windows, the PHP package fetches an additional encoded second stage from apachelicense.vercel.app/getAddress?platform=logmain&id=LOG, decodes it, and launches it with -c.

This technique evades casual code review. The malicious entrypoint is not where reviewers look. There is no install-script hook, no obvious post-install side effect. Only a logging call or a license lookup that quietly contacts an attacker-controlled endpoint when the consuming application runs.

The npm cluster uses a different loader

Node.js gets a distinct loader. The npm packages skip the zip archive entirely. Instead, packages such as dev-log-core fetch a base64-encoded JavaScript payload from the C2, decode it, and execute it in memory using new Function('require', decodedCode)(require). That hands the remote service arbitrary code execution inside the consuming Node.js process, with full access to require().

Infrastructure for the npm cluster differs too. Staging endpoints include ngrok-free.vercel.app, logkit.onrender.com, and logkit-tau.vercel.app. The underlying idea is the same. A remotely controlled loader hides behind a plausible library API.

The Windows-heavy variant

license-utils-kit is the most capable package in the set. On Linux and macOS it follows the familiar zip-staging workflow. On Windows it ships an obfuscated full post-compromise implant. Socket recovered the RAT command map from the deobfuscated payload.

self.command_handlers = {
    1: self.execute_shell_command,
    2: self.handle_delete_command,
    3: self.get_keylog_buffer,
    4: self.run_browser_stealer,
    5: self.handle_upload_command,
    6: self.kill_browsers,
    7: self.deploy_anydesk,
    8: self.upload_sensitive_files,
    9: self.create_encrypted_archive,
    10: self.run_additional_module
}

The implant supports remote shell execution, keylogging, browser and wallet theft, sensitive-file collection, encrypted archiving, and AnyDesk deployment for hands-on remote access. It also supports modular extension. This package introduces a second infrastructure cluster centered on the IP address 66.45.225.94.

Socket's assessment is that the cluster's primary objective is a RAT-enabled infostealer operation. The targets are credentials, browser data, password-manager contents, and cryptocurrency wallet data. The Windows-heavy variant goes beyond that baseline.

Why five ecosystems matters

Contagious Interview previously concentrated on npm, PyPI, and VS Code as initial access channels. This cluster extends the same playbook into Go Modules, crates.io, and Packagist. JavaScript, Python, Go, Rust, and PHP developers are now in the same target set served by a single threat actor cluster.

The consistency across ecosystems is the most significant finding in Socket's report. The same staging endpoints, archive names, extraction directory constant, and payload filenames recur across languages. The habit of hiding malware behind routine-looking methods recurs as well, with only minor adjustments per language. Socket frames this as a "factory model" approach in which the threat actors port the same loader design into new registries with minimal code changes.

Open source registries are no longer separate target surfaces. A single team can publish across five language ecosystems in parallel, treating each registry as an interchangeable initial access channel. Defenders organised around a single ecosystem are blind to the same operators on the other four.

This cluster lands the same week as two other DPRK-linked operations against open source. The UNC1069 hunt for npm maintainers hit the credentials angle, going after individual engineers behind Fastify, Lodash, and dotenv. The Axios compromise showed what happens when one of those credential hunts succeeds. Contagious Interview is the third leg, publishing malicious code directly without needing a maintainer to compromise.

The persona network

The investigation surfaced a persona structure rather than a single account. golangorg was the GitHub account most directly tied to the package cluster. It hosted log-base, dev-log, logger-base, logutilkit, formstash, and logkit.

aokisasakidev was identified by pivoting from npm maintainer metadata. Malicious packages including dev-log-core and logger-base pointed to aokisasakidev repository paths. The pivot also surfaced the Go module github.com/aokisasakidev/mit-license-pkg. Its only Go source file does not implement license functionality. It posts to apachelicense.vercel.app, downloads ecw_update.zip, and executes a server-controlled Python payload.

aokisasakidev1 was the npm persona under which dev-log-core was published, per Socket's screenshot caption.

maxcointech1010 served a different role. The account was populated with dozens of cloned or lightly modified repositories. They spanned AI projects, interview-themed applications, blockchain tooling, trading bots, and offensive utilities including obfuscators, shellcode loaders, wallet tools, and browser-data theft projects. Many repositories preserved the names, structure, and metadata of legitimate upstream projects. Socket assesses this profile as persona-building rather than package publishing. It stages plausible developer content that supports lures, code reuse, and social proof.

A closely related GitHub account, maxcointech0000, showed crossover with maxcointech1010 in repository paths and search results. Socket suggests this is a sibling namespace or prior alias. Two registration emails were tied to the cluster, aokisasaki1122@gmail.com and shiningup1996@gmail.com.

Independent corroboration

Socket's findings on the PyPI side are corroborated by independent reporting. Socket cites Bad Packages (kam193.eu) as having surfaced second-stage evidence with two SHA-256 hashes, 9a541dffb7fc18dc71dbc8523ec6c3a71c224ffeb518ae3a8d7d16377aebee58 and bb2a89001410fa5a11dea6477d4f5573130261badc67fe952cfad1174c2f0edd. Socket also cites Zscaler ThreatLabz as having identified an additional Python-based RAT payload, 7c5adef4b5aee7a4aa6e795a86f8b7d601618c3bc003f1326ca57d03ec7d6524. Socket's IoC table tags the three hashes as Linux, macOS, and Windows respectively.

Contagious Interview originated as a campaign of fake job interviews targeting developers. The lure was a "coding test" repository containing malware. This cluster represents a structural shift. The operation is no longer just social-engineering individual developers into running malicious "coding test" repositories. It now publishes malicious packages directly into shared registries that any developer can pull as a transitive dependency. The two access patterns now run in parallel under the same threat cluster.

Indicators of compromise

Type Indicator
C2 / staging domain apachelicense[.]vercel[.]app
C2 / staging domain ngrok-free[.]vercel[.]app
C2 / staging domain logkit[.]onrender[.]com
C2 / staging domain logkit-tau[.]vercel[.]app
C2 IP (Windows variant) 66[.]45[.]225[.]94
Archive name ecw_update.zip
Extraction directory 410BB449A-72C6-4500-9765-ACD04JBV827V32V
Payload (Unix-like) com.apple.systemevents
Payload (Unix-like) systemd-resolved
SHA-256 (Linux) 9a541dffb7fc18dc71dbc8523ec6c3a71c224ffeb518ae3a8d7d16377aebee58
SHA-256 (macOS) bb2a89001410fa5a11dea6477d4f5573130261badc67fe952cfad1174c2f0edd
SHA-256 (Windows) 7c5adef4b5aee7a4aa6e795a86f8b7d601618c3bc003f1326ca57d03ec7d6524
Registration email aokisasaki1122@gmail[.]com
Registration email shiningup1996@gmail[.]com
GitHub account github[.]com/golangorg
GitHub account github[.]com/aokisasakidev
GitHub account github[.]com/maxcointech1010
GitHub account github[.]com/maxcointech0000

Response and what to do

Socket reported all identified live malicious packages to the affected registries and submitted takedown requests for the associated GitHub accounts. The crates.io security team removed logtrace and the associated account, with the advisory tracked as RUSTSEC-2026-0081. The Go Security team blocked the identified malicious Go modules. The npm security team removed the aokisasakidev packages and the maintainer account.

Some packages had been taken down at the time of writing. Others remained live.

Treat utility packages as high risk if they contact remote infrastructure, retrieve a downloadUrl, rewrite cloud-storage links, download archives, decode remote content, or spawn interpreters from helper functions. Pin direct and transitive dependencies. Scrutinise newly published or low-download packages before adoption. Sandbox suspicious packages before they reach developer workstations or CI systems. Watch especially for unexpected child processes launched by loggers, parsers, lookup helpers, or "update" functions.

Defenders should preserve registry metadata, maintainer aliases, repository links, staging domains, archive names, extraction paths, and payload names. One malicious package can then be tied to adjacent personas, ecosystems, and new waves. The 410BB449A-72C6-4500-9765-ACD04JBV827V32V extraction directory and the ecw_update.zip archive name are both reliable cluster markers across the language samples in this campaign.

Table of Contents

Have a story? Become a contributor.

We work with independent researchers and cybersecurity professionals. Send us a tip or submit your article for editorial review.

Questions on the topic

What is the Contagious Interview cross-ecosystem supply chain attack?
A North Korea-linked operation that publishes malicious packages across npm, PyPI, Go Modules, crates.io, and Packagist from a single threat actor cluster. Socket's tracker lists 1,700+ malicious packages tied to the broader campaign.