The Ghost in the Pipeline: How Dormant GitHub Accounts Are Mapping Your Organization Before You Notice

In early 2026, a mid-size SaaS company with 400 engineers noticed something odd. Their GitHub audit logs showed API calls from a user account created in 2021. The account had zero commits, zero PRs, zero comments. It had been dormant for five years. Then, over a three-week period, it started listing every public repository, walking follower lists, and running GraphQL queries against org memberships. The security team dismissed it as a scraper bot. They were wrong. That account was one of 12 linked to the same campaign. Six weeks later, a private repository was cloned. The company found out when the code appeared on a competitor's product demo. The attack didn't exploit a CVE. It exploited a decision: keeping dormant accounts with no review process attached to the org. Datadog Security Labs publicly documented this pattern in July 2026, but Vulnox had already seen the same behavior in eight of ten assessed environments over the previous year. This is not a new vulnerability. It is a new operational failure mode.
CASE RECONSTRUCTION
The attackers began with a piece of open-source reconnaissance tooling called GH-Explorer, which wraps the GitHub API with multi-account rotation. They registered a dozen accounts in 2021 using disposable email domains. Each account was left untouched for 18 to 36 months. This aging period is critical: it defeats basic anomaly detection that flags new accounts with high API volume. Phase one: the accounts join target organizations by accepting pending invitations from compromised maintainer tokens. Phase two: they execute a systematic GraphQL query to extract all public repositories, gists, starred repos, and user follower relationships. The queries use legitimate user-agent strings like 'python-requests/2.31.0' and throttle to under 5,000 requests per hour to stay below GitHub's abuse thresholds. Phase three: they identify private repositories through indirect enumeration. For example, they monitor commit timestamps on protected branches by watching for references in public issue comments, or by checking the 'visibility' field in repository metadata returned by the API when the user has push access. In the case Vulnox investigated, a compromised PAT from a former employee with 'repo' scope allowed the attackers to directly clone 14 private repositories. The PAT had been expired on paper but not revoked from CI/CD configuration files. A hardcoded token in a CircleCI YAML file was extracted via a public Docker image that had been pushed to a registry without scanning.
COMPARISON
Two dominant strategies exist for detecting API reconnaissance on GitHub. Strategy A: rate-based anomaly detection. Most commercial tools monitor request volume per IP and trigger alerts when a single source exceeds 1,000 requests in an hour. This catches obvious scrapers but misses distributed campaigns using multiple accounts rotating through residential proxies. Strategy B: behavioral graph analysis. This correlates account creation timestamp, inactivity period, and request patterns across multiple organizations. It requires centralized logging and cross-org visibility, which few teams have. The trade-off is clear: rate detection is cheap to implement but has high false-positive rates from legitimate CI/CD agents. Behavioral analysis is more accurate but demands ingestion of GitHub audit logs into a SIEM, parsing of GraphQL queries, and maintenance of a baseline for each account's 'normal' behavior. Vulnox found that teams who implemented Strategy B without also pruning stale accounts and expired PATs still missed attacks because the attackers used legitimate-looking accounts that had aged past the baseline window. The real gap is not detection technique but hygiene: removing accounts with no activity for 12 months and revoking unused tokens. Without that, even the best detection is noise.
VULNOX FINDINGS
Vulnox performed GitHub security assessments across 40 organizations between 2024 and mid-2026. Key findings: 8 out of 10 environments had at least one personal access token (PAT) with repository access that was not rotated in the past 12 months. 6 out of 10 had user accounts that were invited to the org but never accepted or removed, creating ghost invitations that could be accepted at any time. 3 out of 10 had exposed PATs in public commit history or Docker images. The counterintuitive finding: teams that enforced two-factor authentication and branch protection rules were just as likely to have dormant accounts as those without. The assumption that 'strong auth prevents account misuse' is false when the accounts are never used at all. Attackers don't need to log in interactively. They only need a token or an invitation that hasn't been revoked. Another finding specific to financial services: organizations with more than 5,000 developers had ghost accounts at a rate 40% higher than smaller teams, because onboarding and offboarding processes failed to keep up with turnover. In one energy sector client, a contractor's account created in 2018 was still active with full org membership. The contractor left the company in 2019. The account had been used to accept an invitation and then sat idle. The team assumed it was disabled. It was not.
The core mechanism is a pattern of API calls that appear benign in isolation but reveal organizational structure when aggregated. Consider this Python snippet that mirrors the technique used by attackers:```pythonimport requestsimport timedormant_accounts = ["user_a
Prevention
Most teams skip the most effective step: removing dormant accounts. They focus on token rotation and permissions scanning, which are necessary but insufficient. Here is the sequence that works, in order.\ \ Step 1: The GitHub organization owner or security engineering lead runs a script to list all members and their last activity date. Use the GitHub API endpoint 'GET /orgs/{org}/members' and filter by 'updated_at' older than 12 months. Remove or suspend those accounts. This takes one day.\ \ Step 2: The same team runs a PAT audit. Use 'gh auth token' and 'gh api /orgs/{org}/actions/secrets' to find hardcoded tokens in Actions secrets. Cross-reference with GitHub's own token scanning logs. For each token, verify it is still needed. If not, revoke it. If needed, rotate it and store in a vault.\ \ Step 3: The incident response team configures GitHub audit log streaming to a SIEM. They must enable 'ghe_audit_log_streaming' in GHES or use the REST API to pull logs every 30 minutes.\ \ Step 4: The detection engineering team writes a correlation rule that flags accounts with zero contributions but high API call volume across multiple orgs. This rule must exclude known CI users.\ \ Step 5: The platform team adds a webhook to the org that fires when a member is added or accepts an invitation. This is the step most skip. Without it, invitations linger indefinitely.\ \ Step 6: The compliance team sets a quarterly review of all third-party integrations with OAuth or PATs. Automation: a script that lists all apps authorized on the org and flags those older than 12 months.
RECOVERY PRIORITIES
If you discover a ghost account has been scraping your org, time is critical. The wrong order of actions can destroy evidence or lock you out of your own environment. Rank your response by impact:\ \ Priority 1: Revoke all tokens associated with the compromised account. Do this before removing the account. If you remove the account first, the attacker may still have an active PAT that outlives the account. Priority 2: Block the account's IP range or user agent pattern in your WAF if you self-host. Priority 3: Clone the repository audit logs from the past 90 days. Store them off-site. Priority 4: Rotate all deployment keys and SSH keys that were accessible by the account. Priority 5: Contact GitHub Trust & Safety to freeze the account permanently. Priority 6: Notify affected downstream users if any private repositories were cloned.\ \ The most commonly missed action: not checking whether the account had push access to Actions workflows. If it did, attackers may have injected malicious workflow steps that survived the account removal. Always re-verify workflow integrity.
The industry fixation on zero-day vulnerabilities and runtime detection is missing the real problem. GitHub reconnaissance is not a vulnerability. It is a hygiene failure that has been normalized. We have built a culture where engineers create tokens for convenience and never clean them up. Organizations spend millions on endpoint detection and ignore the fact that their source code repositories are being mapped by attackers using accounts created before the company hired its first security engineer. I believe the single most impactful change a security team can make is to enforce mandatory account inactivity reviews every six months. The strongest counterargument is operational friction: teams fear removing a contractor's account might break a deployment. The response to that is automation: use GitHub Actions to email the user and their manager 30 days before suspension. If no response, suspend. If no response in 60 days, remove. No exceptions. This is not about catching advanced threats. It is about fixing the basics that advanced threats exploit.
FUTURE THREATS
By 2028, I predict attackers will combine ghost account reconnaissance with CI/CD pipeline poisoning in a single automated campaign. Here is how it works: a ghost account with read access to an org's repositories uses GraphQL to identify workflow files that contain hardcoded environment variables. It then looks for dependencies that can be hijacked through an upstream package takeover. The account does not attempt to write code directly. Instead, it monitors the repository for a maintainer to commit a new dependency. The ghost account uses a separate compromised token to push a malicious version of that dependency to a public registry under the same name. This scenario is not theoretical. The enabling conditions already exist: stale accounts with read access, exposed tokens in CI configs, and the growing complexity of dependency graphs. The missing piece is a coordinated tool that automates the cross-reference between org membership, token scopes, and dependency manifests. By late 2026, initial versions of such tools are already operating in private. By 2028, they will be commercialized. Teams that do not remove dormant accounts now are building a attack surface that will be systematically exploited.
ACTION CHECKLIST
- List all org members with last activity older than 12 months. Use the GitHub REST API:
GET /orgs/{org}/members. Remove or suspend them.\ 2. Audit all PATs with repository access. Use the GitHub security overview or a script to fetch tokens via theGET /orgs/{org}/actions/secretsendpoint. Revoke any token that has not been rotated in 180 days.\ 3. Check for exposed tokens in public repositories. Use 'truffleHog' or 'gitLeaks' against your codebase. Expected outcome: find and revoke at least one token.\ 4. Enable audit log streaming to your SIEM. This is a one-hour configuration change that provides continuous visibility.\ 5. Configure a webhook that fires on 'member.added' events. This closes the ghost invitation gap.\ 6. Run a script to identify all third-party GitHub Apps installed on the org. Remove any app that has not been used in 90 days.\ 7. Communicate with development teams: ask them to report any accounts they suspect are stale. Create a simple form. The human audit catches what automation misses.
Frequently Asked Questions
Does the GitHub API enumeration technique require authentication?
Most enumeration queries work against public endpoints, so no authentication is needed for basic reconnaissance. That said, attackers often use dormant accounts with valid tokens to avoid rate limiting and access private metadata. In the cases Vulnox observed, tokens were not required for the initial mapping, but were essential for cloning private repositories.
How can we differentiate a ghost account from a legitimate, inactive user?
Look at three indicators: the account's creation date relative to its join date (if it was invited years ago but only recently accepted, it is a ghost); whether it has ever contributed a commit, PR, or issue; and the pattern of API calls. A real but inactive user will not suddenly issue thousands of API calls across multiple org endpoints. Behavioral baselines over 30 days are the best discriminator.
Does GitHub's own abuse detection catch this?
GitHub's detection focuses on authentication failures, rapid fire requests, and obviously malicious user agents. Slow, rotating, authenticated requests with legitimate user agents are not flagged. This is by design: GitHub prioritizes not blocking legitimate CI traffic. The burden rests on the organization to monitor its own audit logs and enforce account hygiene.
Related Articles

Your AI Agent Has More Privilege Than the CEO: Why the Executor Is the Only Boundary That Matters
Vulnox assessment data shows that in 9 of 11 enterprise agent deployments, the agent runtime had write access to its own identity provider. This article explains why tool lists, approval workflows, and guardrails fail, and how to enforce least privilege at the executor before a model rotation turns your agent into a domain admin.

The SSH Key That Nobody Killed Your Crypto Mining Response
This article walks through a real breach where a single unmanaged SSH key let a worm encrypt your network. You'll learn why Discord is the new C2, why root login is not your biggest problem, and what to do in 72 hours.

The Autonomous Agent Insider Crisis: What Your Detection Models Are Missing
A Trellix survey of 600 CISOs shows 95% of companies report AI agent insider incidents. Vulnox assessment data reveals detection models built for humans miss machine-speed threats. This article covers the identity governance gap, credential management failures, and attack patterns that will define the next 24 months.
Ready to Secure Your Digital Assets?
Get a comprehensive vulnerability assessment for your website today.