flawopen.com/Teardowns/cve-2015-3824-android-stagefright-tx3g-integer-overflow
Wie eine ungeprüfte Größenaddition beim Parsen des MPEG-4-Untertitel-Atoms tx3g überlief, einen winzigen Heap-Puffer anlegte und es einem präparierten Video erlaubte, den Speicher von mediaserver ohne jede Nutzerinteraktion zu beschädigen.
Stellen Sie sich eine Druckerei vor, deren Bestellformular nur vier Kästchen für die Länge eines Banners hat. Es sind bereits 9.998 Meter eingetragen, und Sie bestellen 5 weitere. Der Angestellte trägt die Summe in die vier Kästchen ein, die Zahl springt auf 0003, und die Druckerei schneidet ein 3-Meter-Blatt zu. Dann druckt die Maschine die vollen 10.003 Meter Text auf dieses Blatt, und die Tinte läuft über die Aufträge der anderen Kunden auf dem Tisch. Stagefright tat dasselbe mit dem Speicher: Die Summe lief über, ein winziger Puffer wurde angelegt, und alle Videodaten wurden hineingeschrieben.
Stagefright (libstagefright)MPEG-4-Atomtx3gInteger-ÜberlaufMPEG4Extractor::parseChunk() addierte die Größe der bereits gespeicherten Formatdaten zum vom Angreifer kontrollierten chunk_size des tx3g-Atoms, ohne auf Überlauf zu prüfen. Die übergelaufene Summe bestimmte die Größe der Heap-Allokation, und readAt() kopierte anschließend die vollen chunk_size Bytes hinein.
Der Angreifer schickt eine MP4-Datei, deren tx3g-Atom einen chunk_size nahe am Maximalwert angibt, etwa per MMS oder Messenger.
Unter Android 5.1 und älter parste der Mediendienst eingehende Medien, um Vorschauen und Miniaturansichten zu erzeugen, ganz ohne Antippen.
size + chunk_size läuft über, und new[] legt nur einen Puffer von wenigen Bytes an.
readAt() kopiert chunk_size Bytes in den kleinen Puffer, beschädigt den Heap von mediaserver und verschafft dem Angreifer Codeausführung mit dessen Rechten.
// 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.