One of the things that I really like about
ssh-agent is its ability to forward itself to remotes. By sending the agent instead of setting keys on each box, I'm locking down access to a few machines that I know and trust. It's amazingly convenient and has saved me so much headache.
As I was doing research for a previous post, I kept seeing hints that maybe forwarding the agent isn't actually a very good idea.
man ssh quite explicitly cautions users against forwarding.
man ssh_config repeats the same warning.
man ssh-agent goes so far as to say it "is easily abused."
- The Vulnerability
- Full Scripts
In order to forward authentication requests,
ssh creates a UNIX-domain socket between the machines. This is not an open port governed by network protocols, but rather a pathname managed by the kernel. On either end, access to the socket is limited by file and directory permissions. On the remote, this means only the owning user has access to it.
root. It's easy to forget that
root has access to everything. By extension, this means anyone with
root access has access to private sockets. If
root can get there, anyone that can act as
root can get there too.
The socket is fairly easy to discover once you know what you're looking for.
$ find /tmp -path '*ssh*' -type s
By setting your environment's
SSH_AUTH_SOCK, you can gain access to the agent. There's not much information you can gain from the socket itself.
$ sudo SSH_AUTH_SOCK=/tmp/ssh-20mMR4ptdrVJ/agent.2283 ssh-add -l
You can, however, authenticate as the compromised user anywhere the compromised agent can take you. That's the danger. The current machine might be safe (barring some situations) but everything connected to it is not.
I really struggled to wrap my head around this initially. I mean, I understand
root can get anywhere. I just didn't see the implications. To grok the issue, I put together a demo. It's slightly contrived, but it gets the job done. The final steps especially will probably take more time in the real world. They were easier with full knowledge of the network.
holden are two users on a network.
- Both have simple access to
holdenconnects via a service account
millerconnects via a service account
$ vagrant ssh-config
Forward an Agent
holden loads credentials in
ssh-agent and connects to
ceres, forwarding the agent.
$ vagrant ssh earth
Exploit the Socket
With the exposed agent on
miller abuses superuser privileges to access the socket and subsequently connects to
$ vagrant ssh ceres
I'll leave the rest up to your imagination.
ProxyCommand is the generally accepted alternative. Rather than leave a trail of open sockets, you tunnel your way to the desired machine via direct connections. You can, in theory, chain as many as you'd like.
1 2 3 4 5 6
start is straightforward. It just defines an easy way to get to
end does a bit more with
ProxyCommand. Instead of connecting directly, it forwards client I/O to
Depending on where you double-check this post, you might run into
netcat usage. It's still presented as a current solution in many places (including the man pages for OpenSSH 7.6!). However, it was superceded by
-W %h:%p in OpenSSH 5.4, released almost ten years ago.
-W is OpenSSH's
Depending on how new your version of OpenSSH is, you might notice
ProxyJump while poking around the documentation. OpenSSH 7.3 introduced the option, which simplifies the chaining process.
ProxyCommand are mutually exclusive (I believe it's first-come-first-served), so you can't use both. For this situation, where we're just trying to maintain an encrypted connection between us and a far-flung remote,
ProxyJump is perfect. It can be chained in a single line, making lengthy proxy chains more manageable.
1 2 3 4 5 6 7 8 9 10
ssh-agents, like many things in life, trades security for convenience. With minimal time investment, forwarding can be replaced by
Proxy(Command|Jump). Proxying is not as simple as forwarding but does not, at first blush, expose as much of your network.
These are in the repo but I also wanted to lay out everything here.
1 2 3 4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
# -*- mode: ruby -*-
The OpenSSH logo was pulled directly from its website and was not altered. I couldn't find a license but I'm assuming the logo is covered by a BSD license of some sort. I'm not affiliated with OpenSSH at all.