Re: "su -" and "su" - what is the real difference?
Florent Rougon <f.rougon@free.fr> wrote:
> Is it possible for a malicious su wrapper to:
>
> 1. record root's password (of course, yes);
>
> 2. *and then* feed this password to the real "su".
>
> I suspect the real "su" empties the stdin buffer (or something like
> that) to avoid such attacks, but would be glad to hear a confirmation
> from people who know better.
OK, answering my own question. su has the following code:
if (isatty (0) && (cp = ttyname (0))) {
[...]
} else {
if (!amroot) {
fprintf (stderr,
_("%s: must be run from a terminal\n"), Prog);
exit (1);
}
tty = "???";
}
with the result that the attached program fails this way:
% ./autosu.py
su: must be run from a terminal
Child exit status: 1
%
#! /usr/bin/env python
# autosu.py --- Try to su to root, with the password given by the program, not
# the user.
# Copyright (c) 2006 Florent Rougon
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 dated June, 1991.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to the
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
import sys, os, time
class Bug(Exception):
pass
def main():
(rfd, wfd) = os.pipe()
child_pid = os.fork()
if child_pid == 0:
# We are in the child process. We MUST NOT raise any exception.
try:
os.dup2(rfd, 0)
os.execvp("su", ["su", "root", "-c", "id"])
except:
os._exit(127)
# Should not happen unless there is a bug in Python
os._exit(126)
# We are in the father process.
time.sleep(2)
f = os.fdopen(wfd, "wb")
f.write("v3ry s3kr3t p455w0rd\n")
f.flush()
f.close()
exit_info = os.waitpid(child_pid, 0)[1]
if os.WIFEXITED(exit_info):
exit_code = os.WEXITSTATUS(exit_info)
elif os.WIFSIGNALED(exit_info):
sys.exit("Child terminated by signal %u" % os.WTERMSIG(exit_info))
else:
raise Bug()
print "Child exit status: %u" % exit_code
sys.exit(0)
if __name__ == "__main__": main()
--
Florent
Reply to: