Repository navigation
Expand file tree
/
Copy pathapp.py
More file actions
143 lines (130 loc) · 4.9 KB
/
Copy pathapp.py
File metadata and controls
143 lines (130 loc) · 4.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
from flask import Flask
from database import init_app as init_database, initialize_schema
from routes.auth import auth_bp
from routes.tracker import tracker_bp
import os
import threading
import subprocess
import requests
import re
import atexit
import ctypes
import signal
app = Flask(__name__)
app.secret_key = 'the_grimoire_master_key_2026'
init_database(app)
app.register_blueprint(auth_bp)
app.register_blueprint(tracker_bp)
initialize_schema(app)
# --- THE DEAD DROP ENGINE ---
try:
with open('token.txt', 'r') as f:
GITHUB_TOKEN = f.read().strip()
except FileNotFoundError:
GITHUB_TOKEN = "UNSET"
GIST_ID = 'a6434fc786ab4ff847dbd09b35588ec4'
TUNNEL_PROCESS = None
TUNNEL_LOCK = threading.Lock()
def stop_tunnel_process():
"""Stop the tracked Cloudflare tunnel process if it is still running."""
global TUNNEL_PROCESS
with TUNNEL_LOCK:
process = TUNNEL_PROCESS
TUNNEL_PROCESS = None
if not process:
return
try:
if process.poll() is None:
process.terminate()
process.wait(timeout=5)
except subprocess.TimeoutExpired:
process.kill()
except Exception as e:
print(f"\n[-] Tunnel process shutdown warning: {e}")
def kill_zombie_tunnels(send_notification=True):
"""Force tunnel cleanup, mark endpoint offline, and optionally notify."""
print("\n[*] Sealing the Grimoire (Resetting Dead Drop to OFFLINE)...")
update_dead_drop("OFFLINE")
if send_notification and os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
try:
requests.post("https://ntfy.sh/ayan_grimoire_lab_alerts_9921",
data="The Grimoire server has been manually sealed or shut down.",
headers={"Title": "Grimoire Offline", "Priority": "high", "Tags": "warning,skull"},
timeout=3)
except Exception:
pass
stop_tunnel_process()
os.system('taskkill /f /im cloudflared-windows-amd64.exe >nul 2>&1')
def update_dead_drop(live_url):
"""Publish the tunnel URL state to the GitHub Gist endpoint."""
if not GITHUB_TOKEN or GITHUB_TOKEN == "UNSET":
print("\n[-] Dead Drop update skipped: missing GitHub token.")
return False
headers = {'Authorization': f'token {GITHUB_TOKEN}', 'Accept': 'application/vnd.github.v3+json'}
data = {"files": {"grimoire_url.json": {"content": f'{{"url": "{live_url}"}}'}}}
try:
resp = requests.patch(
f'https://api.github.com/gists/{GIST_ID}',
headers=headers,
json=data,
timeout=8,
)
resp.raise_for_status()
print(f"\n[+] Dead Drop updated securely: {live_url}")
return True
except requests.RequestException as e:
print(f"\n[-] Dead Drop update failed: {e}")
return False
def spawn_tunnel():
"""Launch cloudflared and capture the first public tunnel URL emitted."""
global TUNNEL_PROCESS
kill_zombie_tunnels(send_notification=False)
cmd = ['cloudflared-windows-amd64.exe', 'tunnel', '--url', 'http://127.0.0.1:5000']
try:
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
except FileNotFoundError:
print("\n[-] cloudflared-windows-amd64.exe not found. Tunnel was not started.")
update_dead_drop("OFFLINE")
return
except Exception as e:
print(f"\n[-] Failed to launch tunnel: {e}")
update_dead_drop("OFFLINE")
return
with TUNNEL_LOCK:
TUNNEL_PROCESS = process
try:
for line in process.stdout:
match = re.search(r'https://[a-zA-Z0-9-]+\.trycloudflare\.com', line)
if match:
update_dead_drop(match.group(0))
break
except Exception as e:
print(f"\n[-] Tunnel output monitoring error: {e}")
finally:
with TUNNEL_LOCK:
if TUNNEL_PROCESS is process and process.poll() is not None:
TUNNEL_PROCESS = None
def shutdown_grimoire(*_args):
"""Run centralized shutdown cleanup for process and dead-drop state."""
kill_zombie_tunnels(send_notification=True)
atexit.register(shutdown_grimoire)
for sig_name in ('SIGINT', 'SIGTERM'):
sig = getattr(signal, sig_name, None)
if sig is not None:
signal.signal(sig, shutdown_grimoire)
def console_ctrl_handler(ctrl_type):
"""Handle Windows console close events to trigger cleanup."""
if ctrl_type == 2:
print("\n[!] Console window forcefully closed! Firing emergency flare...")
shutdown_grimoire()
return True
return False
win_handler = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.c_uint)(console_ctrl_handler)
try:
ctypes.windll.kernel32.SetConsoleCtrlHandler(win_handler, True)
except Exception:
pass
if __name__ == '__main__':
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
threading.Thread(target=spawn_tunnel, daemon=True).start()
app.run(host='0.0.0.0', debug=True, port=5000)