Fix environment variable vulnerability CVE-2019-14868 using patch emailed to me by Siteshwar Vashisht. https://bugzilla.redhat.com/show_bug.cgi?id=1757324 https://github.com/att/ast/commit/c7de8b641266bac7c77942239ac659edfee9ecd2 --- src/cmd/ksh93/sh/arith.c.orig +++ src/cmd/ksh93/sh/arith.c @@ -567,19 +567,32 @@ Sfdouble_t sh_strnum(Shell_t *shp, const char *str, char **ptr, int mode) { char *last; if (*str == 0) { - if (ptr) *ptr = (char *)str; - return 0; - } - errno = 0; - d = number(str, &last, shp->inarith ? 0 : 10, NULL); - if (*last) { - if (*last != '.' || last[1] != '.') { - d = strval(shp, str, &last, arith, mode); - Varsubscript = true; + d = 0.0; + last = (char *)str; + } else { + d = number(str, &last, shp->inarith ? 0 : 10, NULL); + if (*last && !shp->inarith && sh_isstate(shp, SH_INIT)) { + // This call is to handle "base#value" literals if we're importing untrusted env vars. + d = number(str, &last, 0, NULL); + } + if (*last) { + if (sh_isstate(shp, SH_INIT)) { + // Initializing means importing untrusted env vars. Since the string does not appear + // to be a recognized numeric literal give up. We can't safely call strval() since + // that allows arbitrary expressions which would create a security vulnerability. + d = 0.0; + } else { + if (*last != '.' || last[1] != '.') { + d = strval(shp, str, &last, arith, mode); + Varsubscript = true; + } + if (!ptr && *last && mode > 0) { + errormsg(SH_DICT, ERROR_exit(1), e_lexbadchar, *last, str); + } + } + } else if (d == 0.0 && *str == '-') { + d = -0.0; } - if (!ptr && *last && mode > 0) errormsg(SH_DICT, ERROR_exit(1), e_lexbadchar, *last, str); - } else if (!d && *str == '-') { - d = -0.0; } if (ptr) *ptr = last; return d; --- src/cmd/ksh93/tests/subshell.sh.orig +++ src/cmd/ksh93/tests/subshell.sh @@ -856,3 +856,26 @@ for exp in 65535 65536 do got=$($SHELL -c 'x=$(printf "%.*c" '$exp' x); print ${#x}' 2>&1) [[ $got == $exp ]] || log_error "large command substitution failed" "$exp" "$got" done + +# ========== +# Verify that importing untrusted env vars does not allow evaluating arbitrary expressions but does +# recognize all integer literals recognized by ksh. +expect=8 +actual=$(env SHLVL='7' $SHELL -c 'echo $SHLVL') +[[ $actual == $expect ]] || log_error "decimal int literal not recognized" "$expect" "$actual" + +expect=14 +actual=$(env SHLVL='013' $SHELL -c 'echo $SHLVL') +[[ $actual == $expect ]] || log_error "leading zeros int literal not recognized" "$expect" "$actual" + +expect=4 +actual=$(env SHLVL='2#11' $SHELL -c 'echo $SHLVL') +[[ $actual == $expect ]] || log_error "base#value int literal not recognized" "$expect" "$actual" + +expect=12 +actual=$(env SHLVL='16#B' $SHELL -c 'echo $SHLVL') +[[ $actual == $expect ]] || log_error "base#value int literal not recognized" "$expect" "$actual" + +expect=1 +actual=$(env SHLVL="2#11+x[\$($bin_echo DANGER WILL ROBINSON >&2)0]" $SHELL -c 'echo $SHLVL') +[[ $actual == $expect ]] || log_error "expression allowed on env var import" "$expect" "$actual"