예제 #1
0
    def _construct_ecrypt_array(self, array_start: int, count: int, \
                                cryptdll_types: interfaces.context.ModuleInterface) -> interfaces.context.ModuleInterface:
        """
        Attempts to construct an array of _KERB_ECRYPT structures

        Args:
            array_start: starting virtual address of the array
            count: how many elements are in the array
            cryptdll_types: the reverse engineered types

        Returns:
            The instantiated array
        """

        try:
            array = cryptdll_types.object(
                object_type="array",
                offset=array_start,
                subtype=cryptdll_types.get_type("_KERB_ECRYPT"),
                count=count,
                absolute=True)

        except exceptions.InvalidAddressException:
            vollog.debug(
                "Unable to construct cSystems array at given offset: {:x}".
                format(array_start))
            array = None

        return array
예제 #2
0
    def _find_csystems_with_scanning(
            self, proc_layer_name: str,
            cryptdll_types: interfaces.context.ModuleInterface,
            cryptdll_base: int,
            cryptdll_size: int) -> List[interfaces.context.ModuleInterface]:
        """
        Performs scanning to find potential RC4 HMAC csystem instances

        This function may return several values as it cannot validate which is the active one

        Args:
            proc_layer_name: the lsass.exe process layer name
            cryptdll_types: the types from cryptdll binary analysis
            cryptdll_base: base address of cryptdll.dll inside of lsass.exe
            cryptdll_size: size of the VAD
        Returns:
            A list of csystem instances
        """

        csystems = []

        cryptdll_end = cryptdll_base + cryptdll_size

        proc_layer = self.context.layers[proc_layer_name]

        ecrypt_size = cryptdll_types.get_type("_KERB_ECRYPT").size

        # scan for potential instances of RC4 HMAC
        # the signature is based on the type being 0x17
        # and the block size member being 1 in all test samples
        for address in proc_layer.scan(
                self.context,
                scanners.BytesScanner(b"\x17\x00\x00\x00\x01\x00\x00\x00"),
                sections=[(cryptdll_base, cryptdll_size)]):

            # this occurs across page boundaries
            if not proc_layer.is_valid(address, ecrypt_size):
                continue

            kerb = cryptdll_types.object("_KERB_ECRYPT",
                                         offset=address,
                                         absolute=True)

            # ensure the Encrypt and Finish pointers are inside the VAD
            # these are not manipulated in the attack
            if (cryptdll_base < kerb.Encrypt < cryptdll_end) and \
                    (cryptdll_base < kerb.Finish < cryptdll_end):
                csystems.append(kerb)

        return csystems
예제 #3
0
    def lookup_module_address(
            cls, kernel_module: interfaces.context.ModuleInterface,
            handlers: List[Tuple[str, int, int]], target_address: int):
        """
        Searches between the start and end address of the kernel module using target_address.
        Returns the module and symbol name of the address provided.
        """

        mod_name = "UNKNOWN"
        symbol_name = "N/A"

        for name, start, end in handlers:
            if start <= target_address <= end:
                mod_name = name
                if name == constants.linux.KERNEL_NAME:
                    symbols = list(
                        kernel_module.get_symbols_by_absolute_location(
                            target_address))

                    if len(symbols):
                        symbol_name = symbols[0].split(constants.BANG)[1] if constants.BANG in symbols[0] else \
                            symbols[0]

                break

        return mod_name, symbol_name
예제 #4
0
    def _find_array_with_pdb_symbols(
        self, cryptdll_symbols: str,
        cryptdll_types: interfaces.context.ModuleInterface,
        proc_layer_name: str, cryptdll_base: int
    ) -> Tuple[interfaces.objects.ObjectInterface, int, int, int]:
        """
        Finds the CSystems array through use of PDB symbols

        Args:
            cryptdll_symbols: The symbols table from the PDB file
            cryptdll_types: The types from cryptdll binary analysis
            proc_layer_name: The lsass.exe process layer name
            cryptdll_base: Base address of cryptdll.dll inside of lsass.exe

        Returns:
            Tuple of:
            array: The cSystems array
            rc4HmacInitialize: The runtime address of the expected initialization function
            rc4HmacDecrypt: The runtime address of the expected decryption function
        """
        cryptdll_module = self.context.module(cryptdll_symbols,
                                              layer_name=proc_layer_name,
                                              offset=cryptdll_base)

        rc4HmacInitialize = cryptdll_module.get_absolute_symbol_address(
            "rc4HmacInitialize")

        rc4HmacDecrypt = cryptdll_module.get_absolute_symbol_address(
            "rc4HmacDecrypt")

        count_address = cryptdll_module.get_symbol("cCSystems").address

        # we do not want to fail just because the count is not in memory
        # 16 was the size on samples I tested, so I chose it as the default
        try:
            count = cryptdll_types.object(object_type="unsigned long",
                                          offset=count_address)
        except exceptions.InvalidAddressException:
            count = 16

        array_start = cryptdll_module.get_absolute_symbol_address("CSystems")

        array = self._construct_ecrypt_array(array_start, count,
                                             cryptdll_types)

        if array is None:
            vollog.debug(
                "The CSystem array is not present in memory. Stopping PDB based analysis."
            )

        return array, rc4HmacInitialize, rc4HmacDecrypt
예제 #5
0
    def _enumerate_system_va_type(
        cls, large_page_size: int, system_range_start: int,
        module: interfaces.context.ModuleInterface,
        type_array: interfaces.objects.ObjectInterface
    ) -> Dict[str, List[Tuple[int, int]]]:
        result: Dict[str, List[Tuple[int, int]]] = {}
        system_va_type = module.get_enumeration('_MI_SYSTEM_VA_TYPE')
        start = system_range_start
        prev_entry = -1
        cur_size = large_page_size
        for entry in type_array:
            entry = system_va_type.lookup(entry)
            if entry != prev_entry:
                region_range = result.get(entry, [])
                region_range.append((start, cur_size))
                result[entry] = region_range
                start = start + cur_size
                cur_size = large_page_size
            else:
                cur_size += large_page_size
            prev_entry = entry

        return result
예제 #6
0
 def determine_extended_value(
         self, leaf_type: interfaces.objects.ObjectInterface,
         value: interfaces.objects.ObjectInterface,
         module: interfaces.context.ModuleInterface, length: int
 ) -> Tuple[str, interfaces.objects.ObjectInterface, int]:
     """Reads a value and potentially consumes more data to construct the
     value."""
     excess = 0
     if value >= leaf_type.LF_CHAR:
         sub_leaf_type = self.context.object(
             self.context.symbol_space.get_enumeration(
                 leaf_type.vol.type_name),
             layer_name=leaf_type.vol.layer_name,
             offset=value.vol.offset)
         # Set the offset at just after the previous size type
         offset = value.vol.offset + value.vol.data_format.length
         if sub_leaf_type in [leaf_type.LF_CHAR]:
             value = module.object(object_type='char', offset=offset)
         elif sub_leaf_type in [leaf_type.LF_SHORT]:
             value = module.object(object_type='short', offset=offset)
         elif sub_leaf_type in [leaf_type.LF_USHORT]:
             value = module.object(object_type='unsigned short',
                                   offset=offset)
         elif sub_leaf_type in [leaf_type.LF_LONG]:
             value = module.object(object_type='long', offset=offset)
         elif sub_leaf_type in [leaf_type.LF_ULONG]:
             value = module.object(object_type='unsigned long',
                                   offset=offset)
         else:
             raise TypeError("Unexpected extended value type")
         excess = value.vol.data_format.length
         # Updated the consume/offset counters
     name = module.object(object_type="string",
                          offset=value.vol.offset +
                          value.vol.data_format.length)
     name_str = self.parse_string(name,
                                  leaf_type < leaf_type.LF_ST_MAX,
                                  size=length - excess)
     return name_str, value, excess
예제 #7
0
    def consume_type(
        self, module: interfaces.context.ModuleInterface, offset: int,
        length: int
    ) -> Tuple[Tuple[
            Optional[interfaces.objects.ObjectInterface], Optional[str], Union[
                None, List, interfaces.objects.ObjectInterface]], int]:
        """Returns a (leaf_type, name, object) Tuple for a type, and the number
        of bytes consumed."""
        leaf_type = self.context.object(module.get_enumeration("LEAF_TYPE"),
                                        layer_name=module._layer_name,
                                        offset=offset)
        consumed = leaf_type.vol.base_type.size
        remaining = length - consumed

        if leaf_type in [
                leaf_type.LF_CLASS, leaf_type.LF_CLASS_ST,
                leaf_type.LF_STRUCTURE, leaf_type.LF_STRUCTURE_ST,
                leaf_type.LF_INTERFACE
        ]:
            structure = module.object(object_type="LF_STRUCTURE",
                                      offset=offset + consumed)
            name_offset = structure.name.vol.offset - structure.vol.offset
            name, value, excess = self.determine_extended_value(
                leaf_type, structure.size, module, remaining - name_offset)
            structure.size = value
            structure.name = name
            consumed += remaining
            result = leaf_type, name, structure
        elif leaf_type in [leaf_type.LF_MEMBER, leaf_type.LF_MEMBER_ST]:
            member = module.object(object_type="LF_MEMBER",
                                   offset=offset + consumed)
            name_offset = member.name.vol.offset - member.vol.offset
            name, value, excess = self.determine_extended_value(
                leaf_type, member.offset, module, remaining - name_offset)
            member.offset = value
            member.name = name
            result = leaf_type, name, member
            consumed += member.vol.size + len(name) + 1 + excess
        elif leaf_type in [
                leaf_type.LF_ARRAY, leaf_type.LF_ARRAY_ST,
                leaf_type.LF_STRIDED_ARRAY
        ]:
            array = module.object(object_type="LF_ARRAY",
                                  offset=offset + consumed)
            name_offset = array.name.vol.offset - array.vol.offset
            name, value, excess = self.determine_extended_value(
                leaf_type, array.size, module, remaining - name_offset)
            array.size = value
            array.name = name
            result = leaf_type, name, array
            consumed += remaining
        elif leaf_type in [leaf_type.LF_ENUMERATE]:
            enum = module.object(object_type='LF_ENUMERATE',
                                 offset=offset + consumed)
            name_offset = enum.name.vol.offset - enum.vol.offset
            name, value, excess = self.determine_extended_value(
                leaf_type, enum.value, module, remaining - name_offset)
            enum.value = value
            enum.name = name
            result = leaf_type, name, enum
            consumed += enum.vol.size + len(name) + 1 + excess
        elif leaf_type in [leaf_type.LF_ARGLIST, leaf_type.LF_ENUM]:
            enum = module.object(object_type="LF_ENUM",
                                 offset=offset + consumed)
            name_offset = enum.name.vol.offset - enum.vol.offset
            name = self.parse_string(enum.name,
                                     leaf_type < leaf_type.LF_ST_MAX,
                                     size=remaining - name_offset)
            enum.name = name
            result = leaf_type, name, enum
            consumed += remaining
        elif leaf_type in [leaf_type.LF_UNION]:
            union = module.object(object_type="LF_UNION",
                                  offset=offset + consumed)
            name_offset = union.name.vol.offset - union.vol.offset
            name = self.parse_string(union.name,
                                     leaf_type < leaf_type.LF_ST_MAX,
                                     size=remaining - name_offset)
            result = leaf_type, name, union
            consumed += remaining
        elif leaf_type in [
                leaf_type.LF_MODIFIER, leaf_type.LF_POINTER,
                leaf_type.LF_PROCEDURE
        ]:
            obj = module.object(object_type=leaf_type.lookup(),
                                offset=offset + consumed)
            result = leaf_type, None, obj
            consumed += remaining
        elif leaf_type in [leaf_type.LF_FIELDLIST]:
            sub_length = remaining
            sub_offset = offset + consumed
            fields = []
            while length > consumed:
                subfield, sub_consumed = self.consume_type(
                    module, sub_offset, sub_length)
                sub_consumed += self.consume_padding(module.layer_name,
                                                     sub_offset + sub_consumed)
                sub_length -= sub_consumed
                sub_offset += sub_consumed
                consumed += sub_consumed
                fields.append(subfield)
            result = leaf_type, None, fields
        elif leaf_type in [leaf_type.LF_BITFIELD]:
            bitfield = module.object(object_type="LF_BITFIELD",
                                     offset=offset + consumed)
            result = leaf_type, None, bitfield
            consumed += remaining
        elif leaf_type in [leaf_type.LF_STRING_ID, leaf_type.LF_FUNC_ID]:
            string_id = module.object(object_type=leaf_type.lookup(),
                                      offset=offset + consumed)
            name_offset = string_id.name.vol.offset - string_id.vol.offset
            name = self.parse_string(string_id.name,
                                     leaf_type < leaf_type.LF_ST_MAX,
                                     size=remaining - name_offset)
            result = leaf_type, name, string_id
        elif leaf_type in [
                leaf_type.LF_UDT_SRC_LINE, leaf_type.LF_UDT_MOD_SRC_LINE
        ]:
            src_line = module.object(object_type=leaf_type.lookup(),
                                     offset=offset + consumed)
            result = leaf_type, None, src_line
        elif leaf_type in [leaf_type.LF_BUILDINFO]:
            buildinfo = module.object(object_type=leaf_type.lookup(),
                                      offset=offset + consumed)
            buildinfo.arguments.count = buildinfo.count
            consumed += buildinfo.arguments.vol.size
            result = leaf_type, None, buildinfo
        else:
            raise TypeError("Unhandled leaf_type: {}".format(leaf_type))

        return result, consumed
예제 #8
0
    def determine_map(cls, module: interfaces.context.ModuleInterface) -> \
            Dict[str, List[Tuple[int, int]]]:
        """Returns the virtual map from a windows kernel module."""
        layer = module.context.layers[module.layer_name]
        if not isinstance(layer, intel.Intel):
            raise

        result: Dict[str, List[Tuple[int, int]]] = {}
        system_va_type = module.get_enumeration('_MI_SYSTEM_VA_TYPE')
        large_page_size = (layer.page_size**
                           2) // module.get_type("_MMPTE").size

        if module.has_symbol('MiVisibleState'):
            symbol = module.get_symbol('MiVisibleState')
            visible_state = module.object(
                object_type='pointer',
                offset=symbol.address,
                subtype=module.get_type('_MI_VISIBLE_STATE')).dereference()
            if hasattr(visible_state, 'SystemVaRegions'):
                for i in range(visible_state.SystemVaRegions.count):
                    lookup = system_va_type.lookup(i)
                    region_range = result.get(lookup, [])
                    region_range.append(
                        (visible_state.SystemVaRegions[i].BaseAddress,
                         visible_state.SystemVaRegions[i].NumberOfBytes))
                    result[lookup] = region_range
            elif hasattr(visible_state, 'SystemVaType'):
                system_range_start = module.object(
                    object_type="pointer",
                    offset=module.get_symbol("MmSystemRangeStart").address)
                result = cls._enumerate_system_va_type(
                    large_page_size, system_range_start, module,
                    visible_state.SystemVaType)
            else:
                raise exceptions.SymbolError(None, module.name,
                                             "Required structures not found")
        elif module.has_symbol('MiSystemVaType'):
            system_range_start = module.object(
                object_type="pointer",
                offset=module.get_symbol("MmSystemRangeStart").address)
            symbol = module.get_symbol('MiSystemVaType')
            array_count = (0xFFFFFFFF + 1 -
                           system_range_start) // large_page_size
            type_array = module.object(object_type='array',
                                       offset=symbol.address,
                                       count=array_count,
                                       subtype=module.get_type('char'))

            result = cls._enumerate_system_va_type(large_page_size,
                                                   system_range_start, module,
                                                   type_array)
        else:
            raise exceptions.SymbolError(None, module.name,
                                         "Required structures not found")

        return result
예제 #9
0
    def consume_type(
        self, module: interfaces.context.ModuleInterface, offset: int,
        length: int
    ) -> Tuple[Tuple[
            Optional[interfaces.objects.ObjectInterface], Optional[str], Union[
                None, List, interfaces.objects.ObjectInterface]], int]:
        """Returns a (leaf_type, name, object) Tuple for a type, and the number
        of bytes consumed."""
        leaf_type = self.context.object(module.get_enumeration("LEAF_TYPE"),
                                        layer_name=module._layer_name,
                                        offset=offset)
        consumed = leaf_type.vol.base_type.size
        remaining = length - consumed

        type_handler, has_name, value_attribute = self.type_handlers.get(
            leaf_type.lookup(), ('LF_UNKNOWN', False, None))

        if type_handler in ['LF_FIELDLIST']:
            sub_length = remaining
            sub_offset = offset + consumed
            fields = []
            while length > consumed:
                subfield, sub_consumed = self.consume_type(
                    module, sub_offset, sub_length)
                sub_consumed += self.consume_padding(module.layer_name,
                                                     sub_offset + sub_consumed)
                sub_length -= sub_consumed
                sub_offset += sub_consumed
                consumed += sub_consumed
                fields.append(subfield)
            result = leaf_type, None, fields
        elif type_handler in ['LF_BUILDINFO']:
            parsed_obj = module.object(object_type=type_handler,
                                       offset=offset + consumed)
            parsed_obj.arguments.count = parsed_obj.count
            consumed += parsed_obj.arguments.vol.size
            result = leaf_type, None, parsed_obj
        elif type_handler in self.type_handlers:
            parsed_obj = module.object(object_type=type_handler,
                                       offset=offset + consumed)
            current_consumed = remaining
            if has_name:
                name_offset = parsed_obj.name.vol.offset - parsed_obj.vol.offset
                if value_attribute:
                    name, value, excess = self.determine_extended_value(
                        leaf_type, getattr(parsed_obj, value_attribute),
                        module, remaining - name_offset)
                    setattr(parsed_obj, value_attribute, value)
                    current_consumed = parsed_obj.vol.size + len(
                        name) + 1 + excess
                else:
                    name = self.parse_string(parsed_obj.name,
                                             leaf_type < leaf_type.LF_ST_MAX,
                                             size=remaining - name_offset)
                parsed_obj.name = name
            else:
                name = None
            result = leaf_type, name, parsed_obj
            consumed += current_consumed
        else:
            raise TypeError(f"Unhandled leaf_type: {leaf_type}")

        return result, consumed