3 Perform basic ELF security checks on a series of executables. 4 Exit status will be 0 if succesful, and the program will be silent. 5 Otherwise the exit status will be 1 and it will log which executables failed which checks. 6 Needs `readelf` (for ELF) and `objdump` (for PE). 8 from __future__
import division,print_function
13 READELF_CMD = os.getenv(
'READELF',
'/usr/bin/readelf')
14 OBJDUMP_CMD = os.getenv(
'OBJDUMP',
'/usr/bin/objdump')
18 Check for position independent executable (PIE), allowing for address space randomization. 20 p = subprocess.Popen([READELF_CMD,
'-h',
'-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
21 (stdout, stderr) = p.communicate()
23 raise IOError(
'Error opening file')
26 for line
in stdout.split(
'\n'):
28 if len(line)>=2
and line[0] ==
'Type:' and line[1] ==
'DYN':
33 '''Return type and flags for ELF program headers''' 34 p = subprocess.Popen([READELF_CMD,
'-l',
'-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
35 (stdout, stderr) = p.communicate()
37 raise IOError(
'Error opening file')
41 for line
in stdout.split(
'\n'):
42 if line.startswith(
'Program Headers:'):
48 ofs_typ = line.find(
'Type')
49 ofs_offset = line.find(
'Offset')
50 ofs_flags = line.find(
'Flg')
51 ofs_align = line.find(
'Align')
52 if ofs_typ == -1
or ofs_offset == -1
or ofs_flags == -1
or ofs_align == -1:
53 raise ValueError(
'Cannot parse elfread -lW output')
55 typ = line[ofs_typ:ofs_offset].rstrip()
56 flags = line[ofs_flags:ofs_align].rstrip()
57 headers.append((typ, flags))
63 Check that no sections are writable and executable (including the stack) 66 have_gnu_stack =
False 68 if typ ==
'GNU_STACK':
70 if 'W' in flags
and 'E' in flags:
72 return have_gnu_stack
and not have_wx
76 Check for read-only relocations. 77 GNU_RELRO program header must exist 78 Dynamic section must have BIND_NOW flag 80 have_gnu_relro =
False 87 if typ ==
'GNU_RELRO':
91 p = subprocess.Popen([READELF_CMD,
'-d',
'-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
92 (stdout, stderr) = p.communicate()
94 raise IOError(
'Error opening file')
95 for line
in stdout.split(
'\n'):
97 if len(tokens)>1
and tokens[1] ==
'(BIND_NOW)' or (len(tokens)>2
and tokens[1] ==
'(FLAGS)' and 'BIND_NOW' in tokens[2]):
99 return have_gnu_relro
and have_bindnow
103 Check for use of stack canary 105 p = subprocess.Popen([READELF_CMD,
'--dyn-syms',
'-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
106 (stdout, stderr) = p.communicate()
108 raise IOError(
'Error opening file')
110 for line
in stdout.split(
'\n'):
111 if '__stack_chk_fail' in line:
117 Get PE DllCharacteristics bits 119 p = subprocess.Popen([OBJDUMP_CMD,
'-x', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
120 (stdout, stderr) = p.communicate()
122 raise IOError(
'Error opening file')
123 for line
in stdout.split(
'\n'):
124 tokens = line.split()
125 if len(tokens)>=2
and tokens[0] ==
'DllCharacteristics':
126 return int(tokens[1],16)
131 '''PIE: DllCharacteristics bit 0x40 signifies dynamicbase (ASLR)''' 135 '''NX: DllCharacteristics bit 0x100 signifies nxcompat (DEP)''' 140 (
'PIE', check_ELF_PIE),
141 (
'NX', check_ELF_NX),
142 (
'RELRO', check_ELF_RELRO),
143 (
'Canary', check_ELF_Canary)
146 (
'PIE', check_PE_PIE),
152 with open(filename,
'rb')
as f:
154 if magic.startswith(b
'MZ'):
156 elif magic.startswith(b
'\x7fELF'):
160 if __name__ ==
'__main__':
162 for filename
in sys.argv[1:]:
166 print(
'%s: unknown format' % filename)
171 for (name, func)
in CHECKS[etype]:
172 if not func(filename):
175 print(
'%s: failed %s' % (filename,
' '.join(failed)))
178 print(
'%s: cannot open' % filename)
def check_ELF_NX(executable)
def get_PE_dll_characteristics(executable)
def identify_executable(executable)
def check_PE_NX(executable)
def get_ELF_program_headers(executable)
def check_ELF_RELRO(executable)
def check_PE_PIE(executable)
def check_ELF_Canary(executable)
def check_ELF_PIE(executable)