3333from hashlib import md5
3434import logging
3535from re import DOTALL , compile , search
36- from contextlib import suppress
37-
38- HAVE_CRYPTODOMEX = False
39- with suppress (ImportError ):
40- from Cryptodome .Cipher import AES
41- from Cryptodome .Util .Padding import pad , unpad
42-
43- HAVE_CRYPTODOMEX = True
4436
37+ from Cryptodome .Cipher import AES
38+ from Cryptodome .Util .Padding import pad , unpad
4539
4640from ...config_parser_exception import ConfigParserException
4741from ..data_utils import bytes_to_int , decode_bytes
5347
5448# Is old AES - specifically Rijndael in CBC mode with MD5 hashing for key derivation
5549class ConfigDecryptorRijndael (ConfigDecryptor ):
50+ _ALGO_MAP = {
51+ b"\x17 " : AES .MODE_CBC ,
52+ b"\x1a " : AES .MODE_CFB ,
53+ b"\x18 " : AES .MODE_ECB ,
54+ }
5655 # MD5 hash pattern used to detect AES key
5756 _PATTERN_MD5_HASH = compile (rb"\x7e(.{3}\x04)\x6f.{4}\x11\x06\x0c" , DOTALL )
57+ _PATTERN_MD5_HASH2 = compile (rb"\x7e(.{3}\x04)\x28.{3}\x06\x6f.{4}\x13\x04" , DOTALL )
5858 _KEY_AS_ARG = compile (rb"\x7e(.{3}\x04)\x28.{3}\x06\x7e.{3}\x04\x28.{3}\x06\x80.{3}\x04" , DOTALL )
59+
5960 # key size 16 and 1 = CBC, 2 = ECB
6061 # https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.ciphermode?view=net-9.0
6162 # _PATTERN_AES_MODE = compile(rb"[\x06-\x09]\x6f.{4}\x21(.)\x00{8}\x59\x21(.)\x00{8}\x58\xd4\x8d.{4}\x13\x05")
63+ # check AES mode
64+ _AES_MODE = compile (rb"\x06\x09\x6f.{4}\x06(.)\x6f.{4}\x06\x6f.{4}\x13" , DOTALL )
65+ _SIMPLE_MD5 = compile (rb"\x11\x04\x16\x09\x16\x1f\x10\x28.{4}\x11\x04\x16\x09\x1f\x0f\x1f\x10\x28" , DOTALL )
66+ _PATTERNS_MD5 = (_PATTERN_MD5_HASH , _PATTERN_MD5_HASH2 )
67+ _KEY_PATTERNS = (_KEY_AS_ARG ,)
6268
6369 def __init__ (self , payload : DotNetPEPayload ) -> None :
6470 super ().__init__ (payload )
@@ -69,15 +75,17 @@ def __init__(self, payload: DotNetPEPayload) -> None:
6975 logger .debug ("Incompatible Decryptor" )
7076 raise IncompatibleDecryptorException (e )
7177
72- # Given ciphertext, creates a Cipher object with the AES key and decrypts
78+ # Given ciphertext, creates a Cipher object with the AES/3DES key and decrypts
7379 # the ciphertext
7480 def _decrypt (self , ciphertext : bytes ) -> bytes :
7581 unpadded_text = ""
7682 cipher = AES .new (self .key , mode = self .mode )
7783 block_size = AES .block_size
7884 try :
7985 padded_text = cipher .decrypt (ciphertext )
80- padded_text = padded_text [16 :] # Remove IV
86+ if self .mode == AES .MODE_CBC :
87+ # Remove the first 16 bytes of the decrypted text as they are the IV
88+ padded_text = padded_text [16 :]
8189 except ValueError :
8290 padded_text = cipher .decrypt (pad (ciphertext , block_size ))
8391 try :
@@ -100,13 +108,15 @@ def decrypt_encrypted_strings(self, encrypted_strings: dict[str, str]) -> dict[s
100108 key = encrypted_strings [raw_key_field ]
101109 self .key = self ._derive_key (key )
102110 else :
103- key_hit = search (self ._KEY_AS_ARG , self ._payload .data )
104- key_rva = bytes_to_int (key_hit .groups ()[0 ])
105- raw_key_field = self ._payload .field_name_from_rva (key_rva )
106- key = encrypted_strings [raw_key_field ]
107- self .key = self ._derive_key (key )
111+ for key_pattern in self ._KEY_PATTERNS :
112+ key_hit = search (key_pattern , self ._payload .data )
113+ key_rva = bytes_to_int (key_hit .groups ()[0 ])
114+ raw_key_field = self ._payload .field_name_from_rva (key_rva )
115+ key = encrypted_strings [raw_key_field ]
116+ self .key = self ._derive_key (key )
117+ break
108118 except Exception as e :
109- raise ConfigParserException (f"Failed to derive AES/3DES key: { e } " )
119+ raise ConfigParserException (f"Failed to derive AES key: { e } " )
110120
111121 decrypted_config_strings = {}
112122 for k , v in encrypted_strings .items ():
@@ -120,7 +130,7 @@ def decrypt_encrypted_strings(self, encrypted_strings: dict[str, str]) -> dict[s
120130 b64_exception = False
121131 try :
122132 decoded_val = b64decode (v )
123- except Exception :
133+ except Exception as e :
124134 b64_exception = True
125135 # If it was not base64-encoded, leave the value as it is
126136 if b64_exception :
@@ -152,17 +162,30 @@ def _derive_key(self, key_unhashed: str) -> bytes:
152162 md5_hash .update (key_unhashed .encode ("utf-8" ))
153163 key = md5_hash .digest ()
154164
155- logger .debug (f"Key derived: { key } " )
165+ if search (self ._SIMPLE_MD5 , self ._payload .data ):
166+ # check if simple md5
167+ logger .debug ("Simple MD5 detected" )
168+ key = key [:15 ] + key [:16 ] + b"\x00 "
169+ logger .debug ("Key derived: %s, from key: %s" , key .hex (), key_unhashed )
156170 return key
157171
158172 # Extracts the AES/3DES key RVA from the payload
159173 def _get_key_rva (self ) -> int :
160- logger .debug ("Extracting AES/3Des key value..." )
161- key_hit = search (self ._PATTERN_MD5_HASH , self ._payload .data )
174+ logger .debug ("Extracting AES key value..." )
175+ key_hit = None
176+ for pattern in self ._PATTERNS_MD5 :
177+ key_hit = search (pattern , self ._payload .data )
178+ if key_hit :
179+ break
162180 if not key_hit :
163181 raise ConfigParserException ("Could not find AES key pattern" )
164182
183+ # check if AES mode is different from CBC:
184+ _AES_MODE = search (self ._AES_MODE , self ._payload .data )
185+ if _AES_MODE :
186+ self .mode = self ._ALGO_MAP [_AES_MODE .groups ()[0 ]]
187+ logger .debug (f"AES mode: { self .mode } " )
188+
165189 key_rva = bytes_to_int (key_hit .groups ()[0 ])
166190 logger .debug (f"AES key RVA: { hex (key_rva )} " )
167-
168191 return key_rva
0 commit comments