From: SMTP%"mycroft@mit.edu" 11-OCT-1996 19:16:14.38 To: EVERHART CC: Subj: BoS: SECURITY HOLE IN AUTHENTICATION FORWARDING Resent-Date: Sat, 12 Oct 1996 00:32:58 +1000 Approved-By: ALEPH1@UNDERGROUND.ORG Approved-By: "Charles M. Hannum" Message-ID: <199610110536.BAA14567@zygorthian-space-raiders.MIT.EDU> Date: Fri, 11 Oct 1996 01:36:28 -0400 Reply-To: "Charles M. Hannum" Sender: Bugtraq List From: "Charles M. Hannum" X-To: ssh@clinet.fi To: Multiple recipients of list BUGTRAQ Approved: proff@suburbia.net Resent-Message-ID: <"loZvt1.0.BN7.PgbNo"@suburbia> Resent-From: best-of-security@suburbia.net X-Mailing-List: archive/latest/442 X-Loop: best-of-security@suburbia.net Precedence: list Resent-Sender: best-of-security-request@suburbia.net Subject: BoS: SECURITY HOLE IN AUTHENTICATION FORWARDING 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.