System V.2.2 setuid() broken

wilber at alice.UUCP wilber at alice.UUCP
Fri Jul 15 14:24:16 AEST 1988


John F. Haugh writes:
}>In article <1305 at basser.oz> boyd at basser.oz (Boyd Roberts) writes:
}>I'm root and I run a non-root setuid executable.  Then I want
}>to fork() a shell and do the usual:
}>
}>	setuid(getuid())
}>
}>Of course, I get EPERM, because setuid() is broken.  Now is this
}>a generic System V bug.  I think it is.  Clarification please?

This is, apparently, a generic System V bug.  At least it exists, as I
have recently discovered, on the 3b1 and PC 6300+.

}no, according to SETUID(2), you got the correct behaviour, and on
}close examination, that is the CORRECT behavior.
}
}consider, running the a.out SUID sets the EFFECTIVE UID to the
}non-root user.  getuid(2) returns the REAL UID which equals
}ROOT.  and from the manual
}
}	Setuid (setgid) will fail if the real user (group) ID
}	of the calling process is not equal to uid (gid) and
}	its effective user ID is not super-user. [ EPERM ]
}
}the real user ID is ROOT and the uid is ROOT.  however, the
}effective user ID is not ROOT, so the call fails with EPERM.

Please learn to parse English.  Setuid requires two conditions for
failure:
      (1) The real user ID of the calling process is not equal to uid.
*and* (2) The effective user ID is not super-user.

Only condition (2) fails so setuid should succeed.  One could, if one were
perverse, parse the sentence to mean "Setuid will fail if the real user ID of
the calling process is not equal to uid and setuid will fail if the effective
user ID is not super-user."  If this interpretation were correct that would
mean that only processes running with the effective user id of root could ever
use setuid, which is not correct.  This interpretation would also mean that
even processes running suid to root would only be be able to use setuid to
change the effective uid to the real uid, which is also not correct -- when
running with the effective uid of root a process can setuid to anything,
regardless of the real user ID.

To quote the paragraph of the manual preceding the one quoted by Mr. Haugh,

   If the effective user id of the calling process is not super-user, but
   its real user id is equal to uid, the the effective user id is set to
   uid.

}this `feature' prevents a trojan horse from doing a
}
}	if (getuid () == 0) {
}		setuid (0);
}		chown ("/bin/sh", 0, 0);
}		chmod ("/bin/sh", 04711);
}	}
}
}thereby giving you the famed password free su command.

This "feature" only prevents the security breach when the process is running
suid to something other than root.  At least 95% of all programs don't run suid
to anything -- they take on the effective id of whoever calls them.  So you can
put that code (minus the "setuid(0);" statement) in any program that doesn't
have the suid bit set -- grep, cat, ls, patch, whatever -- and if the super-
user uses the program the trojan horse succeeds.  The moral is that you should
never run anything as super-user that you don't *really* trust.  (BTW, you'd
better change the mode mask to 04755 if you want to crack Unix security,
otherwise you've created a sh that's suid to root but can only be run by root.)

The bug in setuid is a serious one.  There are two lousy ways to work around
it.  You can resign yourself to the fact that everybody *except* the super-user
can run the program.  Or you can change the owner of the program to root, and
run it suid to root.  Then everyone can run the program, including the super-
user, but of course you have drastically reduced the security of your system.

Bob Wilber (apparently not the only non-wizard posting to this newsgroup)



More information about the Comp.unix.wizards mailing list