In ons vorige bericht, we hebben gezien hoe je Direct Poisoned kunt detecteren en ertegen kunt beschermen Pipeline Uitvoering (D-PPE). We hebben ook gezien hoe je die kwetsbaarheid kunt detecteren met behulp van Xygeni-scanner, evenals enkele beschermingsmechanismen.
vergiftigde Pipeline Executie (PPE) wordt geproduceerd wanneer de aanvaller het pipeline logica op twee manieren:
- Door het CI-configuratiebestand (de pipeline) -> Directe PBM (D-PBM)
- Door bestanden te wijzigen waarnaar wordt verwezen door de pipeline (bijvoorbeeld: scripts waarnaar wordt verwezen vanuit de pipeline configuratiebestand) -> Indirecte PBM (I-PBM)
In dit bericht gaan we dieper in op indirecte PBM. Maar laten we eerst, als aanvulling op mijn vorige bericht, eerst kijken hoe GitHub de uitvoering beheert van pipelines en wat zijn de beschermingsmechanismen tegen D-PPE.
Hoe beschermt GitHub de uitvoering van pipelineKomt het van PR's?
Hoe werkt GitHub met betrekking tot het uitvoeren van gewijzigde pipelines?
Gewijzigd pipelines kunnen afkomstig zijn van Pushes of Pull Requests (PR). Als een belangrijke best practice wordt het ten zeerste aanbevolen om elke directe 'push' naar een beschermde branch te vermijden en Pull Requests als een mechanisme om een zekere mate van controle af te dwingen voordat bijgedragen code wordt geaccepteerd.
Pull Requests kan uit twee verschillende bronnen komen:
- PR's komen vandaan vorken
- PR's komen vandaan takken
PR's van vorken kan van beide afkomstig zijn publiek or privaat opslagplaatsen.
Omdat we te maken hebben met PPE (Poisoned Pipeline Uitvoering) is ons belangrijkste punt niet de “acceptatie” van een PR, maar de uitvoering van een gewijzigde PR pipeline tijdens het acceptatie-/goedkeuringsproces van de PR. De kern van een PBM-aanval is de onbedoelde uitvoering van een ‘kwaadwillige’ wijziging pipeline.
In een paar woorden: Vergiftigd Pipeline Uitvoering (PPE) wordt geproduceerd wanneer de aanvaller kan het pipeline logica.
Er zijn er twee varianten:
- Directe PBM (D-PPE): In een D-PPE-scenario, de aanvaller wijzigt het CI-configuratiebestand in een repository waartoe ze toegang hebben, door de wijziging rechtstreeks naar een onbeschermde externe branch op de repo te pushen, of door een PR in te dienen met de wijziging van een branch of een fork. Sinds de CI pipeline uitvoering wordt gedefinieerd door de opdrachten in het gewijzigde CI-configuratiebestand; de kwaadaardige opdrachten van de aanvaller worden uiteindelijk uitgevoerd in het build-knooppunt zodra de build is voltooid pipeline wordt geactiveerd.
- Indirecte PBM (I-PPE): In bepaalde gevallen is de mogelijkheid van D-PPE niet beschikbaar voor een tegenstander met toegang tot een SCM repository (bijvoorbeeld als de pipeline is geconfigureerd om het CI-configuratiebestand uit een afzonderlijke, beveiligde vertakking in dezelfde repository op te halen). In een dergelijk scenario, in plaats van de pipeline zelf injecteert een aanvaller kwaadaardige code in bestanden waarnaar wordt verwezen door de pipeline (bijvoorbeeld: scripts waarnaar wordt verwezen vanuit de pipeline configuratiebestand)
In beide gevallen, GitHub zal het gewijzigde pipeline zonder dat een eerdere beoordeling of goedkeuring nodig is.
PR's vanaf vorken publiek rust
Met GitHub kunt u het gedrag tijdens de verwerking configureren PR's afkomstig van forks in openbare repository's.
Wanneer een PR afkomstig is van een fork, dwingt GitHub altijd een bepaald niveau van ‘goedkeuring’ af voordat de actie wordt uitgevoerd. pipeline verbonden aan de PR. Dit goedkeuringsniveau wisselt van een zwakke naar een strikte goedkeuring.
At Org-niveau (Org>>Instellingen>>Acties>>Algemeen), kunt u kiezen uit verschillende “goedkeurings”-opties:
De strengste is de laatste (“Vereist goedkeuring van alle externe medewerkers”) Omdat GitHub altijd goedkeuring vereist als de PR afkomstig is van vorken van externe medewerkers.
Maar zelfs in dit strikte geval zijn die er wel verschillen tussen bijdragers met lees- en schrijfrechten.
- Wanneer de PR afkomstig is van a dit artikel lezen gebruiker, de uitvoering van de pipeline is gestopt totdat er goedkeuring is voor de wijzigingen. Als de goedkeuring in orde is, wordt het aangepast pipeline is geëxecuteerd.
- Wanneer de PR afkomstig is van a schrijven gebruiker, de goedkeuring is niet nodig en de gewijzigd pipeline wordt altijd uitgevoerd !!
Concluderend: PR's afkomstig van forks op openbare repository's zijn lichtelijk beschermd tegen PPE. Er is enige bescherming tegen externe (lees)gebruikers, maar niets gerelateerd aan interne (schrijf)gebruikers.
Hoe zit het met PR's afkomstig van forks van privérepo's?
PR's vanaf vorken privaat rust
In dit scenario biedt GitHub enkele nuttige configuratie-instellingen.
Bovenstaande instellingen kunnen worden geconfigureerd op Org of repo niveau.
. er is geen optie aangevinkt, GitHub zal dat doen om goedkeuring vragen en het zal de gewijzigde versie niet uitvoeren pipeline. Dit is de veiligste configuratie!!
Het onveiligste configuratie is wanneer "Workflows uitvoeren vanaf een fork pull request" is nagekeken. In dit geval, hetzelfde voor zowel lees- als schrijfgebruikers, zal Github automatisch de gewijzigde versie uitvoeren pipeline!! En deze situatie kan zelfs zijn erger als "Schrijftokens naar workflows verzenden vanaf een fork pull requests"En"Stuur geheimen en variabelen naar workflows vanuit een fork pull requests' zijn aangevinkt. Doe dit niet tenzij dit duidelijk gerechtvaardigd is!!
Als "Goedkeuring voor vork nodig pull request workflows” is aangevinkt, is de bovenstaande situatie enigszins verbeterd: GitHub zal om goedkeuring vragen en de gewijzigde versie niet uitvoeren pipeline voor de leesgebruiker, maar het wordt nog steeds uitgevoerd voor een schrijfgebruiker.
Vorken gezien, hoe zit het? PR's afkomstig van vestigingen?
PR's van takken
Om dit scenario te beschermen, moet u erop vertrouwen Regels voor branchebescherming.
Op repo-niveau kunt u voor elk filiaal filiaalbeschermingsregels maken. Deze regels voegen wat toe beperkingen voor de wijziging van beschermde takken.
Hoewel u een regel configureert om “Vereisen een pull request voor het samenvoegen"En"Goedkeuringen vereisen" de gewijzigde pipeline wordt automatisch uitgevoerd bij het maken van PR.De “goedkeuring” is alleen van toepassing op de samenvoegactie.
Hoe zit het met indirect vergiftigd? Pipeline Uitvoering
Zoals we hierboven hebben gezien, kunnen D-PBM’s worden verzacht door het gebruik ervan pull_request_target, Maar geldt niet voor I-PPE.
Als u pull_request_target gebruikt, is de standaardafrekening de basiscode. Maar als u bepaalde controles op de bijgedragen code (PR-code) wilt valideren, moet u de PR-code expliciet afrekenen. Daarom, als de PR-code een shellscript heeft gewijzigd dat is aangeroepen door de pipeline, de “basis” (veilig) pipeline zal het “gewijzigde” shellscript aanroepen → Indirecte PPE!!
De oplossing hiervoor is iets ingewikkelder (er bestaat geen wondermiddel zoals pull_request_target).
Het pipeline is nu veilig voor D-PPE omdat we pull_request_target gebruiken. Maar het is nog steeds kwetsbaar voor I-PPE.
In ons testvoorbeeld moeten we in principe de PR-code uitchecken om de build te maken, maar de tests worden uitgevoerd op het artefact dat door de build wordt gegenereerd.
Dus .. waarom bekijk je niet beide codebases?
- Bekijk de PR-code, want dit is de bijgedragen code die we willen bouwen en testen
- Afrekenen Basiscode om de originele versie van het uit te voeren pipeline en de build-/testscripts
Dit kan gedaan worden door het uitchecken van die codebases naar verschillende mappen: de basiscode kan worden uitgecheckt naar de hoofdmap en de PR naar een andere map. In dit geval zouden we de build en het testscript vanuit de hoofdmap uitvoeren op basis van de code die in de nieuwe map is geplaatst.
Dit is natuurlijk een makkelijke oplossing!! Maar voor leerdoeleinden zou ik een behoorlijk interessante variant willen introduceren (…)
GitHub workflow_run gebeurtenis triggeren
Behalve pull_request_target, biedt GitHub nog een triggergebeurtenis: workflow_run. Dit evenement maakt het mogelijk uitvoering van een pipeline geconditioneerd voor een ander pipeline's executie.
workflow_run en pull_request_target triggers zijn in één aspect vergelijkbaar: beide worden uitgevoerd in de bevoorrechte modus en, ondanks de PR-wijzigingen, de basis pipeline zal worden uitgevoerd !!
Laten we onze stroom eens bekijken 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
[...]
Het bouwgedeelte is veilig voor D-PPE, maar het testgedeelte is nog steeds kwetsbaar voor I-PPE.
Het pipeline zelf is veilig voor D-PPE vanwege de pull_request_target trekker. Maar de teststap is nog steeds kwetsbaar voor I-PPE vanwege het aanroepen van een extern shellscript.
I-PPE vermijden
Het doel van het bovenstaande pipeline is het bouwen en testen van de bijgedragen code, zodat deze veilig is voor PPE.
Dus .. Waarom splitsen we de pipeline in twee ? Eén om te bouwen en één om te testen..
- de 1st pipeline (Bouw CI) zou bekijk de PR-code (om deze te bouwen), maak de build en genereer een artefact.
- De 2nd pipeline (Test-CI) zou check de basiscode af (om wijziging van het shell-script te voorkomen) en voer de originele scripts uit op het artefact.
- Om het TestCI te synchroniseren pipeline om NA het Build CI uit te voeren pipeline, we zullen de gebruiken workflow_run aansteken, veroorzaken.
Op deze manier:
- pipeline Bouw CI is veilig aan beide D-PPE (door pull_request_target) en I-PPE (omdat het niet langer het shellscript uitvoert).
- pipeline Test-CI Ook veilig aan beide D-PPE (door workflow_run) en I-PPE (omdat het de basiscode uitcheckt om het originele shell-script te krijgen)
Laten we de code van beide bekijken pipelines volgens deze wijzigingen …
1e pipeline (Bouw 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
2e pipeline (Test-CI):
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
[...]
Wauw... mooie oplossing!! Maar….. Zijn we veilig? Ik ben bang van niet 😭
We hebben inderdaad een nieuwe kwetsbaarheid geïntroduceerd!! Welke? Dit zal het onderwerp zijn van ons volgende bericht 🙂 … Blijf op de hoogte!!
PS: Sorry, ik kan niet zwijgen 🤐 ..Heb je er al van gehoord Artefactvergiftiging ?





