diff --git a/app.py b/app.py
index bb1567a..7c6413e 100644
--- a/app.py
+++ b/app.py
@@ -32,7 +32,9 @@ def db():
CREATE TABLE IF NOT EXISTS messages(id INTEGER PRIMARY KEY, user_id INTEGER, sender TEXT, body TEXT, created INTEGER);
CREATE TABLE IF NOT EXISTS mailboxes(id INTEGER PRIMARY KEY, user_id INTEGER, address TEXT UNIQUE, invoice_id TEXT, paid INTEGER DEFAULT 0, expires INTEGER DEFAULT 0, created INTEGER, plan_days INTEGER DEFAULT 7, cnt INTEGER DEFAULT 0);
CREATE TABLE IF NOT EXISTS mails(id INTEGER PRIMARY KEY, mailbox_id INTEGER, sender TEXT, subject TEXT, body TEXT, ts INTEGER);
- CREATE TABLE IF NOT EXISTS passes(id INTEGER PRIMARY KEY, user_id INTEGER, invoice_id TEXT, paid INTEGER DEFAULT 0, expires INTEGER DEFAULT 0, plan_days INTEGER DEFAULT 30);""")
+ CREATE TABLE IF NOT EXISTS passes(id INTEGER PRIMARY KEY, user_id INTEGER, invoice_id TEXT, paid INTEGER DEFAULT 0, expires INTEGER DEFAULT 0, plan_days INTEGER DEFAULT 30);
+ CREATE TABLE IF NOT EXISTS canaries(id INTEGER PRIMARY KEY, user_id INTEGER, token TEXT UNIQUE, tag TEXT, created INTEGER, armed INTEGER DEFAULT 1);
+ CREATE TABLE IF NOT EXISTS canary_hits(id INTEGER PRIMARY KEY, canary_id INTEGER, ts INTEGER, ip TEXT, ua TEXT);""")
return con
import ssl as _ssl
@@ -101,8 +103,10 @@ a{color:var(--acc2)}
◈ AURIGA◈ IP INTEL◈ CARD CHECK◈ SMS RENTAL◈ PROXY LAB◈ STEGO◈ TRACK FILE
+◈ MAIL FORENSICS◈ IMG FORENSICS
+◈ CANARY◈ MAIL◈ INBOX
-◈ PASS◈ TOOLS
+◈ PASSPORT◈ PASS◈ TOOLS{{body}}
@@ -1004,6 +1008,267 @@ def btcpay_webhook():
con.commit()
return jsonify({"ok": True})
+# ---------- 6d. EMAIL HEADER FORENSICS ----------
+def parse_headers(raw):
+ import email as em
+ msg = em.message_from_string(raw)
+ out = {"from": msg.get("From",""), "to": msg.get("To",""), "subject": msg.get("Subject",""),
+ "date": msg.get("Date",""), "return_path": msg.get("Return-Path",""),
+ "reply_to": msg.get("Reply-To",""), "message_id": msg.get("Message-ID","")}
+ hops = []
+ for h in msg.get_all("Received", []) or []:
+ hop = h.strip().replace("\n", " ")
+ hops.append(hop[:300])
+ out["hops"] = list(reversed(hops)) # first-hop origin first
+ auth = msg.get_all("Authentication-Results", []) or []
+ out["auth_results"] = [a.strip()[:300] for a in auth]
+ out["dkim"] = [d.strip()[:200] for d in (msg.get_all("DKIM-Signature", []) or [])][:3]
+ # spoof flags
+ flags = []
+ env_from = out["return_path"].strip("<>")
+ frm = out["from"]
+ m_from = re.search(r"<([^>]+)>", frm)
+ addr_from = (m_from.group(1) if m_from else frm).split()[-1].strip("<>").lower()
+ if env_from and addr_from and env_from.split("@")[-1] != addr_from.split("@")[-1]:
+ flags.append(f"envelope-from domain ({env_from.split('@')[-1]}) != From domain ({addr_from.split('@')[-1]}) — classic spoof marker")
+ if out["reply_to"]:
+ m_rt = re.search(r"<([^>]+)>", out["reply_to"]) or None
+ addr_rt = ((m_rt.group(1) if m_rt else out["reply_to"]).strip()).lower()
+ if addr_rt.split("@")[-1] != addr_from.split("@")[-1]:
+ flags.append(f"Reply-To ({addr_rt}) differs from From — possible reply-hijack")
+ # origin IP = the bottom-most Received header (original sender); in reversed list it's index 0
+ origin_ip = None
+ for h in hops: # reversed order → origin first
+ m = re.search(r"\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\]", h) or re.search(r"\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b", h)
+ if m:
+ origin_ip = m.group(1); break
+ out["origin_ip"] = origin_ip
+ if origin_ip: out["origin_geo"] = enrich_ip(origin_ip)
+ out["flags"] = flags
+ # dmarc/spf/dkim verdict parse from Authentication-Results
+ verdicts = {}
+ blob = " ".join(out["auth_results"]).lower()
+ for k in ("spf","dkim","dmarc"):
+ m = re.search(k + r"=(\w+)", blob)
+ verdicts[k] = m.group(1) if m else "not present"
+ out["verdicts"] = verdicts
+ return out
+
+@app.route("/eh")
+def eh():
+ body = f"""
+
EMAIL FORENSICS
Paste full raw email headers (View source → copy all) — get the real origin, SPF/DKIM/DMARC verdicts, and spoof flags.
+
+
API: POST /api/eh (raw=…) → JSON: origin IP+geo, hop chain, verdicts, spoof flags.
"""
+ return page("tools", body)
+
+@app.route("/eh_result", methods=["POST"])
+def eh_result():
+ d = parse_headers(request.form.get("raw") or "")
+ hops = "".join(f"
hop {i+1}
{esc(h)}
" for i, h in enumerate(d["hops"]))
+ verdicts = " ".join(f'{k.upper()}: {v}' for k, v in d["verdicts"].items())
+ flags = "".join(f"
{esc(f)}
" for f in d["flags"]) or 'no spoof markers found'
+ og = d.get("origin_geo") or {}
+ origin = f"{esc(d.get('origin_ip'))}" + (f" — {esc(og.get('city'))}, {esc(og.get('country'))} · {esc(og.get('isp'))}" if og else "")
+ return page("tools", f"""
+
""")
+
+@app.route("/api/forensics", methods=["POST"])
+def api_forensics():
+ f = request.files.get("image")
+ if not f: return jsonify({"ok": False, "error": "image required"}), 400
+ data = f.read()
+ from PIL import Image
+ im = Image.open(io.BytesIO(data))
+ exif = im.getexif()
+ ex = {}
+ try:
+ from PIL.ExifTags import TAGS
+ except Exception:
+ TAGS = {}
+ for k, v in exif.items():
+ try: ex[str(TAGS.get(k, k) if isinstance(k, int) else k)] = str(v)[:200]
+ except Exception: pass
+ _, maxdiff = _ela_score(data)
+ return jsonify({"ok": True, "exif": ex, "gps_present": bool(exif.get_ifd(0x8825)) if hasattr(exif, "get_ifd") else False,
+ "stego_auriga": "auriga_meta" in im.info, "ela_max_diff": maxdiff,
+ "flags": (["exif-stripped"] if not ex else [])})
+
+# ---------- 6f. CANARY TRAPS ----------
+@app.route("/canary")
+def canary():
+ uid = current_user_id()
+ body = f"""
+
CANARY TRAPS
Plant tripwires. Anyone who touches one — clicks the link, loads the pixel — fires an instant alert into your inbox. Tag each trap with who it belongs to.
+
New trap
+
You get: a link (paste anywhere), a pixel URL (embed in docs/pages), and a fake credential line to drop in files.
+{canary_list()}
+
API: POST /canary (tag) · GET /api/canary/list (login) · hits log like trackables.
"""
+ return page("track", body)
+
+def canary_list():
+ uid = current_user_id()
+ if not uid: return ""
+ con = db()
+ rows = con.execute("SELECT * FROM canaries WHERE user_id=? ORDER BY id DESC LIMIT 20", (uid,)).fetchall()
+ trs = ""
+ for c in rows:
+ hits = con.execute("SELECT COUNT(*) c FROM canary_hits WHERE canary_id=?", (c["id"],)).fetchone()["c"]
+ trs += f"