4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / try_download_libc.py PY
# Attempt to download a full libc file from libc.rip given a partial libc file containing the build id. Then try to print libc version.
# Example: python try_download_libc.py page1_img5.bmp.extracted

import struct
import sys
import pwnlib.libcdb
from argparse import ArgumentParser
import re

# Constant for the standard GNU Build ID Note Type
NT_GNU_BUILD_ID = 3 

def extract_build_id(file_path: str) -> str | None:
    """
    Manually searches the raw bytes of a file for the GNU Build ID note structure,
      checking the Note Type (n_type) to ensure it's a Build ID (type 3).
    """
    try:
        # 1. Read the entire file content
        with open(file_path, 'rb') as f:
            data = f.read()

        # 2. Define the magic marker for the GNU note name ('GNU\x00')
        GNU_NAME = b'GNU\x00'
        
        # 3. Iterate and search for ALL occurrences of the marker
        search_start_index = 0
        while True:
            try:
                # Find the next occurrence of the 'GNU\x00' marker
                name_offset = data.index(GNU_NAME, search_start_index)
            except ValueError:
                break

            # 4. The Note Header (12 bytes) precedes the name.
            header_offset = name_offset - 12
            
            if header_offset < 0:
                # Cannot be a full note header if it's too close to the file start
                search_start_index = name_offset + 1
                continue

            # 5. Unpack the Note Header (n_namesz, n_descsz, n_type)
            # Assuming 32-bit little-endian integers (<III)
            try:
                n_namesz, n_descsz, n_type = struct.unpack(
                    '<III', data[header_offset:header_offset + 12]
                )
            except struct.error:
                # Not enough data for a header
                search_start_index = name_offset + 1
                continue
            
            if n_namesz == 4 and n_type == NT_GNU_BUILD_ID:
                
                # 7. Extract the Build ID data (immediately following the name)
                build_id_data = data[name_offset + n_namesz : name_offset + n_namesz + n_descsz]
                return build_id_data.hex()
            
            # If the note isn't a Build ID, continue searching after the current position
            search_start_index = name_offset + 1
            
        # If the loop finishes without finding the Build ID
        print("Error: No note matching the standard GNU Build ID (type 3) structure was found.")
        return None

    except Exception as e:
        print(f"An error occurred during manual extraction: {e}", file=sys.stderr)
        return None
    
def print_libc_version(libc_path: str):
    """
    Reads the raw file content and uses a regex anchor on the "GNU C Library" string to extract the full version
    """
    try:
        # 1. Read the file content
        with open(libc_path, 'rb') as f:
            data = f.read()

        version_pattern = re.compile(b'(GNU C Library.*?)\x00', re.DOTALL)
        match = version_pattern.search(data)

        if match:
            full_version_string = match.group(1)[:100].decode('ascii', errors='ignore')
            print(f"Extracted libc version: {full_version_string}")
        else:
            print("Error: The complete 'GNU C Library' string followed by a version was not found.")

    except Exception as e:
        print(f"Error occurred getting libc version: {e}", file=sys.stderr)

parser = ArgumentParser(description="Download full libc file from libc.rip given partial libc file containing build id")
parser.add_argument('file', help='input file path containing partial libc file with build id')
parser.add_argument('-o', '--output', help='output file path to save downloaded libc', default='libc')
args = parser.parse_args()

build_id = extract_build_id(args.file)
    
if build_id:
    print(f"Build ID: {build_id}")
else:
    print("Failed to extract Build ID.", file=sys.stderr)
    sys.exit(1)

data = pwnlib.libcdb.provider_libc_rip(build_id, 'build_id')
if not data:
    print("Failed to download libc from libc.rip", file=sys.stderr)
    sys.exit(1)
with open(args.output, 'wb') as f:
    f.write(data)
print(f"Downloaded libc file saved to: {args.output}")

print_libc_version(args.output)