flawopen.com/Teardowns/cve-2015-3824-android-stagefright-tx3g-integer-overflow
Como uma soma de tamanhos sem verificação, ao analisar o átomo de legendas tx3g do MPEG-4, deu a volta, alocou um buffer minúsculo no heap e permitiu que um vídeo malicioso corrompesse a memória do mediaserver sem nenhuma interação do usuário.
Imagine uma gráfica cujo formulário de pedido tem só quatro casas para o comprimento de uma faixa. Já há 9.998 metros registrados e você pede mais 5. O atendente escreve o total nas quatro casas, o número vira 0003, e a gráfica corta uma folha de 3 metros. Depois a impressora despeja os 10.003 metros de texto nessa folha, e a tinta escorre sobre os pedidos dos outros clientes na mesa. O Stagefright fez o mesmo com a memória: o total deu a volta, um buffer minúsculo foi reservado e todos os dados do vídeo foram gravados nele.
Stagefright (libstagefright)Átomo MPEG-4tx3gEstouro de inteiroMPEG4Extractor::parseChunk() somava o tamanho dos dados de formatação já guardados ao chunk_size do átomo tx3g, controlado pelo atacante, sem verificar estouro. A soma que deu a volta definia o tamanho da alocação no heap, e readAt() copiava em seguida todos os chunk_size bytes para dentro dela.
O atacante envia um arquivo MP4 cujo átomo tx3g declara um chunk_size próximo do valor máximo, por exemplo via MMS ou aplicativo de mensagens.
No Android 5.1 e anteriores, o serviço de mídia analisava a mídia recebida para gerar prévias e miniaturas, sem precisar de nenhum toque.
size + chunk_size estoura e o new[] aloca um buffer de poucos bytes.
readAt() copia chunk_size bytes para o buffer pequeno, corrompendo o heap do mediaserver e dando ao atacante execução de código com os privilégios desse processo.
// 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 ativados.