[ISN] Secure Passwordless Logins with SSH Part 3

From: InfoSec News (isnat_private)
Date: Sat Jan 11 2003 - 00:34:19 PST

  • Next message: InfoSec News: "[ISN] U.S. e-mail attack targets key Iraqis"

    +------------------------------------------------------------------+
    |  Linux Security: Tips, Tricks, and Hackery                       |
    |  Published by Onsight, Inc.                                      |
    |                                                                  |
    |  09-January-2003                                                 |
    |  http://www.hackinglinuxexposed.com/articles/20030109.html       |
    +------------------------------------------------------------------+
    
    This issue sponsored by Hacking Linux Exposed, Second Edition.
    
    Hacking Linux Exposed, Second Edition is out in stores and online.
    This new version of the best-selling first edition includes almost
    200 new pages of hacking and cracking materials for Linux and
    Unix-like systems.
    
    Also, if you order online through our web page (via Amazon or Barnes
    and Noble), we'll donate any commissions to the Electronic Frontier
    Foundation. Due to a Slashdot review, we anticipate donating $200
    from January alone. See our books page for more information.
    
    --------------------------------------------------------------------
    
    Secure Passwordless Logins with SSH Part 3
    By Brian Hatch
    
    Summary: How to create passwordless logins to allow remote
    administration tasks securely with SSH - restricting possible actions
    for SSH identities.
    
    Setting up your accounts to allow identity-based authentication gives
    you several new options to allow passwordless access to those
    accounts. This week we'll see how well we can restrict the access
    granted to these identities.
    
    $HOME/.ssh/authorized_keys files, are where you stick the public keys
    of identities you wish to allow. These files without any arguments
    look something like this:
    
      user@server$ cat ~/.ssh/authorized_keys
      ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA3nCnBRQR2x4Ak7I3gS62ASXGiC+5o
      sLmOX5yS894rjFdFEVKgiuhuU0W7NdE3Mymkm0oX3oZM1e7NNxDx/4/Cu/4fISP8o
      pwN4GG2wbubZARFyJpWNMcVe8ZdOdmrlXFYh49a18i++SScHnycmiL8AmEb06Obrh
      kc5iAyVnHAf08Lqk= user@client_machine
    
    The above code listing is wrapped and indented for readability, but
    in reality everything should be in one line. The only spaces occur
    between ssh-rsa and the key (which begins with AAAAAB3N above), and
    before the comment, user@client_machine. If you are using an SSH2 DSA
    key, it will look similar, but have ssh-dsa instead of ssh-rsa.
    
    SSH1 identities, however look slightly different, as seen here:
    
      user@server$ cat ~/.ssh/authorized_keys
      1024 35 118175573790683499614097477370143348554184947854197722520
      41836228683278279897504160922892652206780215894929989167407130782
      38841607878229644157746694554723410376922636437979652730247511201
      90228759786298122911260638958674184210284497057700389946655115171
      90400155609012033981180255475519919550519308405663149133021266378
      user@client_machine
    
    Regardless which version you use, adding options will be done in the
    same way, so we'll assume RSA identities for simplicity.
    
    When set up in this manner, anyone who possesses the private keys
    that go with these public keys can log in as you on the server with
    no restrictions:
    
      # Log in interactively
      client$ ssh user@server -i $HOME/identity_test/id_rsa
      user@server$ hostname
      server
      user@server$ exit
    
      # Run a command remotely
      client$ ssh user@server -i $HOME/identity_test/id_rsa "echo Success!"
      Success!
      client$
    
    We can add several options to this key on the server to restrict in
    which ways it can be used. Options are put at the beginning of the
    line, before the key starts, and are separated from the rest of the
    line by whitespace. Here are some of the more useful options. For a
    full list, see the sshd manual page.
    
    no-port-forwarding no-X11-forwarding no-agent-forwarding
        These options disallow port forwarding (tunnelling side channels
        through the encrypted ssh connection using the -R and -L options
        from the client side)[1], X11 forwarding (allow X11 apps to be
        started on the server, but viewed on the client securely), and
        agent forwarding (the ability to have the ssh-agent available on
        your client be accessible on the server) respectively. If we're
        going to be restricting our clients from doing anything but those
        activities we dictate, we'll probably want to include all of
        these options.
       
    no-pty
        Most remotely-run commands do not require a pty, but allowing one
        isn't the end of the world.
       
    environment="VARIABLE=value"
        If you want to have a specific environment variable set when this
        key is used (for example to change $HOME, make a restrictive
        $PATH, etc) then you can add as many environment options as you
        need. This is helpful if you have a single account used by
        multiple people, all accessing it via SSH identities, to set up
        their $LOGNAME variables, for example. Though you're better off
        using separate accounts and proper file permissions.
       
    from="list"
        The sshd server can globally limit which hosts can connect to
        this machine by using TCP Wrappers, but cannot act on a host-user
        level. Using the from option, you can allow a given identity to
        be used only from specific hosts. Hosts are separated by commas,
        may contain wildcards "*" and "?", or may disallow hosts that are
        prefixed with "!". As an example, you may have
       
          from="!enemy.my_isp.net,*.my_isp.net,home.example.com"
       
    command="shell command"
        When a user connects with this identity, the command specified
        will be run, whether logging in interactively or with a remote
        command. The environment variable SSH_ORIGINAL_COMMAND will
        contain the original command supplied by the user, if any.
    
    So lets set up an example from the ground up.
    
    Say you wished to allow the user "backups" on the machine
    beepbeep.my_net.net[2] to be able to copy the /etc/ directory of the
    machine futzy so you have a backup of the system configuration, just
    in case futzy goes belly up. To make sure we get all the files in /
    etc, we'll want to log into futzy as root. Normally you would run the
    following command:
    
      backups@beepbeep$ scp -pr root@futzy:/etc/ /path/to/etc_backup/
    
    The scp program automatically calls ssh to perform the actual
    connection, which runs scp on the other side with appropriate
    arguments. So, in order for us to do this, we must first configure
    our SSH keys.
    
    Here are the steps you'd need to take to get it working without a
    password so the backups user can do this all from cron, no password
    required. I'll breeze over some steps that were already described in
    detail in the previous articles.
    
    Log into beepbeep as backups and create the key:
    
      backups@beepbeep$ cd
      backups@beepbeep$ mkdir keys
      backups@beepbeep$ cd keys
      backups@beepbeep$ ssh-keygen -t rsa -f futzy.scp.etc
      Generating public/private rsa key pair.
      Enter passphrase (empty for no passphrase): enter
      Enter same passphrase again: enter
      Your identification has been saved in futzy.scp.etc.
      Your public key has been saved in futzy.scp.etc.pub.
    
      backups@beepbeep$ ls
      futzy.scp.etc  futzy.scp.etc.pub
    
    Now, copy the public key up to futzy. (You can use many other
    methods.)
    
      backups@beepbeep$ scp futzy.scp.etc.pub root@futzy:/some/tmp/dir
      root@futzy's password: <type password>
    
    Now, log into futzy as root. We'll edit the public key to prepend a
    "from=" option so the key can only be used from beepbeep, and then
    add this to our authorized_keys file:
    
      root@futzy# cd /root/.ssh
    
      root@futzy# cat /some/tmp/dir/futzy.scp.etc.pub
      ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAqwRPEALGQbrhQQST9Obkj2OJrUsaRi1
      SYtJbkpn6TxWddze2F/lzfKimgzaEhSWKuh/v0onGHvNaYuXWENdEhSWKuh/v0...
    
      root@futzy# vi /some/tmp/dir/futzy.scp.etc.pub
        # put  from="beepbeep.my_net.net" at the beginning
        # of this line, and exit the editor.
    
      root@futzy# cat /some/tmp/dir/futzy.scp.etc.pub
      from="beepbeep.my_net.net" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAqwRP
      EALGQbrhQQST9Obkj2OJrUsaRi1SYtJbkpn6TxWddze2F/lzfKimgzaEhSWKuh...
    
      root@futzy# cat /some/tmp/dir/futzy.scp.etc.pub >> authorized_keys
    
    We now have set up the root account on futzy to allow this key to log
    in without a password, but only from beepbeep.my_net.net. Currently,
    any program can be run from beepbeep, so let's test it out:
    
      backups@beepbeep$ ssh -i futzy.scp.etc root@futzy \
                        'echo "Hi, I am " `hostname`; pwd'
      Hi, I am futzy
      /root
    
    You can also verify that this key is rejected from other machines.[3]
    
      othermachine$ ssh -i futzy.scp.etc root@futsy 
      root@futzy's password:
    
    Now let's set up the key to run a forced command, regardless of what
    the user sends us. On the server, let's create a file called /root
    /.ssh/show-original-command as follows:
    
      root@futzy# cat <<'EOM' > /root/.ssh/show-original-command
      #!/bin/sh
    
      echo "Original SSH command is '$SSH_ORIGINAL_COMMAND'";
      exit 0;
      EOM
    
      root@futzy# chmod u+x /root/.ssh/show-original-command
    
    And then modify the authorized_keys file using your favourite editor
    such that it runs this command. The new version of authorized_keys
    should look like this:
    
      root@futzy# cat /some/tmp/dir/futzy.scp.etc.pub
      command="/root/.ssh/show-original-command",from="beepbeep.my_net.ne
      t" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAqwRPEALGQbrhQQST9Obkj2O...
    
    So now, let's connect from beepbeep exactly as we did before:
    
      backups@beepbeep$ ssh -i futzy.scp.etc root@futsy \
                        'echo "Hi, I am " `hostname`; pwd'
      Original command is 'echo "Hi, I am" `whoami`; pwd'
    
    So, let's try running our scp command and see what happens:
    
      backups@beepbeep$ scp -i futzy.scp.etc -pr \
                         root@futzy:/etc/ /path/to/etc_backup/
      Original command is 'scp -r -p -f /etc/'
    
    Aha! Note that the scp command on beepbeep ends up running a very
    different looking command on futzy, in this case scp -r -p -f /etc/.
    If you think about it, that makes sense. The remote end doesn't care
    where we're locally storing the files. If you try to find the -f
    option in the scp man page, it's not there -- it indicates to scp
    that it is running on the server side of the connection, and isn't
    invoked by the user directly.
    
    Many programs that can operate over SSH, such as rsync, run commands
    that look different on the server side. Our show-original-command
    program comes in handy to help you figure out what actual command was
    sent to the server process.
    
    Of course, since our show-original-command program didn't run scp on
    futzy, our scp failed. Instead we need to force an scp command to run
    on the server. So, for our final modification to authorized_keys,
    make it look like this:
    
      root@futzy# cat /some/tmp/dir/futzy.scp.etc.pub
      command="scp -r -p -f /etc/",from="beepbeep.my_net.net ssh-rsa AA
      AAB3NzaC1yc2EAAAABIwAAAIEAqwRPEALGQbrhQQST9Obkj2OJrUsaRi1SYt...
    
    And, now to run it from beepbeep:
    
      backups@beepbeep$ scp -i futzy.scp.etc -pr \
                         root@futzy:/etc/ /path/to/etc_backup/
      adduser.conf    100% |***********************************|  1660       00:00
      sources.list    100% |***********************************|   949       00:00
      host.conf       100% |***********************************|    26       00:00
      issue           100% |***********************************|    25       00:00
      motd            100% |***********************************|   162       00:00
      nsswitch.conf   100% |***********************************|   465       00:00
      profile          52% |*****************                  |   377       00:00
      ...
    
    Excellent! But wait - what have we forgotten? There were a few other
    options that are useful to restrict other features available to SSH
    connections. Ideally we should add those in too. Adding all the
    restrictions we can muster, our final authorized_keys file will look
    like this:
    
      root@futzy# cat /some/tmp/dir/futzy.scp.etc.pub
      no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,c
      ommand="scp -r -p -f /etc/",from="beepbeep.my_net.net ssh-rsa AA
      AAB3NzaC1yc2EAAAABIwAAAIEAqwRPEALGQbrhQQST9Obkj2OJrUsaRi1SYt...
    
    Whew! Quite a mouthful.
    
    So, what have we accomplished here?
    
      * Created an SSH identity on beepbeep.
      * Installed and restricted that identity on root's account on
        futzy, such that:
          + The identity can only be used from beepbeep.
          + The identity can only be used to allow scp of the /etc/
            directory.
          + The identity can't be used to set up port forwarding over the
            encrypted channel, or other SSH features.
    
    Should the identity fall in the hands of a cracker, it is useless
    from any system other that beepbeep. If he has access to beepbeep,
    all he can do is snag the /etc directory from futzy. Of course, the
    most likely way he got access to the key was by getting access to the
    backups@beepbeep account, so this would have already been available.
    However the key, even though it allows root access to futzy, cannot
    provide the cracker any other roads onto that system.
    
    Now the drawback of this system should become clear if you think
    about the limitations: each identity can only cause one command to
    run. This means if you want to be able to run several commands, for
    example mirroring /etc, downloading some logs, and then restarting
    Apache, you'd need to create separate keys with separate entries in
    authorized_keys for each. This can become a management pain,
    especially since the entries in authorized_keys are fairly long.
    
    Next week[4] I'll introduce a program I've been using for a long time
    that allows you to have multiple authorized programs on a
    host-by-host basis using only one key. You still need to maintain a
    list of acceptable commands, but you do not need to generate separate
    keys for each.
    
    Until then, you may want to read an article by William Stearns
    (author of ssh-keyinstall and more) at [5] which has many fun SSH
    tricks.
    
    NOTES:
    
    [1] If you do want to allow specific port forwarding, you can use the
    permitopen option to specify host:port combinations that are allowed.
    
    [2] Beepbeep is, of course, the sound a truck makes backing up. Nods
    to all my NUMBAlum brethren...
    
    [3] You'd need to copy the key to othermachine, of course.
    
    [4] Really, I promise.
    
    [5] http://www.opensourcedigest.com/modules.php?name=News&file=
    article&sid=6
    
                                -------------                            
    Brian Hatch is Chief Hacker at Onsight, Inc and author of Hacking
    Linux Exposed and Building Linux VPNs. He's been using SSH to secure
    his remote logins since Tatu posted the first version of the code -
    even if the administrators of those machines refused to install it
    for him. Brian can be reached at brianat_private
    
    --------------------------------------------------------------------
    This newsletter is distributed by Onsight, Inc.
    
    The list is managed with MailMan (http://www.list.org). You can
    subscribe, unsubscribe, or change your password by visiting
    http://lists.onsight.com/ or by sending email to
    linux_security-requestat_private
    
    Archives of this and previous newsletters are available at
    http://www.hackinglinuxexposed.com/articles/
    
    --------------------------------------------------------------------
    
    Copyright 2003, Brian Hatch.
    
    
    
    -
    ISN is currently hosted by Attrition.org
    
    To unsubscribe email majordomoat_private with 'unsubscribe isn'
    in the BODY of the mail.
    



    This archive was generated by hypermail 2b30 : Sat Jan 11 2003 - 02:54:52 PST