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.
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
| Type | Indicator | Context |
|---|---|---|
| npm package | claude-team-tracker (14 versions, v1.2.3 latest) | Usage-tracking lure carrying credential theft and C2 |
| npm account | fadilameen (fadilameen63[@]gmail[.]com) | Single-package publisher |
| C2 server | tracker[.]clawodoo[.]com | Device registration, exfiltration, command channel |
| C2 endpoint | /api/report | Exfiltration of credentials, sessions, profile |
| C2 endpoint | /api/commands | Long-poll command channel (rescan, notification, update_client) |
| C2 endpoint | /api/devices/request-setup | Initial device registration and approval |
| Credential source (Linux) | ~/.claude/.credentials.json | Claude Code OAuth credential file |
| Credential source (macOS) | Keychain service Claude Code-credentials | Read via /usr/bin/security find-generic-password |
| API target | api[.]anthropic[.]com/api/oauth/usage | Rate-limit data fetched with stolen token |
| API target | api[.]anthropic[.]com/api/oauth/profile | Account profile harvested with stolen token |
| Persistence (Linux) | ~/.config/systemd/user/claude-team-tracker.service | systemd user service, Restart=always |
| Persistence (Linux) | crontab entry for claude-tracker.js | Sync every 10 minutes |
| Persistence (macOS) | ~/Library/LaunchAgents/com.claudetracker.plist | Periodic sync LaunchAgent |
| Persistence (macOS) | ~/Library/LaunchAgents/com.claudetracker.poll.plist | KeepAlive daemon LaunchAgent |
| Config directory | ~/.claude-tracker/ | Config, machine-id, sync and poll logs |
| Cached data | ~/.claude/claude-tracker-profile.json | Harvested account profile, 24h cache TTL |
| Session scan | ~/.claude/projects/**/*.jsonl | All Claude Code session logs read recursively |