{"id":25348929,"url":"https://github.com/AdnaneKhan/ActionsCacheBlasting","last_synced_at":"2025-10-29T18:31:09.245Z","repository":{"id":228915804,"uuid":"775262918","full_name":"AdnaneKhan/ActionsCacheBlasting","owner":"AdnaneKhan","description":"Proof-of-concept code for research into GitHub Actions Cache poisoning.","archived":false,"fork":false,"pushed_at":"2024-12-12T02:26:07.000Z","size":24,"stargazers_count":22,"open_issues_count":0,"forks_count":7,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-10T22:33:52.495Z","etag":null,"topics":["actions","bugbounty","cicd"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AdnaneKhan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-21T03:48:57.000Z","updated_at":"2025-02-04T14:30:01.000Z","dependencies_parsed_at":"2024-05-03T15:13:39.335Z","dependency_job_id":"fa5a7aae-d9f6-4829-9a93-5a18f33348dc","html_url":"https://github.com/AdnaneKhan/ActionsCacheBlasting","commit_stats":null,"previous_names":["adnanekhan/actionscacheblasting"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdnaneKhan%2FActionsCacheBlasting","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdnaneKhan%2FActionsCacheBlasting/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdnaneKhan%2FActionsCacheBlasting/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdnaneKhan%2FActionsCacheBlasting/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AdnaneKhan","download_url":"https://codeload.github.com/AdnaneKhan/ActionsCacheBlasting/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238869848,"owners_count":19544429,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actions","bugbounty","cicd"],"created_at":"2025-02-14T16:01:07.962Z","updated_at":"2025-10-29T18:31:09.240Z","avatar_url":"https://github.com/AdnaneKhan.png","language":"Python","funding_links":[],"categories":["Research and hacking"],"sub_categories":[],"readme":"# ActionsCacheBlasting\n\n## The code in this repository NO LONGER WORKS now that GitHub has migrated to their Cache V2 backend. Please use [Cacheract](https://github.com/adnanekhan/cacheract), which supports the new backend and works through new restrictions imposed by GitHub on caching.\n\nProof-of-concept code for research into GitHub Actions Cache poisoning. All of this is considered working as intended by GitHub.\n\nYou can find more details on my [blog post](http://adnanthekhan.com/2024/05/06/the-monsters-in-your-build-cache-github-actions-cache-poisoning/) covering GitHub Actions cache poisoning.\n\n## What is it?\n\nThis repository contains code to:\n\n* Exfiltrate the CacheServerUrl Actions Runtime token from a workflow (for example one that runs on `pull_request_target` and checks out user-controlled code). ~The URL and token is valid for ~**6 hours**~ **90 minutes**, even if the workflow you exfiltrated it from only runs for a few seconds. There is no way for the repository maintainer to revoke this token.~\n\n\u003cimg width=\"566\" alt=\"Token Revocation\" src=\"https://github.com/user-attachments/assets/45693d5e-db80-4cfc-9919-309c1367084e\" /\u003e\n\n**UPDATE**: As of ~Nov/Dec 2024, GitHub has implemented functionality to prevent Cache writes once the originating workflow job has completed. This effectively prevents the attack technique of cache stuffing using a single token. To exploit using the eviction method this now you must fill the cache while the first workflow runs and then create a second PR to poison the cache entries. For busy repositories with frequent PRs that re-set the cache entries this makes it very challenging, it also increases risk of detection because the workflow must run while stuffing the cache.\n\nThe alternative technique is to pre-poison cache keys that could be updated by dependabot, etc. This is probably the path forward for a single PR, but will require investment in payload development to quickly poison future cache entries.\n\n* Use the CacheServerUrl and token to write arbitrary values to the repository's GitHub Actions cache to user-controlled cache keys and versions.\n\n**UPDATE**: Sometime in May/June of 2024 GitHub reduced the time the token is valid from 6 hours to 90 minutes. It is still valid after the run conclusion, but in practice (unless an attacker is very lucky) it means an attacker will need to exploit the initial vulnerability to obtain the token twice. Once to fill the cache, and again to set poisoned values. I have not had a chance to dive into any other changes. If you want a fun research project, set up a self-hosted runner, route traffic through Burp, and use caching in a workflow to see if there is anything else that changed.\n\n## How to Poison?\n\nGitHub Actions Cache entries are typically `zstd` compressed archives. You can create one by running:\n\n`tar --zstd -cf poisoned_cache.tzstd cache/contents/here`\n\nIf there is a cache hit then the cache restore action will just extract the archive. If the cache is poisoned you can over-write arbitrary files (ideally a script or something else that the workflow calls after restoring the cache). \n\nIf an injection point is not clear, then [Boost Security's LOTP](https://boostsecurityio.github.io/lotp/) is a great resource for finding which files to overwrite to gain arbitrary execution in a workflow.\n\nIf you cannot find an injection point, then you can probably replace the `action.yml` file for an action used after the cache restore step. I have not done this personally but it is viable given that all GitHub Actions used by a workflow are downloaded at initiation and stored on the runner's filesystem.\n\n## Disclaimer \n\nAll code is provided for research and educational purposes only. You are responsible for using this for research and authorized security testing work only.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAdnaneKhan%2FActionsCacheBlasting","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAdnaneKhan%2FActionsCacheBlasting","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAdnaneKhan%2FActionsCacheBlasting/lists"}