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>