Spaces:
Sleeping
Sleeping
| 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 | |
| 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 --- | |
| 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) | |
| 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') | |
| def dl(filename): | |
| if filename.startswith("JST_REPORT_") and os.path.exists(filename): | |
| return send_file(filename, as_attachment=True) | |
| return "Error", 404 | |
| 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) |