df / app.py
hannabaker's picture
Create app.py
dd2242f verified
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()