flawopen.com/Teardowns/cve-2015-3824-android-stagefright-tx3g-integer-overflow

● CVE-2015-3824 · CVSS v2 10.0 · Tinggi
Riset Keamanan · FlawOpen

CVE-2015-3824: Integer Overflow tx3g pada Stagefright (libstagefright Android)

Bagaimana penjumlahan ukuran tanpa pemeriksaan saat mengurai atom subtitel tx3g MPEG-4 berputar balik, mengalokasikan buffer heap yang sangat kecil, dan membuat video rekayasa bisa merusak memori mediaserver tanpa interaksi pengguna sama sekali.

💡 Penjelasan Sederhana (ELI5)

Bayangkan sebuah percetakan yang formulir pesanannya hanya punya empat kotak untuk panjang spanduk. Sudah tercatat 9.998 meter dan Anda meminta tambahan 5 meter. Petugas menulis totalnya di empat kotak itu, angkanya berputar menjadi 0003, dan percetakan hanya memotong lembaran 3 meter. Lalu mesin mencetak seluruh 10.003 meter teks ke lembaran itu, dan tintanya meluber ke pesanan pelanggan lain di meja. Stagefright melakukan hal yang sama pada memori: total ukurannya berputar balik, buffer kecil dialokasikan, dan seluruh data video ditulis ke dalamnya.

Konsep Kunci & Istilah

Stagefright (libstagefright)
Pustaka media native Android yang mengurai berkas audio dan video di dalam proses istimewa mediaserver.
Atom MPEG-4
Blok berkas MP4 yang mendeskripsikan dirinya sendiri: ukuran yang dinyatakan, tipe empat karakter, dan isinya. Pengurai memercayai ukuran yang dinyatakan untuk menentukan berapa banyak yang dibaca.
tx3g
Atom teks berwaktu 3GPP yang menyimpan format subtitel sebuah trek teks.
Integer overflow
Kondisi ketika hasil perhitungan melampaui kapasitas tipe bilangan bulat dan berputar balik menjadi nilai kecil, misalnya SIZE_MAX + 2 menjadi 1.

Analisis Akar Masalah (Root Cause)

MPEG4Extractor::parseChunk() menjumlahkan ukuran data format yang sudah tersimpan dengan chunk_size atom tx3g yang dikendalikan penyerang tanpa memeriksa overflow. Jumlah yang berputar balik menentukan ukuran alokasi heap, lalu readAt() menyalin seluruh chunk_size byte ke dalamnya.

Alur Serangan Langkah demi Langkah

Step 1

Video rekayasa dikirim

Penyerang mengirim berkas MP4 yang atom tx3g-nya menyatakan chunk_size mendekati nilai maksimum, misalnya lewat MMS atau aplikasi pesan.

Step 2

Penguraian berjalan otomatis

Pada Android 5.1 dan sebelumnya, layanan media mengurai media masuk untuk membuat pratinjau dan thumbnail, sehingga tidak perlu ketukan apa pun.

Step 3

Perhitungan ukuran berputar balik

size + chunk_size mengalami overflow dan new[] hanya mengalokasikan buffer beberapa byte.

Step 4

Heap tertimpa

readAt() menyalin chunk_size byte ke buffer kecil itu, merusak heap mediaserver, dan memberi penyerang eksekusi kode dengan hak proses tersebut.

Kode Sumber: Rentan vs Aman

IMPLEMENTASI RENTAN
// 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;
}
PERBAIKAN AMAN & KUAT
// 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;
}

Daftar Periksa Penguatan Sistem Rekayasa

Sumber