Unintended key info disclosure via ForwardAgent?

grarpamp grarpamp at gmail.com
Thu Jan 22 11:46:23 EST 2009

It seems that users may be disclosing unintended public key info
when logging into remote hosts.

Use of the words keypair/keyid/etc have been bastardized. Signature
is likely better. Note also, the author may be without clue.


[g] - refers to an administrative group of hosts
[n] - refers to a host within that group

ws[g][n] - management workstations [trusted]
 User ssh-add's keys for all local and remote host groups.
  Host locala*
   ForwardAgent yes
   IdentityFile ~/.ssh/id_dsa_locala
  Host remotea*
   IdentityFile ~/.ssh/id_dsa_remotea
  Host remoteb*
   IdentityFile ~/.ssh/id_dsa_remoteb
  Host *
   ForwardAgent no
   IdentitiesOnly yes

local[g][n] - local hosts [generally trusted]
  ssh[d]_config are the installed default, ~/.ssh/config doesn't exist.
  Access is via ~/.ssh/authorized_keys only.

remote[g][n] - remote internet hosts [generally untrusted]
  ssh[d]_config are the installed default, ~/.ssh/config doesn't exist.
  Access is via ~/.ssh/authorized_keys only.

Policy prevents keypairs sitting on the disks of any host except
for ws[g][n]. Therefore, they are not there to be selectable with:
 IdentityFile and IdentitiesOnly

However, they are loaded in ws[g][n]'s agent. So hopping around like
this works fine because ForwardAgent is set by default everywhere:
 wsa1 -> locala1 -> locala2 -> locala3
 wsa1 -> remotea1 -> remotea2 -> remotea3
  Assume for this instance that ForwardAgent was indeed set for
  remotea* in the above config.

If the IdentityFile and IdentitiesOnly options above are not set
for a host, keys are tested against the host in the order they are
listed in ssh-add -l, starting from the top, until one key works.

Here's the problem. If the keys are loaded in the following order:

And the following login path is used: wsa1 -> locala1
 locala1 has no knowledge of an attempt with remotea's keypair against
locala1 because wsa[g][n] have their IdentityFile and IdentitiesOnly
options set to present only the locala keypair to locala1. No problem.

Similarly for the path: wsa1 -> remoteb1
 The IdentityFile and IdentitiesOnly options restrict key presentation
to remoteb1 to be the remoteb keypair. No problem.

However, when using this login path: wsa1 -> locala1 -> remoteb1
 The log below brings the idea that we have key info about remotea
and locala being disclosed to remoteb1. Namely, failed attempts
with the remotea and locala keypairs against remoteb1.

## from locala1
# client tries the first key against remoteb1
client debug1: Offering public key: .ssh/id_dsa_remotea
server debug3: mm_answer_keyallowed: key_from_blob: 0x81db150
server debug3: mm_answer_keyallowed: key 0x81db150 is not allowed
# client tries the second key against remoteb1
client debug1: Offering public key: .ssh/id_dsa_locala
server debug3: mm_answer_keyallowed: key_from_blob: 0x81db170
server debug3: mm_answer_keyallowed: key 0x81db170 is not allowed

Now, assuming that the server operator chooses to look only at what
keys are coming in, their order and their quantity... there are a
few levels of badness with this:

1 - Each attempt carries a unique key id with it. So long as at
least one keypair failed, if any of the host groups are in communication
with each other, they will know that the same user [or a user
possessing the same key] is accessing their respective systems.

2 - The order in which keys are presented for testing may be viewed
as a signature.

3 - The number of keys loaded [or failed key attempts before success]
may be viewed as the signature of a particular user. Particularly
of those users carrying many keys.

4 - Combining the first three items into a set provides a stronger
signature for a given user.

Ultimately, it would be better to have a way to specify which keys
[by fingerprint] from the agent are to be forwarded over the
connection to which hosts. Similar to how IdentityFile and
IdentitiesOnly work for actually logging into each host.

It may be that the user wishes this path to work:
 wsa1 -> locala1 -> remotea1
but not this path:
 wsa1 -> remotea1 -> locala1
by managing only agent identites as forwarded from wsa1.

So the suggested solution is to add the following config options...

ForwardAgent yes/no - Preserve the current function. All or no agent
 keys are forwarded.

ForwardAgentDefault include/exclude - Set the sense of ForwardAgentFP.
 Useful if an agent has many keys loaded. Defaults to exclude.
 Alternatively, ForwardAgent's yes/no may be used for this:
  ForwardAgent yes -> ForwardAgentFP's are excluded.
  ForwardAgent  no -> ForwardAgentFP's are included.

ForwardAgentFP fp1 - include/exclude this one
ForwardAgentFP fp2 - include/exclude this one

This may mean that the host a user came from would deny use of that
key, as if it were not loaded. Ref: the caution about this in
ssh_config(1) ForwardAgent. Perhaps a form of separate agent instance
would be created on the client based on the config stanza of the
host being logged into.

It may also be useful to add LoginAgent* options that work similarly
to how IdentityFile and IdentitiesOnly work with files. For use
when only agent keys are available as above [no file keys]. For
example... once you've logged into a remote host [bringing with you
access to a forwarded set of keys], select from those available
which ones are to be used for the next hop. They would work, and
be named, like ForwardAgent*.

Note that the client host may not have keyfiles available. Either
because they don't exist as per policy above or in an agent only
environment. Along with mount/unmount, there is:
 ssh-agent -a <path_to_chroot>
 SSH_AUTH_* variables

More information about the openssh-unix-dev mailing list