Looney Tunables — glibc ld.so (CVE-2023-4911)
TL;DR: A buffer overflow in glibc’s
GLIBC_TUNABLESparser lets a local user smash the dynamic linker’s stack while executing any SUID binary, yielding a root shell on stock Fedora, Debian, and Ubuntu.
What it is
Qualys disclosed this in October 2023. It lives in __tunables_init() inside ld.so, the dynamic loader linked into every dynamically-linked binary on Linux. When parsing the GLIBC_TUNABLES environment variable, the loader uses __libc_alloca_cutoff to allocate a working copy on the stack, then walks name=value:name=value pairs. A malformed tunable=tunable=AAA entry causes the parser to copy bytes past the allocation while still on the loader’s tiny stack. Because the overflow happens before glibc strips LD_*/GLIBC_TUNABLES from a SUID process — the sanitisation happens after __tunables_init() — the attacker controls memory inside a process running as root.
Preconditions / where it applies
- glibc 2.34 through 2.38 with the tunables refactor (commit 2ed18c) — first shipped in Fedora 37, Ubuntu 22.04+, Debian 12, RHEL 9.x
- Any SUID-root binary on the box that re-execs through
ld.so, e.g./usr/bin/su,/usr/bin/mount,/usr/bin/sudo - Local code execution; no special groups
- Patched in glibc 2.39 and distro updates from 2023-10-03 onwards
Technique
The trigger is a single env var; the rest is heap/stack grooming to land a controllable pointer where dl_audit later reads it:
1
2
3
4
5
6
7
GLIBC_TUNABLES='glibc.malloc.mxfast=glibc.malloc.mxfast=A'
# repeated and padded — the overflow walks into ld.so's static state
env -i \
"GLIBC_TUNABLES=$(python3 -c 'print("glibc.malloc.mxfast=glibc.malloc.mxfast=" + "A"*32768)')" \
"Z=$(python3 -c 'print("B"*4096)')" \
/usr/bin/su --version
A working PoC pads GLIBC_TUNABLES so the corrupted dword overlaps l_info[DT_RPATH] or an audit hook pointer, then arranges a controlled string to be interpreted as a search path. The loader subsequently dlopens an attacker-supplied .so while EUID 0. End-to-end exploits chain this with a writeable $ORIGIN-style path under /dev/shm/ and a payload that calls setuid(0) + execve("/bin/sh").
Detection and defence
- Patch glibc; distros backported within 48h (Ubuntu USN-6409, RHSA-2023:5455, DSA-5514)
- Stopgap with systemd-tmpfiles: a sysctl is not available, but you can blunt impact by stripping SUID from non-essential helpers (
dpkg-statoverride,chmod u-s) - auditd:
-a always,exit -F arch=b64 -S execve -F a0~="GLIBC_TUNABLES"catches the env var on SUID exec; pair withsetuidsyscall transitions - SELinux and AppArmor confine the secondary dlopen — RHEL targeted policy denies arbitrary
.soloads from user-writable dirs instaff_t - Static analysis: scan the running glibc with
ldd --version; anything in the 2.34–2.38 window without the backport is suspect
References
- CVE-2023-4911 — NVD — official record
- Qualys Looney Tunables advisory — discoverer write-up
- Sourceware glibc fix — upstream patch
See also: suid-sgid-binaries, ld-preload-abuse, kernel-exploits-linux.