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

This commit is contained in:
Translator
2026-07-06 15:30:49 +00:00
parent bc1328747e
commit 9322cc1b29
@@ -4,16 +4,16 @@
## Entendendo o risco
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 renderiza expressões ${{ ... }} antes de a step executar. O valor renderizado é colado no programa da step (para run steps, um shell script). Se você interpolar input não confiável diretamente dentro de run:, o atacante controla parte do shell program e pode executar comandos arbitrários.
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
Pontos-chave:
- A renderização ocorre antes da execução. O script de run é gerado com todas as expressões resolvidas e então executado pelo shell.
- Muitos contexts contêm campos controlados pelo usuário dependendo do evento que dispara (issues, PRs, comments, discussions, forks, stars, etc.). Veja a referência de untrusted input: https://securitylab.github.com/resources/github-actions-untrusted-input/
- Shell quoting dentro de run: não é uma defesa confiável, porque a injeção acontece na fase de renderização do template. Atacantes podem sair das aspas ou injetar operadores via input maliciosamente construído.
- A renderização acontece antes da execução. O run script é gerado com todas as expressões resolvidas e então executado pelo shell.
- Muitos contexts contêm campos controlados pelo usuário dependendo do evento que dispara a ação (issues, PRs, comments, discussions, forks, stars, etc.). Veja a referência de untrusted input: https://securitylab.github.com/resources/github-actions-untrusted-input/
- Shell quoting dentro de run: não é uma defesa confiável, porque a injection ocorre na etapa de template rendering. Attackers podem sair de quotes ou injetar operators via input criado para isso.
## Vulnerable pattern → RCE on runner
## Padrão vulnerável → RCE on runner
Workflow vulnerável (disparado quando alguém abre uma nova issue):
```yaml
@@ -36,7 +36,7 @@ with:
github_token: ${{ secrets.GITHUB_TOKEN }}
labels: new
```
Se um atacante abrir uma issue com o título $(id), o step renderizado torna-se:
Se um atacante abrir um issue intitulado $(id), o step renderizado se torna:
```sh
echo "New issue $(id) created"
```
@@ -44,12 +44,48 @@ A substituição de comando executa id no runner. Exemplo de saída:
```
New issue uid=1001(runner) gid=118(docker) groups=118(docker),4(adm),100(users),999(systemd-journal) created
```
Por que colocar entre aspas não te salva:
- As expressões são renderizadas primeiro, depois o script resultante é executado. Se o valor não confiável contiver $(...), `;`, `"`/`'`, ou quebras de linha, ele pode alterar a estrutura do programa apesar das suas aspas.
Por que aspas não salvam você:
- Expressions são renderizadas primeiro, depois o script resultante é executado. Se o valor não confiável contiver $(...), `;`, `"`/`'`, ou quebras de linha, ele pode alterar a estrutura do programa apesar das aspas.
## Padrão seguro (shell variables via env)
## Confusão de estado de comentário: comentários spoofed de bot → shell injection
Mitigação correta: copie a entrada não confiável para uma variável de ambiente, depois use a expansão nativa do shell ($VAR) no run script. Não re-incorpore com ${{ ... }} dentro do comando.
Uma variante perigosa aparece quando um workflow **procura comments e depois trata o comentário retornado como estado confiável de automação**. Por exemplo, `peter-evans/find-comment` pode pesquisar por `body-includes` e expor o `comment-body` correspondente como output de uma step. Se o workflow **não** restringir também `comment-author`, qualquer usuário que possa comentar pode spoof the marker text esperado de um bot.
```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:"
```
Se essa saída for posteriormente incorporada na sintaxe do shell, o workflow se torna explorável, mesmo que a fonte original fosse "apenas um comentário":
```yaml
- run: |
if [ '${{ steps.fc.outputs.comment-body }}' = '' ]; then
echo "new issue needed"
fi
```
Um atacante pode postar um comentário que:
- corresponda à string marker pesquisada, e
- contenha conteúdo que quebre o shell, como `' ]; <cmd>; if [ 'x`
Depois que o GitHub renderiza `${{ ... }}`, o Bash recebe sintaxe controlada pelo atacante, não dados. Isso cria um **exploit em duas etapas**:
1. **Confusion of provenance**: o workflow confunde comentários do atacante com estado do bot.
2. **Script injection**: o `comment-body` retornado é colado em `run:` e executado.
### TOCTOU race against bot comments
Se o comentário legítimo do bot só for criado após alguma etapa anterior, um atacante pode ganhar a corrida postando primeiro o comentário falsificado. Se a ação de busca retornar o comentário do atacante antes de o comentário real do bot existir (ou antes de ele ser selecionado), um comentarista público com baixo privilégio pode transformar um workflow `issue_comment`/`issue` em execução privilegiada no runner.
### Safer patterns for comment-driven automation
- Ao usar `find-comment`, exija **tanto conteúdo quanto provenance** (`comment-author`, identidade do repository/App, ou outro vínculo forte).
- Não use comentários como estado se um label, artifact, campo da issue ou datastore externo puder armazenar o mesmo estado com mais segurança.
- Nunca cole `comment-body`, títulos de issue, labels ou qualquer saída do workflow derivada deles diretamente em `run:`.
- Se você precisar consumir texto de comentário, passe-o via `env:` ou um file e trate-o apenas como dados.
## Safe pattern (shell variables via env)
Mitigação correta: copie a entrada não confiável para uma environment variable, e então use a expansão nativa do shell ($VAR) no run script. Não re-incorpore com ${{ ... }} dentro do comando.
```yaml
# safe
jobs:
@@ -62,32 +98,40 @@ TITLE: ${{ github.event.issue.title }}
run: |
echo "New issue $TITLE created"
```
Notas:
- Avoid using ${{ env.TITLE }} inside run:. That reintroduces template rendering back into the command and brings the same injection risk.
- Prefer passing untrusted inputs via env: mapping and reference them with $VAR in run:.
Notes:
- Evite usar ${{ env.TITLE }} dentro de run:. Isso reintroduz a renderização de template de volta ao command e traz o mesmo risco de injection.
- Prefira passar inputs não confiáveis via env: mapping e referenciá-los com $VAR em run:.
## Reader-triggerable surfaces (treat as untrusted)
## Reader-triggerable surfaces (trate como não confiáveis)
Contas com apenas permissão de leitura em repositórios públicos ainda podem acionar muitos eventos. Qualquer campo em contexts derivados desses eventos deve ser considerado controlado pelo atacante, a menos que se prove o contrário. Exemplos:
Accounts com apenas permissão de leitura em repositórios públicos ainda podem acionar muitos events. Qualquer campo em contexts derivados desses events deve ser considerado controlado por attacker, a menos que se prove o contrário. Examples:
- issues, issue_comment
- discussion, discussion_comment (orgs can restrict discussions)
- discussion, discussion_comment (orgs podem restringir discussions)
- pull_request, pull_request_review, pull_request_review_comment
- pull_request_target (dangerous if misused, runs in base repo context)
- fork (anyone can fork public repos)
- watch (starring a repo)
- Indirectly via workflow_run/workflow_call chains
- pull_request_target (perigoso se usado incorretamente, executa no contexto do base repo)
- fork (qualquer pessoa pode fazer fork de repositórios públicos)
- watch (marcar um repo com star)
- Indiretamente via workflow_run/workflow_call chains
Which specific fields are attacker-controlled is event-specific. Consult GitHub Security Labs untrusted input guide: https://securitylab.github.com/resources/github-actions-untrusted-input/
Quais campos específicos são controlados por attacker é algo específico de cada event. Consulte o guia de untrusted input do GitHub Security Lab: https://securitylab.github.com/resources/github-actions-untrusted-input/
## Local validation without touching the target repo
Você pode reproduzir muitas script injections de GitHub Actions com segurança usando [`act`](https://github.com/nektos/act): gere um synthetic event JSON, execute o vulnerable workflow localmente e substitua a saída da external action por um valor controlado (por example, um `comment-body` mockado). Isso é útil para depurar a payload structure, verificar se o texto injected ainda deixa uma sintaxe válida de Bash e confirmar uma harmless canary exfiltration antes de qualquer live test.
## Practical tips
- Minimize use of expressions inside run:. Prefer env: mapping + $VAR.
- If you must transform input, do it in the shell using safe tools (printf %q, jq -r, etc.), still starting from a shell variable.
- Be extra careful when interpolating branch names, PR titles, usernames, labels, discussion titles, and PR head refs into scripts, command-line flags, or file paths.
- For reusable workflows and composite actions, apply the same pattern: map to env then reference $VAR.
- Minimize o uso de expressions dentro de run:. Prefira env: mapping + $VAR.
- Se precisar transformar input, faça isso no shell usando safe tools (printf %q, jq -r, etc.), ainda começando a partir de uma shell variable.
- Tenha cuidado extra ao interpolar branch names, PR titles, usernames, labels, discussion titles e PR head refs em scripts, command-line flags ou file paths.
- Para reusable workflows e composite actions, aplique o mesmo padrão: mapear para env e então referenciar $VAR.
## 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)