============================================================================================================================================= | # Title : Ubuntu 25.10 Containerd Insecure Directory Permissions Vulnerability | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.1 (64 bits) | | # Vendor : https://ubuntu.com/ | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/214598/ & CVE-2024-25621, CVE-2025-64329 [+] Summary : This Proof of Concept (PoC) demonstrates and detects CVE-2024-25621, a security vulnerability in containerd caused by insecure permissions on critical runtime and data directories. Affected versions may expose container metadata and runtime artifacts due to directories being readable or writable by non-privileged users. The scanner checks the installed containerd version against known vulnerable ranges and audits permissions on sensitive directories such as /var/lib/containerd and /run/containerd/*. When verbose mode is enabled, it safely tests read/write access without performing destructive actions, with optional dry-run support. If vulnerable conditions are detected, the tool highlights potential impacts including unauthorized access, information disclosure, and possible privilege escalation, and provides clear remediation guidance such as upgrading containerd and correcting directory permissions. [+] Usage : chmod +x poc_cve_2024_25621.py ./poc_cve_2024_25621.py --verbose ./poc_cve_2024_25621.py --dry-run [+] POC : #!/usr/bin/env python3 import os import stat import sys import subprocess import argparse import re from datetime import datetime class ContainerdPoC: def __init__(self, verbose=False, dry_run=False): self.verbose = verbose self.dry_run = dry_run self.vulnerable_dirs = [ "/var/lib/containerd", "/run/containerd/io.containerd.grpc.v1.cri", "/run/containerd/io.containerd.sandbox.controller.v1.shim" ] self.vulnerable_versions = { "1.7.0": "1.7.28", "2.0.0": "2.0.6", "2.1.0": "2.1.4", "2.2.0": "2.2.0" } self.test_files = [] def log(self, message, level="INFO"): ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S") print(f"[{ts}] [{level}] {message}") def check_permissions(self, path): try: if not os.path.exists(path): return None, "Directory does not exist" st = os.stat(path) perms = stat.S_IMODE(st.st_mode) perm_str = oct(perms)[-3:] group_write = bool(perms & stat.S_IWGRP) other_write = bool(perms & stat.S_IWOTH) group_read = bool(perms & stat.S_IRGRP) other_read = bool(perms & stat.S_IROTH) vulnerable = group_write or other_write return { "path": path, "permissions": perm_str, "group_write": group_write, "other_write": other_write, "group_read": group_read, "other_read": other_read, "vulnerable": vulnerable, "uid": st.st_uid, "gid": st.st_gid } except Exception as e: return None, str(e) def check_containerd_version(self): try: r = subprocess.run( ["containerd", "--version"], capture_output=True, text=True, timeout=5 ) if r.returncode == 0: m = re.search(r'v(\d+\.\d+\.\d+)', r.stdout) if m: return m.group(1), r.stdout.strip() return None, "Cannot determine version" except FileNotFoundError: return None, "containerd not found" except Exception as e: return None, str(e) def test_directory_access(self, directory): tests = [] try: os.listdir(directory) tests.append(("LIST", "SUCCESS", "Directory readable")) except PermissionError: tests.append(("LIST", "FAILED", "Permission denied")) except Exception as e: tests.append(("LIST", "ERROR", str(e))) if self.dry_run: tests.append(("WRITE", "SKIPPED", "Dry-run enabled")) return tests test_file = os.path.join( directory, f".test_cve_2024_25621_{os.getpid()}" ) try: with open(test_file, "w") as f: f.write("PoC Test\n") self.test_files.append(test_file) tests.append(("WRITE", "SUCCESS", "File created")) with open(test_file, "r") as f: f.read() tests.append(("READ", "SUCCESS", "File read")) except PermissionError: tests.append(("WRITE", "FAILED", "Permission denied")) except Exception as e: tests.append(("WRITE", "ERROR", str(e))) return tests def compare_versions(self, v1, v2): def normalize(v): v = re.sub(r'[-_].*$', '', v) return [int(x) for x in v.split('.')] a = normalize(v1) b = normalize(v2) return (a > b) - (a < b) def cleanup(self): for f in self.test_files: try: if os.path.exists(f): os.remove(f) except: pass def run_scan(self): print("=" * 60) print("Containerd CVE-2024-25621 Vulnerability Scanner By indoushka") print("=" * 60) print("\n[1] Checking containerd version...") version, info = self.check_containerd_version() if version: print(f" Version: {version}") vulnerable = False for min_v, max_v in self.vulnerable_versions.items(): if self.compare_versions(min_v, version) <= 0 and \ self.compare_versions(version, max_v) <= 0: vulnerable = True print(f" VULNERABLE ({min_v} → {max_v})") break if not vulnerable: print(" Version appears patched") else: print(f" {info}") print("\n[2] Checking directory permissions...") found = [] for d in self.vulnerable_dirs: r, e = self.check_permissions(d) if r: print(f"\n {d}") print(f" Perms: {r['permissions']} | Vulnerable: {r['vulnerable']}") if r["vulnerable"]: found.append(d) if self.verbose: for t in self.test_directory_access(d): print(f" {t[0]}: {t[1]} - {t[2]}") else: print(f"\n {d} - {e}") print("\n" + "=" * 60) if found: print(" SYSTEM IS VULNERABLE") for d in found: print(f" - {d}") else: print(" SYSTEM APPEARS SECURE") print("=" * 60) def main(): p = argparse.ArgumentParser() p.add_argument("-v", "--verbose", action="store_true") p.add_argument("-d", "--dry-run", action="store_true") args = p.parse_args() poc = ContainerdPoC(args.verbose, args.dry_run) try: poc.run_scan() finally: poc.cleanup() if __name__ == "__main__": main() Greetings to :============================================================ jericho * Larry W. Cashdollar * r00t * Malvuln (John Page aka hyp3rlinx)*| ==========================================================================