How To Recover Secrets From GitHub Actions

3 mins
Published on 01 July 2022

In this blog post, I’ll share how to recover a secret from a CI/CD service, such as GitHub Actions.

If you’re here, then you already know that secrets are hidden from CI/CD logs with ***, for example:

jobs:
  openssl:
    name: Recover With OpenSSL
    runs-on: ubuntu-20.04
    steps:
      - env:
          MY_CLIENT_SECRET: ${{ secrets.MY_CLIENT_SECRET }}
        run: |
          echo "MY_CLIENT_SECRET (***)     = ${MY_CLIENT_SECRET}"
# Output
MY_CLIENT_SECRET (***)     = ***

The above isn’t very helpful since this is the situation you’re probably in right now.

Quick And Dirty (Dangerous)

For private repositories, it’s possible to use base64 to encode a secret before printing it to the CI/CD service logs; this way, GitHub Actions won’t hide the secret with ***. Then, copy the encoded value and decode it locally.

name: Recovering secrets

# Assumption:
# You've created the following GitHub secrets in your repository:
# MY_CLIENT_ID - encode/decode with base64 - useful for private repositories

on:
  push:
  workflow_dispatch:

jobs:
  base64:
    name: Recover With Base64
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v3
      - env:
          MY_CLIENT_ID: ${{ secrets.MY_CLIENT_ID }}
        run: |
          echo "MY_CLIENT_ID (***)    = ${MY_CLIENT_ID}"
          echo "MY_CLIENT_ID (base64) = $(echo ${MY_CLIENT_ID} | base64)"
          echo "Copy the above value, and then execute locally:"
          echo "echo PASTE_HERE | base64 -D"

recover-github-secret-base64

The above method is hazardous as anyone can decode the secret, so for public repositories, this is a no-go. And here’s proof why it is super dangerous, assuming the printed encoded value is c29tZS1jbGllbnQtaWQtdmFsdWUK

echo c29tZS1jbGllbnQtaWQtdmFsdWUK | base64 -D
# some-client-id-value

I’ve just exposed MY_CLIENT_ID to the whole world … I’m terrified.

How To Recover A Secret From A CICD Service

The best way to recover a secret from a CICD system without exposing it to the outside world is to encrypt the secret before printing it to the CI/CD logs.

name: Recovering secrets

# Assumption:
# You've created the following GitHub secrets in your repository:
# MY_CLIENT_SECRET - encrypt/decrypt with openssl - useful for public and public repositories
# MY_OPENSSL_PASSWORD - used to protect secrets
# MY_OPENSSL_ITER - Use a number of iterations on the password to derive the encryption key.
#                   High values increase the time required to brute-force the resulting file.
#                   This option enables the use of PBKDF2 algorithm to derive the key.

on:
  push:
  workflow_dispatch:

jobs:
  openssl:
    name: Recover With OpenSSL
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v3
      - env:
          MY_CLIENT_SECRET: ${{ secrets.MY_CLIENT_SECRET }}
          MY_OPENSSL_PASSWORD: ${{ secrets.MY_OPENSSL_PASSWORD }}
          MY_OPENSSL_ITER: ${{ secrets.MY_OPENSSL_ITER }}
        run: |
          echo "MY_CLIENT_SECRET (***)     = ${MY_CLIENT_SECRET}"
          echo "MY_CLIENT_SECRET (openssl) = $(echo "${MY_CLIENT_SECRET}" | openssl enc -e -aes-256-cbc -a -pbkdf2 -iter ${MY_OPENSSL_ITER} -k "${MY_OPENSSL_PASSWORD}")"
          echo "Copy the above value, and then execute locally:"
          echo "echo PASTE_HERE | openssl base64 -d | openssl enc -d -pbkdf2 -iter \$MY_OPENSSL_ITER -aes-256-cbc -k \$MY_OPENSSL_PASSWORD"

recover-github-secret-openssl

The only way to decrypt the above string U2FsdGVkX1+6/+7bvNG/Ga7siAI994FkMUn5Njzn4zyNwvf8qM3MY0MMmd9sCFvz is to use the right number of iter and password, otherwise you’ll have to use a brute-force attack, good luck with that :)

Here’s how I decrypted the above value on my local machine:

echo U2FsdGVkX1+CeN0/ScQLZGU8f0ix86fh1oLJg/1M+o2lbCM+pBA8BIUCbkHMCjRZ | openssl base64 -d | openssl enc -d -pbkdf2 -iter $MY_OPENSSL_ITER -aes-256-cbc -k $MY_OPENSSL_PASSWORD

Final Words

I suggest creating a separate workflow for recovering CI/CD (GitHub) secrets, like .github/workflows/recover-github-secrets.yml, followed by running the workflow and then deleting its logs once you’re done recovering the secret.

recover-github-secret-delete-logs

References

Related Posts