Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2023-4911 — Looney Tunables Local privilege escalation (CVE-2023-4911) workshop | Kitploit
Tools/GitHubGitHub/kernelkrise/cve-2023-4911
Privilege EscalationVulnerability AnalysisExploitationLearning & EducationBinary ExploitationLabs & PracticeArchived
GitHubkernelkrise/cve-2023-4911

CVE-2023-4911

Looney Tunables Local privilege escalation (CVE-2023-4911) workshop

View Repository
18392 years agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2023-4911-Looney-Tunables

Looney Tunables Local privilege escalation (CVE-2023-4911) workshop (for educational purposes only)

Links:

  • IPPSEC video
  • Qualsys Blog Post
  • Qualsys Tech Details
  • Exploit POC python script
  • GLIBC sources
  • GLIBC tunables documentation

Description

What is ld.so?

In computing, a dynamic linker is the part of an operating system that loads and links the shared libraries needed by an executable when it is executed, by copying the content of libraries from persistent storage to RAM, filling jump tables and relocating pointers.

For example, we have program which uses openssl library to calculate md5 hash:

$ head md5_hash.c
#include <stdio.h>
#include <string.h>
#include <openssl/md5.h>

ld.so parses the binary and tries to find library related to <openssl/md5.h>

$ ldd md5_hash                       
        linux-vdso.so.1 (0x00007fffa530b000)
        libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 (0x00007f19cda00000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f19cd81e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f19ce032000)

As we can see, it finds necessary crypto library at /lib/x86_64-linux-gnu/libcrypto.so.3 During program startup, it put the code of this library into the process RAM and links all references to this library.

Summary

When a program is initiated, this loader first examines the program to determine the shared libraries it requires. It then searches for these libraries, loads them into memory, and links them with the executable at runtime. In the process, the dynamic loader resolves symbol references, such as function and variable references, ensuring that everything is set for the program’s execution. Given its role, the dynamic loader is highly security-sensitive, as its code runs with elevated privileges when a local user launches a set-user-ID or set-group-ID program.

What is GLIBC Tunables?

Tunables are a feature in the GNU C Library that allows application authors and distribution maintainers to alter the runtime library behavior to match their workload. These are implemented as a set of switches that may be modified in different ways. The current default method to do this is via the GLIBC_TUNABLES environment variable by setting it to a string of colon-separated name=value pairs. For example, the following example enables malloc checking and sets the malloc trim threshold to 128 bytes:

GLIBC_TUNABLES=glibc.malloc.trim_threshold=128:glibc.malloc.check=3
export GLIBC_TUNABLES

Passing --list-tunables to the dynamic loader to print all tunables with minimum and maximum values:

$ /lib64/ld-linux-x86-64.so.2 --list-tunables
glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
glibc.elision.skip_lock_after_retries: 3 (min: 0, max: 2147483647)
glibc.malloc.trim_threshold: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.malloc.perturb: 0 (min: 0, max: 255)
glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max: 0xffffffffffffffff)
glibc.pthread.rseq: 1 (min: 0, max: 1)
glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
glibc.mem.tagging: 0 (min: 0, max: 255)

Vulnerability description

At the very beginning of its execution, ld.so calls __tunables_init() to walk through the environment (at line 279), searching for GLIBC_TUNABLES variables (at line 282); for each GLIBC_TUNABLES that it finds, it makes a copy of this variable (at line 284), calls parse_tunables() to process and sanitize this copy (at line 286), and finally replaces the original GLIBC_TUNABLES with this sanitized copy (at line 288):

// (GLIBC ld.so sources in ./glibc-2.37/elf/dl-tunables.c)
269 void
270 __tunables_init (char **envp)
271 {
272   char *envname = NULL;
273   char *envval = NULL;
274   size_t len = 0;
275   char **prev_envp = envp;
...
279   while ((envp = get_next_env (envp, &envname, &len, &envval,
280                                &prev_envp)) != NULL)
281     {
282       if (tunable_is_name ("GLIBC_TUNABLES", envname)) // searching for GLIBC_TUNABLES variables
283         {
284           char *new_env = tunables_strdup (envname);
285           if (new_env != NULL)
286             parse_tunables (new_env + len + 1, envval); // 
287           /* Put in the updated envval.  */
288           *prev_envp = new_env;
289           continue;
290         }

The first argument of parse_tunables() (tunestr) points to the soon-to-be-sanitized copy of GLIBC_TUNABLES, while the second argument (valstring) points to the original GLIBC_TUNABLES environment variable (in the stack). To sanitize the copy of GLIBC_TUNABLES (which should be of the form "tunable1=aaa:tunable2=bbb"), parse_tunables() removes all dangerous tunables (the SXID_ERASE tunables) from tunestr, but keeps SXID_IGNORE and NONE tunables (at lines 221-235):

// (GLIBC ld.so sources in ./glibc-2.37/elf/dl-tunables.c)
162 static void
163 parse_tunables (char *tunestr, char *valstring)
164 {
...
168   char *p = tunestr;
169   size_t off = 0;
170 
171   while (true)
172     {
173       char *name = p;
174       size_t len = 0;
175 
176       /* First, find where the name ends.  */
177       while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
178         len++;
179 
180       /* If we reach the end of the string before getting a valid name-value
181          pair, bail out.  */
182       if (p[len] == '\0')
183         {
184           if (__libc_enable_secure)
185             tunestr[off] = '\0';
186           return;
187         }
188 
189       /* We did not find a valid name-value pair before encountering the
190          colon.  */
191       if (p[len]== ':')
192         {
193           p += len + 1;
194           continue;
195         }
196 
197       p += len + 1;
198 
199       /* Take the value from the valstring since we need to NULL terminate it.  */
200       char *value = &valstring[p - tunestr];
201       len = 0;
202 
203       while (p[len] != ':' && p[len] != '\0')
204         len++;
205 
206       /* Add the tunable if it exists.  */
207       for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
208         {
209           tunable_t *cur = &tunable_list[i];
210 
211           if (tunable_is_name (cur->name, name))
212             {
...
219               if (__libc_enable_secure)
220                 {
221                   if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
222                     {
223                       if (off > 0)
224                         tunestr[off++] = ':';
225 
226                       const char *n = cur->name;
227 
228                       while (*n != '\0')
229                         tunestr[off++] = *n++;
230 
231                       tunestr[off++] = '=';
232 
233                       for (size_t j = 0; j < len; j++)
234                         tunestr[off++] = value[j];
235                     }
236 
237                   if (cur->security_level != TUNABLE_SECLEVEL_NONE)
238                     break;
239                 }
240 
241               value[len] = '\0';
242               tunable_initialize (cur, value);
243               break;
244             }
245         }
246 
247       if (p[len] != '\0')
248         p += len + 1;
249     }
250 }

Unfortunately, if a GLIBC_TUNABLES environment variable is of the form "tunable1=tunable2=AAA" (where "tunable1" and "tunable2" are SXID_IGNORE tunables, for example "glibc.malloc.mxfast"), then:

Download Tool