I Vulnerability Description:
1. Brief description of snap:
ubuntu official explanation of snap: https://cn.ubuntu.com/blog/what-is-snap-application
Snap is a software packaging and deployment system developed by Canonical for operating systems using Linux kernel. These packages (called snaps) and the tools that use them, snapd, work in a range of Linux distributions.
Canonical Keneng Software Co., Ltd. (mainly engaged in FOSS open source project):
https://zh.wikipedia.org/wiki/Canonical
Snap can contain one or more services, support cli (command line) applications, GUI Graphical applications and no single process restrictions. Therefore, you can call one or more services with a single snap. It is very convenient for some multi service applications. Snaps are isolated from each other, and resources can be exchanged through interface definition. Interface is used to allow snap to access OpenGL acceleration, sound card playback, recording, network and HOME directories. The interface consists of slot s and plug s, that is, providers and consumers.
Query what the snap is under the ubuntu machine, and you can see that there are many * File at the end of snap
To sum up, the snap file path defaults to the following three paths
/var/lib/snapd/snaps /var/lib/snapd/seed /tmp/snap.docker/tmp
2. Brief description of snap confirm:
Snap configure (a suid root program installed) is on Ubuntu by default. Snap is a specification for operating systems using the Linux kernel. Called snaps, and the tool snapd that uses them, work across a range of Linux distributions and allow upstream software. Developers distribute their applications directly to users. A snapshot is a stand-alone application running in a sandbox that accesses the host system.
Snap define is a program used internally by snapd to build the execution environment of snap applications. " (man snap-confine)
Discovering and exploiting vulnerabilities in snap confine has been extremely challenging (especially in Ubuntu, which is installed by default), because snap confine uses a very defensive programming style, including AppArmor configuration file, seccomp filter, Mount namespace and two Go helpers. Finally, we found two vulnerabilities:
Summarize the snap confirm file path
/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: List installed snap package sudo snap list Search for to install snap package sudo snap find <text to search> Install one snap package sudo snap install <snap name> Update a snap Package, if you don't add the name of the package after it, it is to update all the packages snap package sudo snap refresh <snap name> Restore a package to a previously installed version sudo snap revert <snap name> Delete a snap package sudo snap remove <snap name>
II Affected version:
1. Sequence of vulnerability disclosure:
CVE-2021-44731 snap-confine of setup_private_mount() Competitive conditions in CVE-2021-44730 snap-confine of sc_open_snapd_tool() Hard link attack in CVE-2021-3996 util-linux of libmount Unauthorized uninstall in CVE-2021-3995 util-linux of libmount Unauthorized uninstall in CVE-2021-3998 come from glibc of realpath() Unexpected return value for CVE-2021-3999 glibc of getcwd() A buffer overflow in/Underflow CVE-2021-3997 systemd of systemd-tmpfiles Mining uncontrolled recursion in
Competitive conditions
The contention condition occurs when multiple processes or threads are reading and writing data, and the final result depends on the instruction execution order of multiple processes. For example, consider the following example Suppose two processes P1 and P2 Shared variables a. At a certain moment of execution, P1 to update a Is 1, at another moment, P2 to update a Is 2. Therefore, two tasks write variables competitively a. In this case, the "loser" of competition(The last update process) determines the variable a Final value of. Multiple processes access and operate the same data concurrently, and the execution result is related to the specific order of access, which is called competitive condition.
The hole digging boss corresponding to the timeline
James Troup find snap Not properly managed snap Permissions for the directory. Local attackers may use this issue to expose Sensitive information.( CVE-2021-3155 ) Ian Johnson find snapd The content interface is not verified correctly And layout path. Local attackers may exploit this issue to inject arbitrarily AppArmor Policy rules to bypass expected access Restrictions.( CVE-2021-4120 ) Qualys The research team found snapd Not verified correctly snap-confine The location of the binary file. This could be used by a local attacker Issue to execute any other binaries and raise permissions. ( CVE-2021-44730 ) Qualys The research team is working for snap Found while preparing private mount namespace snapd snap-confine A race condition exists in the binary. Local attackers may use this issue to escalate privileges and execute Arbitrary code.( CVE-2021-44731 )
2. Affected version
At present, the main impact scope of the vulnerability is ubuntu Release: 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 To be officially updated
3. Unaffected version
Secure version 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 To be officially updated
III Vulnerability analysis
1. Reverse analysis (patch version snap confirm 2.54.3), first understand the general structure of snap confirm:
Core analysis / usr / lib / snapd / snap confirm file, which is a binary file. Let's look at it in text:
xxd snap-confine > snap-confine.txt
(1) File view file type
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 view call library relationship
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 view function call relationship
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 view elf information
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 displays information from the object file
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 analysis
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
2. Forward patch analysis: core vulnerability function setup_private_mount() competitive condition analysis
snapd_ 2.54.2 vulnerability version link: http://launchpadlibrarian.net/586584361/snapd_2.54.2+20.04ubuntu2_2.54.3+20.04.diff.gz
Query the code of the corresponding patch version snapd-2.54.3 and analyze which part of the patch is repaired?
Record the file path where the function is located / Ubuntu 2 / CMD / snap configure / 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 ----------------------—Split line -—————————————————————————————————————————————————— @@ -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 ----------------------—Split line -—————————————————————————————————————————————————— /*The following is the vulnerability function 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. */ /* Switch to the root group so that the mkdir and open calls create the file system below * Elements that are not owned by the user calling snap configure* 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. - // Create / TMP / snap$ SNAP_ NAME/0700 root. root. Ignore EEXIST because we want to - // Reuse, we will use O_NOFOLLOW opens as shown below. - 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. */ /* This may seem superfluous at the first reading, but it has an inconspicuous effect - * Change the properties of an existing directory that already exists - * But has incorrect ownership or authority. This is possible because - * snap-confine Early errors in and due to some systems - * Use the persistent / tmp directory and may not clean up the remaining files - * Any length. This comment applies to the following two pairs - * fchmod And 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. + // Create / 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. // Create / TMP / snap$ SNAP_ NAME/tmp 01777 root. Root ignored EEXIST because we // To reuse, we will use O_NOFOLLOW opens as shown below. 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);