Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 30 Jan 2016 16:38:26 +0000
From:      bugzilla-noreply@freebsd.org
To:        freebsd-bugs@FreeBSD.org
Subject:   [Bug 206761] Kernel stack overflow in sysctl handler for kern.binmisc.add
Message-ID:  <bug-206761-8@https.bugs.freebsd.org/bugzilla/>

next in thread | raw e-mail | index | archive | help
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D206761

            Bug ID: 206761
           Summary: Kernel stack overflow in sysctl handler for
                    kern.binmisc.add
           Product: Base System
           Version: 11.0-CURRENT
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Only Me
          Priority: ---
         Component: kern
          Assignee: freebsd-bugs@FreeBSD.org
          Reporter: cturt@hardenedbsd.org

There is a stack overflow in the `sysctl_kern_binmisc` code path from
`sys/kern/imgact_binmisc.c`:

        switch(arg2) {
        case IBC_ADD:
                /* Add an entry. Limited to IBE_MAX_ENTRIES. */
                error =3D SYSCTL_IN(req, &xbe, sizeof(xbe));
                if (error)
                        return (error);
                if (IBE_VERSION !=3D xbe.xbe_version)
                        return (EINVAL);
                if (interp_list_entry_count =3D=3D IBE_MAX_ENTRIES)
                        return (ENOSPC);
                error =3D imgact_binmisc_add_entry(&xbe);
                break;

Notice that contents of `xbe` are user controlled, with just a check on the
`xbe_version` member before calling `imgact_binmisc_add_entry`:

static int
imgact_binmisc_add_entry(ximgact_binmisc_entry_t *xbe)
{
        imgact_binmisc_entry_t *ibe;
        char *p;

        if (xbe->xbe_msize > IBE_MAGIC_MAX)
                return (EINVAL);

        for(p =3D xbe->xbe_name; *p !=3D 0; p++)
                if (!isascii((int)*p))
                        return (EINVAL);

        for(p =3D xbe->xbe_interpreter; *p !=3D 0; p++)
                if (!isascii((int)*p))
                        return (EINVAL);

Notice that already 2 out of bounds reads are possible. Since these strings
come from userland, there is no guarantee that they are NULL truncated.
Limiting the contentents of `sb_interpreter` to ASCII characters only is a
minor annoyance, but let's continue:

        /* Make sure we don't have any invalid #'s. */
        p =3D xbe->xbe_interpreter;
        while (1) {
                p =3D strchr(p, '#');
                if (!p)
                        break;

                p++;
                switch(*p) {
                case ISM_POUND:
                        /* "##" */
                        p++;
                        break;

                case ISM_OLD_ARGV0:
                        /* "#a" */
                        p++;
                        break;

                case 0:
                default:
                        /* Anything besides the above is invalid. */
                        return (EINVAL);
                }
        }

        sx_xlock(&interp_list_sx);
        if (imgact_binmisc_find_entry(xbe->xbe_name) !=3D NULL) {
                sx_xunlock(&interp_list_sx);
                return (EEXIST);
        }

        /* Preallocate a new entry. */
        ibe =3D imgact_binmisc_new_entry(xbe);
        if (!ibe)
                return (ENOMEM);

        SLIST_INSERT_HEAD(&interpreter_list, ibe, link);
        interp_list_entry_count++;
        sx_xunlock(&interp_list_sx);

        return (0);
}

Basically, some more checks on the contents, but nothing limiting the size.
Moving onto the `imgact_binmisc_new_entry` call:

static imgact_binmisc_entry_t *
imgact_binmisc_new_entry(ximgact_binmisc_entry_t *xbe)
{
        imgact_binmisc_entry_t *ibe =3D NULL;
        size_t namesz =3D min(strlen(xbe->xbe_name) + 1, IBE_NAME_MAX);

        ibe =3D malloc(sizeof(*ibe), M_BINMISC, M_WAITOK|M_ZERO);

        ibe->ibe_name =3D malloc(namesz, M_BINMISC, M_WAITOK|M_ZERO);
        strlcpy(ibe->ibe_name, xbe->xbe_name, namesz);

        imgact_binmisc_populate_interp(xbe->xbe_interpreter, ibe);

The important thing here is that `xbe->xbe_name` is limited to `IBE_NAME_MA=
X`,
but there is no check on the size of `xbe->xbe_interpreter` string, it is
passed directly to `imgact_binmisc_populate_interp`:

static void
imgact_binmisc_populate_interp(char *str, imgact_binmisc_entry_t *ibe)
{
        uint32_t len =3D 0, argc =3D 1;
        char t[IBE_INTERP_LEN_MAX];
        char *sp, *tp;

        memset(t, 0, sizeof(t));

        /*
         * Normalize interpreter string. Replace white space between args w=
ith
         * single space.
         */
        sp =3D str; tp =3D t;
        while (*sp !=3D '\0') {
                if (*sp =3D=3D ' ' || *sp =3D=3D '\t') {
                        if (++len > IBE_INTERP_LEN_MAX)
                                break;
                        *tp++ =3D ' ';
                        argc++;
                        while (*sp =3D=3D ' ' || *sp =3D=3D '\t')
                                sp++;
                        continue;
                } else {
                        *tp++ =3D *sp++;
                        len++;
                }
        }
        *tp =3D '\0';
        len++;

        ibe->ibe_interpreter =3D malloc(len, M_BINMISC, M_WAITOK|M_ZERO);

        /* Populate all the ibe fields for the interpreter. */
        memcpy(ibe->ibe_interpreter, t, len);
        ibe->ibe_interp_argcnt =3D argc;
        ibe->ibe_interp_length =3D len;
}

Clearly, it is not safe to use this function on a user supplied string!

The characters of the input string (`str`) are iterated over without any ch=
eck
on size:

    while (*sp !=3D '\0') {

There is a check on the `len` value, but only when the string reaches
whitespace:

                if (*sp =3D=3D ' ' || *sp =3D=3D '\t') {
                        if (++len > IBE_INTERP_LEN_MAX)
                                break;

If the string consists of no whitespace we can increase `tp`, `sp`, and `le=
n`
freely (beyond the `IBE_INTERP_LEN_MAX` limit):

                } else {
                        *tp++ =3D *sp++;
                        len++;
                }

We then have a write from `tp`:

    *tp =3D '\0';

And also a `malloc` and `copyin` with `len`:

        ibe->ibe_interpreter =3D malloc(len, M_BINMISC, M_WAITOK|M_ZERO);

        /* Populate all the ibe fields for the interpreter. */
        memcpy(ibe->ibe_interpreter, t, len);

Remember that `t` is a fixed stack buffer:

    char t[IBE_INTERP_LEN_MAX];

So, when `len` exceeds `IBE_INTERP_LEN_MAX`, we have an out of bounds stack
read here, and an out of bounds stack write from `*tp =3D '\0'`.

But the most exploitable thing is the `*tp++ =3D *sp++;` copy; we can use it
write past the `t` buffer on the stack.

Here's the `ximgact_binmisc_entry` struct from `/sys/sys/imgact_binmisc.h`:

typedef struct ximgact_binmisc_entry {
        uint32_t xbe_version;   /* Struct version(IBE_VERSION) */
        uint32_t xbe_flags;     /* Entry flags (IBF_*) */
        uint32_t xbe_moffset;   /* Magic offset in header */
        uint32_t xbe_msize;     /* Magic size */
        uint32_t spare[3];      /* Spare fields for future use */
        char xbe_name[IBE_NAME_MAX];    /* Unique interpreter name */
        char xbe_interpreter[IBE_INTERP_LEN_MAX]; /* Interpreter path + arg=
s */
        uint8_t xbe_magic[IBE_MAGIC_MAX]; /* Header Magic */
        uint8_t xbe_mask[IBE_MAGIC_MAX]; /* Magic Mask */
} ximgact_binmisc_entry_t;

If we make `xbe_interpreter` non-terminated, the following fields, `xbe_mag=
ic`
and `xbe_mask`, will be read from.

Let's look at these some `define`s to get an idea of the sizes:

#define MAXPATHLEN 1024
#define IBE_ARG_LEN_MAX 256
#define IBE_INTERP_LEN_MAX      (MAXPATHLEN + IBE_ARG_LEN_MAX)
#define IBE_MAGIC_MAX   256

`xbe_interpreter` is 1280 bytes.
`xbe_magic` is 256 bytes.
`xbe_mask` is 256 bytes.

Now that we know we control the 512 bytes after the designated area for
`xbe_interpreter`, let's go back to the target code:

        char t[IBE_INTERP_LEN_MAX];
        ...
        sp =3D str; tp =3D t;
        while (*sp !=3D '\0') {
                if (*sp =3D=3D ' ' || *sp =3D=3D '\t') {
                        if (++len > IBE_INTERP_LEN_MAX)
                                break;
                        *tp++ =3D ' ';
                        argc++;
                        while (*sp =3D=3D ' ' || *sp =3D=3D '\t')
                                sp++;
                        continue;
                } else {
                        *tp++ =3D *sp++;
                        len++;
                }
        }
        *tp =3D '\0';

We fill our `xbe_interpreter` (`str`) with non-whitespace, ASCII bytes, 'a'=
 for
example so that the following code happens 1280 times:

    *tp++ =3D *sp++;

We then have stack overflow past `t` from user controlled contents `xbe_mag=
ic`
and `xbe_mask` (ASCII values only) for any desired size up to the next 512
bytes (just place a 0 byte to end the overflow); we can use this to overflow
return addresses on the stack to get kernel to jump anywhere we want as soo=
n as
this function returns, but only addresses with all ASCII bytes (but this is=
n't
a problem).

Unfortunately, the sysctl node, `kern.binmisc.add` is only accessible as ro=
ot.

--=20
You are receiving this mail because:
You are the assignee for the bug.=



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?bug-206761-8>