bug-guix
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

bug#47229: Hardlink mitigation limits


From: Nathan Nye
Subject: bug#47229: Hardlink mitigation limits
Date: Tue, 23 Mar 2021 14:18:14 -0400
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.8.1

Hello,

I'm sharing here for future reference why protected hardlinks alone did not mitigate the recent LPE security advisory, pre-patch:

"The reasons why are lines 2633 and 2637 of nix/libstore/build.cc:

When a package fails to build and the keep failed flag is set (-K/--keep-failed), it runs a recursive chown on the build directory (which is writable following guixbuilder01 changing the permissions to 777). It starts at the top level and chowns downwards.

The first important thing to notice here is that at any point (even pre-chown) the build user has been compromised. The build user can write a SUID /bin/sh to the build path, and because a normal user can traverse into the directory before and during the chown, they can run a SUID shell (allowing them to become guixbuilder01 even after the build user processes are terminated). Becoming the build user allows multiple paths to privilege escalation, but in this scenario we have faster ways of becoming root.

Moving on to getting root, we're choosing not to use a hardlink to show why it isn't necessary. Instead, we create a directory under the build directory with thousands of sequentially named files, the final entry being "passwd" or "shadow". Then we terminate the build and watch for the first entry to be chowned to our user ID (possibly with the inotify API). This way, we have opened a lengthy window of time where it is enumerating over a list of file paths in our chosen directory and chowning each of them. Now we can execute our TOCTOU race condition vulnerability.

At the time of check (TOC), the guix-daemon has a list of file paths to chown under what it assumes is a regular directory (because it ran S_ISDIR on the directory). But we can swap out the directory from under it with a symlink to /etc (most efficiently with renameat2() and using the RENAME_EXCHANGE flag to atomically exchange the paths). At the time of use (TOU) lchown() only checks if the file itself that is being chowned is a symlink, not if the path components are, as can be demonstrated with Python:

$ mkdir td;touch td/tf;python3 -c 'import os;os.lchown("/home/example/td/tf", 1000, 4)';ls -lahtrd td td/tf
-rw-rw-r-- 1 example adm       0    Mar 19 19:20 td/tf
drwxrwxr-x 2 example example   4.0K Mar 19 19:20 td
$ rm -rf td
$ mkdir td; ln -s td td2;touch td2/tf;python3 -c 'import os;os.lchown("/home/example/td2/tf", 1000, 4)';ls -lahtrd td2 td2/tf
lrwxrwxrwx 1 example example 2 Mar 19 19:21 td2 -> td
-rw-rw-r-- 1 example adm     0 Mar 19 19:21 td2/tf

So lchown can blindly chown /etc/passwd to our user by following the directory symlink and subsequently verifying that passwd itself is not a symlink. I hope this explains the TOCTOU race condition and why protected hardlinks help (forcing an attacker to get root using this race condition), but they are not a solution to the problem (alone)."

- Nathan


reply via email to

[Prev in Thread] Current Thread [Next in Thread]