Exploring CI/CD Vulnerabilities in github actions

I hope this message finds you well. Today, I'm thrilled to share my journey and insights into exploring CI/CD vulnerabilities in GitHub Actions.

Getting started

To start with actions It is crucial to understand the intricacies of action workflows and familiarize yourself with their functionality and the significance of each event. Crafting sample workflows can significantly aid comprehension.

To help you get started, I have a YouTube playlist with informative videos that will guide you in getting acquainted with the action workflow

youtube playlist

Understanding Workflow Events

I will only talk about two workflow events that will lead to RCE if misconfigured in action workflows

pull_request_target and workflow_run events

configurations for action:

Let's talk about the Configurations that are used for action.

The default permissions allow first-time contributors to run the action runners without any approval from the maintainers. Most repositories have this default configuration. You can look at the pull requests that are being opened to confirm if you need to become a first-time contributor or not to exploit it.

To become a first contributor you need to have a pull request merged before you create the exploit pull request. I mostly prefer spelling mistakes and sometimes little code changes.

Running Attacker Code

  1. pull_request_target: This event is same as pull_request but the main difference is that it has a write permission for the tokens, So an attacker can execute Malicious commands with write permissions if the event is misconfigured.

Here is a YAML file that is vulnerable to malicious execution by pull_request_target.

Example 1: Vulnerability via pull_request_target

on:
  pull_request_target

jobs:
  build:
    name: Build and test
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
      with:
        ref: ${{ github.event.pull_request.head.sha }} # from here on it will take the attacker code as it is checking out the untrusted input 
    - name: Setup node
      uses: actions/setup-node@v2
      with:
        node-version: '16'

    - name: Install dependencies
      run: npm install

    - name: Build docs
      run: npm run create-docs
      

Explanation:

ref: ${{ github.event.pull_request.head.sha }}

From this part the action will run on the attacker forked code base as it is using head.sha which will take the attacker pull request as an input and make the base repo action to run the attacker's fork code here.

    - name: Setup node
      uses: actions/setup-node@v2
      with:
        node-version: '16'

From this, the code will set up the npm environment from the action runner from the attacker's code.

   - name: Install dependencies
     run: npm install

   - name: Build docs
     run: npm run create-docs

The potentially untrusted code is being run during the npm install and npm run as the referenced packages are controlled by the author of the PR.

Exploitation

Now in the forked Repo, you can modify your input in package.json and create a pull request.

 "scripts": {

"create-docs": "echo 'your code'"
 }

2.workflow_run: This event allows a user to trigger privileged workflows. It triggers when the status of the other workflow is complete/requested and can be triggered from a forked workflow.

Here is a YAML file that is vulnerable to malicious execution by workflow_run

Example 2: Vulnerability via workflow_run

name: Deploy docs

on:
  workflow_run:
    workflows: ['Publish Package']
    types: [completed]

jobs:
  deploy-docs:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }} #if the workflow is successfully completed then it will start the rest of the tasks in the job 
    
    steps:
    - name: Checkout
      uses: actions/checkout@v2
      with:
        ref: ${{ github.event.workflow_run.head_sha }} # checkout specific commit

    - name: Setup node
      uses: actions/setup-node@v2
      with:
        node-version: '16'

    - name: Install dependencies
      run: npm install

    - name: Build docs
      run: npm run create-docs

Explantion:

workflow_run:
    workflows: ['Publish Package']
    types: [completed]

if: ${{ github.event.workflow_run.conclusion == 'success' }}

As the original workflow Publish Package is triggered by pull_request and runs the vulnerable workflow after successful completion we can create the same exploit as we created for the pull_request_target.

Exploitation

modifying package.json file and create the pull request from fork

 "scripts": {

"create-docs": "echo 'your code'"
 }



Conclusion:

In this post, we've explored how misconfigured GitHub Actions workflows can lead to vulnerabilities, such as Remote Code Execution (RCE). Specifically, we've discussed the risks associated with the pull_request_target and workflow_run events. These examples underscore the importance of understanding and properly configuring CI/CD pipelines to prevent potential security breaches.

To mitigate these risks, avoid checking out untrusted code within your action workflows. Assign write permissions only when absolutely necessary, and always validate and sanitize inputs to your workflows.

There are many other potential exploit scenarios and events in GitHub Actions that could lead to vulnerabilities. It’s crucial to continuously review and update your security practices to safeguard your CI/CD pipelines.

Reference:

https://www.legitsecurity.com/blog/github-privilege-escalation-vulnerability

https://cloud.hacktricks.xyz/pentesting-ci-cd/github-security/abusing-github-actions