
CVE-2023-4911 (Looney Tunables) informe de análisis y laboratorio de reproducción Docker
| Elemento | Contenido |
|---|---|
| ID CVE | CVE-2023-4911 |
| Tipo de ataque | Desbordamiento del búfer del montón → Escalada de privilegios local |
| CVSS 3.1 | 7.8 (Alta) |
| Fecha de divulgación | 2023-10-03 |
| Punto vulnerable | Analizador GLIBC_TUNABLES del cargador dinámico de glibc (ld.so) |
| Versiones vulnerables | glibc 2.34 a 2.38 |
CVE-2023-4911 es una vulnerabilidad de desbordamiento del búfer del montón que ocurre cuando el cargador dinámico de GNU C Library (glibc) analiza la variable de entorno GLIBC_TUNABLES. El atacante puede aprovechar este desbordamiento para manipular la ruta de búsqueda de bibliotecas (RPATH) del cargador dinámico, de modo que cuando se ejecute un binario SUID root (su, sudo, etc.), se cargue una biblioteca compartida maliciosa preparada por el atacante en su lugar, permitiendo ejecutar código arbitrario con privilegios de root. Dado que glibc es un componente central de prácticamente todas las principales distribuciones de Linux, esta vulnerabilidad afectó a la mayoría de las distribuciones basadas en glibc lanzadas después de abril de 2021.
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() busca GLIBC_TUNABLES en la lista de variables de entorno.tunables_strdup() asigna un búfer con __minimal_malloc() y copia la cadena original (en este punto, malloc es una implementación muy temprana que aún no está completamente inicializada).parse_tunables() recorre este búfer usando : (dos puntos) como delimitador, separa cada par key=value y asigna el valor al ajustable correspondiente.parse_tunables() procesa un ajustable en el orden: análisis de nombre → avance de p → análisis de valor → avance de p. En una entrada normal, después de procesar todo el valor, p avanza a la posición de inicio del siguiente ajustable para analizar el siguiente elemento.
Sin embargo, si se proporciona una entrada de la forma name=name=value como la siguiente,
GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=AAAA...(긴 문자열)
Durante el primer análisis, glibc.malloc.mxfast=AAAA... completo se reconoce como un único valor y se copia en el búfer tunestr. Luego, como no hay ningún dos puntos (:) después del valor para separar el siguiente ajustable, el puntero de análisis (p) no puede avanzar al siguiente elemento y vuelve a apuntar al inicio del valor ya copiado.
El problema es que este valor en sí mismo tiene la forma name=value. En la siguiente iteración, el analizador lo reconoce erróneamente como un nuevo ajustable y escribe datos duplicados en el búfer. Como tunestr solo está asignado para el tamaño de la cadena original, se produce una escritura duplicada que causa un desbordamiento del búfer del montón.
El desbordamiento del búfer del montón ocurrido sobrescribe las áreas de montón adyacentes asignadas consecutivamente por __minimal_malloc(). El atacante puede usar esto para cambiar el puntero l_info[DT_RPATH] de la estructura interna link_map del cargador dinámico (ld.so) a una dirección de pila controlada por el atacante.
En esa área de la pila se ha colocado una estructura Elf64_Dyn manipulada previamente, la cual especifica el directorio que el atacante desea como nueva ruta de búsqueda de bibliotecas (RPATH). Como resultado, ld.so carga preferentemente la biblioteca compartida preparada por el atacante en lugar de las bibliotecas del sistema normales.
GLIBC_TUNABLES en la forma name=name=value.su) con permisos de usuario normal.parse_tunables() de ld.so.l_info[DT_RPATH] de link_map para que apunte a una dirección de pila donde se encuentra una estructura Elf64_Dyn falsa preparada por el atacante.libc.so.6 preparada por el atacante.libc.so.6 se ejecuta con privilegios de root, realizando setuid(0), setgid(0) y la ejecución de /bin/sh.su, por lo que se obtiene una shell de root sin verificación de contraseña.El PoC utiliza un enfoque de fuerza bruta que repite execve() hasta que la aleatorización (ASLR) produzca el diseño de memoria deseado. Por lo tanto, el éxito del ataque y el tiempo requerido pueden variar según el entorno, y generalmente se necesitan cientos o miles de intentos repetidos.
Primero, clona el contenido del repositorio en un directorio.
git clone https://github.com/baeseungwon1010/CVE-2023-4911

Construye la imagen de Docker con el siguiente comando
cd C* && docker compose run --rm cve-2023-4911-lab

Después de entrar al contenedor, ejecuta el código de exploit
cd /home/student/exploit && ./exp
Después de ejecutarlo y esperar, se puede ver que el usuario normal cambia a sudo(0)

Actualice glibc de la versión vulnerable a una versión superior. Después del parche, si es posible, reinicie/reenicie para que no queden versiones anteriores de glibc en memoria. Si no se puede parchear de inmediato, existe una solución temporal de eliminar procesos innecesarios SUID, SGID, etc.