cve-2021-3156本地提权调试

base

影响版本
1.8.2 - 1.8.31p2
1.9.0 - 1.9.5p1

漏洞类型
因拷贝user_args的结束判断条件可绕过,导致的堆溢出

利用方法
溢出修改service_user中的name字段
在nss_load_library加载对应的"libnss_",ni->name,".so"时,劫持至自编译的so文件弹rootshell

源码和调试

sudo 1.8.21p2漏洞函数源码
path: plugins/sudoers/sudoers.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
846    /* Alloc and build up user_args. */
847 for (size = 0, av = NewArgv + 1; *av; av++)
848 size += strlen(*av) + 1;
849 if (size == 0 || (user_args = malloc(size)) == NULL) {
850 sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
851 debug_return_int(-1);
852 }
853 if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
854 /*
855 * When running a command via a shell, the sudo front-end
856 * escapes potential meta chars. We unescape non-spaces
857 * for sudoers matching and logging purposes.
858 */
859 for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
860 while (*from) {
861 if (from[0] == '\\' && !isspace((unsigned char)from[1]))
862 from++;
863 *to++ = *from++;
864 }
865 *to++ = ' ';
866 }
867 *--to = '\0';
868 } else {

修复后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
        /* 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);
}
if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL) &&
ISSET(sudo_mode, MODE_RUN)) {
/*
* 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] == '\\' && from[1] != '\0' && // add check from[1]!='\0'
!isspace((unsigned char)from[1])) {
from++;
}
if (size - (to - user_args) < 1) {
sudo_warnx(U_("internal error, %s overflow"),
__func__);
debug_return_int(NOT_FOUND_ERROR);
}
*to++ = *from++;
}
if (size - (to - user_args) < 1) {
sudo_warnx(U_("internal error, %s overflow"),
__func__);
debug_return_int(NOT_FOUND_ERROR);
}
*to++ = ' ';
}
*--to = '\0';
} else {

利用调试点
源码位置:nss/nsswitch.c
static int nss_load_library(service_user *ni)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
static int
nss_load_library (service_user *ni)
{
if (ni->library == NULL)
{
/* 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);
if (ni->library == NULL)
return -1;
}

if (ni->library->lib_handle == NULL) /*利用分支*/
{
/* 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);

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