J.S.T.OSINT404 / app.py
jee122's picture
Update app.py
31e0047 verified
Raw
History Blame Contribute Delete
13.8 kB
import os
import subprocess
import json
import re
import time
import uuid
from flask import Flask, render_template_string, request, session, redirect, url_for, send_file, Response, make_response
app = Flask(__name__)
app.secret_key = "JST_STABLE_SECRET_KEY_ZIE_2026"
# Konfigurasi Session dibikin standar aja biar HP gak rewel
app.config.update(
PERMANENT_SESSION_LIFETIME=86400
)
# --- SYSTEM LIMITER (MAX 4 ORANG) ---
ACTIVE_USERS = {}
MAX_CONCURRENT_USERS = 4
@app.before_request
def track_sessions():
now = time.time()
# Bersihin session yang udah afk / mati lebih dari 2 jam (7200 detik)
dead_uids = [u for u, t in ACTIVE_USERS.items() if now - t > 7200]
for d in dead_uids:
del ACTIVE_USERS[d]
if 'uid' in session:
ACTIVE_USERS[session['uid']] = now
# --- IDENTITY & CREDENTIALS ---
OWNER_NAME = "WhyLaugh404"
TEAM_NAME = "JakartaSecTeam"
LOGIN_LOGO = "https://i.pinimg.com/736x/c2/9a/b9/c29ab9478d9c8f3f84e070d65b2514bc.jpg"
DASH_LOGO = "https://i.pinimg.com/736x/22/46/87/224687d938e12d1bfaf9edd0385fd42b.jpg"
PASSWORDS = {
"owner": "izie19281933",
"admin": "admin404jst",
"member": "memberjst404"
}
ACCESS_LEVELS = {
"member": ["sherlock", "socialscan", "holehe", "exiftool"],
"admin": ["sherlock", "maigret", "socialscan", "holehe", "phoneinfoga", "exiftool", "cloud_enum"],
"owner": ["sherlock", "maigret", "socialscan", "holehe", "phoneinfoga", "twint", "exiftool", "ghunt", "cloud_enum"]
}
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
# --- STYLING: FULLY RESPONSIVE B&W ---
CSS = """
:root { --bg: #000; --text: #fff; --panel: #0a0a0a; --border: #333; }
body { background: var(--bg); color: var(--text); font-family: 'Courier New', monospace; margin: 0; padding: 0; overflow-x: hidden; }
/* LAYOUT LOGIN */
.login-screen { display: flex; align-items: center; justify-content: center; min-height: 100vh; flex-direction: column; padding: 20px; box-sizing: border-box; }
.login-logo { width: 150px; height: 150px; border-radius: 50%; margin-bottom: 25px; filter: grayscale(100%); border: 3px solid var(--text); }
.jst-brand { font-size: 2.2em; font-weight: bold; letter-spacing: 5px; text-align: center; margin-bottom: 25px; }
.login-card { background: var(--panel); border: 1px solid var(--border); padding: 40px 30px; width: 100%; max-width: 380px; text-align: center; box-sizing: border-box; }
/* HEADER DASHBOARD */
.header { background: var(--panel); border-bottom: 1px solid var(--border); padding: 20px; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; }
.header-left { display: flex; align-items: center; gap: 15px; }
.mrx-logo { width: 60px; height: 60px; border-radius: 50%; border: 1px solid var(--border); filter: grayscale(100%); }
.header-title { font-size: 1.5em; font-weight: bold; letter-spacing: 3px; }
.header-sub { font-size: 0.7em; color: #aaa; margin-top: 5px; }
/* BUTTONS & INPUTS (Lebih gede biar gampang dipencet di HP) */
.logout-btn { color: var(--bg); background: var(--text); text-decoration: none; padding: 10px 20px; font-size: 0.9em; font-weight: bold; border: 1px solid var(--text); transition: 0.3s; }
.logout-btn:hover { background: var(--bg); color: var(--text); }
input { background: var(--bg); border: 1px solid var(--border); color: var(--text); padding: 15px; width: 100%; box-sizing: border-box; outline: none; margin-bottom: 15px; font-family: inherit; font-size: 1em; }
input:focus { border-color: var(--text); }
button { background: var(--bg); border: 1px solid var(--text); color: var(--text); padding: 15px; width: 100%; cursor: pointer; font-weight: bold; letter-spacing: 2px; font-size: 1em; transition: 0.3s; }
button:hover:not(:disabled) { background: var(--text); color: var(--bg); }
/* GRID SYSTEM MANTAP (Gak Berdempetan) */
.container { max-width: 1400px; margin: 30px auto; padding: 0 20px; }
.guide-box { border: 1px dashed var(--border); padding: 20px; margin-bottom: 30px; background: var(--panel); font-size: 0.9em; line-height: 1.8; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 30px; }
.card { background: var(--panel); border: 1px solid var(--border); padding: 25px; box-sizing: border-box; display: flex; flex-direction: column; }
.card h3 { margin-top: 0; border-bottom: 1px dashed var(--border); padding-bottom: 10px; font-size: 1em; }
/* CONSOLE BOX */
.console-box { background: var(--bg); border: 1px solid var(--border); margin-top: 20px; display: none; flex-direction: column; flex-grow: 1; }
.console-header { background: #111; padding: 10px; font-size: 0.8em; display: flex; justify-content: space-between; border-bottom: 1px solid var(--border); align-items: center; }
.console-body { padding: 15px; height: 300px; overflow-y: auto; font-size: 0.85em; white-space: pre-wrap; color: #ccc; }
.dl-btn { color: var(--bg); background: var(--text); text-decoration: none; padding: 5px 12px; display: none; font-weight:bold; font-size: 0.9em; }
/* RESPONSIVE ANDROID / MOBILE */
@media (max-width: 768px) {
.header { flex-direction: column; text-align: center; gap: 20px; }
.header-left { flex-direction: column; }
.logout-btn { width: 100%; box-sizing: border-box; text-align: center; padding: 15px; }
.grid { grid-template-columns: 1fr; gap: 20px; }
.container { margin: 15px auto; padding: 0 15px; }
.card { padding: 20px; }
}
"""
# --- SERVER LOGIC ---
@app.route('/', methods=['GET', 'POST'])
def entry():
if request.method == 'POST':
p = request.form.get('p', '').strip()
matched_role = None
for role, val in PASSWORDS.items():
if p == val:
matched_role = role
break
if matched_role:
if 'uid' not in session and len(ACTIVE_USERS) >= MAX_CONCURRENT_USERS:
return f"""<body style='background:#000; color:#fff; text-align:center; padding-top:20vh; font-family:monospace;'>
<h2>[!] SERVER FULL</h2><p>sabar dulu temen lu lagi osint mek</p>
<br><a href='/' style='color:#ccc; border:1px solid #fff; padding:10px 20px; text-decoration:none;'>[ RELOAD ]</a></body>"""
if 'uid' not in session:
new_uid = str(uuid.uuid4())
session['uid'] = new_uid
ACTIVE_USERS[new_uid] = time.time()
session['role'] = matched_role
return redirect(url_for('entry'))
return "<body style='background:#000; color:#fff; text-align:center; padding-top:20vh; font-family:monospace;'><h2>[!] ACCESS DENIED</h2><br><a href='/' style='color:#ccc;'>[ BACK ]</a></body>"
if 'role' in session:
# Tambahin proteksi cache di dashboard biar pas logout gak bisa di-back
resp = make_response(render_template_string(T_DASHBOARD, css=CSS, owner=OWNER_NAME, team=TEAM_NAME,
role=session['role'], allowed=ACCESS_LEVELS[session['role']],
mrx_url=DASH_LOGO))
resp.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
resp.headers['Pragma'] = 'no-cache'
resp.headers['Expires'] = '0'
return resp
return render_template_string(T_LOGIN, css=CSS, team=TEAM_NAME, login_logo=LOGIN_LOGO)
@app.route('/stream/<tool_id>')
def stream(tool_id):
if 'role' not in session or tool_id not in ACCESS_LEVELS[session['role']]:
return Response("Unauthorized", status=403)
target = request.args.get('target', '').strip()
def generate():
cmds = {
"sherlock": f"sherlock {target} --timeout 5",
"maigret": f"maigret {target} --timeout 10",
"socialscan": f"socialscan {target}",
"holehe": f"holehe {target}",
"phoneinfoga": f"phoneinfoga scan -n {target}",
"twint": f"twint -u {target}",
"exiftool": f"exiftool {target}",
"ghunt": f"ghunt email {target}",
"cloud_enum": f"python3 /home/user/cloud_enum/cloud_enum.py -k {target}"
}
cmd = cmds.get(tool_id)
report_file = f"JST_REPORT_{tool_id.upper()}_{target.replace('.', '_')}.txt"
init_msg = f"> Initiating: {tool_id.upper()}\n> Target: {target}\n\n"
yield f"data: {json.dumps({'line': init_msg})}\n\n"
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, universal_newlines=True)
full_log = ""
for line in process.stdout:
clean_line = ansi_escape.sub('', line)
full_log += clean_line
yield f"data: {json.dumps({'line': clean_line})}\n\n"
process.wait()
with open(report_file, "w") as f:
f.write(full_log)
yield f"data: {json.dumps({'done': True, 'file': report_file})}\n\n"
return Response(generate(), mimetype='text/event-stream')
@app.route('/dl/<filename>')
def dl(filename):
if filename.startswith("JST_REPORT_") and os.path.exists(filename):
return send_file(filename, as_attachment=True)
return "Error", 404
@app.route('/logout')
def logout():
# Hapus dari tracker 4 orang
if 'uid' in session and session['uid'] in ACTIVE_USERS:
del ACTIVE_USERS[session['uid']]
# Hapus sesi
session.clear()
# PENTING: Paksa browser lupain cache biar beneran mental
resp = redirect(url_for('entry'))
resp.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
resp.headers['Pragma'] = 'no-cache'
resp.headers['Expires'] = '0'
return resp
# --- HTML TEMPLATES ---
T_LOGIN = """
<!DOCTYPE html><html><head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>{{team}} | AUTH</title><style>{{css}}</style></head>
<body class="login-screen">
<img src="{{login_logo}}" class="login-logo" alt="JST Logo">
<div class="jst-brand">{{team}}</div>
<div class="login-card">
<form method="post">
<input type="password" name="p" placeholder="PASSPHRASE" required autofocus>
<button type="submit">LOGIN</button>
</form>
</div>
</body></html>
"""
T_DASHBOARD = """
<!DOCTYPE html><html><head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>{{team}} CONSOLE</title><style>{{css}}</style></head><body>
<div class="header">
<div class="header-left">
<img src="{{mrx_url}}" class="mrx-logo">
<div>
<div class="header-title">{{team}} CONSOLE</div>
<div class="header-sub">OP: {{owner}} | LVL: {{role.upper()}}</div>
</div>
</div>
<a href="/logout" class="logout-btn">LOGOUT SESSION</a>
</div>
<div class="container">
{% if role in ['member', 'admin'] %}
<div class="guide-box">
<strong>[ PANDUAN PENGGUNAAN MODULE ]</strong><br><br>
- <b>SHERLOCK / MAIGRET</b>: Tracing username di ribuan platform sosial media. (Input: <i>username</i>)<br>
- <b>SOCIALSCAN / HOLEHE</b>: Validasi apakah email/username terdaftar di platform tertentu. (Input: <i>email/username</i>)<br>
- <b>PHONEINFOGA</b>: Gathering informasi dari nomor HP. (Input: Gunakan kode negara, ex: <i>+62812...</i>)<br>
- <b>EXIFTOOL</b>: Ekstrak jejak digital/metadata tersembunyi dari file gambar/dokumen. (Input: <i>nama_file.jpg</i>)<br>
- <b>CLOUD_ENUM / GHUNT</b>: Enumerasi asset cloud dan investigasi akun Google. (Input: <i>keyword/email</i>)
</div>
{% endif %}
<div class="grid">
{% for t_id in allowed %}
<div class="card">
<h3>>> MODULE: {{t_id.upper()}}</h3>
<form onsubmit="runTool(event, '{{t_id}}')">
<input type="text" id="target-{{t_id}}" placeholder="Target..." required autocomplete="off" autocapitalize="none">
<button type="submit" id="btn-{{t_id}}">EXECUTE</button>
</form>
<div id="sess-{{t_id}}" class="console-box">
<div class="console-header"><span>LOG: {{t_id.upper()}}</span><a href="#" id="dl-{{t_id}}" class="dl-btn">SAVE LOG</a></div>
<div class="console-body" id="out-{{t_id}}"></div>
</div>
</div>
{% endfor %}
</div>
</div>
<script>
function runTool(e, id) {
e.preventDefault();
const val = document.getElementById('target-'+id).value;
const out = document.getElementById('out-'+id);
const sess = document.getElementById('sess-'+id);
const dl = document.getElementById('dl-'+id);
const btn = document.getElementById('btn-'+id);
sess.style.display = 'flex';
out.innerHTML = "";
dl.style.display = 'none';
btn.disabled = true;
btn.innerText = "RUNNING...";
const source = new EventSource(`/stream/${id}?target=${val}`);
source.onmessage = function(event) {
const data = JSON.parse(event.data);
if(data.line) {
out.innerHTML += data.line;
out.parentElement.scrollTop = out.parentElement.scrollHeight;
}
if(data.done) {
source.close();
btn.disabled = false;
btn.innerText = "EXECUTE";
dl.href = "/dl/" + data.file;
dl.style.display = 'inline-block';
}
};
source.onerror = function() {
source.close();
btn.disabled = false;
btn.innerText = "EXECUTE";
out.innerHTML += "\\n[!] ERR: CONNECTION LOST / TIMEOUT";
};
}
</script>
</body></html>
"""
if __name__ == '__main__':
app.run(host='0.0.0.0', port=7860, threaded=True)