####################################################################### Luigi Auriemma Applicazione: AdPlug http://adplug.sourceforge.net Versioni: <= 2.0 e CVS <= 04 Jul 2006 Piattaforme: Windows, DOS, *nix, *BSD ed altri Bugs: A] heap overflow in the unpacking of CFF files B] heap overflow in the unpacking of MTK files C] heap overflow in the unpacking of DMO files D] buffer-overflow in DTM files E] buffer-overflow in S3M files F] heap overflow in the unpacking of U6M files Exploitation: locale Data: 06 Jul 2006 Autore: Luigi Auriemma e-mail: aluigi@autistici.org web: aluigi.org ####################################################################### 1) Introduzione 2) Bugs 3) The Code 4) Fix ####################################################################### =============== 1) Introduzione =============== AdPlug e' una libreria open source per la lettura di moltissimi files che utilizzano il formato Adlib. Inoltre sono inclusi anche alcuni programmi e dei plugins per Winamp e XMMS. ####################################################################### ======= 2) Bugs ======= La libreria e' affetta da varie vulnerabilita' di heap e stack overflow. Come intuibile dai tipi di bugs quasi tutte le istruzioni di unpacking non verificano la grandezza dei buffers di destinazione ma si fidano unicamente dei valori letti dagli stessi files che sono utilizzati per allocare i buffers necessari (eccetto nei files CFF dove viene utilizzato un valore fisso). Cio' che segue sono le parti contenente il codice buggato: ---------------------------------------------- A] heap overflow in the unpacking of CFF files ---------------------------------------------- Da cff.cpp: bool CcffLoader::load(const std::string &filename, const CFileProvider &fp) ... f->readString(header.id, 16); header.version = f->readInt(1); header.size = f->readInt(2); header.packed = f->readInt(1); f->readString((char *)header.reserved, 12); if (memcmp(header.id,"""\x1A\xDE\xE0",16)) { fp.close(f); return false; } unsigned char *module = new unsigned char [0x10000]; // packed ? if (header.packed) { cff_unpacker *unpacker = new cff_unpacker; unsigned char *packed_module = new unsigned char [header.size + 4]; memset(packed_module,0,header.size + 4); f->readString((char *)packed_module, header.size); fp.close(f); if (!unpacker->unpack(packed_module,module)) ... ---------------------------------------------- B] heap overflow in the unpacking of MTK files ---------------------------------------------- Da mtk.cpp: bool CmtkLoader::load(const std::string &filename, const CFileProvider &fp) ... // read header f->readString(header.id, 18); header.crc = f->readInt(2); header.size = f->readInt(2); // file validation section if(strncmp(header.id,"mpu401tr\x92kk\xeer@data",18)) { fp.close(f); return false; } // load section cmpsize = fp.filesize(f) - 22; cmp = new unsigned char[cmpsize]; org = new unsigned char[header.size]; for(i = 0; i < cmpsize; i++) cmp[i] = f->readInt(1); fp.close(f); while(cmpptr < cmpsize) { // decompress ... ---------------------------------------------- C] heap overflow in the unpacking of DMO files ---------------------------------------------- Da dmo.cpp: #define ARRAY_AS_WORD(a, i) ((a[i + 1] << 8) + a[i]) ... bool CdmoLoader::load(const std::string &filename, const CFileProvider &fp) ... // get file size long packed_length = fp.filesize(f); f->seek(0); unsigned char *packed_module = new unsigned char [packed_length]; // load file f->readString((char *)packed_module, packed_length); fp.close(f); // decrypt unpacker->decrypt(packed_module,packed_length); long unpacked_length = 0x2000 * ARRAY_AS_WORD(packed_module, 12); unsigned char *module = new unsigned char [unpacked_length]; // unpack if (!unpacker->unpack(packed_module+12,module)) ... ------------------------------- D] buffer-overflow in DTM files ------------------------------- Da dtm.cpp: bool CdtmLoader::load(const std::string &filename, const CFileProvider &fp) ... char bufstr[80]; for (i=0;i<16;i++) { // get line length unsigned char bufstr_length = f->readInt(1); // read line if (bufstr_length) { f->readString(bufstr,bufstr_length); for (j=0;jseek(checkhead->ordnum, binio::Add); for(i = 0; i < checkhead->insnum; i++) insptr[i] = f->readInt(2); for(i=0;iinsnum;i++) { f->seek(insptr[i]*16); if(f->readInt(1) >= 2) { adlibins = true; break; } } delete checkhead; if(!adlibins) { fp.close(f); return false; } } // load section f->seek(0); // rewind for load load_header(f, &header); // read header for(i = 0; i < header.ordnum; i++) orders[i] = f->readInt(1); // read orders for(i = 0; i < header.insnum; i++) insptr[i] = f->readInt(2); // instrument parapointers for(i = 0; i < header.patnum; i++) pattptr[i] = f->readInt(2); // pattern parapointers ... ---------------------------------------------- F] heap overflow in the unpacking of U6M files ---------------------------------------------- destination.size viene settato ma poi non viene utilizzato quindi non ci sono reali controlli sulla grandezza del buffer di output. Da u6m.cpp: bool Cu6mPlayer::load(const std::string &filename, const CFileProvider &fp) ... unsigned char pseudo_header[6]; f->readString((char *)pseudo_header, 6); decompressed_filesize = pseudo_header[0] + (pseudo_header[1] << 8); if (!( (pseudo_header[2]==0) && (pseudo_header[3]==0) && (pseudo_header[4] + ((pseudo_header[5] & 0x1)<<8) == 0x100) && (decompressed_filesize > (filesize-4)) )) { fp.close(f); return(false); } ... song_data = new unsigned char[decompressed_filesize]; unsigned char* compressed_song_data = new unsigned char[filesize-4]; f->seek(4); f->readString((char *)compressed_song_data, filesize - 4); fp.close(f); // attempt to decompress the song data // if unsuccessful, deallocate song_data[] on the spot, and return(false) data_block source, destination; source.size = filesize-4; source.data = compressed_song_data; destination.size = decompressed_filesize; destination.data = song_data; if (!lzw_decompress(source,destination)) ... ####################################################################### =========== 3) The Code =========== Ho scritto un semplice proof-of-concept esperimentale ma per alcune limitazioni (non conosco tutti gli algoritmi di compressione utilizzati) non puo' essere utilizzato per testare tutti i bugs. Ad ogni modo non e' completo od ottimizzato quindi non va' considerato come realmente funzionante fatta eccezione unicamente per i bugs 4 e 5. http://aluigi.org/poc/adplugbof.c ####################################################################### ====== 4) Fix ====== CVS 05 Jul 2006 #######################################################################