Coordinated Disclosure Timeline
- 2024-07-08: Report the issue through GitHub’s Private Vulnerability Reporting (PVR).
- 2024-07-19: The Excalidraw team pointed out that this vulnerability was already identified and that the affected workflow was disabled earlier this year.
- 2024-07-23: Publishing for educational purposes.
Summary
Excalidraw is vulnerable to Poisoned Pipeline Execution (PPE) on its autorelease-preview.yml
workflow allowing an external attacker to gain write access to the repository.
Project
Excalidraw
Tested Version
Latest commit at the time of reporting
Details
Execution of untrusted code in autorelease-preview.yml
(GHSL-2024-158
)
The autorelease-preview.yml
workflow is triggered on issue_comment
events, that is when anyone comments on an Issue or Pull Request. Specifically, it only triggers when the comment is @excalibot trigger release
and the issue is a Pull Request:
if: github.event.comment.body == '@excalibot trigger release' && github.event.issue.pull_request
The workflow runs with default write-all
permissions and has access to secrets.
Taking the above into account, the workflow, checkouts the HEAD reference of the commented Pull Request which allow an attacker to introduce arbitrary files into the GitHub’s runner:
- name: Get PR SHA
id: sha
uses: actions/github-script@v4
with:
result-encoding: string
script: |
const { owner, repo, number } = context.issue;
const pr = await github.pulls.get({
owner,
repo,
pull_number: number,
});
return pr.data.head.sha
- uses: actions/checkout@v2
with:
ref: ${{ steps.sha.outputs.result }}
fetch-depth: 2
Later, the workflow calls yarn autorelease preview
which would run arbitrary code since the attacker can control the package.json
file.
- name: Auto release preview
id: "autorelease"
run: |
yarn add @actions/core -W
yarn autorelease preview ${{ github.event.issue.number }}
PoC
- Clone the repository
- Edit
package.json
and replace theautorelease
script with any arbitrary code such as:
...
"scripts": {
...
"autorelease": "echo POISONED",
"prerelease:excalidraw": "node scripts/prerelease.js",
"build:preview": "yarn build && vite preview --port 5000",
"release:excalidraw": "node scripts/release.js"
}
}
- Git add the
package.json
file. - Git commit the changes.
- Create a Pull Request from your fork to Excalidraw’ repository.
- Visit the Pull Request in the WebUI and add a comment with the text
@excalibot trigger release
. - Watch the workflow run and the output of the
Auto release preview
step which should print “POISONED” to the log.
Impact
An attacker may be able to gain write access to the repository and the secrets.NPM_TOKEN
secret.
Resources
- CodeQL for JavaScript - Expression injection in Actions
- Keeping your GitHub Actions and workflows secure Part 2: Untrusted input
- Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests
Resources
- https://github.com/excalidraw/excalidraw/security/advisories/GHSA-6v5c-rqc3-g7h9
Credit
This issue was discovered and reported by GHSL team member @pwntester (Alvaro Muñoz).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2024-158
in any communication regarding this issue.