CVE-2019-14868 (published 2020-04-02T17:15Z)

In ksh version 20120801, a flaw was found in the way it evaluates certain environment variables. An attacker could use this flaw to override or bypass environment restrictions to execute shell commands. Services and applications that allow remote unauthenticated attackers to provide one of those environment variables could allow them to exploit this issue remotely.

Severity: HIGH
CVSS v2 base score: 7.2 (AV:L/AC:L/Au:N/C:C/I:C/A:C)
CVSS v3 base score: 7.8, HIGH (CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-77
cwe_name: Improper Neutralization of Special Elements used in a Command ('Command Injection')
description: The product constructs all or part of a command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended command when it is sent to a downstream component.
extended_description: 
            Many protocols and products have their own custom command language. While OS or shell command strings are frequently discovered and targeted, developers may not realize that these other command languages might also be vulnerable to attacks.
         
         

Fix information:
repo_url: https://github.com/att/ast
commit_details: repo_url: https://github.com/att/ast
author: Kurtis Rader
author_date: 2019-12-12 18:46:50-08:00
msg: Harden env var imports
repository: repo_url: https://github.com/att/ast
repo_name: att/ast
description: AST - AT&T Software Technology
repo_language: C
owner: att
file_changes: filename: CHANGELOG.md
change_type: ModificationType.MODIFY
diff: @@ -4,6 +4,8 @@
 
 ## Notable non-backward compatible changes
 
+- You can no longer use arbitrary expressions in imported numeric env vars;
+  only integer literals are allowed.
 - The `login` and `newgrp` special builtins have been removed. Use
   `exec login` and `exec newgrp` instead (#1348).
 

programming_language: Markdown
filename: arith.c
change_type: ModificationType.MODIFY
diff: @@ -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;

programming_language: C
filename: subshell.sh
change_type: ModificationType.MODIFY
diff: @@ -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"

programming_language: Shell