Spaces:
Paused
Paused
| 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() |