Last active
July 16, 2024 14:53
-
-
Save wdormann/544a9e89fe35a84135e58eb5c7b1721d to your computer and use it in GitHub Desktop.
Get entropy of loaded things (ASLR effectiveness) in Linux
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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