Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Re: BUG: crafting SHELLOPTS and PS4 allows to run arbitrary programs in setuid binaries using system
- X-seq: zsh-workers 39460
- From: Peter Stephenson <p.stephenson@xxxxxxxxxxx>
- To: zsh-workers@xxxxxxx
- Subject: Re: BUG: crafting SHELLOPTS and PS4 allows to run arbitrary programs in setuid binaries using system
- Date: Tue, 27 Sep 2016 10:02:21 +0100
- Cms-type: 201P
- In-reply-to: <20160927075347.GA500@fujitsu.shahaf.local2>
- List-help: <mailto:zsh-workers-help@zsh.org>
- List-id: Zsh Workers List <zsh-workers.zsh.org>
- List-post: <mailto:zsh-workers@zsh.org>
- Mailing-list: contact zsh-workers-help@xxxxxxx; run by ezmlm
- Organization: Samsung Cambridge Solution Centre
- References: <CALDAOts+rgsuZfABkgVBphvY4CLcUiMLFA4xR0bUXPNxnhcHug@mail.gmail.com> <CGME20160927075540eucas1p117bb3a8f5edc7b140696f570982f8c03@eucas1p1.samsung.com> <20160927075347.GA500@fujitsu.shahaf.local2>
On Tue, 27 Sep 2016 07:53:47 +0000
Daniel Shahaf <d.s@xxxxxxxxxxxxxxxxxx> 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 */
Messages sorted by:
Reverse Date,
Date,
Thread,
Author