flawopen.com/Teardowns/cve-2015-3824-android-stagefright-tx3g-integer-overflow
Как непроверенное сложение размеров при разборе атома субтитров tx3g формата MPEG-4 переполнилось, выделило крошечный буфер в куче и позволило специально подготовленному видео повредить память mediaserver без каких-либо действий пользователя.
Представьте типографию, в бланке заказа которой для длины баннера есть всего четыре клетки. У вас уже записано 9 998 метров, и вы просите добавить ещё 5. Сотрудник вписывает сумму в четыре клетки, число превращается в 0003, и типография отрезает лист длиной 3 метра. Затем машина печатает на этом листе все 10 003 метра текста, и краска растекается по заказам других клиентов на столе. Stagefright поступал так же с памятью: сумма размеров переполнилась, выделился крошечный буфер, и в него записались все данные видео.
Stagefright (libstagefright)Атом MPEG-4tx3gЦелочисленное переполнениеMPEG4Extractor::parseChunk() прибавляла размер уже сохранённых данных оформления к управляемому атакующим chunk_size атома tx3g без проверки на переполнение. Переполнившаяся сумма задавала размер выделяемой памяти в куче, после чего readAt() копировала туда все chunk_size байт.
Атакующий отправляет MP4-файл, атом tx3g которого заявляет chunk_size, близкий к максимальному значению, например через MMS или мессенджер.
В Android 5.1 и более ранних версиях медиасервис разбирал входящие файлы для создания превью и миниатюр — нажимать ничего не требовалось.
size + chunk_size переполняется, и new[] выделяет буфер всего в несколько байт.
readAt() копирует chunk_size байт в маленький буфер, повреждая кучу mediaserver и давая атакующему выполнение кода с правами этого процесса.
// 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.