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
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.
Also 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.
Example
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.
Setup
miller
and holden
are two users on a network.
- Both have simple access to
mars
miller
hassudo
onceres
whileholden
connects via a service accountholden
hassudo
onearth
whilemiller
connects via a service account
$ vagrant ssh-config |
Forward an Agent
To begin, holden
loads credentials in ssh-agent
and connects to ceres
, forwarding the agent.
$ vagrant ssh earth |
Exploit the Socket
With the exposed agent on ceres
, miller
abuses superuser privileges to access the socket and subsequently connects to mars
as holden
.
$ vagrant ssh ceres |
I’ll leave the rest up to your imagination.
Alternative
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.
~/.ssh/config | |
1 2 3 4 5 6 | Host start |
start
is straightforward. It just defines an easy way to get to start.fqdn
. end
does a bit more with ProxyCommand
. Instead of connecting directly, it forwards client I/O to %h
ost on %p
ort via start
.
Depending on where you double-check this post, you might run into nc
/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 netcat
mode.
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. ProxyJump
and 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.
~/.ssh/config | |
1 2 3 4 5 6 7 8 9 10 | Host start |
Recap
Forwarding ssh-agent
s, 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.
Full Scripts
These are in the repo but I also wanted to lay out everything here.
keygen
keygen | |
1 2 3 4 | #!/bin/bash |
Vagrantfile
Vagrantfile | |
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 -*- |
Legal
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.