
CVE-2023-4911 (Looney Tunables) Analysebericht und Docker-Reproduktionslabor
| Eintrag | Inhalt |
|---|---|
| CVE ID | CVE-2023-4911 |
| Angriffstyp | Heap-Pufferüberlauf → Lokale Privilegienausweitung (Local Privilege Escalation) |
| CVSS 3.1 | 7.8 (High) |
| Veröffentlichungsdatum | 2023-10-03 |
| Angriffspunkt | Parser des glibc Dynamic Loaders (ld.so) für GLIBC_TUNABLES |
| Anfällige Versionen | glibc 2.34~2.38 |
CVE-2023-4911 ist eine Heap-Pufferüberlauf-Schwachstelle, die beim Parsen der Umgebungsvariablen GLIBC_TUNABLES durch den dynamischen Loader der GNU C Library (glibc) auftritt. Angreifer können diesen Überlauf nutzen, um den Bibliothekssuchpfad (RPATH) des dynamischen Loaders zu manipulieren, sodass bei der Ausführung eines SUID-root-Binärprogramms (wie su, sudo) eine vom Angreifer bereitgestellte bösartige Shared Library anstelle der legitimen geladen wird. Dadurch kann beliebiger Code mit root-Rechten ausgeführt werden. Da glibc eine Kernkomponente nahezu aller wichtigen Linux-Distributionen ist, betraf diese Schwachstelle die meisten auf glibc basierenden Distributionen, die nach April 2021 veröffentlicht wurden.
while (true)
{
char *name = p;
size_t len = 0;
/* 이름(name) 길이 찾기 */
while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
len++;
/* '=' 없이 끝나면 종료 */
if (p[len] == '\0')
{
if (__libc_enable_secure)
tunestr[off] = '\0';
return;
}
/* ':'를 먼저 만나면 잘못된 항목 */
if (p[len] == ':')
{
p += len + 1;
continue;
}
/* '='를 만났으므로 value 시작으로 이동 */
p += len + 1;
/* 원본 문자열에서 value 계산 */
char *value = &valstring[p - tunestr];
len = 0;
/* value 길이 찾기 */
while (p[len] != ':' && p[len] != '\0')
len++;
...
/* tunestr에 복사 */
...
if (p[len] != '\0')
p += len + 1;
}
__tunables_init() sucht in der Liste der Umgebungsvariablen nach GLIBC_TUNABLES.tunables_strdup() weist mit __minimal_malloc() einen Puffer zu und kopiert die ursprüngliche Zeichenkette hinein (zu diesem Zeitpunkt ist malloc eine sehr frühe, noch nicht vollständig initialisierte Implementierung).parse_tunables() durchläuft diesen Puffer getrennt durch : (Doppelpunkt) und weist jedes key=value-Paar dem entsprechenden Tunable zu.parse_tunables() verarbeitet ein Tunable in der Reihenfolge Name parsen → p verschieben → Wert parsen → p verschieben. Bei normaler Eingabe wird p nach der Verarbeitung des Wertes an den Anfang des nächsten Tunables verschoben, um den nächsten Eintrag zu parsen.
Wenn jedoch eine Eingabe der Form name=name=value vorliegt, z. B.:
GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=AAAA...(긴 문자열)
wird beim ersten Parsen die gesamte Zeichenkette glibc.malloc.mxfast=AAAA... als ein Wert erkannt und in den tunestr-Puffer kopiert. Da nach dem Wert kein Doppelpunkt (:) zur Trennung des nächsten Tunables vorhanden ist, bewegt sich der Parsing-Zeiger (p) nicht zum nächsten Eintrag, sondern zeigt erneut auf den Anfang des bereits kopierten Wertes.
Das Problem liegt darin, dass dieser Wert selbst die Form name=value hat. In der nächsten Iteration erkennt der Parser dies fälschlicherweise als neues Tunable und schreibt doppelte Daten in den Puffer. Da tunestr nur für die Größe der ursprünglichen Zeichenkette allokiert wurde, führt das Doppelschreiben zu einem Heap-Pufferüberlauf.
Der auftretende Heap-Pufferüberlauf überschreibt benachbarte Heap-Bereiche, die von __minimal_malloc() hintereinander allokiert wurden. Der Angreifer kann dies nutzen, um den Zeiger auf l_info[DT_RPATH] in der internen Struktur link_map des dynamischen Loaders (ld.so) auf eine vom Angreifer kontrollierte Stack-Adresse zu ändern.
In diesem Stack-Bereich wurde zuvor eine manipulierte Elf64_Dyn-Struktur platziert, die ein vom Angreifer gewünschtes Verzeichnis als neuen Bibliothekssuchpfad (RPATH) angibt. Infolgedessen lädt ld.so anstelle der normalen Systembibliotheken zuerst die vom Angreifer bereitgestellte Shared Library.
GLIBC_TUNABLES-Umgebungsvariable im Format name=name=value.su) mit normalen Benutzerrechten aus.parse_tunables() von ld.so tritt der Heap-Pufferüberlauf auf.l_info[DT_RPATH]-Zeiger von link_map auf die Stack-Adresse zu ändern, an der sich die vorbereitete gefälschte Elf64_Dyn-Struktur befindet.libc.so.6 des Angreifers zu laden.libc.so.6 wird mit root-Rechten ausgeführt und führt setuid(0), setgid(0) sowie die Ausführung von durch.Der Proof-of-Concept (PoC) verwendet eine Brute-Force-Methode, bei der execve() wiederholt aufgerufen wird, bis durch ASLR das gewünschte Speicherlayout erreicht ist. Daher können Erfolgswahrscheinlichkeit und Zeitaufwand je nach Umgebung variieren; typischerweise sind mehrere hundert bis tausend Wiederholungen erforderlich.
Zuerst den Inhalt aus git in ein Verzeichnis klonen:
git clone https://github.com/baeseungwon1010/CVE-2023-4911

Das Docker-Image mit folgendem Befehl bauen:
cd C* && docker compose run --rm cve-2023-4911-lab

Nach dem Betreten des Containers den Exploit-Code ausführen:
cd /home/student/exploit && ./exp
Nach dem Ausführen und Warten sieht man, dass der normale Benutzer zu sudo(0) geworden ist.

Patch der glibc auf eine Version, die nicht mehr anfällig ist. Nach dem Patch, falls möglich, einen Neustart oder Wiederstart durchführen, um sicherzustellen, dass die alte glibc-Version nicht mehr im Speicher verbleibt. Falls ein sofortiger Patch nicht möglich ist, kann als temporäre Maßnahme das Entfernen unnötiger SUID-/SGID-Prozesse eingesetzt werden.
/bin/shsu stattfindet, erhält der Angreifer eine root-Shell ohne Passwortüberprüfung.