flawopen.com/Teardowns/cve-2015-3824-android-stagefright-tx3g-integer-overflow
Cómo una suma de tamaños sin comprobar, al analizar el átomo de subtítulos tx3g de MPEG-4, dio la vuelta, reservó un búfer diminuto en el heap y permitió que un vídeo manipulado corrompiera la memoria de mediaserver sin ninguna interacción del usuario.
Imagina una imprenta cuyo formulario de pedido solo tiene cuatro casillas para la longitud de una pancarta. Ya hay 9.998 metros registrados y pides 5 más. El empleado escribe el total en las cuatro casillas, el número da la vuelta a 0003 y la imprenta corta una hoja de 3 metros. Después la impresora vuelca los 10.003 metros de texto sobre esa hoja y la tinta se derrama sobre los pedidos de los demás clientes. Stagefright hizo lo mismo con la memoria: el total dio la vuelta, se reservó un búfer diminuto y se escribieron en él todos los datos del vídeo.
Stagefright (libstagefright)Átomo MPEG-4tx3gDesbordamiento de enterosMPEG4Extractor::parseChunk() sumaba el tamaño de los datos de formato ya guardados al chunk_size del átomo tx3g, controlado por el atacante, sin comprobar el desbordamiento. La suma que dio la vuelta fijaba el tamaño de la reserva en el heap, y readAt() copiaba después los chunk_size bytes completos en ella.
El atacante envía un archivo MP4 cuyo átomo tx3g declara un chunk_size cercano al valor máximo, por ejemplo por MMS o una aplicación de mensajería.
En Android 5.1 y anteriores, el servicio multimedia analizaba los archivos recibidos para crear vistas previas y miniaturas, sin necesidad de tocar nada.
size + chunk_size se desborda y new[] reserva un búfer de solo unos pocos bytes.
readAt() copia chunk_size bytes en el búfer pequeño, corrompe el heap de mediaserver y da al atacante ejecución de código con los privilegios de ese proceso.
// Simplified from MPEG4Extractor::parseChunk() in libstagefright, before the fix
case FOURCC('t', 'x', '3', 'g'):
{
uint32_t type;
const void *data;
size_t size = 0;
if (!mLastTrack->meta->findData(kKeyTextFormatData, &type, &data, &size)) {
size = 0;
}
// chunk_size is read from the file. size + chunk_size can wrap around,
// so a tiny buffer is allocated for a huge copy.
uint8_t *buffer = new (std::nothrow) uint8_t[size + chunk_size];
if (buffer == NULL) {
return ERROR_MALFORMED;
}
if (size > 0) {
memcpy(buffer, data, size);
}
// Copies the full chunk_size bytes into the undersized buffer.
if ((size_t)mDataSource->readAt(*offset, buffer + size, chunk_size) < chunk_size) {
delete[] buffer;
return ERROR_IO;
}
mLastTrack->meta->setData(kKeyTextFormatData, 0, buffer, size + chunk_size);
delete[] buffer;
*offset += chunk_size;
break;
}
// Simplified from MPEG4Extractor::parseChunk() in libstagefright, after the fix
case FOURCC('t', 'x', '3', 'g'):
{
uint32_t type;
const void *data;
size_t size = 0;
if (!mLastTrack->meta->findData(kKeyTextFormatData, &type, &data, &size)) {
size = 0;
}
// Reject any length whose sum would overflow before allocating.
if (chunk_size < 0 || (uint64_t)chunk_size > SIZE_MAX - size) {
return ERROR_MALFORMED;
}
uint8_t *buffer = new (std::nothrow) uint8_t[size + chunk_size];
if (buffer == NULL) {
return ERROR_MALFORMED;
}
if (size > 0) {
memcpy(buffer, data, size);
}
if ((size_t)mDataSource->readAt(*offset, buffer + size, chunk_size) < chunk_size) {
delete[] buffer;
return ERROR_IO;
}
mLastTrack->meta->setData(kKeyTextFormatData, 0, buffer, size + chunk_size);
delete[] buffer;
*offset += chunk_size;
break;
}
fsanitize=integer activados.