# Qualys Security Advisory: "Looney Tunables" - Local Privilege Escalation in glibc's ld.so (CVE-2023-4911)
The GNU C Library's dynamic loader, `ld.so`, plays a pivotal role in finding and loading shared objects (shared libraries) required by programs, preparing them to run, and then executing them. This component operates with elevated privileges when a local user runs a set-user-ID (SUID) program, a set-group-ID (SGID) program, or a program with capabilities. Over the years, the handling of environment variables like `LD_PRELOAD`, `LD_AUDIT`, and `LD_LIBRARY_PATH` has been a known source of vulnerabilities in the dynamic loader.
Recently, we identified a critical vulnerability, a buffer overflow, in the way `ld.so` processes the `GLIBC_TUNABLES` environment variable. This vulnerability was introduced in April 2021 (glibc 2.34) and is marked by commit 2ed18c ("Fix SXID_ERASE behavior in setuid programs (BZ #27471)").
We successfully exploited this vulnerability on default installations of various Linux distributions, including Fedora 37 and 38, Ubuntu 22.04 and 23.04, Debian 12 and 13, and possibly others. Please note that we will not release our exploit at this time, but this buffer overflow is easily exploitable, and other researchers may publish working exploits shortly after this coordinated disclosure.
At the start of its execution, `ld.so` invokes `__tunables_init()` to process environment variables. Among these variables, `GLIBC_TUNABLES` is searched for, and if found, a sanitized copy is created. However, a vulnerability arises when `GLIBC_TUNABLES` is structured as "tunable1=tunable2=AAA," where "tunable1" and "tunable2" are SXID_IGNORE tunables. During parsing, the entire "tunable1=tunable2=AAA" string is copied into a buffer, potentially overflowing it in the process.
Further analysis reveals that the vulnerability can be exploited by overwriting pointers in a link_map structure, causing `ld.so` to trust a directory that an attacker controls, allowing them to load their own libraries and execute arbitrary code with elevated privileges.
## Proof of Concept
$ env -i "GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A" "Z=`printf '%08192x' 1`" /usr/bin/su --help
Segmentation fault (core dumped)
Buffer Overflow Vulnerability: The vulnerability is a buffer overflow in the dynamic loader (ld.so) of the GNU C Library (glibc). This buffer overflow occurs in the parse_tunables() function due to improper handling of certain environment variables.
Memory Allocation: The buffer that gets overflowed is allocated by the tunables_strdup() function, which is a re-implementation of strdup(). Instead of using the standard malloc(), tunables_strdup() uses ld.so's __minimal_malloc(), which internally calls mmap() to obtain memory from the kernel.
Exploitation Options: The goal is to achieve arbitrary code execution. There are two potential options for overwriting memory:
a. Overwrite ld.so's Own Memory: One option is to overwrite memory within the ld.so process itself. This would involve overwriting parts of the read-write ELF segment of ld.so. However, there is typically an unmapped hole immediately below this segment, making it difficult to overwrite.
b. Overwrite mmap()ed Pages: The other option is to overwrite memory pages allocated by the tunables_strdup() function itself. This is possible because __tunables_init() can process multiple GLIBC_TUNABLES environment variables, and the Linux kernel's mmap() is a top-down allocator.
Exploiting Overwritten Memory: The primary challenge is to exploit the overwritten memory effectively. Several points are discussed:
a. Overwriting Link_map Structure: Initially, the focus was on overwriting the link_map structure, specifically the l_next and l_prev pointers in the doubly linked list of link_map structures. However, this approach failed due to assert()ion failures in setup_vdso().
b. Exploiting l_info[DT_RPATH]: The breakthrough comes from the fact that the l_info array within the link_map structure is not explicitly initialized to NULL. Of particular interest is l_info[DT_RPATH], which relates to the "Library search path." By overwriting this pointer, an attacker can control where ld.so looks for libraries.
c. Pointing to the Stack: To exploit this, the overwritten l_info[DT_RPATH] is made to point to the stack, specifically to the attacker's environment strings on the stack. The Linux stack is randomized within a 16GB region, and the attacker can make educated guesses about the address.
d. Contents of l_info[DT_RPATH]: The content of l_info[DT_RPATH] should resemble an Elf64_Dyn structure, with a specific d_tag and d_val. The d_tag for DT_RPATH is 15, but this value is not checked, so it can be set to any value. The d_val typically points to an offset in the ELF string table of the SUID-root program being executed, which can be set to a controlled value.
e. Overcoming Null-Byte Challenge: A challenge is that the attacker needs to overflow the buffer with many null bytes (to mimic NULL pointers) while parsing a null-terminated C string. To achieve this, the attacker places a large number of empty strings (null bytes) immediately after the GLIBC_TUNABLES in the stack, followed by a specific string that is used to overwrite l_info[DT_RPATH]. This ensures that most pointers are NULL, except for the target l_info[DT_RPATH].
Arbitrary Code Execution: Once l_info[DT_RPATH] is under the attacker's control, they can make ld.so trust a directory that they own, allowing them to load and execute their malicious libc.so.6 or LD_PRELOAD library from that directory. If ld.so is run through a SUID-root program, this allows the attacker to execute code as root.
Limitations: Some SUID-root programs are exceptions, such as sudo, chage, and passwd on certain distributions, as they have additional security mechanisms in place (e.g., SELinux or AppArmor) that can prevent this exploitation.
Version Consideration: The advisory mentions that this exploitation method works against glibc 2.34, but it also notes that glibc 2.34 uses __sbrk() instead of __minimal_malloc(), raising questions about whether glibc 2.34 is exploitable.
In summary, this exploitation leverages a buffer overflow vulnerability to manipulate memory within the ld.so process, ultimately allowing an attacker to execute arbitrary code with root privileges in certain scenarios. The attack involves careful manipulation of memory structures and pointers to achieve code execution.
The Qualys Threat Research Unit for their diligence in discovering and researching this vulnerability.