Exemple #1
0
    def richpe_info(self, pe, cli_mode=False):

        res = {}

        if pe.RICH_HEADER:
            info = get_richpe_info(pe)
            cli_out("ProdId\tVersion\tCount\tProduct", cli_mode)
            for i in info:
                res = {
                    "product_id": i["prodid"],
                    "version": i["version"],
                    "count": i["count"]
                }
                if i['product']:
                    res["product"] = i["product"]
                    cli_out(
                        "{}\t{}\t{}\t{}".format(i['prodid'], i['version'],
                                                i['count'], i['product']),
                        cli_mode)
                else:
                    cli_out(
                        "{}\t{}\t{}".format(i['prodid'], i['version'],
                                            i['count']), cli_mode)
            rich_hash = get_richpe_hash(pe)
            cli_out("\nRichPE Hash: {}".format(rich_hash), cli_mode)
            res["md5"] = rich_hash

        else:
            cli_out("No RichPE Header", cli_mode)

        return {"richpe": res} if res else {}
Exemple #2
0
    def get_results(self,
                    data,
                    min_len=4,
                    wide_only=False,
                    ascii_only=False,
                    cli_mode=False):

        # regular expressions from flare-floss:
        #  https://github.com/fireeye/flare-floss/blob/master/floss/strings.py#L7-L9
        re_narrow = re.compile(b'([%s]{%d,})' % (ASCII_BYTE, min_len))
        re_wide = re.compile(b'((?:[%s]\x00){%d,})' % (ASCII_BYTE, min_len))

        strings = []

        # print ascii strings unless we only want wide strings
        if not wide_only:
            for match in re_narrow.finditer(data):
                s = match.group().decode('ascii')
                strings.append(s)
                cli_out(s, cli_mode)

        # print wide strings unless we only want ascii strings
        if not ascii_only:
            for match in re_wide.finditer(data):
                try:
                    s = match.group().decode('utf-16')
                    cli_out(s, cli_mode)
                    strings.append(s)
                except UnicodeDecodeError:
                    pass

        return {"strings": strings}
Exemple #3
0
    def check_timestamp(self, pe, cli_mode=False):
        """check for suspicious timestamps"""

        date = datetime.datetime.utcfromtimestamp(pe.FILE_HEADER.TimeDateStamp)
        if (date.year < 2005) or (date > datetime.datetime.now()):
            cli_out("[+] Suspicious timestamp : %s" % str(date), cli_mode)
            return {"suspicious_timestamp": str(date)}
        return {}
Exemple #4
0
 def get_results(self, pe, cli_mode=False):
     
     res = {}
     res.update(self.get_sig_info(pe, cli_mode))
     if res:
         data = bytes(pe.write()[self.get_sig_address(pe)+8:])
         signature = cms.ContentInfo.load(data)
         cli_out("", cli_mode)
         res.update(self.get_cert_info(signature, cli_mode))
     return {"signature": res} if res else res
Exemple #5
0
    def check_imphash(self, pe, cli_mode=False):
        """Check imphash in a list of known import hashes"""

        ih = pe.get_imphash()
        if ih in self.imphashes:
            cli_out(
                "[+] Known suspicious import hash: %s" % (self.imphashes[ih]),
                cli_mode)
            return {"sus_imp_hash": self.imphashes[ih]}
        return {}
Exemple #6
0
        def check_resource(pe, resource, parents, cli_mode=False):
            """
            Recursive cecking/printing of suspicious resources. A resource is suspicious if it 
            itself contains a PE header or the resource is a directory and it's name is a known 
            name we're tracking as suspicious.
            """

            if hasattr(resource, "data"):
                # Resource
                offset = resource.data.struct.OffsetToData
                size = resource.data.struct.Size
                data = pe.get_memory_mapped_image()[offset:offset + size]
                if data.startswith(b'\x4d\x5a\x90\x00\x03\x00'):
                    if resource.name:
                        name = '/'.join(parents) + '/' + str(resource.name)
                    else:
                        name = '/'.join(parents) + '/' + str(resource.id)
                    cli_out('[+] PE header in resource {}'.format(name),
                            cli_mode)
                    return {"embedded_pe_resources": [name]}
                else:
                    return {}
            else:
                # directory
                result = {}
                parents = copy.copy(parents)
                suspicious = False

                if resource.id is not None:
                    parents.append(str(resource.id))
                # resources with no IDs are sus
                else:
                    # TODO test this part. haven't found a sample yet with one of these
                    name = resource.name.string.decode('utf-8')
                    parents.append(name)
                    if name in self.resource_names:
                        cli_out(
                            "[+] resource with no ID: {} -> {}".format(
                                name, self.resource_names[name]), cli_mode)
                        sus_resource = {
                            "resource": name,
                            "description": self.resource_names[name]
                        }
                        if "no_id_resources" not in result:
                            result["no_id_resources"] = [sus_resource]
                        else:
                            result["no_id_resources"].append(sus_resource)

                # recurse and merge the downstream results into this one
                for child_resource in resource.directory.entries:
                    merge_sus_resources(
                        result,
                        check_resource(pe, child_resource, parents, cli_mode))

                return result
Exemple #7
0
    def check_pe_size(self, pe, data, cli_mode=False):
        """Check for extra data in the PE file by comparing PE info and data size"""

        length = max(
            map(lambda x: x.PointerToRawData + x.SizeOfRawData, pe.sections))
        if length < len(data):
            cli_out("[+] %i extra bytes in the file" % (len(data) - length),
                    cli_mode)
            return {"extra_bytes": len(data) - length}
        else:
            return {}
Exemple #8
0
    def check_abnormal_section_name(self, pe, cli_mode=False):

        res = [
            x.Name.decode('utf-8', 'ignore').strip('\x00') for x in pe.sections
            if not self._normal_section_name(x.Name)
        ]
        if len(res) > 0:
            cli_out("[+] Abnormal section names: %s" % " ".join(res), cli_mode)
            return {"abnormal_section_names": res}
        else:
            return {}
Exemple #9
0
        def resource_info(pe, r, parents):
            """Recursive info of resources"""

            # resource
            if hasattr(r, "data"):

                # gather all the info
                offset = r.data.struct.OffsetToData
                size = r.data.struct.Size
                data = pe.get_memory_mapped_image()[offset:offset + size]
                parents_path = "/".join(parents) + "/"
                path = parents_path + str(
                    r.id) if not r.name else parents_path + r.name
                m = hashlib.md5()
                m.update(data)
                md5 = m.hexdigest()
                magic_type = magic.from_buffer(data)
                lang = pefile.LANG.get(r.data.lang, 'UNKNOWN')
                sublang = pefile.get_sublang_name_for_lang(
                    r.data.lang, r.data.sublang)

                # display it if we want
                cli_out(
                    "%-19s %-9s %-14s %-17s %-14s %-9s" %
                    (path, "%i B" % size, lang, sublang, magic_type, md5),
                    cli_mode)

                # return it in a dict
                return [{
                    "path": path,
                    "size": size,
                    "md5": md5,
                    "magic_type": magic_type,
                    "lang": lang,
                    "sublang": sublang
                }]

            # directory
            else:

                res = []

                # append this name or ID to the parents list
                parents = copy.copy(parents)
                if r.id is not None:
                    parents.append(str(r.id))
                else:
                    parents.append(r.name.string.decode('utf-8'))

                for r2 in r.directory.entries:
                    res += resource_info(pe, r2, parents)

                return res
Exemple #10
0
 def entry_point_info(self, pe, cli_mode=False):
     entry_point = pe.OPTIONAL_HEADER.AddressOfEntryPoint + pe.OPTIONAL_HEADER.ImageBase
     section = search_section(pe, entry_point, physical=False)
     entry_point = hex(entry_point)
     cli_out("Entry point:\t%s (section %s)" % (entry_point, section),
             cli_mode)
     return {
         "entry_point": {
             "address": entry_point,
             "section_name": section
         }
     }
Exemple #11
0
    def check_pe_sections(self, pe, cli_mode=False):
        """Search for PE headers at the beginning of sections"""

        res = []
        for section in pe.sections:
            if b"!This program cannot be run in DOS mode" in section.get_data()[:400] or \
                    b"This program must be run under Win32" in section.get_data()[:400]:
                res.append(section.Name.decode('utf-8').strip('\x00'))

        if len(res) > 0:
            cli_out("[+] PE header in sections %s" % " ".join(res), cli_mode)
            return {"pe_header_in_sections": res}
        return {}
Exemple #12
0
    def check_peid(self, data, cli_mode=False):
        """Check on PEid signatures"""

        peid_db = os.path.dirname(
            os.path.realpath(__file__))[:-7] + "data/PeID.yar"
        rules = yara.compile(filepath=peid_db)
        matches = rules.match(data=data)
        if len(matches) > 0:
            cli_out(
                "[+] PeID packer: %s" % ", ".join([a.rule for a in matches]),
                cli_mode)
            return {"peid_packer": [a.rule for a in matches]}
        return {}
Exemple #13
0
        def get_name_attr_values(x509_name, sub_object_friendly, res):

            cli_out(sub_object_friendly, cli_mode)
            sub_object = sub_object_friendly.lower()
            if sub_object not in res:
                res[sub_object] = {}

            for name_attr in x509_name:
                friendly_name = OID_NAME_MAP[name_attr.oid]
                key_name = friendly_name.lower().replace(" ", "_")
                value = str(name_attr.value)
                cli_out("\t{:<35} {}".format(friendly_name + ": ", value), cli_mode)
                res[sub_object][key_name] = value
Exemple #14
0
    def headers_info(seld, pe, cli_mode=False):
        """Display header information"""

        res = {}

        # check if file is a DLL
        if pe.FILE_HEADER.IMAGE_FILE_DLL:
            cli_out("DLL File!", cli_mode)
            res["is_dll"] = True

        # gather compile time
        timestamp = pe.FILE_HEADER.TimeDateStamp
        compile_time = str(datetime.datetime.utcfromtimestamp(timestamp))
        cli_out("Compile Time:\t%s (UTC - 0x%-8X)" % (compile_time, timestamp),
                cli_mode)
        res["compile_time"] = compile_time

        return res
Exemple #15
0
    def hashes_info(self, pe, data, cli_mode=False):
        """Display md5, sha1, sh256, and imphash of the data given"""

        res = {}

        # compute md5, sha1, sha256
        for algo in ["md5", "sha1", "sha256"]:
            m = getattr(hashlib, algo)()
            m.update(data)
            h = m.hexdigest()
            cli_out("%-15s %s" % (algo.upper() + ":", h), cli_mode)
            res[algo.lower()] = h

        # get imphash
        imphash = pe.get_imphash()
        cli_out("%-15s %s" % ("Imphash:", pe.get_imphash()), cli_mode)
        if imphash:
            res["imphash"] = imphash
        return res
Exemple #16
0
    def exports_info(self, pe, cli_mode=False):
        """exports"""

        exports = []
        try:
            for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
                address = hex(pe.OPTIONAL_HEADER.ImageBase + exp.address)
                name = exp.name.decode('utf-8', 'ignore')
                ordinal = exp.ordinal
                cli_out("%s %s %s" % (address, name, ordinal), cli_mode)
                exports.append({
                    "address": address,
                    "name": name,
                    "ordinal": ordinal
                })
        except AttributeError:
            pass

        return {"exports": exports} if exports else {}
Exemple #17
0
    def check_known_suspicious_sections(self, pe, cli_mode=False):

        names = [
            x.Name.decode('utf-8', 'ignore').strip('\x00') for x in pe.sections
        ]

        res = []
        for name in names:
            sus_section = self.know_suspicious_sections.get(name)
            if sus_section:
                res.append({"section_name": name, "description": sus_section})

        if len(res) > 0:
            cli_out("[+] Known suspicious sections", cli_mode)
            for r in res:
                cli_out("\t-%s: %s" % (r["section_name"], r["description"]),
                        cli_mode)

            return {"suspicious_sections": res}
        else:
            return {}
Exemple #18
0
    def get_results(self, pe, data, cli_mode=False):

        crypto_db = os.path.dirname(
            os.path.realpath(__file__))[:-7] + "data/yara-crypto.yar"
        if not os.path.isfile(crypto_db):
            if cli_mode:
                print("Problem accessing the yara database")
            else:
                raise Exception("Problem accessing the yara database")

        rules = yara.compile(filepath=crypto_db)
        matches = rules.match(data=data)
        results = []
        if len(matches) > 0:
            for match in matches:

                paddr = match.strings[0][0]
                results.append({"rule": match.rule, "address": hex(paddr)})

                # try to pin down the virtual/logical address if we can
                # TODO add to non-cli mode?
                section, vaddr = self.convert_physical_addr(pe, paddr)
                if section:
                    cli_out(
                        "Found : {} at {} ({} - {})".format(
                            match.rule, hex(paddr), section, hex(vaddr)),
                        cli_mode)
                else:
                    cli_out(
                        "Found : {} at {} (Virtual Address and section not found)"
                        .format(match.rule, hex(paddr)), cli_mode)
        else:
            cli_out("No cryptographic data found!", cli_mode)

        return {} if not results else {"crypto_matches": results}
Exemple #19
0
    def check_section_entropy(self, pe, cli_mode=False):

        res = []
        for s in pe.sections:
            if s.get_entropy() < 1 or s.get_entropy() > 7:
                res.append({
                    "section_name":
                    s.Name.decode('utf-8', 'ignore').strip('\x00'),
                    "entropy":
                    s.get_entropy()
                })

        if len(res) > 0:
            if len(res) == 1:
                cli_out(
                    "[+] Suspicious section's entropy: %s - %3f" %
                    (res[0]["section_name"], res[0]["entropy"]), cli_mode)
            else:
                cli_out("[+] Suspicious entropy in the following sections:",
                        cli_mode)
                for r in res:
                    cli_out("\t- %s - %3f" % (r["section_name"], r["entropy"]),
                            cli_mode)

            return {"suspicious_entropy_sections": res}
        else:
            return {}
Exemple #20
0
    def sections_info(self, pe, cli_mode=False):
        """information about the PE sections"""

        sections = []
        cli_out(
            "{:9} {:4} {:10} {:10} {:9} {:9} {:8} {}".format(
                "Name", "RWX", "VirtSize", "VirtAddr", "RawAddr", "RawSize",
                "Entropy", "md5"), cli_mode)
        for section in pe.sections:
            name = section.Name.decode('utf-8', 'ignore').strip('\x00')
            m = hashlib.md5()
            m.update(section.get_data())
            md5 = m.hexdigest()

            permissions = ""
            if section.IMAGE_SCN_MEM_READ:
                permissions += "R"
            else:
                permissions += "-"

            if section.IMAGE_SCN_MEM_WRITE:
                permissions += "W"
            else:
                permissions += "-"

            if section.IMAGE_SCN_MEM_EXECUTE:
                permissions += "X"
            else:
                permissions += "-"

            vsize = hex(section.Misc_VirtualSize)
            vaddr = hex(section.VirtualAddress)
            raw_addr = hex(section.PointerToRawData)
            size = hex(section.SizeOfRawData)
            entropy = section.get_entropy()

            cli_out(
                "{:9} {:4} {:10} {:10} {:9} {:9} {:6.2f} {}".format(
                    name, permissions, vsize, vaddr, raw_addr, size, entropy,
                    md5), cli_mode)
            sections.append({
                "name": name,
                "permissions": permissions,
                "virtual_size": vsize,
                "virtual_address": vaddr,
                "raw_address": raw_addr,
                "raw_size": size,
                "entropy": entropy,
                "md5": md5
            })

        cli_out("", cli_mode)
        return {"sections": sections} if sections else {}
Exemple #21
0
    def get_sig_info(self, pe, cli_mode=False):

        res = {}
        address = self.get_sig_address(pe)

        if address:
            address = hex(address)
            cli_out("This PE file is signed", cli_mode)
            cli_out("Signature Address: " + address, cli_mode)
            res["address"] = address
        else:
            cli_out("This PE file is not signed", cli_mode)

        return res
Exemple #22
0
 def dotnet_guid_info(self, pe, data, cli_mode=False):
     res = {}
     if is_dot_net_assembly(pe):
         try:
             r = get_guid(pe, data)
             if "mvid" in r:
                 cli_out(".NET MVid\t{}".format(r["mvid"]), cli_mode)
                 res["module_version_id"] = r["mvid"]
             if "typelib_id" in r:
                 cli_out(".NET TypeLib\t{}".format(r['typelib_id']),
                         cli_mode)
                 res["typelib_id"] = r["typelib_id"]
         except:
             cli_out("Impossible to parse .NET GUID", cli_mode)
     return res
Exemple #23
0
    def imports_info(self, pe, cli_mode=False):
        """Display imports"""

        res = {}
        if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
            for entry in pe.DIRECTORY_ENTRY_IMPORT:

                # get the dll name
                dll = entry.dll.decode('utf-8')
                cli_out(dll, cli_mode)

                for i in entry.imports:

                    # get the address, ordinal, and function name
                    addr = str(hex(i.address))
                    ordinal = str(i.ordinal) if i.ordinal else None
                    name = i.name.decode('utf-8') if i.name else None

                    if dll not in res:
                        res[dll] = []

                    import_res = {"address": addr}
                    if name:
                        import_res["name"] = name
                    if ordinal:
                        import_res["ordinal"] = ordinal

                    res[dll].append(import_res)

                    # display
                    # TODO allow for both ordinal and name being present in cli mode
                    if name:
                        cli_out('\t%s %s' % (addr, name), cli_mode)
                    else:
                        cli_out('\t%s ordinal: %s' % (addr, ordinal), cli_mode)

        # flatten the non-key-descriptive res into one that is
        new_res = []
        for dll in res:
            new_res.append({"library": dll, "imports": res[dll]})

        return {"library_imports": new_res} if new_res else {}
Exemple #24
0
    def check_tls(self, pe, cli_mode=False):
        """Check if there are TLS callbacks"""

        res = {}
        callbacks = []
        if (hasattr(pe, 'DIRECTORY_ENTRY_TLS') and \
                    pe.DIRECTORY_ENTRY_TLS and \
                    pe.DIRECTORY_ENTRY_TLS.struct and \
                    pe.DIRECTORY_ENTRY_TLS.struct.AddressOfCallBacks):
            callback_array_rva = pe.DIRECTORY_ENTRY_TLS.struct.AddressOfCallBacks - pe.OPTIONAL_HEADER.ImageBase
            idx = 0
            while True:
                func = pe.get_dword_from_data(
                    pe.get_data(callback_array_rva + 4 * idx, 4), 0)
                if func == 0:
                    break
                callbacks.append({
                    "address":
                    "0x%x" % func,
                    "section":
                    search_section(pe, func, physical=False)
                })
                idx += 1

        if callbacks:

            res["tls_callbacks"] = callbacks

            # display the results
            if len(callbacks) == 1:
                cli_out(
                    "TLS Callback:\t%s (section %s)" %
                    (callbacks[0]["address"], callbacks[0]["section"]),
                    cli_mode)
            else:
                cli_out("TLS Callbacks:", cli_mode)
                for c in callbacks:
                    cli_out(
                        "\t\t%s (section %s)" % (c["address"], c["section"]),
                        cli_mode)

        return res
Exemple #25
0
 def magic_type_info(self, data, cli_mode=False):
     magic_type = magic.from_buffer(data)
     cli_out("Type:\t\t%s" % magic_type, cli_mode)
     return {"magic_type": magic_type}
Exemple #26
0
    def get_cert_info(self, signature, cli_mode=False):

        def get_name_attr_values(x509_name, sub_object_friendly, res):

            cli_out(sub_object_friendly, cli_mode)
            sub_object = sub_object_friendly.lower()
            if sub_object not in res:
                res[sub_object] = {}

            for name_attr in x509_name:
                friendly_name = OID_NAME_MAP[name_attr.oid]
                key_name = friendly_name.lower().replace(" ", "_")
                value = str(name_attr.value)
                cli_out("\t{:<35} {}".format(friendly_name + ": ", value), cli_mode)
                res[sub_object][key_name] = value

        certificates = []
        for cert in signature["content"]["certificates"]:

            cert_res = {}
            parsed_cert = x509.load_der_x509_certificate(cert.dump(), default_backend())

            # general certificate information
            cli_out(("=" * 100) + "\nCertificate\n" + ("=" * 100) + "\n", cli_mode)

            # certificate version
            version = str(parsed_cert.version)
            cert_res["cert_version"] = version
            cli_out("{:<35} {}".format("Version:", version), cli_mode)

            # validity timestamp boundaries
            not_valid_before = str(parsed_cert.not_valid_before)
            not_valid_after = str(parsed_cert.not_valid_after)
            cert_res["not_valid_before"] = not_valid_before
            cert_res["not_valid_after"] = not_valid_after
            cli_out("{:<35} {}".format("Not Valid Before:", not_valid_before), cli_mode)
            cli_out("{:<35} {}".format("Not Valid After:", not_valid_after), cli_mode)
            cli_out("", cli_mode)

            # issuer information
            get_name_attr_values(parsed_cert.issuer, "Issuer", cert_res)
            cli_out("", cli_mode)

            # subject information
            get_name_attr_values(parsed_cert.subject, "Subject", cert_res)

            cli_out("", cli_mode)
            certificates.append(cert_res)

        # TODO check if cert is valid

        return {"certificates": certificates}
Exemple #27
0
 def size_info(self, data, cli_mode=False):
     size = len(data)
     cli_out("Size:\t\t%d bytes" % size, cli_mode)
     return {"size": str(size)}