Dash Core  0.12.2.1
P2P Digital Currency
security-check.py
Go to the documentation of this file.
1 #!/usr/bin/python2
2 '''
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).
7 '''
8 from __future__ import division,print_function
9 import subprocess
10 import sys
11 import os
12 
13 READELF_CMD = os.getenv('READELF', '/usr/bin/readelf')
14 OBJDUMP_CMD = os.getenv('OBJDUMP', '/usr/bin/objdump')
15 
16 def check_ELF_PIE(executable):
17  '''
18  Check for position independent executable (PIE), allowing for address space randomization.
19  '''
20  p = subprocess.Popen([READELF_CMD, '-h', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
21  (stdout, stderr) = p.communicate()
22  if p.returncode:
23  raise IOError('Error opening file')
24 
25  ok = False
26  for line in stdout.split('\n'):
27  line = line.split()
28  if len(line)>=2 and line[0] == 'Type:' and line[1] == 'DYN':
29  ok = True
30  return ok
31 
32 def get_ELF_program_headers(executable):
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()
36  if p.returncode:
37  raise IOError('Error opening file')
38  in_headers = False
39  count = 0
40  headers = []
41  for line in stdout.split('\n'):
42  if line.startswith('Program Headers:'):
43  in_headers = True
44  if line == '':
45  in_headers = False
46  if in_headers:
47  if count == 1: # header line
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')
54  elif count > 1:
55  typ = line[ofs_typ:ofs_offset].rstrip()
56  flags = line[ofs_flags:ofs_align].rstrip()
57  headers.append((typ, flags))
58  count += 1
59  return headers
60 
61 def check_ELF_NX(executable):
62  '''
63  Check that no sections are writable and executable (including the stack)
64  '''
65  have_wx = False
66  have_gnu_stack = False
67  for (typ, flags) in get_ELF_program_headers(executable):
68  if typ == 'GNU_STACK':
69  have_gnu_stack = True
70  if 'W' in flags and 'E' in flags: # section is both writable and executable
71  have_wx = True
72  return have_gnu_stack and not have_wx
73 
74 def check_ELF_RELRO(executable):
75  '''
76  Check for read-only relocations.
77  GNU_RELRO program header must exist
78  Dynamic section must have BIND_NOW flag
79  '''
80  have_gnu_relro = False
81  for (typ, flags) in get_ELF_program_headers(executable):
82  # Note: not checking flags == 'R': here as linkers set the permission differently
83  # This does not affect security: the permission flags of the GNU_RELRO program header are ignored, the PT_LOAD header determines the effective permissions.
84  # However, the dynamic linker need to write to this area so these are RW.
85  # Glibc itself takes care of mprotecting this area R after relocations are finished.
86  # See also http://permalink.gmane.org/gmane.comp.gnu.binutils/71347
87  if typ == 'GNU_RELRO':
88  have_gnu_relro = True
89 
90  have_bindnow = False
91  p = subprocess.Popen([READELF_CMD, '-d', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
92  (stdout, stderr) = p.communicate()
93  if p.returncode:
94  raise IOError('Error opening file')
95  for line in stdout.split('\n'):
96  tokens = line.split()
97  if len(tokens)>1 and tokens[1] == '(BIND_NOW)' or (len(tokens)>2 and tokens[1] == '(FLAGS)' and 'BIND_NOW' in tokens[2]):
98  have_bindnow = True
99  return have_gnu_relro and have_bindnow
100 
101 def check_ELF_Canary(executable):
102  '''
103  Check for use of stack canary
104  '''
105  p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
106  (stdout, stderr) = p.communicate()
107  if p.returncode:
108  raise IOError('Error opening file')
109  ok = False
110  for line in stdout.split('\n'):
111  if '__stack_chk_fail' in line:
112  ok = True
113  return ok
114 
116  '''
117  Get PE DllCharacteristics bits
118  '''
119  p = subprocess.Popen([OBJDUMP_CMD, '-x', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
120  (stdout, stderr) = p.communicate()
121  if p.returncode:
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)
127  return 0
128 
129 
130 def check_PE_PIE(executable):
131  '''PIE: DllCharacteristics bit 0x40 signifies dynamicbase (ASLR)'''
132  return bool(get_PE_dll_characteristics(executable) & 0x40)
133 
134 def check_PE_NX(executable):
135  '''NX: DllCharacteristics bit 0x100 signifies nxcompat (DEP)'''
136  return bool(get_PE_dll_characteristics(executable) & 0x100)
137 
138 CHECKS = {
139 'ELF': [
140  ('PIE', check_ELF_PIE),
141  ('NX', check_ELF_NX),
142  ('RELRO', check_ELF_RELRO),
143  ('Canary', check_ELF_Canary)
144 ],
145 'PE': [
146  ('PIE', check_PE_PIE),
147  ('NX', check_PE_NX)
148 ]
149 }
150 
151 def identify_executable(executable):
152  with open(filename, 'rb') as f:
153  magic = f.read(4)
154  if magic.startswith(b'MZ'):
155  return 'PE'
156  elif magic.startswith(b'\x7fELF'):
157  return 'ELF'
158  return None
159 
160 if __name__ == '__main__':
161  retval = 0
162  for filename in sys.argv[1:]:
163  try:
164  etype = identify_executable(filename)
165  if etype is None:
166  print('%s: unknown format' % filename)
167  retval = 1
168  continue
169 
170  failed = []
171  for (name, func) in CHECKS[etype]:
172  if not func(filename):
173  failed.append(name)
174  if failed:
175  print('%s: failed %s' % (filename, ' '.join(failed)))
176  retval = 1
177  except IOError:
178  print('%s: cannot open' % filename)
179  retval = 1
180  exit(retval)
181 
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)