myrkytetty-pipeline-suoritus-II

Sukella syvään CI/CD Pipelines Haavoittuvuudet (II): Epäsuorasti myrkytetty Pipeline Toteutus (I-PPE)

Edellisessä viestissämme Näimme, miten suora myrkytys havaitaan ja miten siltä suojaudutaan. Pipeline Toteutus (D-PPE). Näimme myös, miten tämä haavoittuvuus havaitaan käyttämällä Xygeni-skannerisekä joitakin suojamekanismeja. 

 Myrkytetty Pipeline Toteutus (PPE) syntyy, kun hyökkääjä voi muokata pipeline logiikka kahdella tavalla:

  • Muokkaamalla CI-määritystiedostoa ( pipeline) -> Suora henkilönsuojain (D-PPE)
  • Muokkaamalla tiedostoja, joihin viittaa pipeline (esimerkiksi: skriptit, joihin viitataan pipeline määritystiedosto) -> Epäsuorat henkilönsuojaimet (I-PPE)
pp2

Tässä postauksessa syvennymme epäsuoriin henkilönsuojauksiin. Mutta ennen sitä, ja täydennyksenä edelliseen postaukseeni, katsotaanpa ensin, miten GitHub hallitsee seuraavien suoritusta: pipelineja mitkä ovat suojamekanismit D-PPE:tä vastaan?

Miten GitHub suojaa suoritusta pipelinetuleeko se PR:ltä?

Miten GitHub toimii muokatun suorittamisen suhteen? pipelines?

Muokattu pipelinevoi tulla työntöistä tai Pull Requests (PR). Tärkeimpänä parhaana käytäntönä on vahvasti suositeltavaa välttää kaikkea suoraa "työntöä" suojattuun haaraan ja käyttää Pull Requests mekanismina, jolla varmistetaan jonkinlainen tarkistus ennen minkä tahansa muun koodin hyväksymistä. 

Pull Requests voi tulla kahdesta eri lähteestä:

  • PR:t tulevat haarukat
  • PR:t tulevat oksat

PR:t alkaen haarukat voi tulla kummastakin julkinen or yksityinen arkistot.

Koska kyseessä ovat myrkytetyt henkilönsuojaimet Pipeline Suoritus), pääasiamme ei ole PR:n "hyväksyminen", vaan muokatun pipeline PR:n hyväksymis-/hyväksyntäprosessin aikana. PPE-hyökkäyksen ytimessä on tahaton "haitallisen" muokatun toiminnon suorittaminen pipeline. 

Muutamalla sanalla, myrkytetty Pipeline Toteutus (PPE) tuotetaan, kun hyökkääjä voi muokata pipeline logiikka.

On kaksi variantit:

  • Suora henkilönsuojain (D-PPE): D-PPE-skenaariossa hyökkääjä muokkaa CI-määritystiedostoa repositoriossa, johon heillä on pääsy, joko lähettämällä muutoksen suoraan repositorion suojaamattomaan etähaaraan tai lähettämällä muutoksen sisältävän PR:n haarasta tai haaraumasta. Koska CI pipeline Vaikka suoritus määritellään muokatun CI-määritystiedoston komennoilla, hyökkääjän haitalliset komennot suoritetaan lopulta rakennussolmussa, kun rakennus on valmis. pipeline käynnistyy.
  • Epäsuorat henkilönsuojaimet (I-PPE): Tietyissä tapauksissa D-PPE:n käyttö ei ole mahdollista vastustajalle, jolla on pääsy SCM arkisto (esim. jos pipeline on määritetty hakemaan CI-määritystiedosto erillisestä, suojatusta haarasta samassa tietovarastossa). Tällaisessa tilanteessa myrkyttämisen sijaan pipeline itse hyökkääjä lisää haitallista koodia tiedostoihin, joihin viittaa pipeline (esimerkiksi: skriptit, joihin viitataan pipeline määritystiedosto)

Kummassakin tapauksessa, GitHub suorittaa muokatun pipeline ilman aiempaa arviointia tai hyväksyntää.

PR:t haarukoista julkinen repomyynnit

GitHub mahdollistaa toiminnan konfiguroinnin prosessoinnin aikana Julkisten repojen haarukoista tulevat PR:t.

Kun PR tulee haarautumasta, GitHub pakottaa aina jonkinasteisen "hyväksynnän" ennen sen suorittamista. pipeline liittyy PR:äänTämä hyväksynnän taso vaihtelee heikosta tiukkaan hyväksyntään.

At Organisaatiotaso (Organisaatio>>Asetukset>>Toiminnot>>Yleiset), voit valita useista hyväksymisvaihtoehdoista:

ppe3

Tiukin on viimeinen ("Vaadi kaikkien ulkopuolisten yhteistyökumppaneiden hyväksyntä”) koska GitHub vaatii aina hyväksynnän, kun PR tulee ulkopuolisten yhteistyökumppaneiden forkeista. 

Mutta jopa tässä tiukassa tapauksessa on olemassa eroja luku- ja kirjoitusoikeuksilla varustettujen yhteistyökumppaneiden välillä.

  • Kun PR tulee jostakin luettu käyttäjä, täytäntöönpano pipeline on PYSÄYTETTY kunnes muutokset on hyväksytty. Jos hyväksyntä on ok, niin muokattu pipeline suoritetaan. 
  • Kun PR tulee jostakin kirjoittaa käyttäjä, hyväksyntää ei tarvita ja muokattu pipeline toteutetaan aina!! 
pp4

Yhteenvetona voidaan todeta, että julkisten repositorioiden haarukoista tulevat PR:t ovat kevyesti suojattuja henkilökohtaisia ​​suojauksia (PPE) vastaan. Ulkoisia (luku)käyttäjiä vastaan ​​on jonkin verran suojausta, mutta sisäisiä (kirjoitus)käyttäjiä vastaan ​​ei ole mitään.

Entä Yksityisten repojen haarautumisista tulevat PR:t?

PR:t haarukoista yksityinen repomyynnit

Tässä tilanteessa GitHub tarjoaa joitakin hyödyllisiä määritysasetuksia.

ppe9

Yllä olevat asetukset voidaan määrittää joko kohdassa urut tai repo tasolla.

Kun mitään vaihtoehtoa ei ole valittuGitHub tulee pyytää hyväksyntää ja se ei suorita muokattua pipelineTämä on turvallisin kokoonpano!!

turvattomin kokoonpano on milloin "Suorita työnkulut haarautumasta pull request” on valittunaTässä tapauksessa, sekä luku- että kirjoitusoikeuksin varustetuille käyttäjille, Github suorittaa muokatun automaattisesti. pipelineJa tämä tilanne voi olla jopa huonompi jos "Lähetä kirjoitustokenit työnkulkuihin haarautumisesta pull requests"Ja"Lähetä salaisuuksia ja muuttujia työnkulkuihin haarautumisesta pull requests” on valittu. Älä tee tätä, ellei se ole selvästi perusteltua!!

Jos “Vaaditaan hyväksyntä haarautumiselle pull request työnkulkuja” on valittuna, yllä oleva tilanne paranee jonkin verran: GitHub pyytää hyväksyntää eikä suorita muokattua pipeline lukukäyttäjälle, mutta se suorittaa sen silti kirjoituskäyttäjälle.

ppe6

Haarukat nähty, entä sitten sivukonttoreista tulevat PR:t?

PR:t alkaen oksat

Suojautuaksesi tässä tilanteessa sinun on luotettava Sivukonttorin suojaussäännöt

Repotasolla voit luoda haaran suojaussääntöjä mille tahansa haaralle. Nämä säännöt lisäävät joitakin suojattujen haarojen muokkaamisen rajoitukset.

Vaikka määrität säännön arvoon ”Vaadi a pull request ennen yhdistämistä"Ja"Vaaditaan hyväksynnät" muokattu pipeline suoritetaan automaattisesti PR:n luomisen yhteydessä”Hyväksyntä” koskee vain yhdistämistoimintoa.

ppe7

Entä epäsuora myrkytys Pipeline Teloitus

Kuten yllä näimme, D-PPE:tä voidaan lieventää käyttämällä pull_request_kohde, mutta se Ei koske henkilönsuojaimia.

Jos käytät pull_request_target-metodia, oletusarvoinen uloskirjaus tehdään peruskoodista. Mutta jos haluat validoida joitakin tarkistuksia lisätylle koodille (PR-koodi), sinun on erikseen uloskirjattava PR-koodi. Jos PR-koodi on muokannut jotakin komentosarjaa, jonka on kutsunut... pipeline, "tukikohta" (turvallinen) pipeline käynnistää "muokatun" komentosarjan → Epäsuora PPE!!

Ratkaisu tähän on hieman monimutkaisempi (ei ole olemassa mitään ihmelääkettä kuten pull_request_target). 

Yhtiömme pipeline on nyt turvallinen D-PPE:lle, koska käytämme pull_request_target-metodia. Mutta se on edelleen haavoittuvainen I-PPE:lle. 

Testiesimerkissämme meidän täytyy pohjimmiltaan tarkistaa PR-koodi koontiversion tekemiseksi, mutta testit suoritetaan koonnin luomalle artefaktille. 

Joten .. Miksi et tarkista molempia koodikantoja? 

  • Tarkista PR-koodi, koska on se koodi, jota haluamme rakentaa ja testata.
  • Checkout Base -koodi alkuperäisen version suorittamiseksi pipeline ja rakennus-/testausskriptit 

Tämä voidaan tehdä noiden koodikantojen siirtäminen eri kansioihinPeruskoodi voidaan siirtää juurikansioon ja testiversio eri kansioon. Tässä tapauksessa suorittaisimme koonti- ja testiskriptin juurikansiosta uuteen kansioon sijoitetulle koodille.

Tämä on helppo ratkaisu, tietenkin!! Mutta oppimistarkoituksessa haluaisin esitellä varsin mielenkiintoisen muunnelman (…) 

GitHub työnkulun_ajo laukaiseva tapahtuma

Lisäksi pull_request_kohdeGitHub tarjoaa toisen laukaisutapahtuman: työnkulun_ajoTämä tapahtuma mahdollistaa suoritus pipeline ehdollistunut toiseen pipelinesuoritus

työnkulun_ajo ja pull_request_kohde liipaisimet ovat samankaltaisia ​​yhdessä suhteessa: molemmat suoritetaan etuoikeutetussa tilassa ja PR-muutoksista huolimatta pohja pipeline teloitetaan!! 

Katsotaanpa nykyistä tilannettamme 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
        [...]
				
			

Rakennusosa on turvallinen D-PPE:lle, mutta testausosa on edelleen altis I-PPE:lle.

pipeline itsessään on turvallinen D-PPE:lle johtuen pull_request_kohde liipaisin. Mutta testivaihe on edelleen haavoittuvainen I-PPE:lle ulkoisen komentosarjan kutsumisen vuoksi.

Henkilönsuojainten välttäminen 

Yllä olevan tarkoitus pipeline on rakentaa ja testata luotua koodia henkilönsuojainten turvallisuuden varmistamiseksi. 

Joten .. Miksi ei jaeta pipeline kahteen? Yksi rakentamiseen ja toinen testaamiseen..

  • Ensimmäinen pipeline (Rakenna CI) olisi tarkista PR-koodi (sen rakentamiseksi), tee koonti ja luo artefakti.
  • 2nd pipeline (Testaa CI) olisi tarkista peruskoodi (jotta vältät komentosarjan muokkaamisen) ja suorita alkuperäiset skriptit artefaktia vastaan. 
  • Testi-CI:n synkronointi pipeline suoritettavaksi Build CI:n JÄLKEEN pipeline, käytämme työnkulun_ajo laukaista. 
ppe8

Tällä tavalla:

  • pipeline Rakenna CI is turvallista molemmille D-PPE (johdosta pull_request_kohde) Ja I-PPE (koska se ei enää suorita komentosarjaa).
  • pipeline Testaa CI On myös turvallista molemmille D-PPE (johdosta työnkulun_ajo) Ja I-PPE (koska se tarkistaa peruskoodin saadakseen alkuperäisen komentosarjan) 

Katsotaanpa molempien koodia pipelinenäiden muutosten mukaan…

1st pipeline (Rakenna 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
				
			

2. pipeline (Testin luottamusväli):

				
					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
        [...]

				
			

Vau… hieno ratkaisu!! Mutta… Olemmeko turvassa? Pelkäänpä ettemme 😭

Olemme tosiaankin esitelleet uuden haavoittuvuuden!! Minkä? Tästä kerromme seuraavassa postauksessamme 🙂 … Pysy kuulolla!! 

PS: Anteeksi, en voi olla hiljaa 🤐 ..Oletko kuullut tästä Artefaktien myrkytys ? 😂

Artefaktien myrkytys ja koodin injektointi

Sukella syvään CI/CD Pipelines Haavoittuvuudet (III)​

Suojautuminen artefaktien myrkytykseltä ohjelmistotodennuksilla

Sukella syvään CI/CD Pipelines Haavoittuvuudet (IV)​

Myrkytetty Pipeline Toteutus (PPE)

Sukella syvään CI/CD Pipelines Haavoittuvuudet (I)​
sca-työkalut-ohjelmisto-koostumusanalyysityökalut
Priorisoi, korjaa ja suojaa ohjelmistoriskisi
Hanki ilmainen tili.
Luottokorttia ei vaadita.

Turvaa ohjelmistokehityksesi ja -toimituksesi

Xygeni-tuotepaketin kanssa