Aephix accepted into the Databricks Startup Program
← Research
Threat report Aug 22, 2026

Tracking down your Claude: claude-team-tracker steals OAuth tokens and installs a persistent command channel

claude-team-tracker, published on npm under the account fadilameen with roughly 2,900 lifetime downloads, poses as a team usage dashboard for Claude Code. On install it reads the Claude Code OAuth token from the credential file or the macOS Keychain, scrapes session data from every project directory, and exfiltrates everything to an external server. Persistent services installed via systemd and LaunchAgent survive logout, accept remote commands, and can run npm install -g with a server-controlled package name.

claude-team-tracker (v1.2.3, roughly 2,900 lifetime downloads, first published Jun 12, 2026) presents as a team usage dashboard for Claude Code. The npm package ships a working CLI with setup, sync, and daemon subcommands. The npm listing has no repository and no README. The code reads the Claude Code OAuth credential from the local file or the macOS Keychain, scrapes every session log under ~/.claude/projects/, uses the stolen token to pull account details from the Anthropic API, and exfiltrates everything to tracker[.]clawodoo[.]com. The first run installs persistent services via systemd and LaunchAgent that survive logout and long-poll the same server for commands, one of which runs npm install -g with a server-controlled package name.

The artifact

The account fadilameen (fadilameen63[@]gmail[.]com) has published this single package on npm. Fourteen versions shipped between Jun 12 and Aug 5, 2026. The declared dependencies are axios (HTTP client) and node-notifier (desktop notifications). The package.json wires a postinstall hook to bin/postinstall.js and exposes a claude-team-tracker CLI binary. The code is clean, readable JavaScript with no obfuscation, complete with inline comments explaining design choices like skipping a service restart during a self-update.

What it reads

The credential routine in lib/rate-limits.js reads the Claude Code OAuth token from two locations. On Linux it reads ~/.claude/.credentials.json directly. On macOS it calls /usr/bin/security find-generic-password with the service name Claude Code-credentials and the -w flag to extract the Keychain item without user interaction. The absolute path to the security binary is deliberate: the Keychain ACL trusts that specific binary, so the call returns the secret silently. A 5-second timeout with SIGKILL prevents the background daemon from hanging on an unexpected authorization dialog.

fetchRateLimits() and fetchProfile() in the same module use the stolen token to query api[.]anthropic[.]com. The rate-limits call hits /api/oauth/usage and pulls back five-hour and seven-day utilization windows with reset timestamps. The profile call hits /api/oauth/profile and extracts account_uuid, email, full name, organization UUID, organization name, organization type, and subscription tier. The profile is cached to ~/.claude/claude-tracker-profile.json with a 24-hour TTL keyed to the last 16 characters of the token.

The session scanner in lib/scanner.js walks ~/.claude/projects/ recursively, parses every .jsonl file, and extracts model names, token counts, session IDs, and timestamps. It respects CLAUDE_CONFIG_DIR and XDG_CONFIG_HOME if set. For each project directory it runs git branch --show-current to capture the active branch name. The scanner aggregates into daily, hourly, and minute-level usage breakdowns alongside per-session metadata: project path, branch, duration, model, and token totals.

How it persists

The postinstall hook in bin/postinstall.js refreshes the cron and systemd entries if the package is already configured, keeping the node binary path current after an update. If not configured and the install is global, it opens /dev/tty directly to bypass npm 10’s stdio pipe and spawns the setup wizard with the real terminal attached. The wizard sends the machine’s hostname and ID to tracker[.]clawodoo[.]com/api/devices/request-setup, prints a device-approval URL, and polls for confirmation for up to 10 minutes.

lib/setup.js installs two persistence mechanisms per platform once the approval comes back. On Linux it writes a crontab entry running sync every 10 minutes and creates a systemd user service (claude-team-tracker.service) with Restart=always and RestartSec=5s. The service runs the daemon subcommand, which long-polls the C2 for commands. loginctl enable-linger keeps the service alive after user logout.

On macOS, two LaunchAgent plists go into ~/Library/LaunchAgents/. com.claudetracker.plist fires the sync subcommand on a periodic interval. com.claudetracker.poll.plist runs the daemon with KeepAlive set to true, so launchd restarts it automatically if it exits.

How it leaves

lib/reporter.js POSTs the collected data to tracker[.]clawodoo[.]com/api/report. The payload includes the API key, device name, hostname, machine ID, platform, client version, daily usage, hourly usage, session windows, minute-level usage breakdowns, per-session metadata, rate limits, and the harvested account profile. The machine ID comes from /etc/machine-id on Linux, IOPlatformUUID via ioreg on macOS, wmic csproduct get uuid on Windows, or a random UUID stored at ~/.claude-tracker/machine-id as a fallback.

The report runs on every sync cycle, triggered every 10 minutes by cron or LaunchAgent, and on demand when the C2 sends a rescan command. Each report carries the full dataset from the latest scan, not a delta.

The command channel

lib/poller.js long-polls tracker[.]clawodoo[.]com/api/commands with a 35-second HTTP timeout. Three command types are implemented.

rescan triggers a full re-harvest: rebuild the usage data, re-read credentials, fetch rate limits and the account profile, and send a fresh report. This lets the operator pull a current snapshot on demand rather than waiting for the next cron cycle.

notification pushes a desktop notification to the machine via node-notifier. The title and body come from the command payload. The notification uses a bundled assets/logo.png as its icon.

update_client runs npm install -g claude-team-tracker@{version} where the version string comes from the server. The command resolves the npm binary from the same prefix as the running node process, falls back to npm on PATH, and executes with a 120-second timeout. Because the package name and version are server-controlled, this is arbitrary code execution: the server can point the install at any npm package. After a successful update, the process exits cleanly and the persistent service restarts the new binary automatically.

claude-team-tracker v1.2.3 npm, postinstall, systemd + LaunchAgent reads scans Claude Code OAuth token Credential file + macOS Keychain Session and profile data ~/.claude/projects/ + OAuth API POST /api/report tracker[.]clawodoo[.]com Commands: rescan, notification, update_client Aephix
The package reads the Claude Code OAuth token and session logs, then exfiltrates them to a C2 server that sends remote commands back, including arbitrary package installation via update_client.

What a defender can do

Search lockfiles and global installs for claude-team-tracker. If it was installed, treat the machine’s Claude Code OAuth session as compromised. Revoke the session at the identity provider rather than deleting the local credential file alone, because the token has already been exfiltrated.

Remove the persistence artifacts: ~/.config/systemd/user/claude-team-tracker.service on Linux, ~/Library/LaunchAgents/com.claudetracker.plist and ~/Library/LaunchAgents/com.claudetracker.poll.plist on macOS, and any crontab line containing claude-team-tracker or claude-tracker.js. Run systemctl --user disable claude-team-tracker and launchctl unload the two plists before deleting the files. Check whether loginctl enable-linger was enabled by the package and revert it if so.

Delete the configuration directory at ~/.claude-tracker/ and the cached profile at ~/.claude/claude-tracker-profile.json.

Setting ignore-scripts=true in .npmrc blocks the postinstall hook, which prevents the setup wizard from running and the persistence layer from deploying. The CLI still works if someone installs and configures it manually, but the automated install-to-exfil chain breaks.

Where Aephix fits

Credential theft and a persistent command channel ship inside a working usage CLI. Weekly Sleuth reports the week’s confirmed artifacts grouped by the operation behind them, so the next variant from this publisher or infrastructure reaches subscribers already linked to this one. Aephix Vantage gives you a free cross-ecosystem check before you install.

Indicators of compromise

TypeIndicatorContext
npm packageclaude-team-tracker (14 versions, v1.2.3 latest)Usage-tracking lure carrying credential theft and C2
npm accountfadilameen (fadilameen63[@]gmail[.]com)Single-package publisher
C2 servertracker[.]clawodoo[.]comDevice registration, exfiltration, command channel
C2 endpoint/api/reportExfiltration of credentials, sessions, profile
C2 endpoint/api/commandsLong-poll command channel (rescan, notification, update_client)
C2 endpoint/api/devices/request-setupInitial device registration and approval
Credential source (Linux)~/.claude/.credentials.jsonClaude Code OAuth credential file
Credential source (macOS)Keychain service Claude Code-credentialsRead via /usr/bin/security find-generic-password
API targetapi[.]anthropic[.]com/api/oauth/usageRate-limit data fetched with stolen token
API targetapi[.]anthropic[.]com/api/oauth/profileAccount profile harvested with stolen token
Persistence (Linux)~/.config/systemd/user/claude-team-tracker.servicesystemd user service, Restart=always
Persistence (Linux)crontab entry for claude-tracker.jsSync every 10 minutes
Persistence (macOS)~/Library/LaunchAgents/com.claudetracker.plistPeriodic sync LaunchAgent
Persistence (macOS)~/Library/LaunchAgents/com.claudetracker.poll.plistKeepAlive daemon LaunchAgent
Config directory~/.claude-tracker/Config, machine-id, sync and poll logs
Cached data~/.claude/claude-tracker-profile.jsonHarvested account profile, 24h cache TTL
Session scan~/.claude/projects/**/*.jsonlAll Claude Code session logs read recursively