Improved EFI binaries search and updated blacklist config (#131)

* Changes in calculating hashes of EFI binaries

Calculating hashes of actual .efi executable files (PE32/TE sections of
EFI binaries) rather than of entire EFI binaries.

There's still an option (WRITE_ALL_HASHES) to calculate hashes of entire
EFI binaries.

* Improved UEFI search and updated blacklist config

1. Updated EFI binaries search - searching only leaf nodes (PE/TE
executable sections)
2. Added exclusion criteria and improved JSON format
3. Changed tools.uefi.blacklist module to return a warning
4. Updated ThinkPwn rules in blacklist.json
- Updated config to match by GUID AND regexp of SmmRuntime protcol GUID
within the binary to skip binaries consuming SmmRuntime protcol
- Added exclusion rules for UEFI update images with patched
SystemSmmRuntimeRt.efi. Config excludes patched binaries from Lenovo and
HP. Tested on Intel, Lenovo, HP and Gigabyte images.
This commit is contained in:
c7zero
2016-12-05 15:11:51 -08:00
committed by GitHub
parent f9738aeac0
commit 8155afb2f4
3 changed files with 202 additions and 93 deletions
+154 -75
View File
@@ -63,6 +63,11 @@ CMD_UEFI_FILE_INSERT_BEFORE = 1
CMD_UEFI_FILE_INSERT_AFTER = 2
CMD_UEFI_FILE_REPLACE = 3
#
# Calculate hashes for all FVs, FW files and sections (PE/COFF or TE executables)
# and write them on the file system
#
WRITE_ALL_HASHES = False
def decompress_section_data( _uefi, section_dir_path, sec_fs_name, compressed_data, compression_type, remove_files=False ):
compressed_name = os.path.join(section_dir_path, "%s.gz" % sec_fs_name)
@@ -155,18 +160,24 @@ class EFI_MODULE(object):
self.clsname = "EFI module"
self.indent = ''
self.MD5 = ''
self.SHA1 = ''
self.SHA256 = ''
self.MD5 = None
self.SHA1 = None
self.SHA256 = None
def name(self):
return "%s {%s}" % (self.clsname,self.Guid)
def __str__(self):
_ind = self.indent + DEF_INDENT
return "%sMD5 : %s\n%sSHA1 : %s\n%sSHA256: %s\n" % (_ind,self.MD5,_ind,self.SHA1,_ind,self.SHA256)
_s = ''
if self.MD5 : _s = "\n%sMD5 : %s" % (_ind,self.MD5)
if self.SHA1 : _s += "\n%sSHA1 : %s" % (_ind,self.SHA1)
if self.SHA256: _s += "\n%sSHA256: %s" % (_ind,self.SHA256)
return _s
class EFI_FV(EFI_MODULE):
def __init__(self, Offset, Guid, Size, Attributes, HeaderSize, Checksum, ExtHeaderOffset, Image, CalcSum):
EFI_MODULE.__init__(self, Offset, Guid, HeaderSize, Attributes, Image)
super(EFI_FV, self).__init__(Offset, Guid, HeaderSize, Attributes, Image)
self.clsname = "EFI firmware volume"
self.Size = Size
self.Checksum = Checksum
@@ -176,14 +187,14 @@ class EFI_FV(EFI_MODULE):
def __str__(self):
schecksum = ('%04Xh (%04Xh) *** checksum mismatch ***' % (self.Checksum,self.CalcSum)) if self.CalcSum != self.Checksum else ('%04Xh' % self.Checksum)
_s = "\n%s%s +%08Xh {%s}: Size %08Xh, Attr %08Xh, HdrSize %04Xh, ExtHdrOffset %08Xh, Checksum %s" % (self.indent,self.clsname,self.Offset,self.Guid,self.Size,self.Attributes,self.HeaderSize,self.ExtHeaderOffset,schecksum)
_s += ("\n" + super(EFI_FV, self).__str__())
_s += super(EFI_FV, self).__str__()
return _s
class EFI_FILE(EFI_MODULE):
def __init__(self, Offset, Name, Type, Attributes, State, Checksum, Size, Image, HeaderSize, UD, CalcSum):
EFI_MODULE.__init__(self, Offset, Name, HeaderSize, Attributes, Image)
def __init__(self, Offset, Guid, Type, Attributes, State, Checksum, Size, Image, HeaderSize, UD, CalcSum):
super(EFI_FILE, self).__init__(Offset, Guid, HeaderSize, Attributes, Image)
self.clsname = "EFI binary"
self.Name = Name
self.Name = Guid
self.Type = Type
self.State = State
self.Size = Size
@@ -194,24 +205,28 @@ class EFI_FILE(EFI_MODULE):
def __str__(self):
schecksum = ('%04Xh (%04Xh) *** checksum mismatch ***' % (self.Checksum,self.CalcSum)) if self.CalcSum != self.Checksum else ('%04Xh' % self.Checksum)
_s = "\n%s%s +%08Xh {%s}\n%sType %02Xh, Attr %08Xh, State %02Xh, Size %06Xh, Checksum %s" % (self.indent,self.clsname,self.Offset,self.Guid,self.indent*2,self.Type,self.Attributes,self.State,self.Size,schecksum)
_s += ("\n" + super(EFI_FILE, self).__str__())
_s += (super(EFI_FILE, self).__str__() + '\n')
return _s
class EFI_SECTION(EFI_MODULE):
def __init__(self, Offset, Name, Type, Image, HeaderSize):
EFI_MODULE.__init__(self, Offset, None, HeaderSize, None, Image)
super(EFI_SECTION, self).__init__(Offset, None, HeaderSize, None, Image)
self.clsname = "EFI section"
self.Name = Name
self.Type = Type
self.ui_string = ''
self.DataOffset = None
self.parentGuid = None
def name(self):
return "%s section of binary {%s}" % (self.Name,self.parentGuid)
def __str__(self):
_s = "%s%s +%08Xh %-16s: Type %02Xh %s" % (self.indent,self.clsname,self.Offset,self.Name,self.Type,self.ui_string)
if self.Guid: _s += ", GUID {%s}" % self.Guid
if self.Attributes: _s += ", Attr %04Xh" % self.Attributes
if self.DataOffset: _s += ", DataOffset %04Xh" % self.DataOffset
if self.Guid: _s += " GUID {%s}" % self.Guid
if self.Attributes: _s += " Attr %04Xh" % self.Attributes
if self.DataOffset: _s += " DataOffset %04Xh" % self.DataOffset
_s += super(EFI_SECTION, self).__str__()
return _s
def dump_fw_file( fwbin, volume_path ):
@@ -219,17 +234,19 @@ def dump_fw_file( fwbin, volume_path ):
pth = os.path.join( volume_path, "%s.%s-%02X" % (fwbin.Name, type_s, fwbin.Type))
if os.path.exists( pth ): pth += ("_%08X" % fwbin.Offset)
write_file( pth, fwbin.Image )
if fwbin.MD5 != '': write_file( ("%s.md5" % pth), fwbin.MD5 )
if fwbin.SHA1 != '': write_file( ("%s.sha1" % pth), fwbin.SHA1 )
if fwbin.SHA256 != '': write_file( ("%s.sha256" % pth), fwbin.SHA256 )
if WRITE_ALL_HASHES:
if fwbin.MD5 : write_file( ("%s.md5" % pth), fwbin.MD5 )
if fwbin.SHA1 : write_file( ("%s.sha1" % pth), fwbin.SHA1 )
if fwbin.SHA256: write_file( ("%s.sha256" % pth), fwbin.SHA256 )
return ("%s.dir" % pth)
def dump_fv( fv, voln, uefi_region_path ):
fv_pth = os.path.join( uefi_region_path, "%02d_%s" % (voln, fv.Guid) )
write_file( fv_pth, fv.Image )
if fv.MD5 != '': write_file( ("%s.md5" % fv_pth), fv.MD5 )
if fv.SHA1 != '': write_file( ("%s.sha1" % fv_pth), fv.SHA1 )
if fv.SHA256 != '': write_file( ("%s.sha256" % fv_pth), fv.SHA256 )
if WRITE_ALL_HASHES:
if fv.MD5 : write_file( ("%s.md5" % fv_pth), fv.MD5 )
if fv.SHA1 : write_file( ("%s.sha1" % fv_pth), fv.SHA1 )
if fv.SHA256: write_file( ("%s.sha256" % fv_pth), fv.SHA256 )
volume_path = os.path.join( uefi_region_path, "%02d_%s.dir" % (voln, fv.Guid) )
if not os.path.exists( volume_path ): os.makedirs( volume_path )
return volume_path
@@ -244,6 +261,10 @@ def dump_section( sec, secn, parent_path, efi_file ):
efi_file = sec_fs_name
section_path = os.path.join(parent_path, sec_fs_name)
write_file( section_path, sec.Image[sec.HeaderSize:] )
if sec.MD5 : write_file( os.path.join(parent_path, "%s.md5" % sec_fs_name), sec.MD5 )
if sec.SHA1 : write_file( os.path.join(parent_path, "%s.sha1" % sec_fs_name), sec.SHA1 )
if sec.SHA256: write_file( os.path.join(parent_path, "%s.sha256" % sec_fs_name), sec.SHA256 )
else:
write_file( section_path, sec.Image[sec.HeaderSize:] )
if sec.Type == EFI_SECTION_USER_INTERFACE:
@@ -251,38 +272,63 @@ def dump_section( sec, secn, parent_path, efi_file ):
if ui_string[-4:] != '.efi': ui_string = "%s.efi" % ui_string
if efi_file is not None:
os.rename(os.path.join(parent_path, efi_file), os.path.join(parent_path, ui_string))
os.rename(os.path.join(parent_path, "%s.md5" % efi_file), os.path.join(parent_path, "%s.md5" % ui_string))
os.rename(os.path.join(parent_path, "%s.sha1" % efi_file), os.path.join(parent_path, "%s.sha1" % ui_string))
os.rename(os.path.join(parent_path, "%s.sha256" % efi_file), os.path.join(parent_path, "%s.sha256" % ui_string))
efi_file = None
section_dir_path = "%s.dir" % section_path
return sec_fs_name,section_dir_path,efi_file
def add_hashes( efi ):
def add_hashes( efi, off=0 ):
if efi.Image is None: return
hmd5 = hashlib.md5()
hmd5.update( efi.Image )
hmd5.update( efi.Image[off:] )
efi.MD5 = hmd5.hexdigest()
hsha1 = hashlib.sha1()
hsha1.update( efi.Image )
hsha1.update( efi.Image[off:] )
efi.SHA1 = hsha1.hexdigest()
hsha256 = hashlib.sha256()
hsha256.update( efi.Image )
hsha256.update( efi.Image[off:] )
efi.SHA256 = hsha256.hexdigest()
#
# Format of EFI binaries match rules (any field can be empty or missing):
# - EFI binaries are searched according to criteria defined by "match" rules.
# - EFI binaries matching exclusion criteria defined by "exclude" rules are excluded from matching.
#
# Format of the matching rules (any field can be empty or missing):
# - Individual rules are OR'ed
# - match criteria within a given rule are AND'ed
# - criteria within a given rule are AND'ed
#
# Example:
# {
# "rule00": { "guid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" }
# "rule01": { "name": "module0", "md5": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "sha1": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "sha256": "", "regexp": "" }
#
# "UEFI_rootkitX": {
# "description": "yet another UEFI implant X",
# "match": {
# "rktX_rule1" : { "guid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" },
# "rktX_rule2" : { "name": "rootkitX.efi" }
# }
# },
#
# "UEFI_vulnerabilityX": {
# "description": "yet another UEFI vulnerability X",
# "match": {
# "vulnX_rule1": { "guid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "regexp": "IAMVULNERABLE" },
# "vulnX_rule2": { "md5": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "sha1": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }
# },
# "exclude": {
# "vulnX_patched": { "md5": "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH", "sha1": "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH" }
# }
# }
#
# Above search configuration will result in a match if the following EFI module is found:
# - module with guid "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
# Above example results in a match if the following EFI binary is found:
# - with GUID "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
# OR
# - module with name "module0" AND md5 hash "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" AND sha1 hash "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
# - with name "module0" AND contains a byte sequence matching regular expression "blah"
# OR
# - with MD5 hash "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" AND SHA-1 hash "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
# Unless it's a EFI binary:
# - with MD5 hash "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH" AND SHA-1 hash "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH"
#
MATCH_NAME = 0x1
MATCH_GUID = (0x1 << 1)
@@ -291,14 +337,15 @@ MATCH_HASH_MD5 = (0x1 << 3)
MATCH_HASH_SHA1 = (0x1 << 4)
MATCH_HASH_SHA256 = (0x1 << 5)
def check_match_criteria( efi, match_criteria ):
def check_rules( efi, rules, entry_name, bLog=True ):
bfound = False
_log = ''
for k in match_criteria.keys():
for rule_name in rules.keys():
what = None
offset = 0
match_mask = 0x00000000
match_result = 0x00000000
rule = match_criteria[k]
fname = "%s.%s" % (entry_name,rule_name)
rule = rules[rule_name]
#
# Determine which criteria are defined in the current rule
#
@@ -308,20 +355,20 @@ def check_match_criteria( efi, match_criteria ):
if ('md5' in rule) and (rule['md5'] != ''): match_mask |= MATCH_HASH_MD5
if ('sha1' in rule) and (rule['sha1'] != ''): match_mask |= MATCH_HASH_SHA1
if ('sha256' in rule) and (rule['sha256'] != ''): match_mask |= MATCH_HASH_SHA256
_s = "[uefi] found matching %s (rule '%s'):" % (efi.clsname,k)
#
# Check criteria defined in the current rule against the current EFI module
#
if (match_mask & MATCH_NAME) == MATCH_NAME:
if type(efi) is EFI_SECTION and efi.ui_string == rule['name']: match_result |= MATCH_NAME
if (match_mask & MATCH_GUID) == MATCH_GUID:
if ((type(efi) is EFI_FILE) and (efi.Name == rule['guid'])) or (efi.Guid == rule['guid']): match_result |= MATCH_GUID
if (type(efi) is EFI_SECTION and efi.parentGuid == rule['guid']) or \
(efi.Guid == rule['guid']): match_result |= MATCH_GUID
if (match_mask & MATCH_REGEXP) == MATCH_REGEXP:
m = re.compile(rule['regexp']).search( efi.Image )
if m:
match_result |= MATCH_REGEXP
_log = " + regexp: bytes '%s' at offset %Xh" % (binascii.hexlify(m.group(0)),m.start())
what = binascii.hexlify(m.group(0))
offset = m.start()
if (match_mask & MATCH_HASH_MD5) == MATCH_HASH_MD5:
if efi.MD5 == rule['md5']: match_result |= MATCH_HASH_MD5
if (match_mask & MATCH_HASH_SHA1) == MATCH_HASH_SHA1:
@@ -330,20 +377,47 @@ def check_match_criteria( efi, match_criteria ):
if efi.SHA256 == rule['sha256']: match_result |= MATCH_HASH_SHA256
brule_match = ((match_result & match_mask) == match_mask)
if brule_match and bLog:
logger().log_important( "match '%s'" % fname )
if (match_result & MATCH_NAME ) == MATCH_NAME : logger().log( " name : '%s'" % rule['name'] )
if (match_result & MATCH_GUID ) == MATCH_GUID : logger().log( " GUID : {%s}" % rule['guid'] )
if (match_result & MATCH_REGEXP ) == MATCH_REGEXP : logger().log( " regexp: bytes '%s' at offset %Xh" % (what,offset) )
if (match_result & MATCH_HASH_MD5 ) == MATCH_HASH_MD5 : logger().log( " MD5 : %s" % rule['md5'] )
if (match_result & MATCH_HASH_SHA1 ) == MATCH_HASH_SHA1 : logger().log( " SHA1 : %s" % rule['sha1'] )
if (match_result & MATCH_HASH_SHA256) == MATCH_HASH_SHA256: logger().log( " SHA256: %s" % rule['sha256'] )
#
# Rules are OR'ed unless matching rule is explicitly excluded from match
#
bfound = bfound or brule_match
if brule_match:
logger().log( _s )
if (match_result & MATCH_NAME ) == MATCH_NAME : logger().log( " + name : '%s'" % rule['name'] )
if (match_result & MATCH_GUID ) == MATCH_GUID : logger().log( " + GUID : {%s}" % rule['guid'] )
if (match_result & MATCH_REGEXP ) == MATCH_REGEXP : logger().log( _log )
if (match_result & MATCH_HASH_MD5 ) == MATCH_HASH_MD5 : logger().log( " + MD5 : %s" % rule['md5'] )
if (match_result & MATCH_HASH_SHA1 ) == MATCH_HASH_SHA1 : logger().log( " + SHA1 : %s" % rule['sha1'] )
if (match_result & MATCH_HASH_SHA256) == MATCH_HASH_SHA256: logger().log( " + SHA256: %s" % rule['sha256'] )
logger().log( efi )
return bfound
def traverse_uefi_section( _uefi, fwtype, data, Size, offset, polarity, parent_offset, printall=True, dumpall=True, parent_path='', match_criteria=None, findall=True ):
def check_match_criteria(efi, criteria):
bfound = False
logger().log("[uefi] checking %s" % efi.name())
for k in criteria.keys():
entry = criteria[k]
# Check if the EFI binary is a match
if 'match' in entry:
bmatch = check_rules(efi, entry['match'], k)
if bmatch:
logger().log_important("found EFI binary matching '%s'" % k)
if 'description' in entry: logger().log(" %s" % entry['description'])
logger().log(efi)
# Check if the matched binary should be excluded
# There's no point in checking a binary against exclusions if it wasn't a match
if 'exclude' in entry:
if check_rules(efi, entry['exclude'], "%s.exclude" % k):
logger().log_important("matched EFI binary is excluded from '%s'. Skipping..." % k)
continue
# we are here if the matched binary wasn't excluded
# the binary is a final match if it matches either of search entries
bfound = bfound or bmatch
return bfound
def traverse_uefi_section( _uefi, fwtype, data, Size, offset, polarity, parent_offset, parent_guid, printall=True, dumpall=True, parent_path='', match_criteria=None, findall=True ):
found, secn, efi_file, section_dir_path = False, 0, None, ''
# caller specified non-empty matching rules so we'll need to look for specific EFI modules as we parse FVs
bsearch = (match_criteria is not None)
@@ -352,40 +426,47 @@ def traverse_uefi_section( _uefi, fwtype, data, Size, offset, polarity, parent_o
while next_offset is not None:
sec = EFI_SECTION( _off, _name, _type, _img, _hdrsz )
sec.indent = DEF_INDENT*2
sec.parentGuid = parent_guid
# pick random file name in case dumpall=False - we'll need it to decompress the section
sec_fs_name = "sect%02d_%s" % (secn, ''.join(random.choice(string.ascii_lowercase) for _ in range(4)))
if sec.Type == EFI_SECTION_USER_INTERFACE:
if sec.Type in (EFI_SECTION_PE32, EFI_SECTION_TE, EFI_SECTION_PIC, EFI_SECTION_COMPATIBILITY16):
# "leaf" executable section: update hashes and check against match criteria
add_hashes( sec, sec.HeaderSize )
if bsearch and check_match_criteria( sec, match_criteria ):
if findall: found = True
else: return True
elif sec.Type == EFI_SECTION_USER_INTERFACE:
# "leaf" UI section: update section's UI name
sec.ui_string = unicode(sec.Image[sec.HeaderSize:], "utf-16-le")[:-1]
elif sec.Type == EFI_SECTION_GUID_DEFINED:
guid0, guid1, guid2, guid3, sec.DataOffset, sec.Attributes = struct.unpack(EFI_GUID_DEFINED_SECTION, sec.Image[sec.HeaderSize:sec.HeaderSize+EFI_GUID_DEFINED_SECTION_size])
sec.Guid = guid_str(guid0, guid1, guid2, guid3)
if printall: logger().log( sec )
if dumpall: sec_fs_name,section_dir_path,efi_file = dump_section( sec, secn, parent_path, efi_file )
# only check the match rules if we need to find specific EFI module
if bsearch and check_match_criteria( sec, match_criteria ):
if findall: found = True
else: return True
# "container" sections: keep parsing
if sec.Type in (EFI_SECTION_COMPRESSION, EFI_SECTION_GUID_DEFINED, EFI_SECTION_FIRMWARE_VOLUME_IMAGE, EFI_SECTION_RAW):
if dumpall: os.makedirs( section_dir_path )
if sec.Type == EFI_SECTION_COMPRESSION:
ul, ct = struct.unpack(EFI_COMPRESSION_SECTION, sec.Image[sec.HeaderSize:sec.HeaderSize+EFI_COMPRESSION_SECTION_size])
d = decompress_section_data( _uefi, section_dir_path, sec_fs_name, sec.Image[sec.HeaderSize+EFI_COMPRESSION_SECTION_size:], ct, True )
if d:
f = traverse_uefi_section( _uefi, fwtype, d, len(d), 0, polarity, 0, printall, dumpall, section_dir_path, match_criteria, findall )
f = traverse_uefi_section( _uefi, fwtype, d, len(d), 0, polarity, 0, parent_guid, printall, dumpall, section_dir_path, match_criteria, findall )
if bsearch and f:
if findall: found = True
else: return True
elif sec.Type == EFI_SECTION_GUID_DEFINED:
guid0, guid1, guid2, guid3, sec.DataOffset, sec.Attributes = struct.unpack(EFI_GUID_DEFINED_SECTION, sec.Image[sec.HeaderSize:sec.HeaderSize+EFI_GUID_DEFINED_SECTION_size])
sec.Guid = guid_str(guid0, guid1, guid2, guid3)
if sec.Guid == EFI_CRC32_GUIDED_SECTION_EXTRACTION_PROTOCOL_GUID:
f = traverse_uefi_section( _uefi, fwtype, sec.Image[sec.DataOffset:], Size - sec.DataOffset, 0, polarity, 0, printall, dumpall, section_dir_path,match_criteria, findall )
f = traverse_uefi_section( _uefi, fwtype, sec.Image[sec.DataOffset:], Size - sec.DataOffset, 0, polarity, 0, parent_guid, printall, dumpall, section_dir_path, match_criteria, findall )
if bsearch and f:
if findall: found = True
else: return True
elif sec.Guid == LZMA_CUSTOM_DECOMPRESS_GUID:
d = decompress_section_data( _uefi, section_dir_path, sec_fs_name, sec.Image[sec.DataOffset:], 2, True )
if d:
f = traverse_uefi_section( _uefi, fwtype, d, len(d), 0, polarity, 0, printall, dumpall, section_dir_path, match_criteria, findall )
f = traverse_uefi_section( _uefi, fwtype, d, len(d), 0, polarity, 0, parent_guid, printall, dumpall, section_dir_path, match_criteria, findall )
if bsearch and f:
if findall: found = True
else: return True
@@ -399,15 +480,17 @@ def traverse_uefi_section( _uefi, fwtype, data, Size, offset, polarity, parent_o
secn += 1
return found
#
# traverse_uefi_region - searches for a specific EFI binary by its file/UI name, EFI GUID or hash
#
# Input arguments:
# _uefi - instance of chipsec.hal.uefi.UEFI class
# data - an image containing UEFI firmware volumes
# fwtype - platform specific firmware type used to detect NVRAM format (VSS, EVSA, NVAR...)
# uefi_path - root path for EFI hierarchy (used if dumpall==True)
# printall - a bool flag that tells to print EFI binaries hierarchy
# dumpall - a bool flag that tells to dump all EFI binaries onto the file system
# uefi_path - root path for EFI hierarchy (used if dumpall==True)
# match_criteria - criteria to search for sepecific node in EFI hierarchy (Name, GUID, hash, etc.)
# findall - a bool flag that tells to find all matching EFI modules in the image (rather than returning upon the first match)
#
@@ -424,9 +507,9 @@ def traverse_uefi_region( _uefi, data, fwtype, uefi_path='', printall=True, dump
if printall: logger().log( fv )
if dumpall: volume_path = dump_fv( fv, voln, uefi_path )
# only check the match rules if we need to find specific EFI module
if bsearch and check_match_criteria( fv, match_criteria ):
if findall: found = True
else: return True
#if bsearch and check_match_criteria( fv, match_criteria ):
# if findall: found = True
# else: return True
polarity = bit_set( fv.Attributes, EFI_FVB2_ERASE_POLARITY )
#
@@ -441,16 +524,12 @@ def traverse_uefi_region( _uefi, data, fwtype, uefi_path='', printall=True, dump
add_hashes( fwbin )
if printall: logger().log( fwbin )
if dumpall: fwbin_dir = dump_fw_file( fwbin, volume_path )
# only check the match rules if we need to find specific EFI module
if bsearch and check_match_criteria( fwbin, match_criteria ):
if findall: found = True
else: return True
if dumpall: os.makedirs( fwbin_dir )
if dumpall:
fwbin_dir = dump_fw_file( fwbin, volume_path )
os.makedirs( fwbin_dir )
if fwbin.Type not in (EFI_FV_FILETYPE_ALL, EFI_FV_FILETYPE_RAW, EFI_FV_FILETYPE_FFS_PAD):
f = traverse_uefi_section( _uefi, fwtype, fwbin.Image, fwbin.Size, fwbin.HeaderSize, polarity, fv.Offset + fwbin.Offset, printall, dumpall, fwbin_dir, match_criteria, findall )
f = traverse_uefi_section( _uefi, fwtype, fwbin.Image, fwbin.Size, fwbin.HeaderSize, polarity, fv.Offset + fwbin.Offset, fwbin.Guid, printall, dumpall, fwbin_dir, match_criteria, findall )
if bsearch and f:
if findall: found = True
else: return True
+36 -10
View File
@@ -1,13 +1,39 @@
{
"HT_rkloader" : { "guid": "F50248A9-2F4D-4DE9-86AE-BDA84D07A41C" },
"HT_rkloader_name" : { "name": "rkloader" },
"HT_Ntfs" : { "guid": "F50258A9-2F4D-4DA9-861E-BDA84D07A44C" },
"HT_Ntfs_name" : { "name": "Ntfs" },
"HT_app" : { "guid": "EAEA9AEC-C9C1-46E2-9D52-432AD25A9B0B" },
"HT_UEFI_Rootkit": {
"description": "HackingTeam UEFI Rootkit (http://www.intelsecurity.com/advanced-threat-research/content/data/HT-UEFI-rootkit.html)",
"match": {
"rkloader" : { "guid": "F50248A9-2F4D-4DE9-86AE-BDA84D07A41C" },
"rkloader_name" : { "name": "rkloader" },
"Ntfs" : { "guid": "F50258A9-2F4D-4DA9-861E-BDA84D07A44C" },
"Ntfs_name" : { "name": "Ntfs" },
"app" : { "guid": "EAEA9AEC-C9C1-46E2-9D52-432AD25A9B0B" }
}
},
"ThinkPwn_SmmRuntimeProtGuid" : { "regexp": "\\xA1\\x97\\x68\\xA5\\x7F\\xA7\\x00\\x46\\x84\\xDB\\x22\\xB0\\xA8\\x01\\xFA\\x9A" },
"ThinkPwn_SystemSmmRuntimeRt_name" : { "name": "SystemSmmRuntimeRt.efi" },
"ThinkPwn_SystemSmmRuntimeRt" : { "guid": "7C79AC8C-5E6C-4E3D-BA6F-C260EE7C172E" },
"ThinkPwn_SmmRuntime_name" : { "name": "SmmRuntime" },
"ThinkPwn_SmmRuntime" : { "guid": "A56897A1-A77F-4600-84DB-22B0A801FA9A" }
"ThinkPwn": {
"description": "ThinkPwn: SystemSmmRuntimeRt SMM vulnerability (http://blog.cr4.sh/2016/06/exploring-and-exploiting-lenovo.html)",
"match": {
"SystemSmmRuntimeRt": { "guid": "7C79AC8C-5E6C-4E3D-BA6F-C260EE7C172E", "regexp": "\\xA1\\x97\\x68\\xA5\\x7F\\xA7\\x00\\x46\\x84\\xDB\\x22\\xB0\\xA8\\x01\\xFA\\x9A" },
"SmmRuntime" : { "guid": "A56897A1-A77F-4600-84DB-22B0A801FA9A", "regexp": "\\xA1\\x97\\x68\\xA5\\x7F\\xA7\\x00\\x46\\x84\\xDB\\x22\\xB0\\xA8\\x01\\xFA\\x9A" }
},
"exclude": {
"Lenovo_T450s_x240" : { "md5": "2a56c7dfaefc4de482f8af4aa5344206", "sha1": "7e6d203f062c9d933f530cf36eb1d2538a37f8cc" },
"Lenovo_Yoga_X1_W550s" : { "md5": "353359a2314d2c666f03abcf38c26409", "sha1": "0ea473b6e15d54281ccebca162fae56af483a4ba" },
"Lenovo_X131e" : { "md5": "0d3b180710f4d0cf87f582a073ef1a93", "sha1": "4aa062e25eb31d6f049b0f98df5df40c566a5dc9" },
"Lenovo_S230_S430" : { "md5": "6143b779f97c7905e6f89f770579f4d3", "sha1": "486ba83b874dcd95cc95dd4e78a65f3f16a5433c" },
"Lenovo_L450" : { "md5": "efe2d006c3b9baf64cf9f716c64413e3", "sha1": "0def22266f9764f19b11fe6a68018b3428442eb3" },
"Lenovo_ThinkPad10" : { "md5": "c0544a33eab93b29092123a668c1bfb7", "sha1": "ee7641d623f1559c39d91a734254b4a34b2acea8" },
"Lenovo_ThinkServerRD340": { "md5": "0dd581382fcc07f4b61c1061083d717f", "sha1": "fdc0806cc23bacdaaf3447d114203a4dceebb81e" },
"Lenovo_ThinkServerRD640": { "md5": "0ed692f3f1ebf038f860b0d94608b573", "sha1": "283e039e7ec093dac3efa088616e549c78a1c2e1" },
"HP_Pavilion13-p_F.0A" : { "md5": "259e47de95e0b167d8deec275ad443d0", "sha1": "427bf3302d73066aee4946012d04c37bf497a416" },
"HP_Pavilion14-f_F.0B" : { "md5": "9c097da7e33d8f26ce69eb90ff89b0bd", "sha1": "3131940c4e05779170b3c3bbdb8c0d19b614f49d" },
"HP_Pavilion14-15-17_F.27" : { "md5": "6c674d78b11b1819440e027cdcb4a6e7", "sha1": "dc7424f60db6f9207372ccb3a4066de9203cc974" },
"HP_Pavilion14-n-15-n_F.70" : { "md5": "75b2bbaf9a25dcd42c39a67152bedf6b", "sha1": "2598f0dde53e56465a2b8176fe315ff42cd92014" },
"HP_ProBook4x3xs-6x65b_F.63" : { "md5": "641afcf52b5ed7abbbeb3dde40cc5bd1", "sha1": "8588fb5b230ec1c0ae12217cee3a5ee98ba112a5" },
"HP_ProBook4x4xs-4x5G1_F.64" : { "md5": "9d1e45eaaaf92d30d0edd00bcdb4213d", "sha1": "5542ad8f0f84323bb7b8416eccb0778f97d52093" },
"HP_ProBook6475b_F.65" : { "md5": "97a19f161359502c9bf2e0898fc35721", "sha1": "3619a78915abc195fc44d6c59984903c9444a1ab" },
"HP_ProBook4x5G2-6x5G1_EliteBook7x5G2": { "md5": "37a51e769fbb2df9a086ed4543380488", "sha1": "b27ac995729a64e84c444d7e3d6a5bd6f07611a3" }
}
}
}
+12 -8
View File
@@ -78,12 +78,9 @@ Examples:
Decodes 'uefi.rom' binary with UEFI firmware image and
checks for black-listed EFI modules defined in 'blacklist.json' config
None: -i and --no_driver arguments can be used in this case because the test
does not depend on the platform and no kernel driver is required when firmware image is specified
Important! This module can only detect what it knows about from its config file.
If a bad or vulnerable binary is not detected then its 'signature' needs to be added to the config.
'''
@@ -94,7 +91,7 @@ class blacklist(BaseModule):
self.uefi = chipsec.hal.uefi.UEFI( self.cs )
self.cfg_name = 'blacklist.json'
self.image = None
self.efi_blacklist = {}
self.efi_blacklist = None
def is_supported(self):
return True
@@ -102,8 +99,15 @@ class blacklist(BaseModule):
def check_blacklist( self ):
res = ModuleResult.PASSED
self.logger.log( "[*] looking for black-listed EFI binaries defined in '%s'..." % self.cfg_name )
#self.logger.log( self.efi_blacklist )
self.logger.log( "[*] searching for EFI binaries that match criteria from '%s':" % self.cfg_name )
for k in self.efi_blacklist.keys():
entry = self.efi_blacklist[k]
self.logger.log( " %-16s - %s" % (k,entry['description'] if 'description' in entry else '') )
#if 'match' in entry:
# for c in entry['match'].keys(): self.logger.log( "[*] %s" % entry['match'][c] )
#if 'exclude' in entry:
# self.logger.log( "[*] excluding binaries:" )
# for c in entry['exclude']: self.logger.log( "[*] %s" % entry['exclude'][c] )
# no need to output the entire hierarchy of EFI modules
printall = False
@@ -118,8 +122,8 @@ class blacklist(BaseModule):
self.logger.log( '' )
if found:
res = ModuleResult.FAILED
self.logger.log_failed_check("Black-listed EFI binary found in the UEFI firmware image")
res = ModuleResult.WARNING
self.logger.log_warn_check("Black-listed EFI binary found in the UEFI firmware image")
else:
self.logger.log_passed_check("Didn't find any black-listed EFI binary")
return res