Skip to content

Instantly share code, notes, and snippets.

@wdormann
Last active July 16, 2024 14:53
Show Gist options
  • Save wdormann/544a9e89fe35a84135e58eb5c7b1721d to your computer and use it in GitHub Desktop.
Save wdormann/544a9e89fe35a84135e58eb5c7b1721d to your computer and use it in GitHub Desktop.
Get entropy of loaded things (ASLR effectiveness) in Linux
import subprocess
import re
import time
import os
from collections import defaultdict
# Function to get the kernel version
def get_kernel():
result = subprocess.Popen(['uname', '-a'], stdout=subprocess.PIPE)
output, _ = result.communicate()
return output.decode('utf-8')
def is_file_2mb_or_larger(file_path):
file_size = os.path.getsize(file_path) # Get the file size in bytes
size_limit = 2 * 1024 * 1024 # 2MB in bytes
if file_size >= size_limit:
return True
else:
return False
def get_entropy_mask(addresses):
if len(addresses) < 2:
return '00' * 4 # 32-bit mask
# Convert addresses to binary and determine the changing bits
address_binaries = ["{0:032b}".format(address) for address in addresses]
num_bits = len(address_binaries[0])
changing_bits = [False] * num_bits
for i in range(num_bits):
bit_set = set(addr[i] for addr in address_binaries)
if len(bit_set) > 1:
changing_bits[i] = True
# Construct the entropy mask
mask = ['0'] * num_bits
for i, bit in enumerate(changing_bits):
if bit:
mask[i] = '1'
# Convert the binary mask to hex bytes
hex_mask = ''
for i in range(0, num_bits, 8):
byte = ''.join(mask[i:i+8])
hex_mask += '{:02x}'.format(int(byte, 2))
return hex_mask
# Function to run the command to get /proc/self/maps
def run_cat_proc_self_maps():
result = subprocess.Popen(['cat', '/proc/self/maps'], stdout=subprocess.PIPE)
output, _ = result.communicate()
return output.decode('utf-8')
# Function to run the command to get /proc/self/maps
def libc_large_enough():
result = subprocess.Popen(['cat', '/proc/self/maps'], stdout=subprocess.PIPE)
output, _ = result.communicate()
output = output.decode('utf-8')
for line in output.splitlines():
if '/libc.' in line or '/libc-' in line:
pattern = re.compile(r'([0-9a-f]+)-[0-9a-f]+ \S+ \S+ \S+ \S+ +(\S+)')
match = pattern.match(line)
if match:
address, library = match.groups()
if is_file_2mb_or_larger(library):
return (library, True)
else:
return (library, False)
# Function to parse the output of /proc/self/maps
def parse_maps_output(output):
library_addresses = defaultdict(list)
pattern = re.compile(r'([0-9a-f]+)-[0-9a-f]+ \S+ \S+ \S+ \S+ +(\S+)')
seen_libraries = set()
for line in output.splitlines():
match = pattern.match(line)
if match:
address, library = match.groups()
if library != '[vdso]' and library != '[vsyscall]' and library != '[vectors]' and library not in seen_libraries:
library_addresses[library].append(int(address, 16))
seen_libraries.add(library)
return library_addresses
# Function to calculate the bit-level entropy
def calculate_bit_entropy(addresses):
if len(addresses) < 2:
return 0
# Convert addresses to binary and determine the changing bits
address_binaries = ["{0:032b}".format(address) for address in addresses]
num_bits = len(address_binaries[0])
changing_bits = [False] * num_bits
for i in range(num_bits):
bit_set = set(addr[i] for addr in address_binaries)
if len(bit_set) > 1:
changing_bits[i] = True
entropy = sum(changing_bits)
return entropy
# Main function to run the script
def main(iterations=1000, interval=0.01):
print(get_kernel())
(library, libclarge) = libc_large_enough()
if(not libclarge):
print('\033[93mWarning: %s is not large enough to trigger ASLRn\'t with this test.\033[0m' % library)
all_library_addresses = defaultdict(list)
for _ in range(iterations):
maps_output = run_cat_proc_self_maps()
current_addresses = parse_maps_output(maps_output)
for library, addresses in current_addresses.items():
all_library_addresses[library].extend(addresses)
time.sleep(interval)
for library, addresses in all_library_addresses.items():
addresses = sorted(set(addresses))
entropy = calculate_bit_entropy(addresses)
mask = get_entropy_mask(addresses)
if entropy == 0:
if os.path.basename(library) == 'cat':
print('\033[91mMapped item: {0}, Entropy: {1} bits (not a PIE executable?)\033[0m'.format(library, entropy))
elif os.path.basename(library).startswith('libc'):
print('\033[91mMapped item: {0}, Entropy: {1} bits (Vulnerable to ASLRn\'t)\033[0m'.format(library, entropy))
else:
print('\033[91mMapped item: {0}, Entropy: {1} bits\033[0m'.format(library, entropy))
else:
print('Mapped item: {0}, Entropy: {1} bits, Mask: {2}'.format(library, entropy, mask))
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment