-
Notifications
You must be signed in to change notification settings - Fork 10
Revert "Refactor and fix config parsing and utility functions" #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -81,21 +81,22 @@ def __init__( | |
| if data is None and not isfile(file_path): | ||
| raise ConfigParserException("File not found") | ||
| # Filled in _decrypt_and_decode_config() | ||
| self._incompatible_decryptors: list[Any] = [] | ||
| self._dnpp = DotNetPEPayload(file_path, yara_rule, data) | ||
| self._incompatible_decryptors: list[int] = [] | ||
| try: | ||
| self._dnpp = DotNetPEPayload(file_path, yara_rule, data) | ||
| except Exception as e: | ||
| raise e | ||
|
Comment on lines
+85
to
+88
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| self.report["sha256"] = self._dnpp.sha256 | ||
| self.report["yara_possible_family"] = self._dnpp.yara_match | ||
|
|
||
| # Assigned in _decrypt_and_decode_config() | ||
| self._decryptor: ConfigDecryptor = None | ||
| self.report["config"] = self._get_config() | ||
| key_hex = "None" | ||
| if self._decryptor is not None and self._decryptor.key is not None: | ||
| if isinstance(self._decryptor.key, bytes): | ||
| key_hex = self._decryptor.key.hex() | ||
| else: | ||
| key_hex = str(self._decryptor.key) | ||
| self.report["key"] = key_hex | ||
| self.report["key"] = ( | ||
| self._decryptor.key.hex() | ||
| if self._decryptor is not None and self._decryptor.key is not None | ||
| else "None" | ||
| ) | ||
|
Comment on lines
+95
to
+99
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This logic for setting key_hex = "None"
if self._decryptor is not None and self._decryptor.key is not None:
if isinstance(self._decryptor.key, bytes):
key_hex = self._decryptor.key.hex()
else:
key_hex = str(self._decryptor.key)
self.report["key"] = key_hex |
||
| self.report["salt"] = ( | ||
| self._decryptor.salt.hex() | ||
| if self._decryptor is not None and self._decryptor.salt is not None | ||
|
|
@@ -121,7 +122,7 @@ def _decrypt_and_decode_config( | |
| config_fields_map[k] = field_name | ||
| item_data[field_name] = v | ||
| if len(item_data) > 0: | ||
| if isinstance(item, config_item.EncryptedStringConfigItem): | ||
| if type(item) is config_item.EncryptedStringConfigItem: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using if isinstance(item, config_item.EncryptedStringConfigItem): |
||
| # Translate config value RVAs to string values | ||
| for k in item_data: | ||
| item_data[k] = self._dnpp.user_string_from_rva(item_data[k]) | ||
|
|
@@ -158,7 +159,7 @@ def _decrypt_and_decode_config( | |
| if self._decryptor is None: | ||
| raise ConfigParserException("All decryptors failed") | ||
|
|
||
| elif isinstance(item, config_item.ByteArrayConfigItem): | ||
| elif type(item) is config_item.ByteArrayConfigItem: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| for k in item_data: | ||
| arr_size, arr_rva = item_data[k] | ||
| item_data[k] = self._dnpp.byte_array_from_size_and_rva( | ||
|
|
@@ -167,7 +168,7 @@ def _decrypt_and_decode_config( | |
|
|
||
| decoded_config.update(item_data) | ||
| # UrlHost is a marker of a special case until this can be standardized | ||
| if len(decoded_config) < min_config_len and "UrlHost" not in decoded_config: | ||
| if len(decoded_config) < min_config_len and "UrlHost" not in item_data: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This check is likely incorrect. It checks if if len(decoded_config) < min_config_len and "UrlHost" not in decoded_config: |
||
| raise ConfigParserException( | ||
| f"Minimum threshold of config items not met: {len(decoded_config)}/{min_config_len}" | ||
| ) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -31,11 +31,11 @@ | |||||
|
|
||||||
|
|
||||||
| # Converts a bytes object to an int object using the specified byte order | ||||||
| def bytes_to_int(data: bytes, order: str = "little") -> int: | ||||||
| def bytes_to_int(bytes: bytes, order: str = "little") -> int: | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The parameter
Suggested change
|
||||||
| try: | ||||||
| return int.from_bytes(data, byteorder=order) | ||||||
| return int.from_bytes(bytes, byteorder=order) | ||||||
| except Exception: | ||||||
| raise ConfigParserException(f"Error parsing int from value: {data}") | ||||||
| raise ConfigParserException(f"Error parsing int from value: {bytes}") | ||||||
|
|
||||||
|
|
||||||
| # Decodes a bytes object to a Unicode string, using UTF-16LE for byte values | ||||||
|
|
@@ -57,8 +57,8 @@ def decode_bytes(byte_str: bytes | str) -> str: | |||||
|
|
||||||
|
|
||||||
| # Converts an int to a bytes object, with the specified length and order | ||||||
| def int_to_bytes(value: int, length: int = 4, order: str = "little") -> bytes: | ||||||
| def int_to_bytes(int: int, length: int = 4, order: str = "little") -> bytes: | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The parameter
Suggested change
|
||||||
| try: | ||||||
| return value.to_bytes(length, order) | ||||||
| return int.to_bytes(length, order) | ||||||
| except Exception: | ||||||
| raise ConfigParserException(f"Error parsing bytes from value: {value}") | ||||||
| raise ConfigParserException(f"Error parsing bytes from value: {int}") | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -67,6 +67,7 @@ def _decrypt(self, iv: bytes, ciphertext: bytes) -> bytes: | |||||
| ) | ||||||
|
|
||||||
| cipher = AES.new(self.key, mode=CBC, iv=iv) | ||||||
| unpadded_text = "" | ||||||
|
|
||||||
|
Comment on lines
+70
to
71
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
| try: | ||||||
| unpadded_text = cipher.decrypt(ciphertext) | ||||||
|
|
@@ -112,7 +113,7 @@ def decrypt_encrypted_strings( | |||||
| result = decode_bytes(self._decrypt(self.iv, decoded_val)) | ||||||
| except ConfigParserException as e: | ||||||
| last_exc = e | ||||||
| logger.debug(f"Error decrypting {k}: {e}") | ||||||
| print("error", e) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using
Suggested change
|
||||||
|
|
||||||
| if result is None: | ||||||
| logger.debug( | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -79,9 +79,9 @@ def _get_hardcoded_hosts(self) -> list[str]: | |||||||||||||
| hardcoded_hosts = [] | ||||||||||||||
| for rva in hardcoded_host_rvas: | ||||||||||||||
| try: | ||||||||||||||
| hardcoded_host = self._payload.user_string_from_rva(bytes_to_int(rva)) | ||||||||||||||
| if hardcoded_host != ".": | ||||||||||||||
| hardcoded_hosts.append(hardcoded_host) | ||||||||||||||
| harcoded_host = self._payload.user_string_from_rva(bytes_to_int(rva)) | ||||||||||||||
| if harcoded_host != ".": | ||||||||||||||
| hardcoded_hosts.append(harcoded_host) | ||||||||||||||
|
Comment on lines
+82
to
+84
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a typo in the variable name
Suggested change
|
||||||||||||||
| except Exception as e: | ||||||||||||||
| logger.error(f"Error translating hardcoded host at {hex(rva)}: {e}") | ||||||||||||||
| continue | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -29,7 +29,7 @@ | |||||||||||||||||||||||||||||||||||||||||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||||||||||||||||||||||||||||||||||||||||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||||||||||||||||||||||||||||||||||||||||
| # SOFTWARE. | ||||||||||||||||||||||||||||||||||||||||||||
| from logging import getLogger | ||||||||||||||||||||||||||||||||||||||||||||
| import logging | ||||||||||||||||||||||||||||||||||||||||||||
| from base64 import b64decode | ||||||||||||||||||||||||||||||||||||||||||||
| from hashlib import md5 | ||||||||||||||||||||||||||||||||||||||||||||
| from re import DOTALL, compile, search | ||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -42,7 +42,7 @@ | |||||||||||||||||||||||||||||||||||||||||||
| from ..dotnetpe_payload import DotNetPEPayload | ||||||||||||||||||||||||||||||||||||||||||||
| from .config_decryptor import ConfigDecryptor, IncompatibleDecryptorException | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| logger = getLogger(__name__) | ||||||||||||||||||||||||||||||||||||||||||||
| logger = logging.getLogger(__name__) | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| # Is old AES - specifically Rijndael in CBC mode with MD5 hashing for key derivation | ||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -110,14 +110,11 @@ def decrypt_encrypted_strings(self, encrypted_strings: dict[str, str]) -> dict[s | |||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||||
| for key_pattern in self._KEY_PATTERNS: | ||||||||||||||||||||||||||||||||||||||||||||
| key_hit = search(key_pattern, self._payload.data) | ||||||||||||||||||||||||||||||||||||||||||||
| if not key_hit: | ||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||
| key_rva = bytes_to_int(key_hit.groups()[0]) | ||||||||||||||||||||||||||||||||||||||||||||
| raw_key_field = self._payload.field_name_from_rva(key_rva) | ||||||||||||||||||||||||||||||||||||||||||||
| if raw_key_field in encrypted_strings: | ||||||||||||||||||||||||||||||||||||||||||||
| key = encrypted_strings[raw_key_field] | ||||||||||||||||||||||||||||||||||||||||||||
| self.key = self._derive_key(key) | ||||||||||||||||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||||||||||||||||
| key = encrypted_strings[raw_key_field] | ||||||||||||||||||||||||||||||||||||||||||||
| self.key = self._derive_key(key) | ||||||||||||||||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
112
to
+117
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change introduces a critical bug by removing necessary safety checks. Without
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||
| raise ConfigParserException(f"Failed to derive AES key: {e}") | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -234,13 +234,6 @@ def custom_attribute_from_type(self, typespacename: str, typename: str) -> dict: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| config = {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ca_map = {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for ca in self.dotnetpe.net.mdtables.CustomAttribute.rows: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| idx = ca.Parent.row_index | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if idx not in ca_map: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ca_map[idx] = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ca_map[idx].append(ca) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for td in self.dotnetpe.net.mdtables.TypeDef.rows: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| td.TypeNamespace.value != typespacename | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -251,21 +244,19 @@ def custom_attribute_from_type(self, typespacename: str, typename: str) -> dict: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.dotnetpe.net.mdtables.Property.rows | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if pd.Name.value.startswith( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Boolean_", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "BorderStyle_", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Color_", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Byte", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Int32_", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "SizeF_", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "String_", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Boolean_", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "BorderStyle_", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Color_", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Byte", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Int32_", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "SizeF_", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "String_", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
246
to
254
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # CustomAttribute Parent index is 1-based | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| target_index = pd_row_index + 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if target_index in ca_map: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for ca in ca_map[target_index]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for ca in self.dotnetpe.net.mdtables.CustomAttribute.rows: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ca.Parent.row_index == pd_row_index + 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): # CustomAttribute Parent index is 1-based | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+256
to
+259
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This nested loop introduces a significant performance regression. By removing the pre-computed |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Extract the value from the CustomAttribute blob | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| blob_data = ca.Value.value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if blob_data and blob_data != b"\x01\x00\x00\x00": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type hint for
_incompatible_decryptorsislist[int], but the code appends decryptor classes to this list (e.g., on line 144:self._incompatible_decryptors.append(decryptor)), not integers. This is a type mismatch. The previous type hintlist[Any]was more appropriate.