zsh-workers
 help / color / mirror / code / Atom feed
* BUG: crafting SHELLOPTS and PS4 allows to run arbitrary programs in setuid binaries using system
@ 2016-09-27  6:59 Mateusz Lenik
  2016-09-27  7:53 ` Daniel Shahaf
  2016-09-27  8:56 ` Oliver Kiddle
  0 siblings, 2 replies; 10+ messages in thread
From: Mateusz Lenik @ 2016-09-27  6:59 UTC (permalink / raw)
  To: zsh-workers

[-- Attachment #1: Type: text/plain, Size: 625 bytes --]

Hello everyone!

I just learned that bash fixed a vulnerability that also affects zsh. It
allowed to run arbitrary programs by crafting SHELLOPTS and PS4 variables
against setuid binaries using system/popen.

Steps to reproduce:
% gcc -xc - -otest <<< 'int main() { setuid(0); system("/bin/date"); }'
% sudo chown root:root test
% sudo chmod 4755 test
% env -i SHELLOPTS=xtrace PS4='$(id)' ./test
uid=0(root) gid=... groups=.../bin/date
Tue Sep 27 08:49:16 CEST 2016
% zsh --version

zsh 5.2 (x86_64-pc-linux-gnu)
%

The solution that bash folks implemented is to drop PS4 from env when the
shell is ran as root.

Best,
mlen

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: BUG: crafting SHELLOPTS and PS4 allows to run arbitrary programs in setuid binaries using system
  2016-09-27  6:59 BUG: crafting SHELLOPTS and PS4 allows to run arbitrary programs in setuid binaries using system Mateusz Lenik
@ 2016-09-27  7:53 ` Daniel Shahaf
  2016-09-27  8:43   ` Mateusz Lenik
  2016-09-27  9:02   ` Peter Stephenson
  2016-09-27  8:56 ` Oliver Kiddle
  1 sibling, 2 replies; 10+ messages in thread
From: Daniel Shahaf @ 2016-09-27  7:53 UTC (permalink / raw)
  To: Mateusz Lenik; +Cc: zsh-workers

Mateusz Lenik wrote on Tue, Sep 27, 2016 at 06:59:18 +0000:
> % gcc -xc - -otest <<< 'int main() { setuid(0); system("/bin/date"); }'
> % sudo chown root:root test
> % sudo chmod 4755 test
> % env -i SHELLOPTS=xtrace PS4='$(id)' ./test
> uid=0(root) gid=... groups=.../bin/date
> Tue Sep 27 08:49:16 CEST 2016

I can't reproduce that either either 5.0.7 or latest master, even with
«setopt promptsubst» in effect.  (Does it reproduce in 'zsh -f'?)

> % zsh --version
> 
> zsh 5.2 (x86_64-pc-linux-gnu)
> %
> 
> The solution that bash folks implemented is to drop PS4 from env when the
> shell is ran as root.

34015 (89012cf94ca) stopped importing non-ASCII envvars.  There may have
been other changes in this area but I couldn't quickly find them.

Thanks for the report,

Daniel


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: BUG: crafting SHELLOPTS and PS4 allows to run arbitrary programs in setuid binaries using system
  2016-09-27  7:53 ` Daniel Shahaf
@ 2016-09-27  8:43   ` Mateusz Lenik
  2016-09-27  9:02   ` Peter Stephenson
  1 sibling, 0 replies; 10+ messages in thread
From: Mateusz Lenik @ 2016-09-27  8:43 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: zsh-workers

[-- Attachment #1: Type: text/plain, Size: 850 bytes --]

On Tue, Sep 27, 2016 at 9:54 AM Daniel Shahaf <d.s@daniel.shahaf.name>
wrote:

> Mateusz Lenik wrote on Tue, Sep 27, 2016 at 06:59:18 +0000:
> > % gcc -xc - -otest <<< 'int main() { setuid(0); system("/bin/date"); }'
> > % sudo chown root:root test
> > % sudo chmod 4755 test
> > % env -i SHELLOPTS=xtrace PS4='$(id)' ./test
> > uid=0(root) gid=... groups=.../bin/date
> > Tue Sep 27 08:49:16 CEST 2016
>
> I can't reproduce that either either 5.0.7 or latest master, even with
> «setopt promptsubst» in effect.  (Does it reproduce in 'zsh -f'?)
>
Thanks for looking into it.
It turned out that I didn't research it thoroughly, sorry about that.

glibc execs /bin/sh, so even when zsh is the root shell, it won't be used.
After pointing /bin/sh link to zsh, it works correctly, so after all it was
that bash bug.

Best,
mlen

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: BUG: crafting SHELLOPTS and PS4 allows to run arbitrary programs in setuid binaries using system
  2016-09-27  6:59 BUG: crafting SHELLOPTS and PS4 allows to run arbitrary programs in setuid binaries using system Mateusz Lenik
  2016-09-27  7:53 ` Daniel Shahaf
@ 2016-09-27  8:56 ` Oliver Kiddle
  2016-09-27  9:28   ` Peter Stephenson
  1 sibling, 1 reply; 10+ messages in thread
From: Oliver Kiddle @ 2016-09-27  8:56 UTC (permalink / raw)
  To: Mateusz Lenik; +Cc: zsh-workers

Mateusz Lenik wrote:
> I just learned that bash fixed a vulnerability that also affects zsh. It
> allowed to run arbitrary programs by crafting SHELLOPTS and PS4 variables
> against setuid binaries using system/popen.

Given that zsh doesn't support the SHELLOPTS variable at all, it doesn't
make sense for zsh to be apparently vulnerable.

> Steps to reproduce:
> % gcc -xc - -otest <<< 'int main() { setuid(0); system("/bin/date"); }'

This attack is directed against the shell that system() runs, i.e.
/bin/sh and not the shell from which the setuid binary is invoked. Did
you have /bin/sh linked to zsh. If it was linked to bash then these
steps are merely reproducing the bash bug in bash.

Zsh also needs the prompt_subst option to enable command substitution in
PS4. Perhaps there's an argument for not importing PS4 from the
environment in certain cases anyway but I can't see any security issue.

Oliver


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: BUG: crafting SHELLOPTS and PS4 allows to run arbitrary programs in setuid binaries using system
  2016-09-27  7:53 ` Daniel Shahaf
  2016-09-27  8:43   ` Mateusz Lenik
@ 2016-09-27  9:02   ` Peter Stephenson
  2016-09-27 19:26     ` Bart Schaefer
  2016-09-28 10:37     ` Simon Ruderich
  1 sibling, 2 replies; 10+ messages in thread
From: Peter Stephenson @ 2016-09-27  9:02 UTC (permalink / raw)
  To: zsh-workers

On Tue, 27 Sep 2016 07:53:47 +0000
Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
> Mateusz Lenik wrote on Tue, Sep 27, 2016 at 06:59:18 +0000:
> > % gcc -xc - -otest <<< 'int main() { setuid(0); system("/bin/date"); }'
> > % sudo chown root:root test
> > % sudo chmod 4755 test
> > % env -i SHELLOPTS=xtrace PS4='$(id)' ./test
> > uid=0(root) gid=... groups=.../bin/date
> > Tue Sep 27 08:49:16 CEST 2016
> 
> I can't reproduce that either either 5.0.7 or latest master, even with
> «setopt promptsubst» in effect.  (Does it reproduce in 'zsh -f'?)

While I can believe there's a problem here, since there's pretty similar
logic around in zsh, SHELLOPTS does nothing with zsh, so I suspect the
case is different if it does show up.

> > The solution that bash folks implemented is to drop PS4 from env when the
> > shell is ran as root.
> 
> 34015 (89012cf94ca) stopped importing non-ASCII envvars.  There may have
> been other changes in this area but I couldn't quickly find them.

I don't think we make any special arrangements for import as root,
currently.  It's straightforward to do so.

I've attempted to tidy up the logic to the point where I think I
understand it.  Does the test "(!getuid() || !geteuid())" make sense or
should that be something else?

pws

diff --git a/Src/params.c b/Src/params.c
index 384c30a..a85e5a5 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -333,6 +333,7 @@ IPDEF6("TRY_BLOCK_ERROR", &try_errflag, varinteger_gsu),
 IPDEF6("TRY_BLOCK_INTERRUPT", &try_interrupt, varinteger_gsu),
 
 #define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
+#define IPDEF7R(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_DONTIMPORT_ROOT},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
 #define IPDEF7U(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
 IPDEF7("OPTARG", &zoptarg),
 IPDEF7("NULLCMD", &nullcmd),
@@ -345,7 +346,7 @@ IPDEF7("PS2", &prompt2),
 IPDEF7U("RPS2", &rprompt2),
 IPDEF7U("RPROMPT2", &rprompt2),
 IPDEF7("PS3", &prompt3),
-IPDEF7("PS4", &prompt4),
+IPDEF7R("PS4", &prompt4),
 IPDEF7("SPROMPT", &sprompt),
 
 #define IPDEF8(A,B,C,D) {{NULL,A,D|PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(colonarr_gsu),0,0,NULL,C,NULL,0}
@@ -689,7 +690,28 @@ split_env_string(char *env, char **name, char **value)
     } else
 	return 0;
 }
-    
+
+/**
+ * Check parameter flags to see if parameter shouldn't be imported
+ * from environment at start.
+ *
+ * return 1: don't import: 0: ok to import.
+ */
+static int dontimport(int flags)
+{
+    /* If explicitly marked as don't export */
+    if (flags & PM_DONTIMPORT)
+	return 1;
+    /* If value already exported */
+    if (flags & PM_EXPORTED)
+	return 1;
+    /* If security issue when exporting as root */
+    if ((flags & PM_DONTIMPORT_ROOT) && (!getuid() || !geteuid()))
+	return 1;
+    /* OK to import */
+    return 0;
+}
+
 /* Set up parameter hash table.  This will add predefined  *
  * parameter entries as well as setting up parameter table *
  * entries for environment variables we inherit.           */
@@ -781,8 +803,13 @@ createparamtable(void)
 	    envp2 = environ; *envp2; envp2++) {
 	if (split_env_string(*envp2, &iname, &ivalue)) {
 	    if (!idigit(*iname) && isident(iname) && !strchr(iname, '[')) {
+		/*
+		 * Parameters that aren't already in the parameter table
+		 * aren't special to the shell, so it's always OK to
+		 * import.  Otherwise, check parameter flags.
+		 */
 		if ((!(pm = (Param) paramtab->getnode(paramtab, iname)) ||
-		     !(pm->node.flags & PM_DONTIMPORT || pm->node.flags & PM_EXPORTED)) &&
+		     !dontimport(pm->node.flags)) &&
 		    (pm = assignsparam(iname, metafy(ivalue, -1, META_DUP),
 				       ASSPM_ENV_IMPORT))) {
 		    pm->node.flags |= PM_EXPORTED;
diff --git a/Src/zsh.h b/Src/zsh.h
index bb8ce13..052d754 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -1802,6 +1802,7 @@ struct tieddata {
 #define PM_ZSHSTORED	(1<<18) /* function stored in zsh form              */
 
 /* Remaining flags do not correspond directly to command line arguments */
+#define PM_DONTIMPORT_ROOT (1<<19) /* do not import if running as root */
 #define PM_SINGLE       (1<<20) /* special can only have a single instance  */
 #define PM_LOCAL	(1<<21) /* this parameter will be made local        */
 #define PM_SPECIAL	(1<<22) /* special builtin parameter                */


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: BUG: crafting SHELLOPTS and PS4 allows to run arbitrary programs in setuid binaries using system
  2016-09-27  8:56 ` Oliver Kiddle
@ 2016-09-27  9:28   ` Peter Stephenson
  0 siblings, 0 replies; 10+ messages in thread
From: Peter Stephenson @ 2016-09-27  9:28 UTC (permalink / raw)
  To: zsh-workers

On Tue, 27 Sep 2016 10:56:47 +0200
Oliver Kiddle <okiddle@yahoo.co.uk> wrote:
> Zsh also needs the prompt_subst option to enable command substitution in
> PS4. Perhaps there's an argument for not importing PS4 from the
> environment in certain cases anyway but I can't see any security issue.

PROMPT_SUBST is enabled in any sh-style emulation, so that's an issue.

I can't offhand think of any way of turning on XTRACE from the
environment, though.  Note that $_ is already marked PM_DONTIMPORT.

pws


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: BUG: crafting SHELLOPTS and PS4 allows to run arbitrary programs in setuid binaries using system
  2016-09-27  9:02   ` Peter Stephenson
@ 2016-09-27 19:26     ` Bart Schaefer
  2016-09-28 10:37     ` Simon Ruderich
  1 sibling, 0 replies; 10+ messages in thread
From: Bart Schaefer @ 2016-09-27 19:26 UTC (permalink / raw)
  To: zsh-workers

On Sep 27, 10:02am, Peter Stephenson wrote:
} Subject: Re: BUG: crafting SHELLOPTS and PS4 allows to run arbitrary progr
}
} I've attempted to tidy up the logic to the point where I think I
} understand it.  Does the test "(!getuid() || !geteuid())" make sense or
} should that be something else?

I don't know of anything else more appropriate.


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: BUG: crafting SHELLOPTS and PS4 allows to run arbitrary programs in setuid binaries using system
  2016-09-27  9:02   ` Peter Stephenson
  2016-09-27 19:26     ` Bart Schaefer
@ 2016-09-28 10:37     ` Simon Ruderich
  2016-09-28 19:04       ` Bart Schaefer
  2016-09-29 13:20       ` Peter Stephenson
  1 sibling, 2 replies; 10+ messages in thread
From: Simon Ruderich @ 2016-09-28 10:37 UTC (permalink / raw)
  To: zsh-workers

[-- Attachment #1: Type: text/plain, Size: 767 bytes --]

On Tue, Sep 27, 2016 at 10:02:21AM +0100, Peter Stephenson wrote:
> I've attempted to tidy up the logic to the point where I think I
> understand it.  Does the test "(!getuid() || !geteuid())" make sense or
> should that be something else?

I don't see a reason why zsh running as root shouldn't import
these variables. Only when running in a setuid context possible
security issues arise (ignoring the fact that any setuid program
calling a shell is broken anyway because we will always miss some
env-variable which can be abused).

I think the test should be changed to getuid() != geteuid() or
similar to trigger only in setuid cases.

Regards
Simon
-- 
+ privacy is necessary
+ using gnupg http://gnupg.org
+ public key id: 0x92FEFDB7E44C32F9

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: BUG: crafting SHELLOPTS and PS4 allows to run arbitrary programs in setuid binaries using system
  2016-09-28 10:37     ` Simon Ruderich
@ 2016-09-28 19:04       ` Bart Schaefer
  2016-09-29 13:20       ` Peter Stephenson
  1 sibling, 0 replies; 10+ messages in thread
From: Bart Schaefer @ 2016-09-28 19:04 UTC (permalink / raw)
  To: zsh-workers

On Sep 28, 12:37pm, Simon Ruderich wrote:
}
} I think the test should be changed to getuid() != geteuid() or
} similar to trigger only in setuid cases.

Upon consideration, I agree.


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: BUG: crafting SHELLOPTS and PS4 allows to run arbitrary programs in setuid binaries using system
  2016-09-28 10:37     ` Simon Ruderich
  2016-09-28 19:04       ` Bart Schaefer
@ 2016-09-29 13:20       ` Peter Stephenson
  1 sibling, 0 replies; 10+ messages in thread
From: Peter Stephenson @ 2016-09-29 13:20 UTC (permalink / raw)
  To: zsh-workers

On Wed, 28 Sep 2016 12:37:32 +0200
Simon Ruderich <simon@ruderich.org> wrote:
> I think the test should be changed to getuid() != geteuid() or
> similar to trigger only in setuid cases.

That seems to catch all the cases where there might be surprises.

Oliver pointed out there's already the PRIVILEGED option for this
purpose, which is set up before variables.  It's definition is a little
different,

    opts[PRIVILEGED] = (getuid() != geteuid() || getgid() != getegid());

but that's probably good enough.

pws

diff --git a/Src/params.c b/Src/params.c
index 87586a2..8271a8b 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -333,7 +333,7 @@ IPDEF6("TRY_BLOCK_ERROR", &try_errflag, varinteger_gsu),
 IPDEF6("TRY_BLOCK_INTERRUPT", &try_interrupt, varinteger_gsu),
 
 #define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
-#define IPDEF7R(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_DONTIMPORT_ROOT},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
+#define IPDEF7R(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_DONTIMPORT_SUID},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
 #define IPDEF7U(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
 IPDEF7("OPTARG", &zoptarg),
 IPDEF7("NULLCMD", &nullcmd),
@@ -705,8 +705,8 @@ static int dontimport(int flags)
     /* If value already exported */
     if (flags & PM_EXPORTED)
 	return 1;
-    /* If security issue when exporting as root */
-    if ((flags & PM_DONTIMPORT_ROOT) && (!getuid() || !geteuid()))
+    /* If security issue when importing and running with some privilege */
+    if ((flags & PM_DONTIMPORT_SUID) && isset(PRIVILEGED))
 	return 1;
     /* OK to import */
     return 0;
diff --git a/Src/zsh.h b/Src/zsh.h
index 052d754..79747d6 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -1802,7 +1802,7 @@ struct tieddata {
 #define PM_ZSHSTORED	(1<<18) /* function stored in zsh form              */
 
 /* Remaining flags do not correspond directly to command line arguments */
-#define PM_DONTIMPORT_ROOT (1<<19) /* do not import if running as root */
+#define PM_DONTIMPORT_SUID (1<<19) /* do not import if running setuid */
 #define PM_SINGLE       (1<<20) /* special can only have a single instance  */
 #define PM_LOCAL	(1<<21) /* this parameter will be made local        */
 #define PM_SPECIAL	(1<<22) /* special builtin parameter                */


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2016-09-29 13:21 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-27  6:59 BUG: crafting SHELLOPTS and PS4 allows to run arbitrary programs in setuid binaries using system Mateusz Lenik
2016-09-27  7:53 ` Daniel Shahaf
2016-09-27  8:43   ` Mateusz Lenik
2016-09-27  9:02   ` Peter Stephenson
2016-09-27 19:26     ` Bart Schaefer
2016-09-28 10:37     ` Simon Ruderich
2016-09-28 19:04       ` Bart Schaefer
2016-09-29 13:20       ` Peter Stephenson
2016-09-27  8:56 ` Oliver Kiddle
2016-09-27  9:28   ` Peter Stephenson

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).