flawopen.com/Teardowns/cve-2015-3824-android-stagefright-tx3g-integer-overflow
MPEG-4 के सबटाइटल एटम tx3g को पार्स करते समय बिना जाँच के किया गया आकार का जोड़ पलटकर छोटा हो गया, हीप पर एक बहुत छोटा बफ़र बना, और एक गढ़ा हुआ वीडियो बिना किसी उपयोगकर्ता क्रिया के mediaserver की मेमोरी को भ्रष्ट कर सका।
एक प्रिंटिंग प्रेस की कल्पना कीजिए जिसके ऑर्डर फ़ॉर्म में बैनर की लंबाई लिखने के लिए सिर्फ़ चार खाने हैं। रिकॉर्ड में पहले से 9,998 मीटर दर्ज हैं और आप 5 मीटर और माँगते हैं। क्लर्क कुल जोड़ चार खानों में लिखता है, संख्या पलटकर 0003 बन जाती है, और प्रेस सिर्फ़ 3 मीटर की शीट काटती है। फिर मशीन पूरे 10,003 मीटर का पाठ उसी शीट पर छापती है और स्याही मेज़ पर रखे दूसरे ग्राहकों के ऑर्डर पर फैल जाती है। Stagefright ने मेमोरी के साथ यही किया: आकार का जोड़ पलट गया, बहुत छोटा बफ़र बना और वीडियो का सारा डेटा उसमें लिख दिया गया।
Stagefright (libstagefright)MPEG-4 एटमtx3gइंटीजर ओवरफ़्लोMPEG4Extractor::parseChunk() पहले से सहेजे गए फ़ॉर्मैट डेटा का आकार हमलावर-नियंत्रित tx3g एटम के chunk_size में जोड़ता था, पर ओवरफ़्लो की जाँच नहीं करता था। पलटा हुआ जोड़ हीप आवंटन का आकार तय करता था, और फिर readAt() पूरे chunk_size बाइट उसमें कॉपी कर देता था।
हमलावर MMS या मैसेजिंग ऐप से ऐसी MP4 फ़ाइल भेजता है जिसके tx3g एटम में chunk_size अधिकतम मान के क़रीब घोषित है।
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 चालू रखकर मीडिया और फ़ाइल पार्सरों की लगातार फ़ज़िंग करें।