4 new tools: email forensics (origin+SPF/DKIM/DMARC+spoof flags), image forensics (EXIF+GPS+ELA), canary traps (link+pixel+alerts), agent passport; geo-enriched open events

This commit is contained in:
2026-09-30 16:39:31 -07:00
parent 6bc48b6ab8
commit 474ad22973

273
app.py
View File

@@ -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 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 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 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 return con
import ssl as _ssl import ssl as _ssl
@@ -101,8 +103,10 @@ a{color:var(--acc2)}
<a href=/ class={{o(home)}}>◈ AURIGA</a><a href=/ip class={{o(ip)}}>◈ IP INTEL</a><a href=/card class={{o(card)}}>◈ CARD CHECK</a> <a href=/ class={{o(home)}}>◈ AURIGA</a><a href=/ip class={{o(ip)}}>◈ IP INTEL</a><a href=/card class={{o(card)}}>◈ CARD CHECK</a>
<a href=/sms class={{o(sms)}}>◈ SMS RENTAL</a><a href=/proxy class={{o(proxy)}}>◈ PROXY LAB</a> <a href=/sms class={{o(sms)}}>◈ SMS RENTAL</a><a href=/proxy class={{o(proxy)}}>◈ PROXY LAB</a>
<a href=/steg class={{o(steg)}}>◈ STEGO</a><a href=/track class={{o(track)}}>◈ TRACK FILE</a> <a href=/steg class={{o(steg)}}>◈ STEGO</a><a href=/track class={{o(track)}}>◈ TRACK FILE</a>
<a href=/eh class={{o(eh)}}>◈ MAIL FORENSICS</a><a href=/forensics class={{o(forensics)}}>◈ IMG FORENSICS</a>
<a href=/canary class={{o(canary)}}>◈ CANARY</a>
<a href=/mail class={{o(steg2)}}>◈ MAIL</a><a href=/inbox class={{o(inbox)}}>◈ INBOX</a> <a href=/mail class={{o(steg2)}}>◈ MAIL</a><a href=/inbox class={{o(inbox)}}>◈ INBOX</a>
<a href=/pass class={{o(sms2)}}>◈ PASS</a><a href=/tools class={{o(tools)}}>◈ TOOLS</a> <a href=/passport class={{o(passport)}}>◈ PASSPORT</a><a href=/pass class={{o(sms2)}}>◈ PASS</a><a href=/tools class={{o(tools)}}>◈ TOOLS</a>
</nav><main>{{body}}</main> </nav><main>{{body}}</main>
<footer>Built for agents &amp; humans · <a href="{{bmac}}" target=_blank rel=noopener>☕ fuel the lab</a></footer> <footer>Built for agents &amp; humans · <a href="{{bmac}}" target=_blank rel=noopener>☕ fuel the lab</a></footer>
<script defer src="https://analytics.thetempleofdoom.com/script.js" data-website-id="953c15df-ba4c-453a-a7c6-465fa9e3f202"></script> <script defer src="https://analytics.thetempleofdoom.com/script.js" data-website-id="953c15df-ba4c-453a-a7c6-465fa9e3f202"></script>
@@ -1004,6 +1008,267 @@ def btcpay_webhook():
con.commit() con.commit()
return jsonify({"ok": True}) 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"""
<h1>EMAIL <span>FORENSICS</span></h1><p class=sub>Paste full raw email headers (View source → copy all) — get the real origin, SPF/DKIM/DMARC verdicts, and spoof flags.</p>
<div class=card><form method=post action=/eh_result><textarea name=raw rows=10 style="width:100%" placeholder="Received: from …&#10;Authentication-Results: …"></textarea>
<button style=margin-top:.5rem>Analyze</button></form></div>
<div class=card style=color:var(--dim)>API: POST /api/eh (raw=…) → JSON: origin IP+geo, hop chain, verdicts, spoof flags.</div>"""
return page("tools", body)
@app.route("/eh_result", methods=["POST"])
def eh_result():
d = parse_headers(request.form.get("raw") or "")
hops = "".join(f"<div class=msg><div class=who>hop {i+1}</div>{esc(h)}</div>" for i, h in enumerate(d["hops"]))
verdicts = " ".join(f'<span class="tag {"ok" if v=="pass" else ("bad" if v in ("fail","softfail") else "warn")}">{k.upper()}: {v}</span>' for k, v in d["verdicts"].items())
flags = "".join(f"<div class=tag bad style=margin:.2rem>{esc(f)}</div><br>" for f in d["flags"]) or '<span style=color:var(--ok)>no spoof markers found</span>'
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"""
<h1>VERDICT <span>{esc(d.get('subject') or '(no subject)')}</span></h1>
{kv([("From", esc(d.get('from'))), ("Envelope-from", esc(d.get('return_path'))), ("Reply-To", esc(d.get('reply_to') or '—')), ("Origin IP", origin)])}
<div class=card><b>Authentication</b><br>{verdicts}<br><br><b>Spoof flags</b><br>{flags}</div>
<div class=card><b>Relay chain (origin first)</b>{hops or '<i style=color:var(--dim)>no Received headers</i>'}</div>""")
@app.route("/api/eh", methods=["POST"])
def api_eh():
return jsonify(parse_headers(param("raw") or ""))
# ---------- 6e. IMAGE FORENSICS ----------
@app.route("/forensics")
def forensics():
body = f"""
<h1>IMAGE <span>FORENSICS</span></h1><p class=sub>EXIF dump, GPS extraction, date/software flags, error-level analysis (ELA) — spot edits, and sniff out OTHER people's stego.</p>
<div class=card><form action=/forensics_result method=post enctype=multipart/form-data>
<div class=drop onclick="document.getElementById('fi').click()">🖼 drop an image<input id=fi type=file name=image accept="image/*" style=display:none required></div>
<div id=fifn style=color:var(--dim);font-size:.85rem></div>
<button style=margin-top:.5rem>Analyze</button></form></div>
<script>document.getElementById('fi').addEventListener('change',function(){{document.getElementById('fifn').textContent=this.files[0].name}})</script>
<div class=card style=color:var(--dim)>API: POST /api/forensics (image) → JSON: exif, gps, flags, ELA score.</div>"""
return page("steg", body)
def _ela_score(img_bytes):
from PIL import Image, ImageChops, ImageEnhance
im = Image.open(io.BytesIO(img_bytes)).convert("RGB")
resaved = io.BytesIO(); im.save(resaved, "JPEG", quality=90)
ela = ImageChops.difference(im, Image.open(resaved))
extrema = ela.getextrema()
maxdiff = max(e[1] for e in extrema)
enh = ImageEnhance.Brightness(ela).enhance(15)
out = io.BytesIO(); enh.save(out, "PNG")
return out.getvalue(), maxdiff
@app.route("/forensics_result", methods=["POST"])
def forensics_result():
f = request.files.get("image")
if not f: return page("steg", "no image")
data = f.read()
from PIL import Image
im = Image.open(io.BytesIO(data))
exif = im.getexif()
rows = []
gps = {}
try:
from PIL.ExifTags import TAGS, GPSTAGS
except Exception:
TAGS, GPSTAGS = {}, {}
for k, v in exif.items():
name = TAGS.get(k, k) if isinstance(k, int) else k
try: rows.append((str(name), str(v)[:120]))
except Exception: pass
# GPS
try:
gifd = exif.get_ifd(0x8825)
if gifd:
for k, v in gifd.items():
gps[GPSTAGS.get(k, k)] = str(v)[:60]
except Exception: pass
flags = []
if not rows: flags.append("EXIF stripped/absent — edited or privacy-scrubbed")
else:
for k, v in rows:
if "software" in k.lower(): flags.append(f"software: {v}")
if "Photoshop" in v or "GIMP" in v: flags.append(f"⚠ EDITED IN {v}")
stego = "auriga_meta" in im.info
ela_png, maxdiff = _ela_score(data)
fn = (f.filename or "image")[:60]
verdict = "CLEAN-ISH" if maxdiff < 12 and not flags else "SUSPECT — check ELA"
rows_html = "".join(f"<tr><td>{esc(k)}</td><td>{esc(v)}</td></tr>" for k, v in rows)
gps_html = " ".join(f"<div>{esc(k)}: {esc(v)}</div>" for k, v in gps.items()) or "—"
import base64 as b64mod
ela_b64 = b64mod.b64encode(ela_png).decode()
return page("steg", f"""
<h1>FORENSICS <span>{esc(fn)}</span></h1>
{kv([("Verdict", f'<span class="tag {"ok" if verdict.startswith("CLEAN") else "bad"}">{verdict}</span>'), ("ELA max diff", f"{maxdiff} (low=uniform=re-saved clean)"), ("EXIF", f"{len(rows)} tags"), ("Stego", "AURIGA payload present ✓" if stego else "none detected")])}
<div class=card><b>Flags</b><br>{'<br>'.join(esc(x) for x in flags) or '<span style=color:var(--ok)>none</span>'}</div>
<div class=card><b>ELA (amplified 15×)</b><br><img src="data:image/png;base64,{ela_b64}" style="max-width:100%;border-radius:8px"> <a href=/api/forensics/ela?download=1 style=color:var(--acc)>full PNG</a> <button style="padding:.2rem .6rem;font-size:.8rem" onclick="cp(document.querySelector('img').src)">copy data-uri</button></div>
<div class=card><b>EXIF table</b><table>{rows_html or '<tr><td colspan=2 style=color:var(--dim)>no EXIF</td></tr>'}</table></div>
<div class=card><b>GPS</b>{gps_html}</div>""")
@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"""
<h1>CANARY <span>TRAPS</span></h1><p class=sub>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.</p>
<div class=card><b>New trap</b><form method=post>
<input name=tag placeholder="tag: who/where (e.g. 'resume-dropbox', 'backup-folder')" style="width:70%" required>
<button style=margin-left:.5rem>Create trap</button></form>
<div style=color:var(--dim);font-size:.85rem;margin-top:.5rem>You get: a link (paste anywhere), a pixel URL (embed in docs/pages), and a fake credential line to drop in files.</div></div>
{canary_list()}
<div class=card style=color:var(--dim)>API: POST /canary (tag) · GET /api/canary/list (login) · hits log like trackables.</div>"""
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"<tr><td>{esc(c['tag'])}</td><td><code>{SITE}/c/{c['token']}</code> <a href=# onclick=\"cp('{SITE}/c/{c['token']}');return false\" style=color:var(--acc)>copy</a></td><td>{SITE}/c/{c['token']}.png</td><td><b>{hits}</b></td><td>{'armed' if c['armed'] else 'triggered ⚠'}</td></tr>"
return f'<div class=card><b>Your traps</b><table><tr><th>Tag</th><th>Link</th><th>Pixel</th><th>Hits</th><th>Status</th></tr>{trs or "<tr><td colspan=5 style=color:var(--dim)>none yet</td></tr>"}</table></div>'
@app.route("/canary", methods=["POST"])
def canary_create():
uid = current_user_id()
if not uid: return page("track", "<div class=card>login required</div>")
tag = (param("tag") or "untagged")[:80]
con = db()
token = secrets.token_urlsafe(12)
con.execute("INSERT INTO canaries(user_id,token,tag,created,armed) VALUES(?,?,?,?,1)", (uid, token, esc(tag), int(time.time())))
con.commit()
resp = Response(status=302); resp.headers["Location"] = "/canary"
return resp
@app.route("/c/<token>")
def canary_hit(token):
con = db()
c = con.execute("SELECT * FROM canaries WHERE token=?", (token,)).fetchone()
if not c: return "not found", 404
con.execute("INSERT INTO canary_hits(canary_id,ts,ip,ua) VALUES(?,?,?,?)",
(c["id"], int(time.time()), request.headers.get("X-Real-IP") or request.remote_addr, request.headers.get("User-Agent","")))
con.execute("UPDATE canaries SET armed=0 WHERE id=?", (c["id"],))
if c["user_id"]:
ip = request.headers.get("X-Real-IP") or request.remote_addr
geo = enrich_ip(ip)
where = f" — {geo.get('city','')}, {geo.get('countryCode','')} · {geo.get('isp','')}" if geo else ""
con.execute("INSERT INTO messages(user_id,sender,body,created) VALUES(?,?,?,?)",
(c["user_id"], "operator-bot", f"🚨 CANARY TRIGGERED: '{c['tag']}' — IP <b>{esc(ip)}</b>{esc(where)} · device {esc(request.headers.get('User-Agent','')[:80])}", int(time.time())))
con.commit()
return "Not Found", 404
@app.route("/c/<token>.png")
def canary_pixel(token):
canary_hit(token)
px = base64.b64decode("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")
return Response(px, mimetype="image/gif", headers={"Cache-Control": "no-store"})
@app.route("/api/canary/list")
def api_canary_list():
uid = current_user_id()
if not uid: return jsonify({"ok": False, "error": "login required"})
con = db()
rows = [dict(r) | {"hits": con.execute("SELECT COUNT(*) c FROM canary_hits WHERE canary_id=?", (r["id"],)).fetchone()["c"]} for r in con.execute("SELECT * FROM canaries WHERE user_id=? ORDER BY id DESC LIMIT 50", (uid,))]
return jsonify(rows)
# ---------- 6g. AGENT PASSPORT ----------
@app.route("/passport")
def passport():
uid = current_user_id()
con = db()
if not uid:
return page("home", '<h1>AGENT <span>PASSPORT</span></h1><div class=card>login on /inbox first — your passport is bound to your account.</div>')
u = con.execute("SELECT * FROM users WHERE id=?", (uid,)).fetchone()
n_sms = con.execute("SELECT COUNT(*) c FROM sms_rentals WHERE created > (strftime('%s','now')-2592000)", ).fetchone()["c"]
pas = has_pass(uid)
badge = {"holder": u["username"], "issued": u["created"], "pass_active": pas,
"tool_usage_30d": {"sms_rentals": n_sms}, "site": "auriga.thetempleofdoom.com", "v": 1,
"principles": ["no-KYC", "BTC-only", "agent-friendly"]}
body = f"""
<h1>AGENT <span>PASSPORT</span></h1><p class=sub>Machine-readable identity + trust badge for agents operating on AURIGA.</p>
{kv([("Holder", esc(u['username'])), ("Issued", time.strftime("%b %d %Y", time.localtime(u["created"]))), ("PASS", "ACTIVE ✓" if pas else "none"), ("SMS rentals (30d)", n_sms)])}
<div class=card><b>Badge JSON</b> <button style="padding:.2rem .6rem;font-size:.8rem" onclick="cp(document.getElementById('bp').textContent)">copy</button><br><pre id=bp style=white-space:pre-wrap>{json.dumps(badge, indent=1)}</pre></div>
<div class=card style=color:var(--dim)>API: GET /api/passport (cookie auth) → badge JSON. Embed in your agent's llms.txt / tool card.</div>"""
return page("home", body)
@app.route("/api/passport")
def api_passport():
uid = current_user_id()
if not uid: return jsonify({"ok": False, "error": "login required"})
con = db()
u = con.execute("SELECT * FROM users WHERE id=?", (uid,)).fetchone()
return jsonify({"holder": u["username"], "issued": u["created"], "pass_active": has_pass(uid), "site": "auriga.thetempleofdoom.com"})
# ---------- 7. INBOX (no-KYC site-only messaging) ---------- # ---------- 7. INBOX (no-KYC site-only messaging) ----------
def hash_pw(pw): return hashlib.scrypt(pw.encode(), salt=b"auriga-salt", n=16384, r=8, p=1).hex() def hash_pw(pw): return hashlib.scrypt(pw.encode(), salt=b"auriga-salt", n=16384, r=8, p=1).hex()
@@ -1181,6 +1446,10 @@ def index():
<div class=card><h3>◈ Burner Mail</h3><p style=color:var(--dim)>Receive-only mailboxes, 7d/$3 → 90d/$20, live countdown.</p><a href=/mail><button>Open</button></a></div> <div class=card><h3>◈ Burner Mail</h3><p style=color:var(--dim)>Receive-only mailboxes, 7d/$3 → 90d/$20, live countdown.</p><a href=/mail><button>Open</button></a></div>
<div class=card><h3>◈ PASS</h3><p style=color:var(--dim)>$10/mo all-access (3mo $25 · 1yr $80) — every tool, proxy rentals excluded.</p><a href=/pass><button>Open</button></a></div> <div class=card><h3>◈ PASS</h3><p style=color:var(--dim)>$10/mo all-access (3mo $25 · 1yr $80) — every tool, proxy rentals excluded.</p><a href=/pass><button>Open</button></a></div>
<div class=card><h3>◈ Inbox</h3><p style=color:var(--dim)>No-KYC site messaging with the operator.</p><a href=/inbox><button>Open</button></a></div> <div class=card><h3>◈ Inbox</h3><p style=color:var(--dim)>No-KYC site messaging with the operator.</p><a href=/inbox><button>Open</button></a></div>
<div class=card><h3>◈ Mail Forensics</h3><p style=color:var(--dim)>Real origin, SPF/DKIM/DMARC verdicts, spoof flags.</p><a href=/eh><button>Open</button></a></div>
<div class=card><h3>◈ Image Forensics</h3><p style=color:var(--dim)>EXIF + GPS + ELA — expose edits and hidden stego.</p><a href=/forensics><button>Open</button></a></div>
<div class=card><h3>◈ Canary Traps</h3><p style=color:var(--dim)>Tripwire links &amp; pixels — instant alerts when touched.</p><a href=/canary><button>Open</button></a></div>
<div class=card><h3>◈ Agent Passport</h3><p style=color:var(--dim)>Machine-readable trust badge for agents.</p><a href=/passport><button>Open</button></a></div>
<div class=card><h3>◈ Free Tools</h3><p style=color:var(--dim)>DNS, headers, JWT, hasher, generators.</p><a href=/tools><button>Open</button></a></div> <div class=card><h3>◈ Free Tools</h3><p style=color:var(--dim)>DNS, headers, JWT, hasher, generators.</p><a href=/tools><button>Open</button></a></div>
</div>""" </div>"""
return page("home", body) return page("home", body)