首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CVE-2021-44731 linux snap 本地提权漏洞分析

CVE-2021-44731 linux snap 本地提权漏洞分析

原创
作者头像
万海旭
修改2022-03-06 10:31:42
3K0
修改2022-03-06 10:31:42
举报

一.漏洞简述

1.snap简述

ubuntu官方对snap的解释:https://cn.ubuntu.com/blog/what-is-snap-application

Snap是Canonical为使用Linux内核的操作系统开发的软件打包和部署系统。这些包(称为 snaps)和使用它们的工具 snapd 可在一系列 Linux 发行版中工作。

Canonical科能软件有限公司(主业FOSS开源项目的)

https://zh.wikipedia.org/wiki/Canonical

https://canonical.com/

Snap可包含一个或多个服务,支持cli(命令行)应用,GUI图形应用以及无单进程限制。因此,你可以单个snap下调用一个或多个服务。对于某些多服务的应用来说,非常方便。snap间相互隔离,可以通过interface(接口)定义来做资源交换。interface被用于让snap可访问OpenGL加速,声卡播放、录制,网络和HOME目录。Interface由slot和plug组成即提供者和消费者。

snap资源交换机制,通过interface进行资源交换
snap资源交换机制,通过interface进行资源交换

ubuntu机器下查询下snap是啥,可以看到有许多*.snap结尾的文件

snap文件路径
snap文件路径

归纳下,snap文件路径默认在以下三个路径

/var/lib/snapd/snaps
/var/lib/snapd/seed
/tmp/snap.docker/tmp

2.snap-confine简述

snap-confine(安装的一个 SUID-root 程序) 默认情况下在 Ubuntu 上。Snap 是由适用于使用 Linux 内核的操作系统的规范。称为 snaps,以及使用它们的工具 snapd,工作跨一系列 Linux 发行版并允许上游软件。开发人员将他们的应用程序直接分发给用户。快照是在沙箱中运行的独立应用程序访问主机系统。

snap-confine 是 snapd 内部使用的一个程序,用于构建snap 应用程序的执行环境。”(man snap-confine)

发现和利用 snap-confine 中的漏洞已经极具挑战性(尤其是在默认安装的 Ubuntu 中), 因为 snap-confine 使用了一种非常防御性的编程风格,AppArmor 配置文件、seccomp 过滤器、挂载命名空间和两个 Go 帮助程序。 最终,我们发现了两个漏洞:

总结下snap-confine文件路径

/var/lib/snapd/apparmor/snap-confine
/usr/lib/snapd/snap-confine
/snap/snapd/12398/usr/lib/snapd/snap-confine
/snap/snapd/12398/var/lib/snapd/apparmor/snap-confine
/snap/snapd/12159/usr/lib/snapd/snap-confine
/snap/snapd/12159/var/lib/snapd/apparmor/snap-confine
/snap/core20/1026/var/lib/snapd/apparmor/snap-confine
/snap/core/11316/usr/lib/snapd/snap-confine
/snap/core/11316/var/lib/snapd/apparmor/snap-confine
/snap/core/11187/usr/lib/snapd/snap-confine
/snap/core/11187/var/lib/snapd/apparmor/snap-confine
snap command:

列出已经安装的snap包
sudo snap list
搜索要安装的snap包
sudo snap find <text to search>
安装一个snap包
sudo snap install <snap name>
更新一个snap包,如果你后面不加包的名字的话那就是更新所有的snap包
sudo snap refresh <snap name>
把一个包还原到以前安装的版本
sudo snap revert <snap name>
删除一个snap包
sudo snap remove <snap name>

二.影响版本:

1.漏洞披露先后:

CVE-2021-44731	snap-confine 的 setup_private_mount() 中的竞争条件
CVE-2021-44730	snap-confine 的 sc_open_snapd_tool() 中的硬链接攻击
CVE-2021-3996	util-linux 的 libmount 中未经授权的卸载
CVE-2021-3995	util-linux 的 libmount 中未经授权的卸载
CVE-2021-3998	来自 glibc 的 realpath() 的意外返回值
CVE-2021-3999	glibc 的 getcwd() 中的一个缓冲区溢出/下溢
CVE-2021-3997	systemd 的 systemd-tmpfiles 中不受控制的递归的挖掘

竞争条件

 竞争条件发生在当多个进程或者线程在读写数据时,其最终的的结果依赖于多个进程的指令执行顺序。
例如:考虑下面的例子
  假设两个进程P1和P2共享了变量a。在某一执行时刻,P1更新a为1,在另一时刻,P2更新a为2。
因此两个任务竞争地写变量a。在这个例子中,竞争的“失败者”(最后更新的进程)决定了变量a的最终值。
多个进程并发访问和操作同一数据且执行结果与访问的特定顺序有关,称为竞争条件。 

对应时间线的挖洞大佬

James Troup 发现 snap 没有正确管理
snap 目录的权限。本地攻击者可能会利用此问题来暴露
敏感信息。( CVE-2021-3155 )

Ian Johnson 发现 snapd 没有正确验证内容界面
和布局路径。本地攻击者可能会利用此问题注入
任意 AppArmor 策略规则,从而绕过预期的访问
限制。( CVE-2021-4120 )

Qualys 研究团队发现 snapd 没有正确验证
snap-confine 二进制文件的位置。本地攻击者可能会使用此
问题来执行其他任意二进制文件并提升权限。
( CVE-2021-44730 )

Qualys 研究团队在为snap 准备私有挂载命名空间时发现 snapd snap-confine 二进制文件中存在竞争条件。
本地攻击者可能会使用此问题来提升权限并执行
任意代码。( CVE-2021-44731 )

2.受影响版本

该漏洞目前主要影响范围为ubuntu发行版:
Ubuntu 21.10 snapd/snap-confine < 2.54.3+21.10.1
Ubuntu 20.04 LTS snapd/snap-confine < 2.54.3+20.04
Ubuntu 18.04 LTS snapd/snap-confine < 2.54.3+18.04
Ubuntu 16.04 ESM 待官方更新

3.不受影响版本

安全版本
Ubuntu 21.10 snapd/snap-confine >= 2.54.3+21.10.1
Ubuntu 20.04 LTS snapd/snap-confine >= 2.54.3+20.04
Ubuntu 18.04 LTS snapd/snap-confine >= 2.54.3+18.04
Ubuntu 16.04 ESM 待官方更新

三.漏洞分析

1.反向分析(补丁版本 snap-confine 2.54.3),先了解snap-confine的大致结构:

核心分析/usr/lib/snapd/snap-confine文件,该文件是一个二进制文件,搞成文本下来看下:

xxd snap-confine > snap-confine.txt

(1)file查看文件类型

root@VM-0-5-ubuntu:/usr/lib/snapd# file snap-confine
snap-confine: setuid ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f946d2d193895203adbd114d6b9aad311a211a46, for GNU/Linux 3.2.0, stripped

(2)ldd查看调用库关系

root@VM-0-5-ubuntu:/usr/lib/snapd# ldd snap-confine
        linux-vdso.so.1 (0x00007ffd1cca9000)
        libudev.so.1 => /lib/x86_64-linux-gnu/libudev.so.1 (0x00007f513097a000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f5130974000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f5130951000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f513075f000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f51309e3000)

(3)ltrace查看函数调用关系

root@VM-0-5-ubuntu:/usr/lib/snapd# ltrace  /usr/lib/snapd/snap-confine
Usage: snap-confine <security-tag> <executable>
application or hook security tag was not provided

(4)readelf查看elf信息

root@VM-0-5-ubuntu:/usr/lib/snapd# readelf -h snap-confine
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x60c0
  Start of program headers:          64 (bytes into file)
  Start of section headers:          140776 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         13
  Size of section headers:           64 (bytes)
  Number of section headers:         30
  Section header string table index: 29

(5)objdump从对象文件中显示信息

root@VM-0-5-ubuntu:/usr/lib/snapd# objdump -d snap-confine | head

snap-confine:     file format elf64-x86-64

Disassembly of section .init:

0000000000004000 <.init>:
    4000:       f3 0f 1e fa             endbr64 
    4004:       48 83 ec 08             sub    $0x8,%rsp
    4008:       48 8b 05 c9 ef 01 00    mov    0x1efc9(%rip),%rax        # 22fd8 <__gmon_start__>

(6)strace:

root@VM-0-5-ubuntu:/usr/lib/snapd# strace -f  /usr/lib/snapd/snap-confine
execve("/usr/lib/snapd/snap-confine", ["/usr/lib/snapd/snap-confine"], 0x7fffa6ec0368 /* 21 vars */) = 0
brk(NULL)                               = 0x560d7306f000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffe7b9f6ae0) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=87696, ...}) = 0
mmap(NULL, 87696, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fda927f9000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libudev.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0`\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=178528, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fda927f7000
mmap(NULL, 182536, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fda927ca000
mmap(0x7fda927cf000, 114688, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x5000) = 0x7fda927cf000
mmap(0x7fda927eb000, 40960, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x21000) = 0x7fda927eb000
mmap(0x7fda927f5000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2a000) = 0x7fda927f5000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \22\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=18816, ...}) = 0
mmap(NULL, 20752, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fda927c4000
mmap(0x7fda927c5000, 8192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1000) = 0x7fda927c5000
mmap(0x7fda927c7000, 4096, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7fda927c7000
mmap(0x7fda927c8000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7fda927c8000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220\201\0\0\0\0\0\0"..., 832) = 832
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\345Ga\367\265T\320\374\301V)Yf]\223\337"..., 68, 824) = 68
fstat(3, {st_mode=S_IFREG|0755, st_size=157224, ...}) = 0
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\345Ga\367\265T\320\374\301V)Yf]\223\337"..., 68, 824) = 68
mmap(NULL, 140408, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fda927a1000
mmap(0x7fda927a8000, 69632, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x7000) = 0x7fda927a8000
mmap(0x7fda927b9000, 20480, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18000) = 0x7fda927b9000
mmap(0x7fda927be000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c000) = 0x7fda927be000
mmap(0x7fda927c0000, 13432, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fda927c0000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360q\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
fstat(3, {st_mode=S_IFREG|0755, st_size=2029224, ...}) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
mmap(NULL, 2036952, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fda925af000
mprotect(0x7fda925d4000, 1847296, PROT_NONE) = 0
mmap(0x7fda925d4000, 1540096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7fda925d4000
mmap(0x7fda9274c000, 303104, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19d000) = 0x7fda9274c000
mmap(0x7fda92797000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7fda92797000
mmap(0x7fda9279d000, 13528, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fda9279d000
close(3)                                = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fda925ad000
arch_prctl(ARCH_SET_FS, 0x7fda925adc00) = 0
mprotect(0x7fda92797000, 12288, PROT_READ) = 0
mprotect(0x7fda927be000, 4096, PROT_READ) = 0
mprotect(0x7fda927c8000, 4096, PROT_READ) = 0
mprotect(0x7fda927f5000, 4096, PROT_READ) = 0
mprotect(0x560d713b9000, 4096, PROT_READ) = 0
mprotect(0x7fda9283c000, 4096, PROT_READ) = 0
munmap(0x7fda927f9000, 87696)           = 0
set_tid_address(0x7fda925aded0)         = 4055346
set_robust_list(0x7fda925adee0, 24)     = 0
rt_sigaction(SIGRTMIN, {sa_handler=0x7fda927a8bf0, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x7fda927b63c0}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {sa_handler=0x7fda927a8c90, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO, sa_restorer=0x7fda927b63c0}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
prctl(PR_CAPBSET_READ, CAP_MAC_OVERRIDE) = 1
prctl(PR_CAPBSET_READ, 0x30 /* CAP_??? */) = -1 EINVAL (Invalid argument)
prctl(PR_CAPBSET_READ, 0x28 /* CAP_??? */) = -1 EINVAL (Invalid argument)
prctl(PR_CAPBSET_READ, CAP_BLOCK_SUSPEND) = 1
prctl(PR_CAPBSET_READ, 0x26 /* CAP_??? */) = -1 EINVAL (Invalid argument)
prctl(PR_CAPBSET_READ, CAP_AUDIT_READ)  = 1
brk(NULL)                               = 0x560d7306f000
brk(0x560d73090000)                     = 0x560d73090000
write(2, "Usage: snap-confine <security-ta"..., 99Usage: snap-confine <security-tag> <executable>

application or hook security tag was not provided
) = 99
exit_group(1)                           = ?
+++ exited with 1 +++

7.radare2分析

r2 snap-confine
[0x000060c0]> afl
0x0000b060    5 237          fcn.0000b060
0x000060f0    4 41   -> 34   fcn.000060f0
0x00006330   19 352  -> 345  fcn.00006330
0x00006490    7 208  -> 203  fcn.00006490
0x00006560   12 512  -> 506  fcn.00006560
0x00006760   42 1776 -> 1752 fcn.00006760
0x00007150   77 2744 -> 2704 fcn.00007150
0x00007c10   11 192          fcn.00007c10
0x00008880   14 240          fcn.00008880
0x00008c00    0 0            fcn.00008c00
0x00008970    7 320          fcn.00008970
0x00008ab0    7 336          fcn.00008ab0
0x0000a230    9 176          fcn.0000a230
0x0000a2e0   15 464  -> 453  fcn.0000a2e0
0x0000a750    1 39           fcn.0000a750
0x0000c340   24 443  -> 441  fcn.0000c340
0x0000ca70    3 128          fcn.0000ca70
0x0000d280    0 0            fcn.0000d280
0x0000caf0   48 1936 -> 1909 fcn.0000caf0
0x0000d3a0    4 122          fcn.0000d3a0
0x0000d420    3 62   -> 57   fcn.0000d420
0x0000d460   18 576  -> 573  fcn.0000d460
0x0000e1b0    5 128          fcn.0000e1b0
0x0000e230    1 22           fcn.0000e230
0x0000e600    4 156  -> 152  fcn.0000e600
0x0000ecf0   12 544  -> 538  fcn.0000ecf0
0x0000ff10   17 209  -> 192  fcn.0000ff10
0x0000fff0    4 55           fcn.0000fff0
0x00011320   23 448  -> 436  fcn.00011320
0x000128e0    3 65   -> 55   fcn.000128e0
0x00012890    3 79   -> 75   fcn.00012890
0x000133b0   28 392  -> 378  fcn.000133b0
0x00013540    5 116  -> 110  fcn.00013540
0x00014290   22 209  -> 190  fcn.00014290
0x00014370   15 268  -> 260  fcn.00014370
0x00015c20    4 46   -> 36   fcn.00015c20
0x00015a60   31 447  -> 438  fcn.00015a60
0x00015980    9 212  -> 206  fcn.00015980
0x0000d280    8 288          fcn.0000d280
0x00008c00   11 224          fcn.00008c00
snap-confine程序调用结构
snap-confine程序调用结构

2.正向patch分析:核心漏洞函数setup_private_mount()竞争条件分析

snapd_2.54.2漏洞版本链接:http://launchpadlibrarian.net/586584361/snapd_2.54.2+20.04ubuntu2_2.54.3+20.04.diff.gz

查询对应补丁(patch)版本 snapd-2.54.3 比较的代码,分析下patch修复的是哪一部分?

记录下函数所在文件路径 /ubuntu2/cmd/snap-confine/mount-support.c

diff -Nru snapd-2.54.2+20.04ubuntu2/cmd/snap-confine/mount-support.c snapd-2.54.3+20.04/cmd/snap-confine/mount-support.c
--- snapd-2.54.2+20.04ubuntu2/cmd/snap-confine/mount-support.c	2022-01-06 21:25:16.000000000 +0000
+++ snapd-2.54.3+20.04/cmd/snap-confine/mount-support.c	2022-02-15 16:45:13.000000000 +0000

—————————————————————————————————————————————分割线———————————————————————————————————————————————————
@@ -14,6 +14,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -51,6 +52,91 @@

 static void sc_detach_views_of_writable(sc_distro distro, bool normal_mode);

+static int must_mkdir_and_open_with_perms(const char *dir, uid_t uid, gid_t gid,
+					  mode_t mode)
+{
+	int retries = 10;
+	int fd;
+
+ mkdir:
+	if (--retries == 0) {
+		die("lost race to create dir %s too many times", dir);
+	}
+	// Ignore EEXIST since we want to reuse and we will open with
+	// O_NOFOLLOW, below.
+	if (mkdir(dir, 0700) < 0 && errno != EEXIST) {
+		die("cannot create directory %s", dir);
+	}
+	fd = open(dir, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
+	if (fd < 0) {
+		// if is not a directory then remove it and try again
+		if (errno == ENOTDIR && unlink(dir) == 0) {
+			goto mkdir;
+		}
+		die("cannot open directory %s", dir);
+	}
+	// ensure base_dir has the expected permissions since it may have
+	// already existed
+	struct stat st;
+	if (fstat(fd, &st) < 0) {
+		die("cannot stat base directory %s", dir);
+	}
+	if (st.st_uid != uid || st.st_gid != gid
+	    || st.st_mode != (S_IFDIR | mode)) {
+		unsigned char random[10] = { 0 };
+		char random_dir[MAX_BUF] = { 0 };
+		int offset;
+		size_t i;
+
+		// base_dir isn't what we expect - create a random
+		// directory name and rename the existing erroneous
+		// base_dir to this then try recreating it again - NOTE we
+		// don't use mkdtemp() here since we don't want to actually
+		// create the directory yet as we want rename() to do that
+		// for us
+#ifdef SYS_getrandom
+		// use syscall(SYS_getrandom) since getrandom() is
+		// not available on older glibc
+		if (syscall(SYS_getrandom, random, sizeof(random), 0) !=
+		    sizeof(random)) {
+			die("cannot get random bytes");
+		}
+#else
+		// use /dev/urandom on older systems which don't support
+		// SYS_getrandom
+		int rfd = open("/dev/urandom", O_RDONLY);
+		if (rfd < 0) {
+			die("cannot open /dev/urandom");
+		}
+		if (read(rfd, random, sizeof(random)) != sizeof(random)) {
+			die("cannot get random bytes");
+		}
+		close(rfd);
+#endif
+		offset =
+		    sc_must_snprintf(random_dir, sizeof(random_dir), "%s.",
+				     dir);
+		for (i = 0; i < sizeof(random); i++) {
+			offset +=
+			    sc_must_snprintf(random_dir + offset,
+					     sizeof(random_dir) - offset,
+					     "%02x", (unsigned int)random[i]);
+		}
+		// try and get dir which we own by renaming it to something
+		// else then creating it again
+
+		// TODO - change this to use renameat2(RENAME_EXCHANGE)
+		// once we can use a newer version of glibc for snapd
+		if (rename(dir, random_dir) < 0) {
+			die("cannot rename base_dir to random_dir '%s'",
+			    random_dir);
+		}
+		close(fd);
+		goto mkdir;
+	}
+	return fd;
+}
+
 // TODO: simplify this, after all it is just a tmpfs
 // TODO: fold this into bootstrap


—————————————————————————————————————————————分割线———————————————————————————————————————————————————
/*以下是漏洞函数setup_private_mount()*/

static void setup_private_mount(const char *snap_name)
@@ -86,29 +172,8 @@

	/* Switch to root group so that mkdir and open calls below create filesystem
 	 * elements that are not owned by the user calling into snap-confine. */
	/* 切换到根组,以便 mkdir 和 open 调用在下面创建文件系统
	 * 调用 snap-confine 的用户不拥有的元素。 *
 	sc_identity old = sc_set_effective_identity(sc_root_group_identity());


-	// Create /tmp/snap.$SNAP_NAME/ 0700 root.root. Ignore EEXIST since we want
-	// to reuse and we will open with O_NOFOLLOW, below.
-   // 创建 /tmp/snap.$SNAP_NAME/0700 root.root。忽略 EEXIST,因为我们想要
-   // 重用,我们将使用 O_NOFOLLOW 打开,如下所示。
-	if (mkdir(base_dir, 0700) < 0 && errno != EEXIST) {
-		die("cannot create base directory %s", base_dir);
-	}
-	base_dir_fd = open(base_dir,
-			   O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
-	if (base_dir_fd < 0) {
-		die("cannot open base directory %s", base_dir);
-	}
-


	/* This seems redundant on first read but it has the non-obvious
-	 * property of changing existing directories  that have already existed
-	 * but had incorrect ownership or permission. This is possible due to
-	 * earlier bugs in snap-confine and due to the fact that some systems
-	 * use persistent /tmp directory and may not clean up leftover files
-	 * for arbitrarily long. This comment applies the following two pairs
-	 * of fchmod and fchown. */
	/* 这在第一次阅读时似乎是多余的,但它有不明显的
	- * 更改已存在的现有目录的属性
	- * 但拥有不正确的所有权或权限。这是可能的,因为
	- * snap-confine 中的早期错误以及由于某些系统
	- * 使用持久化/tmp 目录并且可能不会清理剩余文件
	- * 任意长。此评论适用以下两对
	- * fchmod 和 fchown。 */
-	if (fchmod(base_dir_fd, 0700) < 0) {
-		die("cannot chmod base directory %s to 0700", base_dir);
-	}
-	if (fchown(base_dir_fd, 0, 0) < 0) {
-		die("cannot chown base directory %s to root.root", base_dir);
-	}


+	// Create /tmp/snap.$SNAP_NAME/ 0700 root.root.
+   // 创建 /tmp/snap.$SNAP_NAME/ 0700 root.root。
+	base_dir_fd = must_mkdir_and_open_with_perms(base_dir, 0, 0, 0700);

	// Create /tmp/snap.$SNAP_NAME/tmp 01777 root.root Ignore EEXIST since we
 	// want to reuse and we will open with O_NOFOLLOW, below.
	// 创建 /tmp/snap.$SNAP_NAME/tmp 01777 root.root 忽略 EEXIST,因为我们
	// 想要重用,我们将使用 O_NOFOLLOW 打开,如下所示。
 	if (mkdirat(base_dir_fd, "tmp", 01777) < 0 && errno != EEXIST) {


@@ -120,14 +185,14 @@
 	if (tmp_dir_fd < 0) {
 		die("cannot open private tmp directory %s/tmp", base_dir);
 	}
-	if (fchmod(tmp_dir_fd, 01777) < 0) {
-		die("cannot chmod private tmp directory %s/tmp to 01777",
-		    base_dir);
-	}
 	if (fchown(tmp_dir_fd, 0, 0) < 0) {
 		die("cannot chown private tmp directory %s/tmp to root.root",
 		    base_dir);
 	}
+	if (fchmod(tmp_dir_fd, 01777) < 0) {
+		die("cannot chmod private tmp directory %s/tmp to 01777",
+		    base_dir);
+	}
 	sc_do_mount(tmp_dir, "/tmp", NULL, MS_BIND, NULL);
 	sc_do_mount("none", "/tmp", NULL, MS_PRIVATE, NULL);
 }
@@ -464,7 +529,8 @@
 	sc_identity old = sc_set_effective_identity(sc_root_group_identity());
 	if (mkdir(SC_HOSTFS_DIR, 0755) < 0) {
 		if (errno != EEXIST) {
-			die("cannot perform operation: mkdir %s", SC_HOSTFS_DIR);
+			die("cannot perform operation: mkdir %s",
+			    SC_HOSTFS_DIR);
 		}
 	}
 	(void)sc_set_effective_identity(old);

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一.漏洞简述
    • 1.snap简述
      • 2.snap-confine简述
      • 二.影响版本:
        • 1.漏洞披露先后:
          • 2.受影响版本
            • 3.不受影响版本
            • 三.漏洞分析
              • 1.反向分析(补丁版本 snap-confine 2.54.3),先了解snap-confine的大致结构:
                • 2.正向patch分析:核心漏洞函数setup_private_mount()竞争条件分析
                相关产品与服务
                容器服务
                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档