
CVE-2018-4248: Lectura fuera de los límites en libxpc durante la serialización de cadenas.
xpc-string-leak es una prueba de concepto de explotación para una lectura de memoria fuera de los límites en libxpc. Este exploit utiliza la vulnerabilidad para leer memoria del heap fuera de los límites desde diagnosticd, un proceso root sin sandbox con el privilegio task_for_pid-allow.
En macOS 10.13.5 y iOS 11.4, la función _xpc_string_deserialize no verifica que la cadena deserializada tenga la longitud adecuada antes de crear un objeto XPC string con _xpc_string_create. Esto puede provocar una lectura del heap fuera de los límites al estilo Heartbleed si el XPC string se serializa posteriormente en otro mensaje XPC.
Aquí está la implementación de _xpc_string_deserialize, descompilada usando IDA:
OS_xpc_string *__fastcall _xpc_string_deserialize(OS_xpc_serializer *xserializer)
{
OS_xpc_string *xstring; // rbx@1
char *string; // rax@4
char *contents; // [rsp+8h] [rbp-18h]@1
size_t size; // [rsp+10h] [rbp-10h]@1 MAPDST
xstring = 0LL;
contents = 0LL;
size = 0LL;
if ( _xpc_string_get_wire_value(xserializer, (const char **)&contents, &size) )
{
if ( contents[size - 1] || (string = _xpc_try_strdup(contents)) == 0LL )
{
xstring = 0LL;
}
else
{
xstring = _xpc_string_create(string, size - 1);
LOBYTE(xstring->flags) |= 1u;
}
}
return xstring;
}
_xpc_string_deserialize primero llama a _xpc_string_get_wire_value para obtener un puntero a los datos de la cadena, así como el tamaño serializado de la cadena, según lo informado por el encabezado de la cadena. Luego, _xpc_string_deserialize comprueba que la cadena tenga un terminador nulo al final de su tamaño informado, pero crucialmente no comprueba que no haya un terminador nulo antes en los datos. Finalmente, crea una copia de la cadena en el heap y crea el objeto OS_xpc_string usando _xpc_string_create.
Aquí está el código descompilado de _xpc_string_create:
OS_xpc_string *__fastcall _xpc_string_create(const char *string, size_t length)
{
OS_xpc_string *xstring; // rax@1
xstring = (OS_xpc_string *)_xpc_base_create(&OBJC_CLASS___OS_xpc_string, 16LL);
if ( (((_DWORD)length + 4) & 0xFFFFFFFC) + 4 < length )
_xpc_api_misuse("Unreasonably large string");
xstring->wire_length = ((length + 4) & 0xFFFFFFFC) + 4;
xstring->string = string;
xstring->length = length;
return xstring;
}
_xpc_string_create confía en el valor de length proporcionado por _xpc_string_deserialize y establece los campos correspondientes en el objeto OS_xpc_string. En este punto, la cadena deserializada puede tener un campo length mayor que los datos de cadena asignados.
Teóricamente, esto podría usarse para provocar una corrupción de memoria en servicios que obtienen la longitud de la cadena usando xpc_string_get_length, pero este patrón parece poco común. Una estrategia de explotación menos potente pero más práctica es lograr que la cadena se vuelva a serializar y se nos envíe de vuelta, dándonos una ventana al estilo Heartbleed hacia la memoria del proceso víctima.
Esta es la implementación de _xpc_string_serialize:
void __fastcall _xpc_string_serialize(OS_xpc_string *string, OS_xpc_serializer *serializer)
{
int type; // [rsp+8h] [rbp-18h]@1
int size; // [rsp+Ch] [rbp-14h]@1
type = *((_DWORD *)&OBJC_CLASS___OS_xpc_string + 10);
_xpc_serializer_append(serializer, &type, 4uLL, 1, 0, 0);
size = LODWORD(string->length) + 1;
_xpc_serializer_append(serializer, &size, 4uLL, 1, 0, 0);
_xpc_serializer_append(serializer, string->string, string->length + 1, 1, 0, 0);
}
El parámetro length de OS_xpc_string se considera confiable durante la serialización, lo que significa que se leen muchos bytes del heap hacia el mensaje serializado. Si la cadena deserializada era más corta que su longitud reportada, el mensaje se llenará con datos del heap fuera de los límites.
Todavía estamos limitados a explotar servicios XPC que reflejan alguna parte del mensaje XPC de vuelta al cliente, pero esto es mucho más común. Por ejemplo, en macOS e iOS, diagnosticd es un candidato prometedor que además no tiene sandbox, es root y tiene privilegios task_for_pid. Diagnosticd es responsable de procesar mensajes de diagnóstico (por ejemplo, mensajes generados por os_log) y transmitirlos a los clientes interesados en recibirlos. Al registrarnos para recibir nuestro propio flujo de diagnóstico y luego enviar un mensaje de diagnóstico con una cadena más corta de lo esperado, podemos obtener una instantánea de algunos datos en el heap de diagnosticd, lo que puede ayudar a lograr la ejecución de código en el proceso.
Para compilar, ejecute make. Consulte la parte superior del Makefile para ver varias opciones de compilación.
Ejecute el exploit especificando el tamaño de la fuga en la línea de comandos:
$ ./xpc-string-leak 0x40
0x2000000000000000 0xe00007ff39bf0992
0x00007fff56858570 0x00007fff7ed23d0e
0x0000000000000000 0x0000000000000000
0x00007fff7ed52be2 0x00007fff7ed29ed6
El tamaño de la fuga debe ser múltiplo de 8 y al menos 16.
El código de xpc-string-leak se libera al dominio público. Como cortesía, solicito que si hace referencia o utiliza alguno de estos códigos, me atribuya la autoría.
Descubrí este error a principios de 2018 (enero o febrero), pero olvidé investigarlo hasta mayo. Informé el problema a Apple el 9 de mayo, y se le asignó CVE-2018-4248 y se parcheó en iOS 11.4.1 y macOS 10.13.6 el 9 de julio.
Brandon Azad