import gradio as gr import subprocess import os import tempfile import time def estimate_time(length, charset): """Estimate time for brute force based on charset and length""" charset_sizes = { "a": 26, "A": 26, "1": 10, "a1": 36, "aA": 52, "aA1": 62 } size = charset_sizes.get(charset, 26) total_combinations = sum(size ** i for i in range(1, length + 1)) # Rough estimate: 100k passwords per second on CPU seconds = total_combinations / 100000 if seconds < 60: return f"{seconds:.1f} seconds" elif seconds < 3600: return f"{seconds/60:.1f} minutes" elif seconds < 86400: return f"{seconds/3600:.1f} hours" else: return f"{seconds/86400:.1f} days" def crack_zip(file, method, max_length=6, charset="a", progress=gr.Progress()): if file is None: return "Please upload a ZIP file" # Show estimated time for brute force if method == "Brute Force": est_time = estimate_time(max_length, charset) progress(0, desc=f"Starting brute force (estimated max time: {est_time})") # Use temp directory that user has access to temp_dir = tempfile.mkdtemp(dir="/tmp") temp_path = os.path.join(temp_dir, "uploaded.zip") try: # Read and save the uploaded file with open(temp_path, "wb") as f: f.write(file.read()) if method == "Dictionary Attack": progress(0.5, desc="Running dictionary attack...") return crack_with_john(temp_path) else: return crack_with_fcrackzip_progress(temp_path, max_length, charset, progress) except Exception as e: return f"Error: {str(e)}" finally: # Clean up try: if os.path.exists(temp_path): os.remove(temp_path) if os.path.exists(temp_dir): os.rmdir(temp_dir) except: pass def crack_with_fcrackzip_progress(file_path, max_length, charset, progress): """Run fcrackzip with progress updates""" try: # Try lengths progressively for length in range(1, max_length + 1): progress(length / max_length, desc=f"Trying length {length}/{max_length}") cmd = [ "fcrackzip", "-b", # brute force "-c", charset, # character set "-l", f"{length}-{length}", # specific length "-u", # unzip to verify "-v", # verbose file_path ] result = subprocess.run(cmd, capture_output=True, text=True) output = result.stdout + result.stderr if "PASSWORD FOUND" in output: # Extract password from output for line in output.split('\n'): if "PASSWORD FOUND" in line: password = line.split("== ")[-1].strip() return f"✅ Password found: {password}\n\nFound at length: {length}" return f"❌ Password not found (tried all combinations up to {max_length} characters)" except Exception as e: return f"Error with fcrackzip: {str(e)}" def crack_with_john(file_path): try: # Use /tmp for hash file (writable by user) hash_file = "/tmp/zip_hash.txt" # Extract hash result = subprocess.run( ["zip2john", file_path], capture_output=True, text=True ) if result.returncode != 0: return f"Error extracting hash: {result.stderr}" # Write hash to file with open(hash_file, "w") as f: f.write(result.stdout) # Create custom john directory in /tmp john_dir = "/tmp/.john" os.makedirs(john_dir, exist_ok=True) os.environ["JOHN_PRIVATE_HOME"] = john_dir # Run John with wordlist crack_result = subprocess.run( ["john", "--wordlist=/usr/share/john/password.lst", hash_file], capture_output=True, text=True, timeout=300 # 5 minute timeout ) # Get cracked password show_result = subprocess.run( ["john", "--show", hash_file], capture_output=True, text=True ) # Parse output if show_result.stdout: lines = show_result.stdout.strip().split('\n') for line in lines: if ':' in line and not line.startswith('0 password'): parts = line.split(':') if len(parts) >= 2: password = parts[1] return f"✅ Password found: {password}" return "❌ Password not found in wordlist" except subprocess.TimeoutExpired: return "⏱️ Dictionary attack timed out after 5 minutes" except Exception as e: return f"Error with John: {str(e)}" # Create Gradio interface with gr.Blocks(title="ZIP Password Recovery", theme=gr.themes.Soft()) as demo: gr.Markdown("# 🔐 ZIP Password Recovery Tool") gr.Markdown("Upload a password-protected ZIP file to attempt password recovery") with gr.Row(): with gr.Column(): file_input = gr.File( label="Upload ZIP File", file_types=[".zip"], type="binary" ) method = gr.Radio( ["Dictionary Attack", "Brute Force"], label="Recovery Method", value="Dictionary Attack" ) with gr.Accordion("Brute Force Settings", open=False): max_length = gr.Slider( minimum=1, maximum=12, # Extended to 12 value=4, step=1, label="Max Password Length", info="⚠️ Length 8+ can take hours/days!" ) charset = gr.Dropdown( choices=[ ("Lowercase letters (a-z)", "a"), ("Uppercase letters (A-Z)", "A"), ("Numbers (0-9)", "1"), ("Lowercase + Numbers", "a1"), ("All letters (a-z, A-Z)", "aA"), ("All letters + Numbers", "aA1") ], value="a", label="Character Set", info="More characters = much longer time" ) # Time estimate display time_estimate = gr.Textbox( label="Estimated Maximum Time", value="~0.1 seconds", interactive=False ) crack_btn = gr.Button("Start Recovery", variant="primary") with gr.Column(): output = gr.Textbox( label="Result", lines=5, max_lines=10 ) # Update time estimate when settings change def update_estimate(length, charset): return f"~{estimate_time(length, charset)}" max_length.change(update_estimate, [max_length, charset], time_estimate) charset.change(update_estimate, [max_length, charset], time_estimate) crack_btn.click( fn=crack_zip, inputs=[file_input, method, max_length, charset], outputs=output ) gr.Markdown(""" ### 📊 Time Estimates (approximate): - **Length 4**: seconds to minutes - **Length 6**: minutes to hours - **Length 8**: hours to days - **Length 10+**: days to weeks/months ### ⚡ Tips: - Start with Dictionary Attack first - For brute force, start with shorter lengths - Lowercase only is fastest - Each additional character exponentially increases time - HF Spaces may timeout on very long operations ### ⚠️ Important: - Only use on files you own - Consider if recreating the content would be faster - GPU instances would be much faster but cost money """) if __name__ == "__main__": demo.launch()