[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

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: