Em nossa postagem anterior, vimos como detectar e proteger contra envenenamento direto Pipeline Execução (D-PPE). Também vimos como detectar essa vulnerabilidade usando Scanner Xygeni, bem como alguns mecanismos de proteção.
Envenenado Pipeline Execução (EPP) é produzido quando o invasor pode modificar o pipeline lógica de duas maneiras:
- Modificando o arquivo de configuração do CI (o pipeline) -> EPI direto (D-PPE)
- Ao modificar os arquivos referenciados pelo pipeline (por exemplo: scripts referenciados dentro do pipeline arquivo de configuração) -> EPI indireto (EPI-I)
Neste post, vamos nos aprofundar no Veja maisto Indirect PPE. Mas, antes disso, e como complemento ao meu post anterior, vamos ver primeiro como o GitHub gerencia a execução de pipelinese e quais são os mecanismos de proteção contra D-PPE.
Como o GitHub protege a execução de pipelineestá vindo de relações públicas?
Como funciona o GitHub em relação à execução de modificados pipelines?
Modificado pipelines podem vir de Pushes ou Pull Requests (RP). Como uma prática recomendada importante, é altamente recomendável evitar qualquer “push” direto para um branch protegido e usar Pull Requests como um mecanismo para impor alguma revisão antes de aceitar qualquer código contribuído.
Pull Requests pode chegar de duas fontes diferentes:
- PRs vindos de forks
- PRs vindos de ramos
RP de forks pode vir de público or investidores privados repositórios.
Como estamos tratando de EPI (Envenenado Pipeline Execução), nosso ponto principal não é a “aceitação” de um PR, mas a execução de um projeto modificado pipeline durante o processo de aceitação/aprovação do PR. No centro de um ataque PPE, há uma execução não intencional de um ataque modificado “malicioso”. pipeline.
Em poucas palavras, envenenado Pipeline A execução (EPI) é produzida quando o invasor pode modificar o pipeline lógica.
Há dois variantes:
- EPI direto (D-EPI): Em um cenário D-PPE, o invasor modifica o arquivo de configuração do CI em um repositório ao qual eles têm acesso, seja enviando a alteração diretamente para uma ramificação remota desprotegida no repositório ou enviando um PR com a alteração de uma ramificação ou bifurcação. Desde o CI pipeline a execução for definida pelos comandos no arquivo de configuração do CI modificado, os comandos maliciosos do invasor serão executados no nó de compilação assim que a compilação pipeline é acionado.
- EPI indireto (I-EPI): Em certos casos, a possibilidade de D-PPE não está disponível para um adversário com acesso a um SCM repositório (por exemplo, se o pipeline está configurado para extrair o arquivo de configuração do CI de uma ramificação separada e protegida no mesmo repositório). Nesse cenário, em vez de envenenar o pipeline em si, um invasor injeta código malicioso em arquivos referenciados pelo pipeline (por exemplo: scripts referenciados dentro do pipeline arquivo de configuração)
Em ambos os casos, GitHub executará o modificado pipeline sem necessidade de revisão ou aprovação prévia.
PRs de garfos em diante público repos
GitHub permite configurar o comportamento durante o processamento PRs provenientes de bifurcações em repositórios públicos.
Quando um PR vem de uma bifurcação, o GitHub sempre força algum nível de “aprovação” antes de executar o pipeline associado ao PR. Este nível de aprovação varia de uma aprovação fraca a uma aprovação estrita.
At Nível organizacional (Org>>Configurações>>Ações>>Geral), você pode decidir entre várias opções de “aprovação”:
O mais rigoroso é o último (“Exigir aprovação de todos os colaboradores externos”) porque o GitHub sempre exigirá aprovação quando o PR vier de forks de colaboradores externos.
Mas mesmo neste caso estrito, existem diferenças entre colaboradores com permissões de leitura e gravação.
- Quando o PR vem de um ler usuário, o execução do pipeline está parado até que haja aprovação das alterações. Se a aprovação estiver correta, então o modificado pipeline É executado.
- Quando o PR vem de um escrever usuário, o aprovação não é necessária e o modificado pipeline é sempre executado !!
Concluindo, os PRs provenientes de bifurcações em repositórios públicos são levemente protegidos contra EPI. Há alguma proteção contra usuários externos (leitura), mas nada relacionado a usuários internos (gravação).
Sobre o quê PRs provenientes de forks de repositórios privados?
PRs de garfos em diante investidores privados repos
Nesse cenário, o GitHub fornece algumas definições de configuração úteis.
As configurações acima podem ser definidas em Org ou em Repo nível.
Ao nenhuma opção está marcada, o GitHub irá pedir aprovação e não executará o modificado pipeline. Esta é a configuração mais segura!!
O processo de configuração mais insegura é quando "Executar fluxos de trabalho a partir do fork pull request" está checado. Neste caso, o mesmo para usuários de leitura e gravação, o Github executará automaticamente o modificado pipeline!! E esta situação pode ser ainda pior E se "Enviar tokens de gravação para fluxos de trabalho do fork pull requestseEnviar segredos e variáveis para fluxos de trabalho do fork pull requests” são verificados. Não faça isso a menos que seja claramente justificado!!
E se "Exigir aprovação para bifurcação pull request fluxos de trabalho”está marcado, a situação acima é um pouco melhorada: o GitHub solicitará aprovação e não executará o modificado pipeline para o usuário de leitura, mas ainda o executará para um usuário de gravação.
Forks vistos, e quanto PRs vindos de filiais?
RP de ramos
Para proteger este cenário você deve confiar em Regras de proteção de filiais.
No nível do repositório, você pode criar regras de proteção de ramificação para qualquer ramificação. Estas regras acrescentam alguns restrições à modificação de ramificações protegidas.
Embora você configure uma regra para “Exigir um pull request antes de fundireExigir aprovações" o modificado pipeline será executado automaticamente após a criação do PR.A “aprovação” só se aplicará à ação de mesclagem.
E quanto ao envenenamento indireto Pipeline Execução
Como vimos acima, o D-PPE pode ser mitigado usando pull_request_target, Mas não se aplica ao EPI.
Se você usar pull_request_target, o checkout padrão será o código base. Mas se você quiser validar algumas verificações no código contribuído (código PR), você precisa verificar explicitamente o código PR. Portanto, se o código PR modificou qualquer script de shell invocado pelo pipeline, a “base” (segura) pipeline irá invocar o shell script “modificado” → EPI indireto!!
A solução para isso é um pouco mais complicada (não existe uma solução mágica como pull_request_target).
Nossa pipeline agora é seguro para D-PPE porque estamos usando pull_request_target. Mas ainda é vulnerável ao I-PPE.
Em nosso exemplo de teste, precisamos basicamente verificar o código PR para fazer a construção, mas os testes são executados no artefato gerado pela construção.
Então .. por que não verifica ambas as bases de código?
- Confira o código PR porque é o código contribuído que queremos construir e testar
- Confira o código base para executar a versão original do pipeline e os scripts de construção/testes
Isto pode ser feito por verificando essas bases de código em pastas diferentes: o código base pode ser retirado para a pasta raiz e o PR para uma pasta diferente. Nesse caso, executaríamos o build e o script de teste da pasta raiz em relação ao código colocado na nova pasta.
Esta é uma solução fácil, claro!! Mas, para efeitos de aprendizagem, gostaria de introduzir uma variante bastante interessante (…)
GitHub fluxo de trabalho_executar evento desencadeador
Além de pull_request_target, o GitHub fornece outro evento de gatilho: fluxo de trabalho_executar. Este evento permite execução de um pipeline condicionado a outro pipelineexecução.
fluxo de trabalho_executar e pull_request_target os gatilhos são semelhantes em um aspecto: ambos serão executados em modo privilegiado e, apesar das modificações de PR, a base pipeline será executado !!
Vamos ver nosso atual pipeline:
name: PR TARGET CI
on:
pull_request_target:
branches: [ main ]
env:
MY_SECRET: ${{ secrets.MY_SECRET }}
jobs:
prt_build_test_and_merge:
runs-on: ubuntu-latest
steps:
# checkout PR code
- name: Checkout repository
uses: actions/checkout@v4
with:
# This is to get the PR code instead of the repo code
ref: ${{ github.event.pull_request.head.sha }}
# Simulation of a compilation
- name: Building ...
run: |
mkdir ./bin
touch ./bin/mybin.exe
ls -lR
# Simulation of running tests
- name: Running tests ...
id : run_tests
run: |
echo Running tests..
chmod +x runtests.sh
./runtests.sh
echo Tests executed.
#
# Let’s omit the check conditions at this moment …
#
- name: pr_check_conditions_to_merge
[...]
A seção de construção é segura para D-PPE, mas a seção de teste ainda é vulnerável ao I-PPE.
O processo de pipeline em si é seguro para D-PPE devido ao pull_request_target acionar. Mas a etapa de teste ainda é vulnerável ao I-PPE devido à invocação de um script de shell externo.
Evitando EPI
O objetivo do acima pipeline é construir e testar o código contribuído, sendo seguro para o EPI.
Então .. Por que não dividir o pipeline em dois ? Um para construção e outro para teste..
- O 1st pipeline (Construir IC) gostaria verifique o código PR (para construí-lo), faça a construção e gere um artefato.
- O 2nd pipeline (Testar IC) gostaria verifique o código base (para evitar modificação do shell script) e execute os scripts originais no artefato.
- Para sincronizar o IC de teste pipeline para executar DEPOIS do Build CI pipeline, usaremos o fluxo de trabalho_executar desencadear.
Desta maneira:
- pipeline Construir IC is segura para ambos D-EPI (devido a pull_request_target) e I-EPI (porque não executa mais o shell script).
- pipeline Testar IC É também segura para ambos D-EPI (devido a fluxo de trabalho_executar) e I-EPI (porque verifica o código base para obter o script de shell original)
Vamos ver o código de ambos pipelineestá de acordo com essas modificações…
1st pipeline (Construir CI):
name: Build CI
on:
pull_request_target:
branches: [ main ]
env:
MY_SECRET: ${{ secrets.MY_SECRET }}
GITHUB_PAT: ${{ secrets.GH_PAT }}
jobs:
prt_build_and_upload:
runs-on: ubuntu-latest
steps:
- name: Checking out PR code
uses: actions/checkout@v4
if: ${{ github.event_name == 'pull_request_target' }}
with:
# This is to get the PR code instead of the repo code
ref: ${{ github.event.pull_request.head.sha }}
- name: Building ...
run: |
mkdir ./bin
touch ./bin/mybin.exe
# Save some PR info for later use by the 2nd pipeline
echo "${{github.event.pull_request.title}}" > ./bin/PR_TITLE.txt
echo "${{github.event.number}}" > ./bin/PR_ID.txt
# Upload the binary as a pipeline artifact
- name: Archive building artifacts
uses: actions/upload-artifact@v3
with:
name: archive-bin
path: |
bin
2nd pipeline (Teste IC):
ame: Test CI
on:
workflow_run:
workflows: [ 'PR TARGET CI' ]
types: [completed]
env:
MY_SECRET: ${{ secrets.MY_SECRET }}
GITHUB_PAT: ${{ secrets.GH_PAT }}
jobs:
deploy:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
# By default, checks out base code (not PR code)
- name: Checkout repository
uses: actions/checkout@v4
# Download the artifact
- name: 'Download artifact'
uses: actions/github-script@v6
with:
script: |
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name == "archive-bin"
})[0];
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
let fs = require('fs');
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/myartifact.zip`, Buffer.from(download.data));
# Unzip the artifact
- name: 'Unzip artifact'
run: |
unzip -o myartifact.zip
# Runs tests
- name: Running tests ...
id : run_tests
run: |
echo Running tests..
chmod +x runtests.sh
./runtests.sh
echo Tests executed.
#
# Let’s omit the check conditions at this moment …
#
- name: pr_check_conditions_to_merge
[...]
Uau… boa solução!! Mas….. Estamos seguros? Receio que não 😭
Na verdade, introduzimos uma nova vulnerabilidade!! Qual deles? Esse será o assunto do nosso próximo post 🙂… Fique ligado!!
PS: Desculpe, não consigo ficar quieto 🤐 ..Você já ouviu falar sobre Envenenamento por artefato ? 😂





