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""" +

VERDICT {esc(d.get('subject') or '(no subject)')}

+{kv([("From", esc(d.get('from'))), ("Envelope-from", esc(d.get('return_path'))), ("Reply-To", esc(d.get('reply_to') or '—')), ("Origin IP", origin)])} +
Authentication
{verdicts}

Spoof flags
{flags}
+
Relay chain (origin first){hops or 'no Received headers'}
""") + +@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""" +

IMAGE FORENSICS

EXIF dump, GPS extraction, date/software flags, error-level analysis (ELA) — spot edits, and sniff out OTHER people's stego.

+
+
🖼 drop an image
+
+
+ +
API: POST /api/forensics (image) → JSON: exif, gps, flags, ELA score.
""" + 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"{esc(k)}{esc(v)}" for k, v in rows) + gps_html = " ".join(f"
{esc(k)}: {esc(v)}
" for k, v in gps.items()) or "—" + import base64 as b64mod + ela_b64 = b64mod.b64encode(ela_png).decode() + return page("steg", f""" +

FORENSICS {esc(fn)}

+{kv([("Verdict", f'{verdict}'), ("ELA max diff", f"{maxdiff} (low=uniform=re-saved clean)"), ("EXIF", f"{len(rows)} tags"), ("Stego", "AURIGA payload present ✓" if stego else "none detected")])} +
Flags
{'
'.join(esc(x) for x in flags) or 'none'}
+
ELA (amplified 15×)
full PNG
+
EXIF table{rows_html or ''}
no EXIF
+
GPS{gps_html}
""") + +@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"{esc(c['tag'])}{SITE}/c/{c['token']} copy{SITE}/c/{c['token']}.png{hits}{'armed' if c['armed'] else 'triggered ⚠'}" + return f'
Your traps{trs or ""}
TagLinkPixelHitsStatus
none yet
' + +@app.route("/canary", methods=["POST"]) +def canary_create(): + uid = current_user_id() + if not uid: return page("track", "
login required
") + 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/") +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 {esc(ip)}{esc(where)} · device {esc(request.headers.get('User-Agent','')[:80])}", int(time.time()))) + con.commit() + return "Not Found", 404 + +@app.route("/c/.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", '

AGENT PASSPORT

login on /inbox first — your passport is bound to your account.
') + 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""" +

AGENT PASSPORT

Machine-readable identity + trust badge for agents operating on AURIGA.

+{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)])} +
Badge JSON
{json.dumps(badge, indent=1)}
+
API: GET /api/passport (cookie auth) → badge JSON. Embed in your agent's llms.txt / tool card.
""" + 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) ---------- 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():

◈ Burner Mail

Receive-only mailboxes, 7d/$3 → 90d/$20, live countdown.

◈ PASS

$10/mo all-access (3mo $25 · 1yr $80) — every tool, proxy rentals excluded.

◈ Inbox

No-KYC site messaging with the operator.

+

◈ Mail Forensics

Real origin, SPF/DKIM/DMARC verdicts, spoof flags.

+

◈ Image Forensics

EXIF + GPS + ELA — expose edits and hidden stego.

+

◈ Canary Traps

Tripwire links & pixels — instant alerts when touched.

+

◈ Agent Passport

Machine-readable trust badge for agents.

◈ Free Tools

DNS, headers, JWT, hasher, generators.

""" return page("home", body)