Subject: SECURITY HOLE IN AUTHENTICATION FORWARDING

From: "Charles M. Hannum" <[email-masked]>
Date: Fri, 11 Oct 1996 01:36:28 -0400 (EDT)

Herein I describe an attack on ssh's authentication forwarding
mechanism that allows another user on the same machine to steal a
connection to your authentication agent.  This allows said user to
connect to servers as yourself.

Note that this is an active attack.  It relies on a race condition,
and may only be performed while a ssh process is being started up and
is constructing a tunnel to the authentication agent.  Nevertheless,
it is a serious hole.

If you are connecting to or from a machine that may have other users,
you should kill any `ssh-agent' processes you may have running, and
put `ForwardAgent no' in your .ssh/config file until this hole is
fixed.

To understand how this is possible, I will describe how your ssh
process talks to the authentication agent.  It's not immediately
obvious from looking at the code, and doesn't appear to be documented
anywhere.

Basically, the following occurs:

* Your agent or (sshd on a machine you've logged into) has created
either:

  * a file descriptor which is inherited by child processes, and which
  we assume that only those processes can access, or

  * a Un*x domain socket that can theoretically only be accessed by
  the correct user or a privileged user.

* When you run a new ssh process, it creates a tunnel to the agent.
It does this with a rather strange protocol that works as follows:

  * The ssh process binds a port on the localhost address (127.0.0.1),
  and opens the listen queue.

  * If the agent is using a Un*x-domain socket, the ssh process opens
  this socket.

  * The ssh process then sends a message over the socket opened in the
  previous step, or the inherited descriptor from the agent, informing
  the agent that it should connect to the address created in the first
  step.

  * The agent does this, and you've now established a tunnel.  There
  is no authentication between the endpoints of this tunnel.

Herein lies the problem.  There is a race condition between the bind()
and the connect() where I can steal the port to the ssh process.

Now, if you were a little too naive, you might think that this isn't
relevant, because you've only gotten access to the authentication
challenges coming in from whereever this ssh is trying to connect to.
But this is not the case.

By having access to the bound port in the ssh process, you can get
access to the other side, simply by connecting, closing the
connection, and then binding the port yourself and waiting for the
agent to connect to you.  After doing this, you have access to the
original authentication tunnel, and can submit queries to it yourself.
This obviously allows for a man in the middle attack.

As I said, this relies on a race condition, and as such may not be
easy to exercise.  But as with most security holes with such serious
consequences, I recommend not taking the chance.

So, how do you fix this?

In the case where you're using a Un*x-domain socket for the agent,
this is really easy to fix, just by ripping out the use of the local
IP port, and simply allowing multiple connections to the socket.

In the case where the agent is using an inherited file descriptor,
it's a bit more complicated.  One option is to create temporary named
Un*x-domain sockets and bind them; however, if you do this, it's not
clear that it's worth using the inherited descriptor at all.

An alternative solution (pointed out by Marc Horowitz), is to leave
the original listening socket around.  This has the disadvantage that
it still allows someone to steal the port to the incoming challenges.
Although how this would be a hole isn't immediately obvious to me, it
seems inherently wrong to allow it.