flawopen.com/Teardowns/cve-2015-3824-android-stagefright-tx3g-integer-overflow
Comment une addition de tailles non vérifiée, lors de l'analyse de l'atome de sous-titres tx3g du MPEG-4, a débordé, alloué un tampon minuscule sur le tas et permis à une vidéo piégée de corrompre la mémoire de mediaserver sans aucune interaction de l'utilisateur.
Imaginez une imprimerie dont le bon de commande n'a que quatre cases pour la longueur d'une banderole. Il y a déjà 9 998 mètres enregistrés et vous en demandez 5 de plus. L'employé écrit le total dans les quatre cases, le compteur repasse à 0003 et l'imprimerie découpe une feuille de 3 mètres. Puis la machine imprime les 10 003 mètres de texte sur cette feuille, et l'encre se répand sur les commandes des autres clients posées sur la table. Stagefright a fait de même avec la mémoire : le total a débordé, un tampon minuscule a été réservé et toutes les données de la vidéo y ont été écrites.
Stagefright (libstagefright)Atome MPEG-4tx3gDépassement d'entierMPEG4Extractor::parseChunk() ajoutait la taille des données de mise en forme déjà stockées au chunk_size de l'atome tx3g, contrôlé par l'attaquant, sans vérifier le dépassement. La somme repassée à zéro fixait la taille de l'allocation sur le tas, puis readAt() y copiait l'intégralité des chunk_size octets.
L'attaquant envoie un fichier MP4 dont l'atome tx3g déclare un chunk_size proche de la valeur maximale, par exemple par MMS ou via une messagerie.
Sous Android 5.1 et versions antérieures, le service multimédia analysait les fichiers reçus pour créer des aperçus et des miniatures, sans aucun geste de l'utilisateur.
size + chunk_size dépasse la capacité du type et new[] n'alloue qu'un tampon de quelques octets.
readAt() copie chunk_size octets dans le petit tampon, corrompt le tas de mediaserver et offre à l'attaquant l'exécution de code avec les privilèges de ce processus.
// 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 activés.