7.8 High
CVSS3
Attack Vector
LOCAL
Attack Complexity
LOW
Privileges Required
LOW
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
8.1 High
AI Score
Confidence
High
0.014 Low
EPSS
Percentile
86.5%
A buffer overflow was discovered in the GNU C Library’s dynamic loader ld.so while processing the GLIBC_TUNABLES environment variable. This issue could allow a local attacker to use maliciously crafted GLIBC_TUNABLES environment variables when launching binaries with SUID permission to execute code with elevated privileges.
Recent assessments:
jheysel-r7 at January 09, 2024 5:30pm UTC reported:
This is a privilege escalation vulnerability in the dynamic loader of glibc. GLIBC_TUNABLES
is an environment variable which contains a colon separated set of switches that allow the user to tweak how glibc runs without having to recompile the binary. Example of how to properly set the environment variable:
GLIBC_TUNABLES=tunable1=AAA:tunable2=BBB
When the GLIBC_TUNABLES
environment variable is parsed in vulnerable versions, we can cause a buffer overflow by setting:
GLIBC_TUNABLES=tunable1=tunable2=AAA
Taking a look at glibc source code we can see why this works; the key is the absence of the colon in the above example and how that is handled at lines 203-204:
// (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 }
In the initial loop of “while (true)” within parse_tunables() (at lines 221-235), the entire “tunable1=tunable2=AAA” gets copied directly into tunestr, effectively filling it up. Moving to lines 247-248, p remains unchanged (as p[len] is ‘\0’ due to the absence of ‘:’ at lines 203-204), hence still referencing the value of “tunable1,” specifically “tunable2=AAA.” As the second loop of “while (true)” in parse_tunables() executes, “tunable2=AAA” gets added (as if it were another parameter) to tunestr, which is already at maximum capacity, resulting in a tunestr overflow. This can be used to invoke a SUID binary which will then, if we’re lucky, invoke the shell code we’ve overflowed onto the stack.
There were a wide range of linux distributions that shipped with the affected glibc library. However to exploit each distribution you need to determine a specific “magic” offset in order for the buffer overflow to result in RCE. The original PoC shipped with tooling for users to determine this magic offset when ASLR was disabled, however it wasn’t working for Fedora, RedHat, Amazon Linux, Gentoo and a couple others I tested. Due to the low level complexity of the vuln the PoC likely needed some massaging in order to exploit those distros.
Because ASLR is enabled by default, you need to loop over the exploit many times in order for it to be successful. You have approximately a 1/2700 chance of the exploit being successful every time you attempt to exploit it. You can easily loop over exploit attempts quickly so that part doesn’t affect exploitability too much though does add a significant wait time for the exploit to be successful.
By default Ubuntu patches this vuln automatically as a part of the Automatic Security Updates, so you do have to go out of your way to set up a vulnerable instance.
MadDud at October 04, 2023 12:49pm UTC reported:
This is a privilege escalation vulnerability in the dynamic loader of glibc. GLIBC_TUNABLES
is an environment variable which contains a colon separated set of switches that allow the user to tweak how glibc runs without having to recompile the binary. Example of how to properly set the environment variable:
GLIBC_TUNABLES=tunable1=AAA:tunable2=BBB
When the GLIBC_TUNABLES
environment variable is parsed in vulnerable versions, we can cause a buffer overflow by setting:
GLIBC_TUNABLES=tunable1=tunable2=AAA
Taking a look at glibc source code we can see why this works; the key is the absence of the colon in the above example and how that is handled at lines 203-204:
// (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 }
In the initial loop of “while (true)” within parse_tunables() (at lines 221-235), the entire “tunable1=tunable2=AAA” gets copied directly into tunestr, effectively filling it up. Moving to lines 247-248, p remains unchanged (as p[len] is ‘\0’ due to the absence of ‘:’ at lines 203-204), hence still referencing the value of “tunable1,” specifically “tunable2=AAA.” As the second loop of “while (true)” in parse_tunables() executes, “tunable2=AAA” gets added (as if it were another parameter) to tunestr, which is already at maximum capacity, resulting in a tunestr overflow. This can be used to invoke a SUID binary which will then, if we’re lucky, invoke the shell code we’ve overflowed onto the stack.
There were a wide range of linux distributions that shipped with the affected glibc library. However to exploit each distribution you need to determine a specific “magic” offset in order for the buffer overflow to result in RCE. The original PoC shipped with tooling for users to determine this magic offset when ASLR was disabled, however it wasn’t working for Fedora, RedHat, Amazon Linux, Gentoo and a couple others I tested. Due to the low level complexity of the vuln the PoC likely needed some massaging in order to exploit those distros.
Because ASLR is enabled by default, you need to loop over the exploit many times in order for it to be successful. You have approximately a 1/2700 chance of the exploit being successful every time you attempt to exploit it. You can easily loop over exploit attempts quickly so that part doesn’t affect exploitability too much though does add a significant wait time for the exploit to be successful.
By default Ubuntu patches this vuln automatically as a part of the Automatic Security Updates, so you do have to go out of your way to set up a vulnerable instance.
Assessed Attacker Value: 4
Assessed Attacker Value: 4Assessed Attacker Value: 3
packetstormsecurity.com/files/174986/glibc-ld.so-Local-Privilege-Escalation.html
packetstormsecurity.com/files/176288/Glibc-Tunables-Privilege-Escalation.html
seclists.org/fulldisclosure/2023/Oct/11
www.openwall.com/lists/oss-security/2023/10/03/2
www.openwall.com/lists/oss-security/2023/10/03/3
www.openwall.com/lists/oss-security/2023/10/05/1
www.openwall.com/lists/oss-security/2023/10/13/11
www.openwall.com/lists/oss-security/2023/10/14/3
www.openwall.com/lists/oss-security/2023/10/14/5
www.openwall.com/lists/oss-security/2023/10/14/6
access.redhat.com/errata/RHSA-2023:5453
access.redhat.com/errata/RHSA-2023:5454
access.redhat.com/errata/RHSA-2023:5455
access.redhat.com/errata/RHSA-2023:5476
access.redhat.com/errata/RHSA-2024:0033
access.redhat.com/security/cve/CVE-2023-4911
bugzilla.redhat.com/show_bug.cgi?id=2238352
cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-4911
github.com/leesh3288/CVE-2023-4911
github.com/RickdeJager/CVE-2023-4911
lists.fedoraproject.org/archives/list/[email protected]/message/4DBUQRRPB47TC3NJOUIBVWUGFHBJAFDL/
lists.fedoraproject.org/archives/list/[email protected]/message/DFG4P76UHHZEWQ26FWBXG76N2QLKKPZA/
lists.fedoraproject.org/archives/list/[email protected]/message/NDAQWHTSVOCOZ5K6KPIWKRT3JX4RTZUR/
security.gentoo.org/glsa/202310-03
security.netapp.com/advisory/ntap-20231013-0006/
www.debian.org/security/2023/dsa-5514
www.qualys.com/2023/10/03/cve-2023-4911/looney-tunables-local-privilege-escalation-glibc-ld-so.txt
www.qualys.com/cve-2023-4911/
7.8 High
CVSS3
Attack Vector
LOCAL
Attack Complexity
LOW
Privileges Required
LOW
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
8.1 High
AI Score
Confidence
High
0.014 Low
EPSS
Percentile
86.5%