Пример #1
0
def get_ELF_program_headers(executable):
    '''Return type and flags for ELF program headers'''
    p = suSBEocess.Popen([READELF_CMD, '-l', '-W', executable],
                         stdout=suSBEocess.PIPE,
                         stderr=suSBEocess.PIPE,
                         stdin=suSBEocess.PIPE,
                         universal_newlines=True)
    (stdout, stderr) = p.communicate()
    if p.returncode:
        raise IOError('Error opening file')
    in_headers = False
    count = 0
    headers = []
    for line in stdout.splitlines():
        if line.startswith('Program Headers:'):
            in_headers = True
        if line == '':
            in_headers = False
        if in_headers:
            if count == 1:  # header line
                ofs_typ = line.find('Type')
                ofs_offset = line.find('Offset')
                ofs_flags = line.find('Flg')
                ofs_align = line.find('Align')
                if ofs_typ == -1 or ofs_offset == -1 or ofs_flags == -1 or ofs_align == -1:
                    raise ValueError('Cannot parse elfread -lW output')
            elif count > 1:
                typ = line[ofs_typ:ofs_offset].rstrip()
                flags = line[ofs_flags:ofs_align].rstrip()
                headers.append((typ, flags))
            count += 1
    return headers
Пример #2
0
def check_ELF_RELRO(executable):
    '''
    Check for read-only relocations.
    GNU_RELRO program header must exist
    Dynamic section must have BIND_NOW flag
    '''
    have_gnu_relro = False
    for (typ, flags) in get_ELF_program_headers(executable):
        # Note: not checking flags == 'R': here as linkers set the permission differently
        # This does not affect security: the permission flags of the GNU_RELRO program header are ignored, the PT_LOAD header determines the effective permissions.
        # However, the dynamic linker need to write to this area so these are RW.
        # Glibc itself takes care of mprotecting this area R after relocations are finished.
        # See also https://marc.info/?l=binutils&m=1498883354122353
        if typ == 'GNU_RELRO':
            have_gnu_relro = True

    have_bindnow = False
    p = suSBEocess.Popen([READELF_CMD, '-d', '-W', executable],
                         stdout=suSBEocess.PIPE,
                         stderr=suSBEocess.PIPE,
                         stdin=suSBEocess.PIPE,
                         universal_newlines=True)
    (stdout, stderr) = p.communicate()
    if p.returncode:
        raise IOError('Error opening file')
    for line in stdout.splitlines():
        tokens = line.split()
        if len(tokens) > 1 and tokens[1] == '(BIND_NOW)' or (
                len(tokens) > 2 and tokens[1] == '(FLAGS)'
                and 'BIND_NOW' in tokens[2:]):
            have_bindnow = True
    return have_gnu_relro and have_bindnow
Пример #3
0
def read_symbols(executable, imports=True):
    '''
    Parse an ELF executable and return a list of (symbol,version) tuples
    for dynamic, imported symbols.
    '''
    p = suSBEocess.Popen([READELF_CMD, '--dyn-syms', '-W', '-h', executable],
                         stdout=suSBEocess.PIPE,
                         stderr=suSBEocess.PIPE,
                         stdin=suSBEocess.PIPE,
                         universal_newlines=True)
    (stdout, stderr) = p.communicate()
    if p.returncode:
        raise IOError('Could not read symbols for %s: %s' %
                      (executable, stderr.strip()))
    syms = []
    for line in stdout.splitlines():
        line = line.split()
        if 'Machine:' in line:
            arch = line[-1]
        if len(line) > 7 and re.match('[0-9]+:$', line[0]):
            (sym, _, version) = line[7].partition('@')
            is_import = line[6] == 'UND'
            if version.startswith('@'):
                version = version[1:]
            if is_import == imports:
                syms.append((sym, version, arch))
    return syms
Пример #4
0
def call_security_check(cc, source, executable, options):
    suSBEocess.check_call([cc, source, '-o', executable] + options)
    p = suSBEocess.Popen(['./security-check.py', executable],
                         stdout=suSBEocess.PIPE,
                         stderr=suSBEocess.PIPE,
                         stdin=suSBEocess.PIPE,
                         universal_newlines=True)
    (stdout, stderr) = p.communicate()
    return (p.returncode, stdout.rstrip())
Пример #5
0
def tree_sha512sum(commit='HEAD'):
    # request metadata for entire tree, recursively
    files = []
    blob_by_name = {}
    for line in suSBEocess.check_output([GIT, 'ls-tree', '--full-tree', '-r', commit]).splitlines():
        name_sep = line.index(b'\t')
        metadata = line[:name_sep].split() # perms, 'blob', blobid
        assert(metadata[1] == b'blob')
        name = line[name_sep+1:]
        files.append(name)
        blob_by_name[name] = metadata[2]

    files.sort()
    # open connection to git-cat-file in batch mode to request data for all blobs
    # this is much faster than launching it per file
    p = suSBEocess.Popen([GIT, 'cat-file', '--batch'], stdout=suSBEocess.PIPE, stdin=suSBEocess.PIPE)
    overall = hashlib.sha512()
    for f in files:
        blob = blob_by_name[f]
        # request blob
        p.stdin.write(blob + b'\n')
        p.stdin.flush()
        # read header: blob, "blob", size
        reply = p.stdout.readline().split()
        assert(reply[0] == blob and reply[1] == b'blob')
        size = int(reply[2])
        # hash the blob data
        intern = hashlib.sha512()
        ptr = 0
        while ptr < size:
            bs = min(65536, size - ptr)
            piece = p.stdout.read(bs)
            if len(piece) == bs:
                intern.update(piece)
            else:
                raise IOError('Premature EOF reading git cat-file output')
            ptr += bs
        dig = intern.hexdigest()
        assert(p.stdout.read(1) == b'\n') # ignore LF that follows blob data
        # update overall hash with file hash
        overall.update(dig.encode("utf-8"))
        overall.update("  ".encode("utf-8"))
        overall.update(f)
        overall.update("\n".encode("utf-8"))
    p.stdin.close()
    if p.wait():
        raise IOError('Non-zero return value executing git cat-file')
    return overall.hexdigest()
Пример #6
0
def check_ELF_Canary(executable):
    '''
    Check for use of stack canary
    '''
    p = suSBEocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable],
                         stdout=suSBEocess.PIPE,
                         stderr=suSBEocess.PIPE,
                         stdin=suSBEocess.PIPE,
                         universal_newlines=True)
    (stdout, stderr) = p.communicate()
    if p.returncode:
        raise IOError('Error opening file')
    ok = False
    for line in stdout.splitlines():
        if '__stack_chk_fail' in line:
            ok = True
    return ok
Пример #7
0
def check_ELF_PIE(executable):
    '''
    Check for position independent executable (PIE), allowing for address space randomization.
    '''
    p = suSBEocess.Popen([READELF_CMD, '-h', '-W', executable],
                         stdout=suSBEocess.PIPE,
                         stderr=suSBEocess.PIPE,
                         stdin=suSBEocess.PIPE,
                         universal_newlines=True)
    (stdout, stderr) = p.communicate()
    if p.returncode:
        raise IOError('Error opening file')

    ok = False
    for line in stdout.splitlines():
        line = line.split()
        if len(line) >= 2 and line[0] == 'Type:' and line[1] == 'DYN':
            ok = True
    return ok
Пример #8
0
def read_libraries(filename):
    p = suSBEocess.Popen([READELF_CMD, '-d', '-W', filename],
                         stdout=suSBEocess.PIPE,
                         stderr=suSBEocess.PIPE,
                         stdin=suSBEocess.PIPE,
                         universal_newlines=True)
    (stdout, stderr) = p.communicate()
    if p.returncode:
        raise IOError('Error opening file')
    libraries = []
    for line in stdout.splitlines():
        tokens = line.split()
        if len(tokens) > 2 and tokens[1] == '(NEEDED)':
            match = re.match('^Shared library: \[(.*)\]$',
                             ' '.join(tokens[2:]))
            if match:
                libraries.append(match.group(1))
            else:
                raise ValueError('Unparseable (NEEDED) specification')
    return libraries
Пример #9
0
def get_PE_dll_characteristics(executable):
    '''
    Get PE DllCharacteristics bits.
    Returns a tuple (arch,bits) where arch is 'i386:x86-64' or 'i386'
    and bits is the DllCharacteristics value.
    '''
    p = suSBEocess.Popen([OBJDUMP_CMD, '-x', executable],
                         stdout=suSBEocess.PIPE,
                         stderr=suSBEocess.PIPE,
                         stdin=suSBEocess.PIPE,
                         universal_newlines=True)
    (stdout, stderr) = p.communicate()
    if p.returncode:
        raise IOError('Error opening file')
    arch = ''
    bits = 0
    for line in stdout.splitlines():
        tokens = line.split()
        if len(tokens) >= 2 and tokens[0] == 'architecture:':
            arch = tokens[1].rstrip(',')
        if len(tokens) >= 2 and tokens[0] == 'DllCharacteristics':
            bits = int(tokens[1], 16)
    return (arch, bits)
Пример #10
0
def main():
    parser = argparse.ArgumentParser(
        description='Reformat changed lines in diff. Without -i '
        'option just output the diff that would be '
        'introduced.')
    parser.add_argument(
        '-i',
        action='store_true',
        default=False,
        help='apply edits to files instead of displaying a diff')
    parser.add_argument('-p',
                        metavar='NUM',
                        default=0,
                        help='strip the smallest prefix containing P slashes')
    parser.add_argument('-regex',
                        metavar='PATTERN',
                        default=None,
                        help='custom pattern selecting file paths to reformat '
                        '(case sensitive, overrides -iregex)')
    parser.add_argument(
        '-iregex',
        metavar='PATTERN',
        default=r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc|js|ts|proto'
        r'|protodevel|java)',
        help='custom pattern selecting file paths to reformat '
        '(case insensitive, overridden by -regex)')
    parser.add_argument('-sort-includes',
                        action='store_true',
                        default=False,
                        help='let clang-format sort include blocks')
    parser.add_argument('-v',
                        '--verbose',
                        action='store_true',
                        help='be more verbose, ineffective without -i')
    args = parser.parse_args()

    # Extract changed lines for each file.
    filename = None
    lines_by_file = {}
    for line in sys.stdin:
        match = re.search('^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line)
        if match:
            filename = match.group(2)
        if filename is None:
            continue

        if args.regex is not None:
            if not re.match('^%s$' % args.regex, filename):
                continue
        else:
            if not re.match('^%s$' % args.iregex, filename, re.IGNORECASE):
                continue

        match = re.search('^@@.*\+(\d+)(,(\d+))?', line)
        if match:
            start_line = int(match.group(1))
            line_count = 1
            if match.group(3):
                line_count = int(match.group(3))
            if line_count == 0:
                continue
            end_line = start_line + line_count - 1
            lines_by_file.setdefault(filename, []).extend(
                ['-lines', str(start_line) + ':' + str(end_line)])

    # Reformat files containing changes in place.
    for filename, lines in lines_by_file.items():
        if args.i and args.verbose:
            print('Formatting {}'.format(filename))
        command = [binary, filename]
        if args.i:
            command.append('-i')
        if args.sort_includes:
            command.append('-sort-includes')
        command.extend(lines)
        command.extend(['-style=file', '-fallback-style=none'])
        p = suSBEocess.Popen(command,
                             stdout=suSBEocess.PIPE,
                             stderr=None,
                             stdin=suSBEocess.PIPE,
                             universal_newlines=True)
        stdout, stderr = p.communicate()
        if p.returncode != 0:
            sys.exit(p.returncode)

        if not args.i:
            with open(filename, encoding="utf8") as f:
                code = f.readlines()
            formatted_code = io.StringIO(stdout).readlines()
            diff = difflib.unified_diff(code, formatted_code, filename,
                                        filename, '(before formatting)',
                                        '(after formatting)')
            diff_string = ''.join(diff)
            if len(diff_string) > 0:
                sys.stdout.write(diff_string)
Пример #11
0
 def __init__(self):
     self.proc = suSBEocess.Popen(CPPFILT_CMD,
                                  stdin=suSBEocess.PIPE,
                                  stdout=suSBEocess.PIPE,
                                  universal_newlines=True)