|
| 1 | +```toml |
| 2 | +[advisory] |
| 3 | +id = "RUSTSEC-0000-0000" |
| 4 | +package = "below" |
| 5 | +date = "2025-03-12" |
| 6 | +url = "https://www.openwall.com/lists/oss-security/2025/03/12/1" |
| 7 | +# Valid categories: "code-execution", "crypto-failure", "denial-of-service", "file-disclosure" |
| 8 | +# "format-injection", "memory-corruption", "memory-exposure", "privilege-escalation" |
| 9 | +categories = ["privilege-escalation"] |
| 10 | +aliases = ["CVE-2025-27591"] |
| 11 | +cvss = "CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N" |
| 12 | + |
| 13 | +[versions] |
| 14 | +patched = [">= 0.9.0"] |
| 15 | +``` |
| 16 | + |
| 17 | +# World Writable Directory in /var/log/below Allows Local Privilege Escalation |
| 18 | + |
| 19 | +Below is a tool for recording and displaying system data like |
| 20 | +hardware utilization and cgroup information on Linux. |
| 21 | + |
| 22 | +## Symlink Attack in `/var/log/below/error_root.log` |
| 23 | + |
| 24 | +Below's systemd service runs with full `root` privileges. It attempts to |
| 25 | +create a world-writable directory in `/var/log/below`. Even if the |
| 26 | +directory already exists, the Rust code ensures [1] that it receives |
| 27 | +mode 0777 permissions: |
| 28 | + |
| 29 | +``` |
| 30 | + if perm.mode() & 0o777 != 0o777 { |
| 31 | + perm.set_mode(0o777); |
| 32 | + match dir.set_permissions(perm) { |
| 33 | + Ok(()) => {} |
| 34 | + Err(e) => { |
| 35 | + bail!( |
| 36 | + "Failed to set permissions on {}: {}", |
| 37 | + path.to_string_lossy(), |
| 38 | + e |
| 39 | + ); |
| 40 | + } |
| 41 | + } |
| 42 | + } |
| 43 | +``` |
| 44 | + |
| 45 | +This logic leads to different outcomes depending on the packaging on Linux |
| 46 | +distributions: |
| 47 | + |
| 48 | +- in openSUSE Tumbleweed the directory was packaged with 01755 |
| 49 | + permissions (below.spec [2] line 73), thus causing the |
| 50 | + `set_permissions()` call to run, resulting in a directory with mode |
| 51 | + 0777 during runtime. |
| 52 | +- in Gentoo Linux the directory is created with mode 01755 resulting in |
| 53 | + the same outcome as on openSUSE Tumbleweed (below.ebuild [3]). Where |
| 54 | + the 01755 mode is exactly coming from is not fully clear, maybe the |
| 55 | + `cargo` build process assigns these permissions during installation. |
| 56 | +- in Fedora Linux the directory is packaged with 01777 permissions, thus |
| 57 | + the `set_permissions()` code will not run, because the `if` condition |
| 58 | + masks out the sticky bit. The directory stays at mode 01777 |
| 59 | + (rust-below.spec [4]). |
| 60 | +- the Arch Linux AUR package [5] (maybe wrongly) does not pre-create |
| 61 | + the log directory. Thus the `set_permissions()` code will run and |
| 62 | + create the directory with mode 0777. |
| 63 | + |
| 64 | +Below creates a log file in `/var/log/below/error_root.log` and assigns |
| 65 | +mode 0666 to it. This (somewhat confusingly) happens via a `log_dir` |
| 66 | +variable [6], which has been changed to point to the `error_root.log` |
| 67 | +file. The 0666 permission assignment to the logfile happens in |
| 68 | +`logging::setup()` [7], also accompanied by a somewhat strange comment |
| 69 | +in the code. |
| 70 | + |
| 71 | +A local unprivileged attacker can stage a symlink attack in this |
| 72 | +location and cause an arbitrary file in the system to obtain 0666 |
| 73 | +permissions, likely leading to a full local root exploit, if done right, |
| 74 | +e.g. by pointing the symlink to `/etc/shadow`. Even if the file already |
| 75 | +exists it can be removed and replaced by a symlink, because of the |
| 76 | +world-writable directory permissions. The attack is thus not limited to |
| 77 | +scenarios in which the file has not yet been created by Below. |
| 78 | + |
| 79 | +## Further Issues |
| 80 | + |
| 81 | +Even on Fedora Linux, where `/var/log/below` has "safe" 01777 |
| 82 | +permissions, there is a time window during which problems can arise. As |
| 83 | +long as `below.service` has not been started, another local user can |
| 84 | +pre-create `/var/log/below/error_root.log` and e.g. place a FIFO special |
| 85 | +file there. This will pose a local DoS against the below service, since |
| 86 | +it will fail to open the path and thus fail to start. |
| 87 | + |
| 88 | +If `/var/log/below` were to be deleted for any reason, then Below would |
| 89 | +still recreate it using the bad 0777 mode permissions, which can also |
| 90 | +happen on distributions that initially package `/var/log/below` using |
| 91 | +permissions that do not trigger the `set_permissions()` call in Below's |
| 92 | +code. |
| 93 | + |
| 94 | +[1]: https://github.com/facebookincubator/below/blob/v0.8.1/below/src/main.rs#L379 |
| 95 | +[2]: https://build.opensuse.org/projects/openSUSE:Factory/packages/below/files/below.spec?expand=1&rev=5e78e7f743f87bea8648eeee673c649b |
| 96 | +[3]: https://github.com/gentoo/gentoo/blob/master/sys-process/below/below-0.8.1-r1.ebuild#L344 |
| 97 | +[4]: https://src.fedoraproject.org/rpms/rust-below/blob/6ae58353b5d12e58462425c20a2aedfbae2e769a/f/rust-below.spec#_108 |
| 98 | +[5]: https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=below#n34 |
| 99 | +[6]: https://github.com/facebookincubator/below/blob/v0.8.1/below/src/main.rs#L552 |
| 100 | +[7]: https://github.com/facebookincubator/below/blob/v0.8.1/below/src/open_source/logging.rs#L68 |
| 101 | + |
0 commit comments