X11
With over 20,000 servers in place, our initial vulnerability scans were overloaded with the menial issues, missing SSL certificates, lack of security headers, outdated TLS support, etc. However, after reviewing each of these issues for possible vulnerabilities, we focused on an unauthenticated X11 server.
For some context, X11 is the protocol many Linux desktop environments use to draw GUI windows. It’s possible to access this remotely and when misconfigured, it is possible to access this without authentication. This means anyone connected to your network could connect to your machine, see what windows you have open and most importantly, keylog or send keystrokes to your machine.
Luckily for us, this is quite an old vulnerability and as a result, many ways of exploiting it. We used Metasploit’s “exploit/unix/x11/x11_keyboard_exec” which dropped us straight into a shell. We began our post exploitation by first finding out what user we were logged in as. A quick command showed us the following:
$ id
uid=1000(josh) gid=1000(josh) groups=1000(josh),4(adm),20(dialout),24(cdrom),
25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),117(netdev),1001(docker)
This was an attacker’s dream. We went from having no access to any of the Linux-based infrastructure to having access to the entire company’s Linux-based infrastructure as an IT admin. While we didn’t have access to their password (and therefore couldn’t use sudo to get root), we did have access to their SSH key. And luckily for us, there was no passphrase on it, meaning we now had a very powerful foothold to pivot around the network.
Mass Ownage
So, what do you do with an SSH private key of an administrator, but no password? Luckily for us, this engagement wasn’t focused on stealth (although, this company didn’t have any effective network logging, so they wouldn’t have noticed anyways!) so we started spraying this SSH key around the network to enumerate what machines we now had access to. We used sshspray.py, although we made some modifications to make it easier to parse the targets.
$ sshspray.py targets.txt dave_ssh_key
This gave us some very, very promising targets. The problem we had with most of these servers however was that we still didn’t have root access. All of them, to their credit, required root access to read configurations, modify services, etc. All of the servers we reviewed had some form of auto-updates configured, so we couldn’t utilise easy privilege escalation using recent CVEs such as sudo, Polkit, Dirty Pipe, etc.
Local Privilege Escalation
Through further enumeration we realised that some of the servers had Docker installed. The UNIX socket that handles communication with the Docker daemon was writable by the input group. And conveniently, our account was in the input group! But why is this important..?
You read that right, hidden away inside the Docker documentation mentions why that access to the Docker daemon is the same as giving root access to the user. This is fantastic for us as we’re not in possession of our hijacked administrator’s password and therefore can’t sudo. Getting root on this server is as simple as using the following command:
$ docker run -v /:/mnt –rm -it alpine chroot /mnt sh
We identified a couple of servers with Docker installed – these servers were generally not doing a whole lot, usually just running static Nginx containers or file servers with non-sensitive content on. Unfortunately, these weren’t very interesting and we went back to the drawing board.
Title
Something we had noticed during all of this was that regardless of what server we logged onto, our home directory was shared between all of them. A quick look at the mounts of any host revealed that our home directory was mounted using NFS.
Not only was NFS being used, NFSv3 was being used. NFSv3, from a security perspective, sucks. Its authentication mainly revolves around the UID and GID of the connecting user. This means that, if you have root, you can forge the UID and GID to access their home directory (in our case, being root means we can simply su to another user.)
Through querying an internal LDAP server, we gathered a list of admins accounts and started changing our UID / GID to match those accounts. With our newly acquired access to these user directories, we found several SSH private keys – again with none of them requiring a passphrase to use.
We also noticed something strange from the machine we were root on, there was an authorised SSH key that could connect as the root account. The entry looked something like this:
After checking a few other machines, we confirmed that this public key was configured for root on many of the Linux machines. As these entries usually contain the hostname of the machine they’re generated on, is there a chance that srv14.acme.ltd still has the private key inside root’s home directory?
With our SSH keys that we gathered through enumerating user’s home directories, we tested whether we could access srv14.acme.ltd. Of the ten admin SSH keys we had, a single one could without requiring a password or MFA. However, we were stuck with the same issue as last time, we still did not have the user’s password to use sudo and therefore needed a way to escalate our privileges. When we logged onto the machine, the following prompt appeared:
It turns out that this server should have been decommissioned a while back, but it hadn’t actually been turned off yet. A quick look around at the system revealed that it fell out of maintenance a while ago and as such, was vulnerable to CVE-2021-4034 aka. Polkit Privilege Escalation.
Final Steps
One short download from Github later and upload to the machine via scp meant we were now root. After all that, we checked inside /root/.ssh/ and lo and behold, the private key. No passphrase required. The private key that could access:
- Over 5,000 machines as root.
- Financial backups dating back to 2015.
- The organisation’s PKI infrastructure (VPNs, internal CA, etc.)
- The organisation’s internal Git repositories (Containing their crown jewels).
While the purpose of this key was to make maintenance of the thousands of servers easier, it had now become the ultimate key to the kingdom.
In our debrief session, the client said they had very little visibility of their Linux infrastructure. None of our SSH spraying was noticed and they only noticed a couple of logins on a few machines where they were testing their EDR solutions.
Of the machines we authenticated to, only a few of them presented MFA push logins, however the users did not alert the IT team that they received these (where they should have revoked the key). This test was a real eye-opener for the client, as it showed how a malicious actor with only VPN access could quite easily compromise their entire Linux infrastructure and gain access to their most sensitive data.
Conclusion
So what can you learn from this?
- Regular penetration testing is essential.
- Don’t use “master” SSH keys, give access on an individual basis.
- Require passphrases on SSH keys, especially for high privileged users.
- Immediately decommission machines once they are no longer used.
- Setup MFA for accessing critical infrastructure.
- Ensure your backups are stored in a secure location, require MFA to access and are encrypted.
- Sometimes, simple mistakes and good enumeration is all it takes for your organisation to be compromised.
Credits
Our client, for allowing us to publish this story.
https://www.exploit-db.com/exploits/50689
https://www.rapid7.com/db/modules/exploit/unix/x11/x11_keyboard_exec/
https://docs.docker.com/engine/install/linux-postinstall/