409 lines
10 KiB
Python
409 lines
10 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from collections import Counter
|
|
|
|
from flask import redirect, render_template, request, url_for
|
|
|
|
from cracklab.server.db.connection import connect_database
|
|
from cracklab.server.repositories.clients import ClientsRepository
|
|
from cracklab.server.repositories.jobs import JobsRepository
|
|
from cracklab.server.repositories.methods import MethodsRepository
|
|
from cracklab.server.repositories.results import ResultsRepository
|
|
from cracklab.server.services.methods import MethodService, MethodServiceError
|
|
|
|
from cracklab.server.web import cracklab_bp
|
|
|
|
|
|
@cracklab_bp.route("/")
|
|
def dashboard():
|
|
conn = connect_database()
|
|
|
|
try:
|
|
methods = MethodsRepository(conn).list_methods()
|
|
jobs = JobsRepository(conn).list_jobs()
|
|
clients = ClientsRepository(conn).list_clients()
|
|
results = ResultsRepository(conn).list_results()
|
|
finally:
|
|
conn.close()
|
|
|
|
job_statuses = Counter(
|
|
row["status"]
|
|
for row in jobs
|
|
)
|
|
|
|
recent_jobs = jobs[:10]
|
|
|
|
method_names = {
|
|
row["id"]: row["name"]
|
|
for row in methods
|
|
}
|
|
|
|
client_names = {
|
|
row["id"]: row["name"]
|
|
for row in clients
|
|
}
|
|
|
|
return render_template(
|
|
"dashboard.html",
|
|
active_page="dashboard",
|
|
methods_count=len(methods),
|
|
jobs_count=len(jobs),
|
|
clients_count=len(clients),
|
|
results_count=len(results),
|
|
active_jobs_count=sum(
|
|
job_statuses.get(status, 0)
|
|
for status in (
|
|
"CREATED",
|
|
"QUEUED",
|
|
"RUNNING",
|
|
"PARTIAL",
|
|
)
|
|
),
|
|
job_statuses=job_statuses,
|
|
recent_jobs=recent_jobs,
|
|
method_names=method_names,
|
|
client_names=client_names,
|
|
)
|
|
|
|
|
|
@cracklab_bp.route("/jobs/")
|
|
def jobs_page():
|
|
return _placeholder(
|
|
"Jobs",
|
|
"Job management will be implemented here.",
|
|
"jobs",
|
|
)
|
|
|
|
|
|
@cracklab_bp.route("/methods/")
|
|
def methods_page():
|
|
conn = connect_database()
|
|
|
|
try:
|
|
service = MethodService(conn)
|
|
methods = service.list_methods()
|
|
|
|
method_rows = []
|
|
|
|
for method in methods:
|
|
versions = service.list_method_versions(method["id"])
|
|
|
|
latest = versions[-1] if versions else None
|
|
|
|
steps_count = 0
|
|
|
|
if latest is not None:
|
|
try:
|
|
definition = json.loads(
|
|
latest["definition_json"]
|
|
)
|
|
steps_count = len(
|
|
definition.get("steps", [])
|
|
)
|
|
except (TypeError, ValueError):
|
|
steps_count = 0
|
|
|
|
method_rows.append(
|
|
{
|
|
"id": method["id"],
|
|
"name": method["name"],
|
|
"description": method["description"],
|
|
"created_at": method["created_at"],
|
|
"version": (
|
|
latest["version"]
|
|
if latest is not None
|
|
else None
|
|
),
|
|
"steps_count": steps_count,
|
|
}
|
|
)
|
|
|
|
finally:
|
|
conn.close()
|
|
|
|
return render_template(
|
|
"methods.html",
|
|
active_page="methods",
|
|
methods=method_rows,
|
|
)
|
|
|
|
|
|
@cracklab_bp.route("/methods/create/", methods=["GET", "POST"])
|
|
def method_create_page():
|
|
error = None
|
|
|
|
if request.method == "POST":
|
|
try:
|
|
name = request.form.get("name", "").strip()
|
|
description = request.form.get(
|
|
"description",
|
|
"",
|
|
).strip()
|
|
|
|
hash_mode_raw = request.form.get(
|
|
"hash_mode",
|
|
"22000",
|
|
).strip()
|
|
|
|
if not name:
|
|
raise MethodServiceError(
|
|
"Введите название метода."
|
|
)
|
|
|
|
if hash_mode_raw != "22000":
|
|
raise MethodServiceError(
|
|
"Сейчас поддерживается только hash mode 22000."
|
|
)
|
|
|
|
hash_mode = 22000
|
|
|
|
steps = _build_steps_from_form()
|
|
|
|
definition = {
|
|
"schema_version": 1,
|
|
"hash_mode": hash_mode,
|
|
"steps": steps,
|
|
}
|
|
|
|
conn = connect_database()
|
|
|
|
try:
|
|
service = MethodService(conn)
|
|
|
|
with conn:
|
|
method_id = service.create_method(
|
|
name=name,
|
|
description=description or None,
|
|
)
|
|
|
|
service.create_method_version(
|
|
method_id=method_id,
|
|
version=1,
|
|
definition=definition,
|
|
)
|
|
|
|
finally:
|
|
conn.close()
|
|
|
|
return redirect(
|
|
url_for(
|
|
"cracklab.methods_page",
|
|
)
|
|
)
|
|
|
|
except (MethodServiceError, ValueError) as exc:
|
|
error = str(exc)
|
|
|
|
return render_template(
|
|
"method_create.html",
|
|
active_page="methods",
|
|
error=error,
|
|
)
|
|
|
|
|
|
def _build_steps_from_form() -> list[dict]:
|
|
step_count_raw = request.form.get(
|
|
"step_count",
|
|
"1",
|
|
).strip()
|
|
|
|
try:
|
|
step_count = int(step_count_raw)
|
|
except ValueError as exc:
|
|
raise MethodServiceError(
|
|
"Некорректное количество шагов."
|
|
) from exc
|
|
|
|
if step_count < 1:
|
|
raise MethodServiceError(
|
|
"Метод должен содержать хотя бы один шаг."
|
|
)
|
|
|
|
steps = []
|
|
|
|
for index in range(1, step_count + 1):
|
|
prefix = f"step_{index}"
|
|
|
|
attack_mode_raw = request.form.get(
|
|
f"{prefix}_attack_mode",
|
|
"",
|
|
).strip()
|
|
|
|
if not attack_mode_raw:
|
|
raise MethodServiceError(
|
|
f"Для шага {index} не выбран attack mode."
|
|
)
|
|
|
|
try:
|
|
attack_mode = int(attack_mode_raw)
|
|
except ValueError as exc:
|
|
raise MethodServiceError(
|
|
f"Некорректный attack mode в шаге {index}."
|
|
) from exc
|
|
|
|
if attack_mode not in (0, 1, 3, 6, 7):
|
|
raise MethodServiceError(
|
|
f"Attack mode {attack_mode} "
|
|
f"не поддерживается клиентом CrackLab."
|
|
)
|
|
|
|
step = {
|
|
"step_id": f"step-{index:03d}",
|
|
"attack_mode": attack_mode,
|
|
}
|
|
|
|
workload_raw = request.form.get(
|
|
f"{prefix}_workload_profile",
|
|
"2",
|
|
).strip()
|
|
|
|
if workload_raw:
|
|
try:
|
|
workload_profile = int(workload_raw)
|
|
except ValueError as exc:
|
|
raise MethodServiceError(
|
|
f"Некорректный workload profile "
|
|
f"в шаге {index}."
|
|
) from exc
|
|
|
|
if workload_profile not in (1, 2, 3, 4):
|
|
raise MethodServiceError(
|
|
f"Workload profile в шаге {index} "
|
|
"должен быть от 1 до 4."
|
|
)
|
|
|
|
step["workload_profile"] = workload_profile
|
|
|
|
if attack_mode == 0:
|
|
step["dictionary"] = {
|
|
"resource": _get_dictionary(
|
|
prefix,
|
|
"dictionary",
|
|
index,
|
|
),
|
|
}
|
|
|
|
elif attack_mode == 1:
|
|
step["dictionaries"] = [
|
|
{
|
|
"resource": _get_dictionary(
|
|
prefix,
|
|
"dictionary_1",
|
|
index,
|
|
),
|
|
},
|
|
{
|
|
"resource": _get_dictionary(
|
|
prefix,
|
|
"dictionary_2",
|
|
index,
|
|
),
|
|
},
|
|
]
|
|
|
|
elif attack_mode == 3:
|
|
step["mask"] = _get_mask(
|
|
prefix,
|
|
index,
|
|
)
|
|
|
|
elif attack_mode == 6:
|
|
step["dictionary"] = {
|
|
"resource": _get_dictionary(
|
|
prefix,
|
|
"dictionary",
|
|
index,
|
|
),
|
|
}
|
|
step["mask"] = _get_mask(
|
|
prefix,
|
|
index,
|
|
)
|
|
|
|
elif attack_mode == 7:
|
|
step["mask"] = _get_mask(
|
|
prefix,
|
|
index,
|
|
)
|
|
step["dictionary"] = {
|
|
"resource": _get_dictionary(
|
|
prefix,
|
|
"dictionary",
|
|
index,
|
|
),
|
|
}
|
|
|
|
steps.append(step)
|
|
|
|
return steps
|
|
|
|
|
|
def _get_dictionary(
|
|
prefix: str,
|
|
field: str,
|
|
index: int,
|
|
) -> str:
|
|
value = request.form.get(
|
|
f"{prefix}_{field}",
|
|
"",
|
|
).strip()
|
|
|
|
if not value:
|
|
raise MethodServiceError(
|
|
f"Для шага {index} необходимо указать словарь."
|
|
)
|
|
|
|
if not value.startswith("wordlist:"):
|
|
value = f"wordlist:{value}"
|
|
|
|
return value
|
|
|
|
|
|
def _get_mask(
|
|
prefix: str,
|
|
index: int,
|
|
) -> str:
|
|
value = request.form.get(
|
|
f"{prefix}_mask",
|
|
"",
|
|
).strip()
|
|
|
|
if not value:
|
|
raise MethodServiceError(
|
|
f"Для шага {index} необходимо указать маску."
|
|
)
|
|
|
|
return value
|
|
|
|
|
|
@cracklab_bp.route("/clients/")
|
|
def clients_page():
|
|
return _placeholder(
|
|
"Clients",
|
|
"Client management will be implemented here.",
|
|
"clients",
|
|
)
|
|
|
|
|
|
@cracklab_bp.route("/results/")
|
|
def results_page():
|
|
return _placeholder(
|
|
"Results",
|
|
"Result history will be implemented here.",
|
|
"results",
|
|
)
|
|
|
|
|
|
def _placeholder(
|
|
title: str,
|
|
description: str,
|
|
active_page: str,
|
|
):
|
|
return render_template(
|
|
"placeholder.html",
|
|
active_page=active_page,
|
|
title=title,
|
|
description=description,
|
|
)
|