في مشاركتنا السابقة، لقد رأينا كيفية الكشف والحماية من التسمم المباشر Pipeline التنفيذ (D-PPE). لقد رأينا أيضًا كيفية اكتشاف هذه الثغرة الأمنية باستخدام ماسح زيجينيبالإضافة إلى بعض آليات الحماية.
تسمم Pipeline تنفيذ (معدات الوقاية الشخصية) يتم إنتاجه عندما يتمكن المهاجم من تعديل ملف pipeline المنطق بإحدى الطريقتين:
- عن طريق تعديل ملف تكوين CI (ملف pipeline) -> معدات الوقاية الشخصية المباشرة (D-PPE)
- عن طريق تعديل الملفات المشار إليها بواسطة pipeline (على سبيل المثال: البرامج النصية المشار إليها من داخل ملف pipeline ملف التكوين) -> معدات الوقاية الشخصية غير المباشرة (I-PPE)
في هذا المنشور، سوف نتعمق في معدات الوقاية الشخصية غير المباشرة. ولكن، قبل ذلك، واستكمالاً لمشاركتي السابقة، دعونا نرى أولاً كيف يدير GitHub تنفيذ pipelineوما هي آليات الحماية ضد D-PPE.
كيف يحمي GitHub تنفيذ pipelineقادم من العلاقات العامة؟
كيف يعمل GitHub فيما يتعلق بتنفيذ التعديلات pipelines?
تم التعديل pipelineيمكن أن تأتي s من Pushes أو Pull Requests (العلاقات العامة). كأفضل ممارسة رئيسية، يوصى بشدة بتجنب أي "دفع" مباشر إلى فرع محمي واستخدامه Pull Requests كآلية لفرض بعض المراجعة قبل قبول أي كود مساهم.
Pull Requests قد تصل من مصدرين مختلفين:
- العلاقات العامة القادمة من الشوك
- العلاقات العامة القادمة من الفروع
العلاقات العامة من الشوك يمكن أن تأتي إما من جمهور or خاص المستودعات.
لأننا نتعامل مع معدات الوقاية الشخصية (المسمومة Pipeline التنفيذ)، نقطتنا الرئيسية ليست "قبول" العلاقات العامة ولكن تنفيذ التعديل pipeline أثناء عملية قبول/موافقة ممثل العلاقات العامة. في قلب هجوم معدات الحماية الشخصية، يوجد تنفيذ غير مقصود لنسخة معدلة "ضارة". pipeline.
في بضع كلمات، مسموم Pipeline يتم تنفيذ (PPE) عندما يمكن للمهاجم تعديل pipeline منطق.
هناك اثنان المتغيرات:
- معدات الوقاية الشخصية المباشرة (D- معدات الوقاية الشخصية): في سيناريو D-PPE، يقوم المهاجم بتعديل ملف تكوين CI في المستودع الذي يمكنهم الوصول إليه، إما عن طريق دفع التغيير مباشرة إلى فرع بعيد غير محمي في الريبو، أو عن طريق إرسال طلب PR مع التغيير من فرع أو تفرع. منذ سي آي pipeline يتم تحديد التنفيذ من خلال الأوامر الموجودة في ملف تكوين CI المعدل، ويتم تشغيل الأوامر الضارة للمهاجم في النهاية في عقدة الإنشاء بمجرد الإنشاء pipeline يتم تشغيل.
- معدات الوقاية الشخصية غير المباشرة (معدات الوقاية الشخصية): في بعض الحالات، لا تتوفر إمكانية استخدام معدات الحماية الشخصية الدفاعية (D-PPE) للخصم الذي لديه إمكانية الوصول إلى SCM المستودع (على سبيل المثال إذا كان pipeline تم تكوينه لسحب ملف تكوين CI من فرع منفصل ومحمي في نفس المستودع). في مثل هذا السيناريو، بدلا من تسميم pipeline في حد ذاته، يقوم المهاجم بإدخال تعليمات برمجية ضارة في الملفات المشار إليها بواسطة ملف pipeline (على سبيل المثال: البرامج النصية المشار إليها من داخل ملف pipeline ملف الضبط)
في كلتا الحالتين، سيقوم GitHub بتنفيذ التعديل pipeline دون الحاجة إلى مراجعة أو موافقة سابقة.
العلاقات العامة من الشوك على جمهور اتفاقيات إعادة الشراء
يسمح GitHub بتكوين السلوك عند المعالجة العلاقات العامة القادمة من الشوكات في اتفاقيات إعادة الشراء العامة.
عندما تأتي العلاقات العامة من مفترق الطرق، يفرض GitHub دائمًا مستوى معينًا من "الموافقة" قبل تنفيذ الأمر. pipeline المرتبطة بالعلاقات العامة. ويتراوح هذا المستوى من الموافقة من الموافقة الضعيفة إلى الموافقة الصارمة.
At المستوى التنظيمي (المؤسسة>>الإعدادات>>الإجراءات>>عامة)، يمكنك الاختيار من بين عدة خيارات "للموافقة":
الأشد صرامة هو الأخير ("تتطلب موافقة من جميع المتعاونين الخارجيين") لأن GitHub سيتطلب دائمًا الموافقة عندما تأتي العلاقات العامة من شوكات من متعاونين خارجيين.
ولكن حتى في هذه الحالة الصارمة، هناك الاختلافات بين المتعاونين الذين لديهم أذونات القراءة والكتابة.
- عندما يأتي العلاقات العامة من أ اقرأ المستخدم إعدام pipeline توقف حتى تتم الموافقة على التغييرات. إذا كانت الموافقة على ما يرام، ثم تعديلها pipeline يتم تنفيذ.
- عندما يأتي العلاقات العامة من أ اكتب المستخدم ليست هناك حاجة للموافقة وتعديلها pipeline يتم تنفيذه دائما !!
في الختام، فإن العلاقات العامة القادمة من الشوكات الموجودة في المستودعات العامة محمية بشكل طفيف ضد معدات الوقاية الشخصية. هناك بعض الحماية ضد المستخدمين الخارجيين (القراءة)، ولكن لا شيء يتعلق بالمستخدمين الداخليين (الكتابة).
ماذا عن العلاقات العامة القادمة من الشوكات من اتفاقيات إعادة الشراء الخاصة?
العلاقات العامة من الشوك على خاص اتفاقيات إعادة الشراء
في هذا السيناريو، يوفر GitHub بعض إعدادات التكوين المفيدة.
يمكن تكوين الإعدادات أعلاه إما في المؤسسة أو في الريبو .
متى لم يتم تحديد أي خيارسوف يقوم جيثب بذلك اطلب الموافقة و لن يتم تنفيذ التعديل pipeline. هذا هو التكوين الأكثر أمانا!!
استخدم التكوين غير الآمن عندما "تشغيل سير العمل من الشوكة pull request" مفحوص. في هذه الحالة، كما هو الحال بالنسبة لمستخدمي القراءة والكتابة، سيقوم Github تلقائيًا بتنفيذ التعديل pipeline!! ويمكن أن يكون هذا الوضع متساويًا أسوأ لو "إرسال رموز الكتابة إلى سير العمل من الشوكة pull requests"و"إرسال الأسرار والمتغيرات إلى سير العمل من الشوكة pull requests"يتم فحصها. لا تفعل هذا إلا إذا كان له ما يبرره بشكل واضح!!
إذا "تتطلب الموافقة على الشوكة pull request سير العمل" تم تحديده، تم تحسين الوضع أعلاه إلى حد ما: سيطلب GitHub الموافقة ولن ينفذ التعديل pipeline لمستخدم القراءة، لكنه سيستمر في تنفيذه لمستخدم الكتابة.
رأيت الشوك، ماذا عن العلاقات العامة القادمة من الفروع?
العلاقات العامة من الفروع
لحماية هذا السيناريو يجب الاعتماد عليه قواعد حماية الفروع.
على مستوى الريبو، يمكنك إنشاء قواعد حماية الفرع لأي فرع. تضيف هذه القواعد بعضًا القيود المفروضة على تعديل الفروع المحمية.
على الرغم من قيامك بتكوين قاعدة على "تتطلب أ pull request قبل الدمج"و"تتطلب الموافقات"، المعدلة pipeline سيتم تنفيذه تلقائيًا عند إنشاء العلاقات العامة.ستنطبق "الموافقة" على إجراء الدمج فقط.
ماذا عن التسمم غير المباشر Pipeline التنفيذ
كما رأينا أعلاه، يمكن تخفيف D-PPE باستخدام pull_request_target، ولكنه لا ينطبق على I-PPE.
إذا كنت تستخدم pull_request_target، فسيكون الخروج الافتراضي هو الرمز الأساسي. ولكن إذا كنت تريد التحقق من صحة بعض عمليات التحقق من الكود المساهم (رمز PR)، فأنت بحاجة إلى التحقق بشكل صريح من رمز PR. لذلك، إذا قام رمز PR بتعديل أي برنامج نصي لـ Shell تم استدعاؤه بواسطة pipeline"القاعدة" (الآمنة) pipeline سوف يستدعي البرنامج النصي "المعدل" → معدات الوقاية الشخصية غير المباشرة !!
الحل لهذا الأمر أكثر تعقيدًا بعض الشيء (لا توجد حل سحري مثل pull_request_target).
pipeline أصبح الآن آمنًا لـ D-PPE لأننا نستخدم pull_request_target. لكنها لا تزال عرضة لـ I-PPE.
في مثال الاختبار الخاص بنا، نحتاج إلى التحقق من رمز PR بشكل أساسي لإجراء البناء، ولكن يتم تنفيذ الاختبارات على القطعة الأثرية التي تم إنشاؤها بواسطة البناء.
هكذا .. لماذا لا تتحقق من كلا قاعدتي التعليمات البرمجية؟
- رمز Checkout PR لأنه الرمز المساهم الذي نريد بناءه واختباره
- رمز Checkout الأساسي لتشغيل الإصدار الأصلي من pipeline والبرامج النصية للبناء/الاختبارات
قد يتم ذلك عن طريق التحقق من قواعد التعليمات البرمجية هذه إلى مجلدات مختلفة: قد يتم سحب الرمز الأساسي إلى المجلد الجذر، وPR إلى مجلد مختلف. في هذه الحالة، سنقوم بتنفيذ عملية الإنشاء والبرنامج النصي للاختبار من المجلد الجذر مقابل الكود الموجود في المجلد الجديد.
وهذا الحل سهل طبعا!! ولكن، لأغراض التعلم، أود أن أقدم متغيرًا مثيرًا للاهتمام (...)
GitHub جيثب: Workflow_run حدث مسبب
بالإضافة إلى pull_request_target، يوفر GitHub حدث تشغيل آخر: Workflow_run. يسمح هذا الحدث تنفيذ أ pipeline مشروط إلى آخر pipelineإعدام.
Workflow_run و pull_request_target المشغلات متشابهة في جانب واحد: سيتم تنفيذ كلاهما في الوضع المميز و، على الرغم من تعديلات العلاقات العامة، القاعدة pipeline سيتم اعدامه !!
دعونا نرى الحالية لدينا 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
[...]
يعتبر قسم البناء آمنًا لـ D-PPE، لكن قسم الاختبار لا يزال عرضة لـ I-PPE.
استخدم pipeline في حد ذاته آمن لـ D-PPE بسبب pull_request_target مشغل. لكن خطوة الاختبار لا تزال عرضة لـ I-PPE بسبب استدعاء برنامج نصي خارجي.
تجنب معدات الوقاية الشخصية
الغرض مما سبق pipeline هو بناء واختبار التعليمات البرمجية المساهمة، لتكون آمنة لمعدات الحماية الشخصية.
هكذا .. لماذا لا يتم تقسيم pipeline إلى قسمين ؟ واحد للبناء والآخر للاختبار..
- و1st pipeline (بناء سي آي) سيكون الخروج من رمز العلاقات العامة (لبنائه)، قم ببناء وإنشاء قطعة أثرية.
- و 2nd pipeline (اختبار سي) سيكون قم بالخروج من الكود الأساسي (لتجنب تعديل البرنامج النصي لـ Shell) وتنفيذ النصوص الأصلية ضد القطعة الأثرية.
- لمزامنة اختبار CI pipeline للتشغيل بعد Build CI pipeline، سوف نستخدم Workflow_run اثار.
في هذا الطريق:
- pipeline بناء سي آي is خزنة على كل D- معدات الوقاية الشخصية (بسبب pull_request_target) و معدات الوقاية الشخصية (لأنه لم يعد ينفذ البرنامج النصي Shell).
- pipeline اختبار سي هو أيضا خزنة على كل D- معدات الوقاية الشخصية (بسبب Workflow_run) و معدات الوقاية الشخصية (لأنه يقوم بالتحقق من الكود الأساسي للحصول على نص شل الأصلي)
دعونا نرى رمز كليهما 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 (اختبار سي):
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
[...]
واو... حل جميل!! ولكن….. هل نحن آمنون؟ اخاف لا 😭
وبالفعل أدخلنا ثغرة جديدة!! أيها؟ وهذا سيكون موضوع مقالتنا القادمة 🙂 … ترقبوا !!
PS: آسف، لا أستطيع الصمت 🤐 ..هل سمعت عن التسمم بالقطع الأثرية ؟ 😂





