Cve-2021-44731 analysis of Linux snap local right lifting vulnerability

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

https://canonical.com/

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);

Added by NIGHTSBIRD on Sat, 05 Mar 2022 08:06:01 +0200