Translated ['', 'src/pentesting-ci-cd/github-security/abusing-github-act

This commit is contained in:
Translator
2026-07-06 15:32:18 +00:00
parent 4bf3f7ddfb
commit d94ccc1f64
@@ -2,20 +2,20 @@
{{#include ../../../banners/hacktricks-training.md}}
## Riskin anlaşılması
## Riski anlamak
GitHub Actions renders expressions ${{ ... }} before the step executes. The rendered value is pasted into the steps program (for run steps, a shell script). If you interpolate untrusted input directly inside run:, the attacker controls part of the shell program and can execute arbitrary commands.
GitHub Actions, step çalışmadan önce expression'ları ${{ ... }} olarak render eder. Render edilen değer, stepin programının içine yapıştırılır (run stepleri için bir shell script). Güvenilmeyen inputu doğrudan run: içinde interpolate ederseniz, attacker shell programının bir kısmını kontrol eder ve arbitrary commands çalıştırabilir.
Docs: https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions and contexts/functions: https://docs.github.com/en/actions/learn-github-actions/contexts
Önemli noktalar:
- Render işlemi yürütmeden önce gerçekleşir. Tüm ifadeler çözümlendikten sonra run script oluşturulur ve sonra shell tarafından çalıştırılır.
- Birçok contexts kullanıcı-kontrollü alanlar içerir; bu, tetikleyici olaya bağlıdır (issues, PRs, comments, discussions, forks, stars, vb.). Güvenilmeyen input referansına bakın: https://securitylab.github.com/resources/github-actions-untrusted-input/
- run: içindeki shell quoting güvenilir bir savunma değildir, çünkü enjeksiyon şablon render aşamasında gerçekleşir. Saldırganlar tırnaklardan çıkabilir veya özel olarak hazırlanmış girdilerle operatörler enjekte edebilir.
Key points:
- Rendering executiondan önce gerçekleşir. run script, tüm expressionlar resolve edildikten sonra üretilir ve ardından shell tarafından çalıştırılır.
- Birçok context, tetikleyen evente bağlı olarak user-controlled alanlar içerir (issues, PRs, comments, discussions, forks, stars, vb.). Untrusted input referansına bakın: https://securitylab.github.com/resources/github-actions-untrusted-input/
- run: içinde shell quoting güvenilir bir defense değildir, çünkü injection template rendering aşamasında gerçekleşir. Attackerlar craft edilmiş input ile quotelardan çıkabilir veya operatorler inject edebilir.
## Zayıf örüntü → RCE on runner
## Vulnerable pattern → RCE on runner
Zayıf workflow (birisi yeni bir issue açtığında tetiklenir):
Vulnerable workflow (birisi yeni bir issue açtığında tetiklenir):
```yaml
name: New Issue Created
on:
@@ -36,20 +36,56 @@ with:
github_token: ${{ secrets.GITHUB_TOKEN }}
labels: new
```
Eğer bir saldırgan $(id) başlıklı bir issue açarsa, renderlenen adım şöyle olur:
Eğer bir attacker $(id) başlıklı bir issue açarsa, render edilen step şu hale gelir:
```sh
echo "New issue $(id) created"
```
Komut değişimi runner üzerinde id komutunu çalıştırır. Örnek çıktı:
Komut substitution, runner üzerinde id çalıştırır. Örnek çıktı:
```
New issue uid=1001(runner) gid=118(docker) groups=118(docker),4(adm),100(users),999(systemd-journal) created
```
Neden tırnaklama sizi kurtarmaz:
- İfadeler önce render edilir, sonra ortaya çıkan script çalıştırılır. Eğer güvensiz değer $(...), `;`, `"`/`'`, veya yeni satırlar içeriyorsa, tırnaklama yapmış olsanız bile program yapısını değiştirebilir.
Neden quoting sizi korumaz:
- Expressions önce render edilir, ardından oluşan script çalışır. Güvenilmeyen değer $(...), `;`, `"`/`'` veya yeni satırlar içeriyorsa, quotinge rağmen program yapısını değiştirebilir.
## Güvenli desen (shell variables via env)
## Comment-state confusion: sahte bot yorumları → shell injection
Doğru önlem: güvensiz girdiyi bir environment variable'a kopyalayın, sonra run script içinde native shell expansion ($VAR) kullanın. Komutun içinde ${{ ... }} ile tekrar gömmeyin.
Tehlikeli bir varyant, bir workflow **yorumları aradığında ve daha sonra dönen yorumu trusted automation state olarak ele aldığında** ortaya çıkar. Örneğin, `peter-evans/find-comment` `body-includes` ile arama yapabilir ve eşleşen `comment-body` değerini bir step output olarak açığa çıkarabilir. Eğer workflow, **ayrıca** `comment-author` ile kısıtlamazsa, yorum yapabilen herhangi bir kullanıcı bottan beklenen marker metnini spoof edebilir.
```yaml
- uses: peter-evans/find-comment@v4
id: fc
with:
issue-number: ${{ github.event.issue.number }}
body-includes: "Opened a new issue in org/repo:"
```
Bu çıktı daha sonra shell syntax içine gömülürse, orijinal kaynak "sadece bir comment" olsa bile workflow exploitable hale gelir:
```yaml
- run: |
if [ '${{ steps.fc.outputs.comment-body }}' = '' ]; then
echo "new issue needed"
fi
```
Bir saldırgan, aşağıdakilerin ikisini de yapan bir comment gönderebilir:
- searched marker string ile eşleşir, ve
- `' ]; <cmd>; if [ 'x` gibi shell-breaking content içerir
GitHub `${{ ... }}` kısmını render ettikten sonra, Bash attacker-controlled syntax alır, data değil. Bu, **iki aşamalı exploit** oluşturur:
1. **Provenance confusion**: workflow, attacker comments’ı bot state sanır.
2. **Script injection**: dönen `comment-body` `run:` içine yapıştırılır ve çalıştırılır.
### Bot commentsa karşı TOCTOU race
Eğer meşru bot commenti ancak daha sonraki bir stepten sonra oluşturuluyorsa, bir saldırgan sahte commenti önce göndererek yarış kazanabilir. Search action, gerçek bot commenti mevcut olmadan önce (veya seçilmeden önce) saldırganın commentini döndürürse, düşük yetkili bir public commenter bir `issue_comment`/issue workflowunu privileged runner executiona çevirebilir.
### Comment-driven automation için daha güvenli patternler
- `find-comment` kullanırken, **hem content hem provenance** isteyin (`comment-author`, repository/App identity, veya başka güçlü bir binding).
- Aynı state bir label, artifact, issue field, veya external datastore içinde daha güvenli tutulabiliyorsa commentleri state olarak kullanmayın.
- `comment-body`, issue titles, labels, veya bunlardan türetilmiş herhangi bir workflow outputu asla doğrudan `run:` içine yapıştırmayın.
- Comment textini kullanmanız gerekiyorsa, onu `env:` veya bir dosyadan geçirin ve yalnızca data olarak ele alın.
## Safe pattern (shell variables via env)
Doğru mitigation: güvenilmeyen inputu bir environment variablea kopyalayın, sonra run script içinde native shell expansion ($VAR) kullanın. Komut içinde ${{ ... }} ile yeniden gömmeyin.
```yaml
# safe
jobs:
@@ -62,32 +98,40 @@ TITLE: ${{ github.event.issue.title }}
run: |
echo "New issue $TITLE created"
```
Notlar:
- run: içinde ${{ env.TITLE }} kullanmaktan kaçının. Bu, şablon render'ını komutun içine yeniden sokar ve aynı enjeksiyon riskini getirir.
- Güvensiz girdileri env: mapping ile geçirmek ve run: içinde bunlara $VAR ile referans vermek tercih edin.
Notes:
- ${{ env.TITLE }} içini run: içinde kullanmaktan kaçının. Bu, template renderingi komuta yeniden sokar ve aynı injection riskini geri getirir.
- Güvenilmeyen girdileri env: mapping üzerinden geçirmeyi ve run: içinde bunlara $VAR ile referans vermeyi tercih edin.
## Reader-triggerable surfaces (treat as untrusted)
## Reader-triggerable surfaces (güvenilmeyen olarak değerlendirin)
Sadece public repository'lerde okuma izni olan hesaplar bile birçok eventi tetikleyebilir. Bu olaylardan türetilen contexts içindeki herhangi bir alan, aksi kanıtlanana kadar saldırgan kontrolünde kabul edilmelidir. Örnekler:
Public repositories üzerinde yalnızca read yetkisine sahip hesaplar bile birçok olayı tetikleyebilir. Bu olaylardan türetilen contexts içindeki herhangi bir field, aksi kanıtlanmadıkça attacker-controlled olarak kabul edilmelidir. Örnekler:
- issues, issue_comment
- discussion, discussion_comment (org'lar discussions'ı kısıtlayabilir)
- discussion, discussion_comment (orgs discussions üzerinde kısıtlama getirebilir)
- pull_request, pull_request_review, pull_request_review_comment
- pull_request_target (kötü kullanılırsa tehlikeli, base repo context'inde çalışır)
- fork (herkes public repo'yu fork'layabilir)
- watch (bir repo'yu star'lamak)
- Dolaylı olarak workflow_run/workflow_call zincirleri vasıtasıyla
- pull_request_target (kötü kullanılırsa tehlikelidir, base repo contextinde çalışır)
- fork (herkes public repos fork edebilir)
- watch (bir repoyu starlamak)
- Dolaylı olarak workflow_run/workflow_call zincirleri üzerinden
Hangi spesifik alanların saldırgan kontrolünde olduğu olay bazlıdır. GitHub Security Labin untrusted input rehberine bakın: https://securitylab.github.com/resources/github-actions-untrusted-input/
Hangi spesifik fieldların attacker-controlled olduğu evente özeldir. GitHub Security Labın untrusted input guide dokümanına bakın: https://securitylab.github.com/resources/github-actions-untrusted-input/
## Practical tips
## Hedef repoya dokunmadan yerel validation
Birçok GitHub Actions script injections vakasını [`act`](https://github.com/nektos/act) ile güvenli şekilde yeniden üretebilirsiniz: sentetik bir event JSON üretin, vulnerable workflowu yerelde çalıştırın ve external action outputunu kontrollü bir değerle değiştirin (örneğin mocked bir `comment-body`). Bu, payload yapısını debug etmek, injected textin hâlâ geçerli Bash syntax bırakıp bırakmadığını doğrulamak ve herhangi bir canlı testten önce zararsız canary exfiltration’ı onaylamak için kullanışlıdır.
## Pratik ipuçları
- run: içinde expressions kullanımını en aza indirin. env: mapping + $VAR tercih edin.
- Eğer girdiyi dönüştürmeniz gerekiyorsa, bunu shell içinde güvenli araçlarla yapın (printf %q, jq -r, vb.), yine bir shell değişkeninden başlayarak.
- Branch isimlerini, PR başlıklarını, kullanıcı adlarını, label'ları, discussion başlıklarını ve PR head ref'lerini script'lere, komut satırı flag'lerine veya dosya yollarına interpolasyon yaparken ekstra dikkatli olun.
- Reusable workflows ve composite actions için de aynı deseni uygulayın: env'e map edip sonra $VAR ile referanslayın.
- Girdiyi dönüştürmeniz gerekiyorsa, bunu shell içinde güvenli araçlarla yapın (printf %q, jq -r, vb.), yine de bir shell variabledan başlayarak.
- branch names, PR titles, usernames, labels, discussion titles ve PR head refsi scriptlere, command-line flagse veya file pathse yerleştirirken ekstra dikkatli olun.
- reusable workflows ve composite actions için de aynı deseni uygulayın: enve map edin, sonra $VAR ile referans verin.
## References
- [Find Comment, Get Shell: Command Injection in dbts GitHub Actions](https://landh.tech/blog/20260701-find-comment-get-shell)
- [peter-evans/find-comment](https://github.com/peter-evans/find-comment)
- [GHSL-2023-109: GitHub Actions command injection in a TDesign Vue Next workflow](https://securitylab.github.com/advisories/GHSL-2023-109_TDesign_Vue_Next/)
- [nektos/act](https://github.com/nektos/act)
- [GitHub Actions: A Cloudy Day for Security - Part 1](https://binarysecurity.no/posts/2025/08/securing-gh-actions-part1)
- [GitHub workflow syntax](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions)
- [Contexts and expression syntax](https://docs.github.com/en/actions/learn-github-actions/contexts)