Skip to main content

drm-pentesting-toolkit

Plugin-based DRM pentesting toolkit with GUI for device management, PSSH extraction, license key retrieval, and content downloading

Informações da origem

Repositório
reason-machines/security-skills
Última atividade na origem
12 de junho de 2026 às 20:50
Idioma detectado do SKILL.md
inglês
Estrelas
12
Forks
1

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
drm-pentesting-toolkit
description
Plugin-based DRM pentesting toolkit with GUI for device management, PSSH extraction, license key retrieval, and content downloading
triggers
["extract DRM keys from streaming content","test Widevine or PlayReady device files","parse PSSH from MPD manifest","download encrypted streaming content","validate DRM device credentials","create DRM pentesting plugin","chain DRM pentesting operations","extract license server keys"]
# DRM Pentesting Toolkit Skill > Skill by [ara.so](https://ara.so) — Security Skills collection. This skill enables AI agents to assist with DRM pentesting using a modular, plugin-based toolkit. The project provides a CustomTkinter GUI for managing DRM devices (Widevine `.wvd`, PlayReady `.prd`), extracting PSSH from MPD manifests, communicating with license servers, and downloading protected content. ## What It Does The DRM Pentesting Toolkit is a comprehensive framework for: - Loading and validating Widevine/PlayReady device files - Parsing PSSH (Protection System Specific Header) data from DASH/MPD manifests - Extracting decryption keys from license servers - Generating M3U playlists with embedded keys - Downloading and decrypting content via N_m3u8DL-RE - Managing credentials, proxies, and HTTP configurations - Chaining multiple operations through a plugin stack system ## Installation ```bash # Clone the repository git clone https://github.com/fairy-root/drm-pentesting-toolkit.git cd drm-pentesting-toolkit # Install dependencies pip install -r requirements.txt # Set up directory structure mkdir -p devices/widevine devices/playready credentials proxies N_m3u8DL-RE OUTPUT ``` **Required dependencies** (from requirements.txt): ``` customtkinter>=5.2.0 pywidevine>=1.8.0 pyplayready>=1.3.0 requests>=2.31.0 Brotli>=1.1.0 protobuf>=4.25.0 ``` **Additional setup**: 1. Place DRM device files in `devices/widevine/*.wvd` or `devices/playready/*.prd` 2. Download N_m3u8DL-RE and shaka-packager executables to `N_m3u8DL-RE/` 3. Create credential files in `credentials/` (format: `username:password`) 4. Create proxy files in `proxies/` (format: `host:port:user:pass`) ## Running the Application ```bash # Launch GUI python main.py ``` The GUI provides tabs for: - **MPD Data**: Configure manifest URLs, headers, cookies - **License Data**: Set license server URLs and authentication - **Logs & Output**: Monitor plugin execution - **Downloads**: Track N_m3u8DL-RE download progress ## Core Architecture ### Plugin System Plugins are Python files in `plugins/` that define a `run()` function. The plugin manager uses introspection to: 1. Auto-detect required parameters 2. Map UI context to function arguments 3. Pass results between chained plugins **Plugin context variables** available: ```python device_path: str # Selected device file path device_type: str # 'widevine' or 'playready' cdm: object # CDM instance (Widevine/PlayReady) session_id: bytes # Active CDM session ID pssh: str # Extracted PSSH box license_url: str # License server URL mpd_url: str # MPD manifest URL mpd_headers: dict # HTTP headers for MPD requests mpd_cookies: dict # Cookies for MPD requests license_headers: dict # HTTP headers for license requests license_cookies: dict # Cookies for license requests license_post_data: str # POST data for license requests keys: list # Extracted decryption keys credentials: dict # Exposed credentials (if enabled) proxy_line: str # Selected proxy configuration search_query: str # User search input ``` ### Creating a Plugin ```python # plugins/my_custom_plugin.py def run(mpd_url, mpd_headers, log_callback=None): """ Custom plugin that fetches and analyzes MPD manifest Args: mpd_url: The DASH manifest URL mpd_headers: Dictionary of HTTP headers log_callback: Function to log messages to GUI Returns: dict: Context updates for next plugin """ import requests if log_callback: log_callback(f"Fetching MPD from: {mpd_url}") try: response = requests.get(mpd_url, headers=mpd_headers, timeout=10) response.raise_for_status() # Parse MPD content mpd_content = response.text if log_callback: log_callback(f"MPD size: {len(mpd_content)} bytes") # Return context for next plugin return { 'mpd_content': mpd_content, 'mpd_size': len(mpd_content) } except Exception as e: if log_callback: log_callback(f"Error: {str(e)}") raise ``` ## Common Workflows ### Key Extraction Workflow ```python # Plugin sequence: # 1. Load device # 2. Parse PSSH # 3. Send license request # 4. Extract keys # Example: 1_load_devices.py def run(device_path, device_type, log_callback=None): from pywidevine.cdm import Cdm as WidevineCdm from pywidevine.device import Device as WidevineDevice if device_type == 'widevine': device = WidevineDevice.load(device_path) cdm = WidevineCdm.from_device(device) session_id = cdm.open() if log_callback: log_callback(f"Widevine session opened: {session_id.hex()}") return { 'cdm': cdm, 'session_id': session_id, 'device_security_level': device.security_level } # Example: 2_parse_pssh.py def run(mpd_url, mpd_headers, mpd_cookies, http_method='GET', log_callback=None): import requests import re from base64 import b64decode response = requests.request( method=http_method, url=mpd_url, headers=mpd_headers, cookies=mpd_cookies, timeout=15 ) # Extract PSSH from MPD pssh_pattern = r'<cenc:pssh>([A-Za-z0-9+/=]+)</cenc:pssh>' matches = re.findall(pssh_pattern, response.text) if matches: pssh = matches[0] if log_callback: log_callback(f"PSSH extracted: {pssh[:50]}...") return {'pssh': pssh} raise ValueError("No PSSH found in MPD") # Example: 4_send_license.py def run(cdm, session_id, pssh, license_url, license_headers, license_cookies, license_post_data, log_callback=None): import requests from base64 import b64decode, b64encode # Generate license challenge challenge = cdm.get_license_challenge(session_id, b64decode(pssh)) # Prepare license request payload = license_post_data.replace('{CHALLENGE}', b64encode(challenge).decode()) response = requests.post( license_url, headers=license_headers, cookies=license_cookies, data=payload ) # Parse license cdm.parse_license(session_id, response.content) # Extract keys keys = [] for key in cdm.get_keys(session_id): if key.type == 'CONTENT': key_str = f"{key.kid.hex()}:{key.key.hex()}" keys.append(key_str) if log_callback: log_callback(f"Key: {key_str}") return {'keys': keys} ``` ### Download Workflow ```python # Example: 6_n_m3u8dl_re.py def run(mpd_url, keys, search_query='output', log_callback=None): import subprocess import os exe_path = os.path.join('N_m3u8DL-RE', 'N_m3u8DL-RE.exe') # Build key arguments key_args = [] for key in keys: key_args.extend(['--key', key]) # Build command cmd = [ exe_path, mpd_url, '--save-dir', 'OUTPUT', '--save-name', search_query, '--auto-select', '--no-log' ] + key_args if log_callback: log_callback(f"Starting download: {search_query}") # Execute download process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True ) return { 'download_process': process, 'output_name': search_query } ``` ### Credential Integration ```python # Plugins can access credentials when "Expose Credentials" is enabled def run(credentials, license_url, log_callback=None): """ Use credentials to authenticate with license server credentials structure: { 'username': 'user@example.com', 'password': 'pass123', 'api_key': 'abc123xyz', 'mac_address': '00:11:22:33:44:55' } """ import requests # Example: Use credentials for authentication if 'api_key' in credentials: headers = { 'Authorization': f"Bearer {credentials['api_key']}" } elif 'username' in credentials and 'password' in credentials: headers = { 'X-Username': credentials['username'], 'X-Password': credentials['password'] } else: raise ValueError("No valid credentials found") response = requests.post(license_url, headers=headers) if log_callback: log_callback(f"Authenticated with {list(credentials.keys())}") return {'auth_headers': headers} ``` ## Configuration ### Environment Settings Edit `settings/environment.ini` to configure credential types: ```ini [CREDENTIALS] EMAIL_PASSWORD = ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}:.+$ USERNAME_PASSWORD = ^[a-zA-Z0-9_]+:.+$ API_KEY = ^[a-zA-Z0-9]{20,}$ MAC_ADDRESS = ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ``` ### State Persistence Application state is saved to `settings/savestate.json`: ```json { "mpd_url": "https://example.com/manifest.mpd", "mpd_headers": "{\"User-Agent\": \"Mozilla/5.0\"}", "license_url": "https://license.example.com/widevine", "selected_device": "devices/widevine/device.wvd", "selected_credentials": "credentials/account.txt", "plugin_stack": ["1_load_devices", "2_parse_pssh", "4_send_license"] } ``` ### Proxy Configuration Format proxy files as `proxies/myproxies.txt`: ``` # Non-authenticated proxy1.example.com:8080 192.168.1.100:3128 # Authenticated proxy2.example.com:8080:username:password ``` Access in plugins: ```python def run(proxy_line, log_callback=None): if proxy_line: parts = proxy_line.split(':') if len(parts) == 2: proxy = f"http://{parts[0]}:{parts[1]}" elif len(parts) == 4: proxy = f"http://{parts[2]}:{parts[3]}@{parts[0]}:{parts[1]}" proxies = { 'http': proxy, 'https': proxy } return {'proxies': proxies} ``` ## Programmatic Usage ### Using Plugin Manager Directly ```python from core.plugin_manager import PluginManager # Initialize plugin manager pm = PluginManager(plugins_dir='plugins') pm.scan_plugins() # Build context context = { 'device_path': 'devices/widevine/l3.wvd', 'device_type': 'widevine', 'mpd_url': 'https://example.com/manifest.mpd', 'mpd_headers': {'User-Agent': 'Mozilla/5.0'}, 'license_url': 'https://license.example.com/widevine' } # Execute plugin stack plugin_stack = ['1_load_devices', '2_parse_pssh', '4_send_license'] for plugin_name in plugin_stack: plugin_info = pm.plugins[plugin_name] result = pm.run_plugin(plugin_info, context, log_callback=print) # Merge results into context if isinstance(result, dict): context.update(result) # Access extracted keys print(f"Extracted keys: {context.get('keys', [])}") ``` ### Validating Device Files ```python from pywidevine.cdm import Cdm from pywidevine.device import Device def validate_widevine_device(device_path): """Validate a Widevine device file""" try: device = Device.load(device_path) cdm = Cdm.from_device(device) session_id = cdm.open() print(f"✓ Valid Widevine device") print(f" Security Level: {device.security_level}") print(f" Client ID: {device.client_id.token[:20].hex()}...") cdm.close(session_id) return True except Exception as e: print(f"✗ Invalid device: {e}") return False validate_widevine_device('devices/widevine/device.wvd') ``` ## Troubleshooting ### Plugin Not Detecting Parameters **Issue**: Plugin not receiving expected context variables **Solution**: Check function signature matches available context:
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub