
LID — انحراف سلامة لينكس: تجاوز AppArmor عبر إعادة كتابة مسار eBPF. التلاعب بوسائط استدعاء النظام قبل LSM بدون أي أثر تدقيق. "لينكس يحتضر"
```
██╗ ██╗██████╗
██║ ██║██╔══██╗
██║ ██║██║ ██║
██║ ██║██║ ██║
███████╗██║██████╔╝
╚══════╝╚═╝╚═════╝
```
— "لينكس يحتضر" —
اكتشاف منهجي لمسارات كود النواة التي تتجاوز ضمانات أمان LSM
لم تُخترق البوابة أبدًا. بل تم الالتفاف حولها.
إطار وحدة أمان لينكس لديه ضمان أساسي واحد استمر لأكثر من 20 عامًا:
وحدات الأمان يمكنها فقط إضافة قيود. لا يمكنها أبدًا إزالتها.
هذا الضمان صحيح. LID لا يخترقه.
LID يعثر على مسارات كود النواة التي تتجاوز خطافات LSM بالكامل — أنظمة فرعية تقوم بعمليات حساسة أمنيًا دون استشارة إطار LSM. الفحص الأمني صحيح. المشكلة هي أن النواة لا تسأل أبدًا.
كل اكتشاف له بعدان متميزان لا ينبغي الخلط بينهما:
النقطة العمياء المعمارية. السؤال ليس "هل يمكن لمهاجم استغلال هذا؟"، بل:
إذا حدثت عملية حساسة أمنيًا ولم تقم طبقة الإنفاذ بتقييمها أبدًا، فإنك تعاني من فجوة رؤية — بغض النظر عما إذا كان بإمكان المهاجم إساءة استخدامها عمليًا اليوم. هذا مهم لـ الامتثال، الطب الشرعي، و افتراضات الدفاع المتعمق.
سؤال قابلية الاستغلال في العالم الحقيقي:
هذان الشيئان مختلفان. يمكن أن يكون الاكتشاف فجوة رؤية حرجة (مراقبتك عمياء) دون أن يكون تصعيد امتياز عملي (المهاجم يحتاج بالفعل إلى الجذر). على العكس، يمكن أن يكون الاكتشاف مسار تصعيد مباشر بمتطلبات مسبقة ضئيلة.
كل اكتشاف له متطلبات محددة من النواة/التكوين/الامتياز. إذا كانت بيئتك لا تتطابق، فلن يتكرر الاكتشاف.
كل اكتشاف يتبع نفس النمط:``` ┌─────────────────────────────────────────────────────────────┐ │ THE LID PATTERN │ │ │ │ Kernel Subsystem A LSM Framework │ │ (eBPF / io_uring / mount API) (AppArmor / SELinux) │ │ │ │ ┌───────────────────┐ │ │ │ Performs operation │──── should call ──── security_*() │ │ │ or manipulates │ but │ │ │ security input │ DOESN'T ██ │ │ └───────────────────┘ │ │ │ │ The LSM framework is correct. │ │ The kernel just doesn't always use it. │ └─────────────────────────────────────────────────────────────┘
Two kernel subsystems. Incompatible trust assumptions. One gap.
<br>
---
## LID-001: eBPF إعادة كتابة اسم المسار
**الاكتشاف الرئيسي.** يعيد كشف BPF kprobe على `do_sys_openat2` كتابة اسم الملف في ذاكرة المستخدم قبل أن تنسخه النواة. يقوم AppArmor بفحص المسار المعاد كتابته، ويمنح الوصول. لا يوجد أثر تدقيق.```
process: open("/tmp/secret.txt")
│
▼
do_sys_openat2()
│
★ LID kprobe fires here
│ bpf_probe_write_user()
│ rewrites "/tmp/secret.txt" → "/tmp/.bypass_link"
│
▼
getname_flags() ← kernel copies the (rewritten) path
│
▼
security_file_open() ← LSM hooks check "/tmp/.bypass_link"
│ AppArmor: ALLOW ✓
▼
VFS opens inode ← same file content (hard link)
│
▼
return fd to process ← success, zero audit trace
sudo ./scripts/check_prerequisites.sh sudo ./scripts/setup_env.sh make sudo ./scripts/setup_demo.sh sudo ./scripts/run_demo.sh
### إخراج توضيحي```
╔══════════════════════════════════════════════════════════╗
║ Phase 1: AppArmor ENFORCING — access should be DENIED ║
╚══════════════════════════════════════════════════════════╝
$ /tmp/test_reader
[-] DENIED: open() failed: Permission denied (errno=13)
╔══════════════════════════════════════════════════════════╗
║ Phase 2: Loading LID — BPF kprobe pathname rewrite ║
╚══════════════════════════════════════════════════════════╝
[*] BPF kprobe attached to do_sys_openat2
╔══════════════════════════════════════════════════════════╗
║ Phase 3: With LID active — access should be GRANTED ║
╚══════════════════════════════════════════════════════════╝
$ /tmp/test_reader
[+] SUCCESS: Read 44 bytes: SECRET_DATA=this_is_protected_content_12345
╔══════════════════════════════════════════════════════════╗
║ Phase 4: Stealth check — audit log inspection ║
╚══════════════════════════════════════════════════════════╝
$ dmesg | grep apparmor | grep DENIED
(empty — no denial was ever generated)
IORING_OP_MSG_RING مع IORING_MSG_SEND_FD تقوم بنقل واصفات الملفات بين حلقات io_uring دون استدعاء security_file_receive(). كل آلية نقل fd أخرى تقوم باستدعائها:
// security_file_receive() call from __receive_fd()
// security_file_receive() call from scm_recv()
// But io_uring does not call it
MSG_RING SEND_FD: __io_fixed_fd_install() ← NO security_file_receive() FIXED_FD_INSTALL: receive_fd() ← security_file_receive() ✓ SCM_RIGHTS: receive_fd() ← security_file_receive() ✓ binder: security_binder_transfer_file() ✓
**موقع الخلل:** `io_uring/msg_ring.c`، `io_msg_install_complete()` — تستدعي `__io_fixed_fd_install()` مباشرةً، متجاوزةً خطاف LSM.
**تم التحقق باستخدام ftrace:** `security_file_receive` يُطلق لـ SCM_RIGHTS ولكن ليس لـ MSG_RING.
**المتأثر:** Linux 5.18+ حتى v7.1-rc3 (غير مُصحح اعتبارًا من 2026-05-17).
**PoC:** [`findings/lid-002-iouring-msgring/msg_ring_bypass.c`](https://github.com/azqzazq1/lid/blob/HEAD/findings/lid-002-iouring-msgring/)
<br>
---
## LID-003: واجهة التركيب الجديدة تتجاوز AppArmor
واجهة التركيب الجديدة (`fsopen` + `fsconfig` + `fsmount` + `move_mount`) **لا تستدعي أبدًا `security_sb_mount()`** — وهو خطاف التركيب الوحيد الذي تنفذه AppArmor.```
OLD API: mount("proc", "/mnt", "proc", 0, NULL)
→ security_sb_mount() → AppArmor: DENY ✗
NEW API: fsopen("proc") → fsconfig(CMD_CREATE) → fsmount() → move_mount()
→ security_sb_kern_mount() → AppArmor: (not implemented) → ALLOW ✓
يسجل AppArmor 4 خطافات تثبيت. يسجل SELinux 14. يستخدم واجهة برمجة التثبيت الجديدة خطافات ينفذها SELinux فقط.
ثغرات إضافية تم العثور عليها:
open_tree(OPEN_TREE_CLONE): صفر خطافات أمان — يتجاوز سياسة التثبيت المرتبطmount_setattr(): لا يوجد خطاف LSM نهائيًا في النواةmove_mount() مع تثبيتات منفصلة: يرى AppArmor مسار مصدر NULLالتفاصيل: findings/lid-003-mount-api/
يقوم نظام رمز BPF الفرعي (Linux 6.9+) بتفويض قدرات BPF إلى العمليات غير المميزة في مساحات أسماء المستخدمين. تحدد النواة 9 خطافات BPF LSM. يقوم SELinux بتنفيذ جميع الـ 9 مع تطبيق avc_has_perm() الفعلي. ينفذ AppArmor صفر.```
Kernel defines 9 BPF LSM hooks:
┌─────────────────────────────────────────────────────────────────┐
│ bpf, bpf_map, bpf_prog, bpf_map_create, bpf_prog_load, │
│ bpf_token_create, bpf_token_free, bpf_token_cmd, │
│ bpf_token_capable │
└─────────────────────────────────────────────────────────────────┘
SELinux: ████████████████████████████ 9/9 implemented AppArmor: 0/9 implemented Smack: 0/9 implemented
على Ubuntu/Debian، يمكن لحاوية مع تفويض bpffs أن:
- تنشئ رموز BPF → لا يرى AppArmor شيئًا
- تستخدم الرموز لتحميل برامج التتبع → لا يرى AppArmor شيئًا
- تفوض CAP_PERFMON → تعطيل تخفيفات Spectre → لا يرى AppArmor شيئًا
**التفاصيل:** [`findings/lid-004-bpf-token/`](https://github.com/azqzazq1/lid/blob/HEAD/findings/lid-004-bpf-token/)
<br>
---
## LID-005: تجاوز خروج tc لـ AF_XDP من حاوية Docker الافتراضية
مسار الإرسال لوضع النسخ في AF_XDP (`xsk_generic_xmit` → `__dev_direct_xmit`) **يتجاوز مصنفات tc للخروج** على واجهة الحاوية. تم التحقق باستخدام tc u32 DROP-ALL — تم حظر AF_PACKET، ومرت AF_XDP.
**مع ذلك:** تم اختبارها مع Cilium v1.19 (مجموعة kind، سياسة شبكة خروج deny-all) — **لم يتم تجاوز Cilium.** يقوم Cilium بالفرض على دخول نظير veth جانب العقدة (`cil_from_container` عبر tcx/ingress) + لديه التحقق من عنوان IP المصدر. تم إسقاط حزم AF_XDP كـ "Invalid source ip" في `bpf_lxc.c:1603`. التأثير محدود على البيئات التي تعتمد فقط على مصنفات tc للخروج لسياسة الشبكة (Docker عادي + قواعد tc، بدون CNI).```
Normal TX path:
socket → dev_queue_xmit() → sch_handle_egress() → tc classifiers → driver
↑
ENFORCED (Cilium eBPF, Calico, tc u32)
AF_XDP copy-mode TX:
xsk_sendmsg() → xsk_generic_xmit() → __dev_direct_xmit() → driver
↑
SKIPPED (tc never runs)
تم التحقق منها ديناميكيًا على النواة 6.8.0 مع حاويات Docker الافتراضية:``` tc egress DROP-ALL on container eth0:
AF_PACKET sendto → ENOBUFS (BLOCKED by tc) AF_XDP sendto → 0 (BYPASS — 3 spoofed packets reached docker0 bridge)
تحمل الحزم رؤوس إيثرنت يتحكم بها المهاجم — MAC مزيفة، IP مزيفة — وتصل إلى جسر Docker على الرغم من سياسة DROP النشطة.
**PoC والتفاصيل:** [`findings/lid-005-afxdp-tc-bypass/`](https://github.com/azqzazq1/lid/blob/HEAD/findings/lid-005-afxdp-tc-bypass/)
<br>
---
## البنية: لماذا لا يستطيع BPF LSM إصلاح هذا```
LSM Hook Chain: call_int_hook(file_open, ...)
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Lockdown │──>│ Capability │──>│ AppArmor │──>│ BPF LSM │
│ return 0 │ │ return 0 │ │ return -13 │ │ (never │
│ (allow) │ │ (allow) │ │ (DENY) ██ │ │ reached) │
└─────────────┘ └─────────────┘ └──────┬───────┘ └─────────────┘
│
loop breaks
RC = -EACCES
★ The LSM framework is correct. No module can undo another's denial.
★ LID operates OUTSIDE the framework — before hooks run, or where hooks don't exist.
┌───────────────────────────────────────┐
│ THE ATTACK TIMELINE │
│ │
Syscall Entry │ ★ LID ─── manipulates input │ │ │ (security check sees wrong data) │ ▼ │ │ LSM Check │ LSM allows (or never runs) ✓ │ │ │ │ ▼ │ │ Syscall Exit │ ★ SunnyDayBPF ─── rewrites telemetry │ │ │ (monitoring sees wrong data) │ ▼ │ │ Audit/Log │ SIEM sees nothing ✓ │ │ │ │ Combined: ghost access │ └───────────────────────────────────────┘
<br>
## هيكل المشروع```
LID/
├── src/
│ ├── bpf/
│ │ └── lid.bpf.c # BPF kprobe — pathname rewriter (LID-001)
│ └── loader/
│ └── lid_loader.c # Userspace loader + event monitor
├── findings/
│ ├── lid-001-ebpf-pathname/ # eBPF pathname rewriting bypass
│ ├── lid-002-iouring-msgring/ # io_uring MSG_RING missing LSM hook
│ │ ├── msg_ring_bypass.c # PoC with ftrace verification
│ │ └── ADVISORY.md # Technical advisory
│ ├── lid-003-mount-api/ # New mount API AppArmor bypass
│ ├── lid-004-bpf-token/ # BPF token AppArmor/Smack zero coverage
│ └── lid-005-afxdp-tc-bypass/ # AF_XDP tc egress bypass from container
├── tests/
│ └── test_reader.c # Victim binary for LID-001 demo
├── scripts/
│ ├── check_prerequisites.sh # Verify system requirements
│ ├── setup_env.sh # Install build dependencies
│ ├── setup_demo.sh # Create demo environment
│ ├── run_demo.sh # Run full LID-001 demonstration
│ └── teardown.sh # Clean up everything
├── docs/
│ └── RESEARCH.md # Full technical research paper
├── publish/ # Articles for Medium, dev.to, etc.
├── Makefile
├── LICENSE
├── SECURITY.md
└── README.md
للحصول على توجيهات مفصلة للتخفيف، راجع docs/RESEARCH.md.
راجع مصفوفة قابلية إعادة الإنتاج للحصول على التفاصيل الكاملة لكل اكتشاف. الملخص:
|
Azizcan Daştan
|
تم نشر هذه الأداة لأغراض اختبار الأمان المصرح به والبحث والتعليم فقط. لا تستخدمها ضد أنظمة لا تملكها أو ليس لديك إذن كتابي صريح لاختبارها.
LID — لأن النزاهة لم تكن مقفلة أبدًا.
| الاكتشاف | فجوة الرؤية | التصعيد العملي |
|---|
| LID-001 | حرجة — AppArmor لا يرى شيئًا، سجل التدقيق فارغ، لا أثر طب شرعي صفري | محدودة — يتطلب الجذر أو CAP_BPF+CAP_PERFMON (مميز بالفعل). ليس تصعيد امتياز. التأثير: التهرب من السياسة + العمى التدقيقي. |
| LID-002 | عالية — security_file_receive() لا يُطلق أبدًا، نقل واصف الملف غير مرئي لجميع LSMs | عالية — يعمل من مساحة مستخدم غير مميزة عبر io_uring. يعبر حدود إنفاذ LSM دون أي امتياز. |
| LID-003 | عالية — تجاوز security_sb_mount()، سياسة تثبيت AppArmor هي كود ميت | متوسطة — يتطلب الوصول إلى مساحة اسم التثبيت (CAP_SYS_ADMIN في مساحة اسم المستخدم). متاح في العديد من تكوينات الحاويات. |
| LID-004 | حرجة — AppArmor لا يرى شيئًا، لا خطافات BPF (0/9)، لا أثر تدقيق لأي عملية رمز BPF | متوسطة — يتطلب تفويض bpffs من المضيف + CAP_BPF في مساحة اسم المستخدم. متاح في بيئات تشغيل الحاويات مع تفويض BPF (LXD/Incus). |
| LID-005 | متوسطة — مصنفات tc الصادرة على واجهة الحاوية لا تقيم أبدًا حركة مرور AF_XDP | محدودة — يتجاوز tc الصادر على eth0 للحاوية (تم التحقق). لم يتم تجاوز Cilium — يفرض على واجهة veth الجانبية للمضيف + التحقق من IP المصدر (تم اختباره). التأثير محدود لإعدادات Docker العادية مع تصفية tc الصادرة فقط. |
| الشرط | مطلوب | ملاحظات |
|---|
| إصدار النواة | 5.x+ | تم اختباره على 5.15، 6.1، 6.6، 6.8 |
CONFIG_BPF_SYSCALL | =y | افتراضي في جميع التوزيعات الرئيسية |
CONFIG_BPF_KPROBE_OVERRIDE | =y | أوبونتو/ديبيان يفعلانه، RHEL لا يفعل |
CONFIG_SECURITY_APPARMOR | =y | LSM المستهدف يجب أن يكون AppArmor |
| ملف تعريف AppArmor | وضع فرض، قاعدة منع على المسار المستهدف | يعمل مع أي قاعدة منع قائمة على المسار |
| الامتيازات | root أو CAP_BPF + CAP_PERFMON | لا يمكن تشغيله بدون امتيازات |
kernel.lockdown | none أو integrity | وضع confidentiality يمنع ربط kprobe |
kernel.unprivileged_bpf_disabled | غير ذي صلة | يتطلب CAP_BPF بغض النظر |
fs.protected_hardlinks | 0 للروابط عبر المستخدمين | 1 (الافتراضي) لا يزال يسمح بالروابط الصلبة لنفس المستخدم |
| SELinux بدلاً من AppArmor | لا يعمل | SELinux يعتمد على inode وليس على اسم المسار |
| الشرط | مطلوب | ملاحظات |
|---|
| إصدار النواة | 6.0+ | IORING_MSG_SEND_FD أُضيف في 6.0 |
CONFIG_IO_URING | =y | افتراضي في جميع التوزيعات الرئيسية |
| الامتيازات | لا شيء | يعمل من مساحة مستخدم غير مميزة |
sysctl io_uring_disabled | 0 (الافتراضي) | 2 يمنع غير المميزين، 1 يمنع الجميع |
| LSM المستهدف | أي (SELinux، AppArmor، Smack) | security_file_receive() هو خطاف LSM عام |
kernel.lockdown | غير ذي صلة | لا يشمل BPF |
| الشرط | مطلوب | ملاحظات |
|---|
| إصدار النواة | 6.9+ | رمز BPF أُدخل في 6.9 |
CONFIG_BPF_SYSCALL | =y | افتراضي في جميع التوزيعات الرئيسية |
CONFIG_SECURITY_APPARMOR | =y | افتراضي أوبونتو/ديبيان |
| bpffs مع التفويض | نعم | يجب على المضيف تثبيته مع خيارات delegate_* |
| الامتيازات | CAP_BPF في مساحة اسم المستخدم | متاح بسهولة لجذر userns |
| SELinux بدلاً من AppArmor | غير متأثر | SELinux ينفذ جميع خطافات BPF التسعة |
| الشرط | مطلوب | ملاحظات |
|---|
| إصدار النواة | 5.2+ | fsopen/fsmount أُدخلا في 5.2 |
CONFIG_SECURITY_APPARMOR | =y | فقط AppArmor متأثر |
| الامتيازات | CAP_SYS_ADMIN في مساحة اسم المستخدم | متاح مع unshare -m |
| SELinux بدلاً من AppArmor | لا يعمل | SELinux ينفذ security_sb_kern_mount() |
| بيئة تشغيل الحاوية | يعتمد على مرشح seccomp | fsopen يمنعه seccomp الافتراضي لـ Docker — قد لا يمنعه Podman/LXC |
| الشرط | مطلوب | ملاحظات |
|---|
| إصدار النواة | 4.18+ | AF_XDP أُدخل في 4.18 |
CONFIG_XDP_SOCKETS | =y | افتراضي في جميع التوزيعات الرئيسية |
| الامتيازات | CAP_NET_RAW فقط | افتراضي في Docker، مجموعات Kubernetes |
| بيئة تشغيل الحاوية | Docker، K8s، LXC | مجموعة الامتيازات الافتراضية |
| سياسة شبكة قائمة على tc | نعم | Cilium eBPF، Calico، tc u32/flower |
| البيئة | LID-001 | LID-002 | LID-003 | LID-004 | LID-005 |
|---|
| أوبونتو 22.04+ (AppArmor، افتراضي) | يعمل | يعمل | يعمل | يعمل (6.9+) | يعمل |
| ديبيان 12+ (AppArmor) | يعمل | يعمل | يعمل | يعمل (6.9+) | يعمل |
| RHEL/Fedora (SELinux) | لا | يعمل | لا | لا | يعمل |
lockdown=confidentiality | لا | يعمل | يعمل | جزئيًا | يعمل |
| مستخدم غير مميز | لا | يعمل | يعتمد على userns | يعتمد على تفويض bpffs | لا |
| حاوية (بدون CAP_BPF) | لا | يعتمد على io_uring | يعتمد على seccomp | لا | يعمل |
| حاوية (تم إسقاط CAP_NET_RAW) | لا | يعتمد | يعتمد | لا | لا |
| المعرف | المتجه | الهدف | ما يحدث |
|---|
| LID-001 | إعادة كتابة اسم مسار eBPF kprobe | AppArmor | يعيد kprobe كتابة اسم الملف قبل copy_from_user ← يتحقق AppArmor من المسار الخطأ |
| LID-002 | io_uring MSG_RING SEND_FD | SELinux، AppArmor، Smack | نقل واصف الملف يتجاوز security_file_receive() — كل نقل واصف آخر يستدعيه |
| LID-003 | واجهة برمجة التطبيقات للتثبيت الجديدة (fsopen/fsmount) | AppArmor | security_sb_mount() لا يُستدعى أبدًا — تجاوز خطاف التثبيت الوحيد لـ AppArmor |
| LID-004 | تفويض رمز BPF | AppArmor، Smack | صفر خطافات BPF — إنشاء الرمز، استخدامه، تفويض القدرة غير مرئي بالكامل |
| LID-005 | AF_XDP __dev_direct_xmit | tc الصادر، Cilium، Calico | وضع النسخ للإرسال من Docker الافتراضي يتجاوز مصنفات tc — تصل الحزم المزيفة إلى الجسر |
| المؤشر | الظهور | ملاحظات |
|---|
| سجل تدقيق AppArmor | لا شيء | لا يحدث رفض أبدًا |
auditd / journald | لا شيء | لا يتم إنشاء حدث أمني |
dmesg | تحذير لمرة واحدة | رسالة عامة bpf_probe_write_user |
bpftool prog list | مرئي | يُظهر kprobe المرفقة (إذا تم التحقق) |
| الرابط الثابت على القرص | قابل للكشف | find -samefile (بطيء، مزعج) |
| التخفيف | الفعالية | المقايضة |
|---|
kernel.lockdown=confidentiality | يمنع BPF بالكامل | يقتل المراقبة المشروعة |
تعطيل bpf_probe_write_user | يمنع إعادة كتابة مسار LID-001 | يتطلب إعادة بناء النواة |
fs.protected_hardlinks=1 | يحد من إنشاء الروابط الثابتة | افتراضي في النواة الحديثة |
مراقبة bpftool prog list | يكشف عن التحقيقات المرفقة | يتطلب استقصاء نشط |
| الترحيل إلى SELinux | يعتمد على inode، يهزم إعادة كتابة المسار | ترحيل معقد |
| تقييد io_uring | يمنع LID-002 | قد يعطل التطبيقات |
| الاكتشاف | الحد الأدنى للنواة | الصلاحيات | الهدف |
|---|
| LID-001 | 5.x+ | root / CAP_BPF+CAP_PERFMON | AppArmor فقط |
| LID-002 | 6.0+ | لا شيء | أي (SELinux، AppArmor، Smack) |
| LID-003 | 5.2+ | CAP_SYS_ADMIN (user ns ok) | AppArmor فقط |
| LID-004 | 6.9+ | CAP_BPF (user ns ok) | AppArmor، Smack |
| LID-005 | 4.18+ | CAP_NET_RAW (افتراضي Docker) | tc egress (Cilium، Calico، إلخ.) |