The way the GitHub Actions platform stores build artifacts could allow attackers to inject malicious code into software projects with CI/CD (Continuous Integration and Continuous Delivery) workflows that do not perform adequate filtering when downloading artifacts. Cybersecurity researchers have identified several popular artifact download scripts used by thousands of repositories that are vulnerable to this issue.
“We found that when artifacts are transferred between different workflows, there is a major risk of artifact poisoning – a technique in which an attacker replaces the content of a legitimate artifact with a modified malicious artifact to launch a supply chain attack,” from the vendor Researchers at chain security company Legit Security said in analyzing the issue.
To attack a vulnerable project’s CI/CD pipeline that downloads and consumes artifacts generated by other workflows, an attacker would only need to fork the repository containing those workflows and modify them in the local copy so that they generate rogue artifacts and then return pull requests to the original repository that does not need to accept these requests.
Logical flaw in artifact storage API
GitHub Actions is a CI/CD platform for automating building and testing of software code. The service is free for public repositories and includes free worker runtime and storage space for private repositories. It is widely adopted by projects that use GitHub to host and manage their source code repositories.
1 second of 27 secondsVolume 0%
GitHub Actions workflows are automated processes defined in .yml files using YAML syntax that execute when certain triggers or events occur, such as when new code is committed to a repository. Build artifacts are compiled binaries, logs, and other files that result from the execution of a workflow and its individual jobs. These artifacts are saved in buckets, and each workflow run is assigned a specific bucket from which it can upload and later download files.
The reference “action” (script) provided by GitHub for downloading artifacts does not support cross-workflow artifact downloading, but reusing artifacts generated by different workflows as input to subsequent build steps is a common use case for software projects. That’s why developers create their own custom scripts that rely on the GitHub Actions API to use more complex filtering to download artifacts, such as those created by specific workflow files, specific users, specific branches, etc.
The problem discovered by Legit Security is that the API does not differentiate between artifacts uploaded by forked repositories and base repositories, so if a download script filters artifacts generated by a specific workflow file from a specific repository, the API will serve the latest version of the artifacts generated by that file, But this could be a malicious version automatically generated through a pull request operation from a forked version of the repository.
“Simply put: In a vulnerable workflow, any GitHub user can create a branch that builds an artifact,” the researchers said. “This artifact is then injected into the original repository build process and its output is modified. This is another form of software supply chain attack where the build output is modified by the attacker.
Researchers discovered four custom actions developed by the community to download all vulnerable artifacts. One of them is listed as a dependency in over 12,000 repositories.
One of the repositories that uses such custom scripts in one of its workflows is the official repository of the Rust programming language. The vulnerable workflow named ci.yml is responsible for building and testing the repository’s code and uses a custom action to download an artifact named libgccjit.so (a Linux library file), which is provided by a third-party repository. Workflow generation.
All an attacker has to do is fork a third-party repository, modify that repository’s workflow to produce a malicious version of the library, and issue a pull request to the original repository to produce the artifact. If a Rust workflow subsequently introduced a poisoned version of the library, an attacker would be able to execute malicious code in the Rust repository using the workflow’s permissions.
“Once exploited, an attacker could modify repository branches, pull requests, issues, releases, and all entities available for workflow token permissions,” the researchers said.
Users need to implement stricter filtering on artifact downloads
GitHub responded to Legit’s report by adding more filtering capabilities to the API that developers can use to better identify artifacts created by a specific running instance of a workflow (workflow run ID). However, this change cannot be forced into existing implementations without breaking workflows, so users will need to update their workflows with stricter filtering in order to be protected.
Another mitigation is to filter downloaded artifacts by the hash of the commit that generated them, or to exclude artifacts created by pull requests entirely using the exclude_pull_requests option. Legit Security also contacted the author of the vulnerable custom artifact download script they discovered.
“When it comes to supply chain security, the focus has always been on preventing people from contributing malicious code, so every time you make a change in a repository, create a pull request, or make a change request, GitHub has a lot of built-in validation controls,” Legit Security’s Chief Technology Officer Liav Caspi told CSO. “Someone has to approve your code, someone has to merge it, so someone is involved. What we’ve been trying to find are techniques that exploit logic issues where anyone can influence it without review, and I think this is one of those . If anyone knew about this, they might inject the artifact without any approval.”
Typically, Caspi says, a CI pipeline’s workflow will run automatically on a pull request to test the code before manually reviewing it, and if the pull request contains any artifacts that need to be built, the workflow will build it. He said that a sophisticated attacker could create a pull request to build an artifact and then delete the request by closing the commit, and all the noise of activity present in today’s source code repositories would most likely go unnoticed and it would go unnoticed.