From 1c4c7b6a4d17294df028322b70c53803a402233d Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Mon, 3 Sep 2018 14:39:25 +0000 Subject: CVE-2018-0502, CVE-2018-13259: Fix two security issues in shebang line parsing. See NEWS for more information. Patch by Anthony Sottile and Buck Evan. --- ChangeLog | 5 +++++ Etc/FAQ.yo | 2 +- NEWS | 21 +++++++++++++++++++++ README | 6 +++--- Src/exec.c | 36 ++++++++++++++++++++---------------- Test/A05execution.ztst | 22 ++++++++++++++++++++++ 6 files changed, 72 insertions(+), 20 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9d95cda22..85d564601 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2018-09-03 Anthony Sottile + + * CVE-2018-0502, CVE-2018-13259: Fix two security issues in + shebang line parsing. [With Buck Evan] + 2018-09-03 Daniel Shahaf * 43367: Makefile.in: Add maintainer targets 'tarxz-src' and diff --git a/Etc/FAQ.yo b/Etc/FAQ.yo index 12bd4eb3d..d4dc8a150 100644 --- a/Etc/FAQ.yo +++ b/Etc/FAQ.yo @@ -306,7 +306,7 @@ sect(On what machines will it run?) sect(What's the latest version?) - Zsh 5.5.1 is the latest production version. For details of all the + Zsh 5.6 is the latest production version. For details of all the changes, see the NEWS file in the source distribution. A beta of the next version is sometimes available. Development of zsh is diff --git a/NEWS b/NEWS index 93d879ce3..7b3e5692c 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,27 @@ CHANGES FROM PREVIOUS VERSIONS OF ZSH Note also the list of incompatibilities in the README file. +Changes from 5.5.1-test-2 to 5.6 +-------------------------------- + +CVE-2018-0502: Data from the second line of a #! script file might be passed to +execve(). For example, in the following situation - +. + printf '#!foo\nbar' > baz + ./baz +. +the shell might take "bar" rather than "foo" for the argv[0] to be passed to +execve(). [ Reported by Anthony Sottile and Buck Evan. ] + +CVE-2018-13259: A shebang line longer than 64 characters would be truncated. +For example, in the following situation: +. + ( printf '#!'; repeat 64 printf 'x'; printf 'y' ) > foo + ./foo +. +the shell might execute x...x (64 repetitions) rather than x...xy (64 x's, +one y). [ Reported by Daniel Shahaf. ] + Changes from 5.5.1 to 5.5.1-test-2 ---------------------------------- diff --git a/README b/README index 8817433e4..ed29d0107 100644 --- a/README +++ b/README @@ -5,9 +5,9 @@ THE Z SHELL (ZSH) Version ------- -This is version 5.5.1-test-2 of the shell. This is a test release. There -are some significant bug fixes and a few user visible additions since -5.5.1. All zsh installations are encouraged to upgrade. +This is version 5.6 of the shell. This is a security and feature release. +There are some significant bug fixes and a few user visible additions since +5.5.1. All zsh installations are encouraged to upgrade as soon as possible. Note in particular the changes highlighted under "Incompatibilities since 5.5.1" below. See NEWS for more information. diff --git a/Src/exec.c b/Src/exec.c index 615a5086f..09ee13209 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -458,7 +458,7 @@ execcursh(Estate state, int do_exec) /* execve after handling $_ and #! */ -#define POUNDBANGLIMIT 64 +#define POUNDBANGLIMIT 128 /**/ static int @@ -499,18 +499,20 @@ zexecve(char *pth, char **argv, char **newenvp) if ((fd = open(pth, O_RDONLY|O_NOCTTY)) >= 0) { argv0 = *argv; *argv = pth; - execvebuf[0] = '\0'; + memset(execvebuf, '\0', POUNDBANGLIMIT + 1); ct = read(fd, execvebuf, POUNDBANGLIMIT); close(fd); if (ct >= 0) { - if (execvebuf[0] == '#') { - if (execvebuf[1] == '!') { - for (t0 = 0; t0 != ct; t0++) - if (execvebuf[t0] == '\n') - break; + if (ct >= 2 && execvebuf[0] == '#' && execvebuf[1] == '!') { + for (t0 = 0; t0 != ct; t0++) + if (execvebuf[t0] == '\n') + break; + if (t0 == ct) + zerr("%s: bad interpreter: %s: %e", pth, + execvebuf + 2, eno); + else { while (inblank(execvebuf[t0])) execvebuf[t0--] = '\0'; - execvebuf[POUNDBANGLIMIT] = '\0'; for (ptr = execvebuf + 2; *ptr && *ptr == ' '; ptr++); for (ptr2 = ptr; *ptr && *ptr != ' '; ptr++); if (eno == ENOENT) { @@ -519,10 +521,16 @@ zexecve(char *pth, char **argv, char **newenvp) *ptr = '\0'; if (*ptr2 != '/' && (pprog = pathprog(ptr2, NULL))) { - argv[-2] = ptr2; - argv[-1] = ptr + 1; - winch_unblock(); - execve(pprog, argv - 2, newenvp); + if (ptr == execvebuf + t0 + 1) { + argv[-1] = ptr2; + winch_unblock(); + execve(pprog, argv - 1, newenvp); + } else { + argv[-2] = ptr2; + argv[-1] = ptr + 1; + winch_unblock(); + execve(pprog, argv - 2, newenvp); + } } zerr("%s: bad interpreter: %s: %e", pth, ptr2, eno); @@ -537,10 +545,6 @@ zexecve(char *pth, char **argv, char **newenvp) winch_unblock(); execve(ptr2, argv - 1, newenvp); } - } else if (eno == ENOEXEC) { - argv[-1] = "sh"; - winch_unblock(); - execve("/bin/sh", argv - 1, newenvp); } } else if (eno == ENOEXEC) { for (t0 = 0; t0 != ct; t0++) diff --git a/Test/A05execution.ztst b/Test/A05execution.ztst index 0804691a7..fb39d0589 100644 --- a/Test/A05execution.ztst +++ b/Test/A05execution.ztst @@ -12,7 +12,14 @@ print '#!/bin/sh\necho This is dir2' >dir2/tstcmd + print -n '#!sh\necho This is slashless' >tstcmd-slashless + print -n '#!echo foo\necho This is arg' >tstcmd-arg + print '#!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxnyyy' >tstcmd-interp-too-long + print '#!/bin/sh\necho should not execute; exit 1' >xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxn + chmod 755 tstcmd dir1/tstcmd dir2/tstcmd + chmod 755 tstcmd-slashless tstcmd-arg tstcmd-interp-too-long + chmod 755 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxn %test ./tstcmd @@ -33,6 +40,21 @@ 0:path (2) >This is top + PATH=/bin:${ZTST_testdir}/command.tmp/ tstcmd-slashless +0:path (3) +>This is slashless + + PATH=/bin:${ZTST_testdir}/command.tmp tstcmd-arg +0:path (4) +*>foo */command.tmp/tstcmd-arg + + path=(/bin ${ZTST_testdir}/command.tmp/) + tstcmd-interp-too-long 2>&1; echo "status $?" + path=($storepath) +0:path (5) +*>*tstcmd-interp-too-long: bad interpreter: x*xn: no such file or directory +>status 127 + functst() { print $# arguments:; print -l $*; } functst "Eines Morgens" "als Gregor Samsa" functst "" -- cgit 1.4.1