Ubuntu 18.04
sudo-1.8.21p2
glibc-2.27
以该参数为例:
set args -s ‘\’ “AAAAAAAAAAAAAAAAAAA”
当form[0] == ‘\’时,from++ 当前字符串的结束符 ,to++ = from++ ,form指向下个字符串的起始位置(此时为A),while 循环内,将第二个字符串又写入to中,实际写入长度会超过堆块大小。
可利用点:
作者说有三种利用手段,我选择了可能最好复现的手段。
将堆块溢出到service_user 字段的name
nss_load_library 会使用该结构体来载入一个新的动态链接库。据说nss_load_librray在有堆溢出的情况下,常用于提权。之后可以继续深入研究该函数的调用过程和用处,
这个函数我们需要去命中
观察判断我们的目标在if (ni->library->lib_handle == NULL)内部,但是由于 ASLR 的存在,并且没有堆地址的泄露,所以我们没有办法确保ni->library->lib_handle 一定为空,但是在if (ni->library == NULL)的初始化情况,会新建一个service_user ,此时的ni->library->lib_handle 为空。
还有一个问题就是在载入过程中会遍历service_user 的next指针,所以不能随意将该字段写垃圾指针,应将其置为NULL。由于heap溢出可能会覆盖目标service_user结构体之前的service_user 导致遍历时出错。所以尽量覆盖到第一个service_user 来确保成功利用。
所以堆溢出覆盖service_user的数据为
在文章他们用名称为systemd的service_user ,从复现的角度来看我们也选择该名称的service_user ,当然也应该明白为什么,将这一步留到之后的分析中。
在文章中作者,在内存空间搜索了“systemd ” 和 “mymachine”来定位待溢出的目标,但是我们通过搜索字符串我们发现只能找到systemd的堆块。
我们知道service_user 是个单链表,这里通过next指针将这条service_user 链表还原。
0x563f627d6c58该地址并非一个堆块地址,所以猜测是一个存放service_user *的堆空间那么这个链有两个结构。
而name == “systemd “的第二个service_user链表,链上仅有一个结构体。
但是调试exp时发现,实际使用的溢出的目标堆块地址均不在这两条链上,可能在后期研究nss_load_library 时,会得到解答。也可以通过next指针回溯当时堆块所在的service_user 链。
由于name=”x/x”
按照拼接规则
实际会加载libnss_x/s.so.2 的动态链接库, 动态链接库代码:
本次复现还残留了不少问题
本次复现算是踏上了从ctf到真实环境漏洞的第一步, 希望锲而不舍。
https://www.kalmarunionen.dk/writeups/sudo/
https://blog.qualys.com/vulnerabilities-research/2021/01/26/cve-2021-3156-heap-based-buffer-overflow-in-sudo-baron-samedit
https://github.com/Rvn0xsy/CVE-2021-3156-plus
static
int
set_cmnd(void){
.....
/
/
获取所有命令行参数的长度
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
);
}
/
/
将命令行参数复制到user_args 。
for
(to
=
user_args, av
=
NewArgv
+
1
; (
from
=
*
av); av
+
+
) {
while
(
*
from
) {
/
/
漏洞点,当以反斜杠结尾时造成heap overflow
if
(
from
[
0
]
=
=
'\\'
&& !isspace((unsigned char)
from
[
1
]))
from
+
+
;
*
to
+
+
=
*
from
+
+
;
}
*
to
+
+
=
' '
;
}
*
-
-
to
=
'\0'
;
static
int
set_cmnd(void){
.....
/
/
获取所有命令行参数的长度
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
);
}
/
/
将命令行参数复制到user_args 。
for
(to
=
user_args, av
=
NewArgv
+
1
; (
from
=
*
av); av
+
+
) {
while
(
*
from
) {
/
/
漏洞点,当以反斜杠结尾时造成heap overflow
if
(
from
[
0
]
=
=
'\\'
&& !isspace((unsigned char)
from
[
1
]))
from
+
+
;
*
to
+
+
=
*
from
+
+
;
}
*
to
+
+
=
' '
;
}
*
-
-
to
=
'\0'
;
typedef struct service_user
{
/
*
And the link to the
next
entry.
*
/
struct service_user
*
next
;
/
*
Action according to result.
*
/
lookup_actions actions[
5
];
/
*
Link to the underlying library
object
.
*
/
service_library
*
library;
/
*
Collection of known functions.
*
/
void
*
known;
/
*
Name of the service (`files
', `dns'
, `nis', ...).
*
/
char name[
0
];
} service_user;
typedef struct service_user
{
/
*
And the link to the
next
entry.
*
/
struct service_user
*
next
;
/
*
Action according to result.
*
/
lookup_actions actions[
5
];
/
*
Link to the underlying library
object
.
*
/
service_library
*
library;
/
*
Collection of known functions.
*
/
void
*
known;
/
*
Name of the service (`files
', `dns'
, `nis', ...).
*
/
char name[
0
];
} service_user;
static
int
nss_load_library (service_user
*
ni)
{
if
(ni
-
>library
=
=
NULL)
{
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];
/
/
构建动态库的名称 libnss_
*
.so
__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. Try a fallback.
*
/
int
n
=
__snprintf(shlib_name, shlen,
"libnss_%s.so.%d.%d"
,
ni
-
>library
-
>name, __GLIBC__, __GLIBC_MINOR__);
if
(n >
=
shlen)
ni
-
>library
-
>lib_handle
=
NULL;
else
ni
-
>library
-
>lib_handle
=
__libc_dlopen (shlib_name);
if
(ni
-
>library
-
>lib_handle
=
=
NULL)
{
/
*
Ok, really fail now.
*
/
ni
-
>library
-
>lib_handle
=
(void
*
)
-
1l
;
__set_errno (saved_errno);
}
}
static
int
nss_load_library (service_user
*
ni)
{
if
(ni
-
>library
=
=
NULL)
{
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];
/
/
构建动态库的名称 libnss_
*
.so
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!