| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283 |
- """Extract JWT token from 996box Electron app's localStorage (LevelDB).
- The app's web UI stores the login session in:
- C:\\Users\\<user>\\AppData\\Roaming\\996传奇盒子\\Local Storage\\leveldb\\
- Key is 'React-ant-Admin-user' (the redux user state from chunk.js code).
- Value is JSON: {"token": "eyJ...", "userinfo": {...}}
- We search all .ldb files for the JWT prefix (eyJ) and extract the full token.
- """
- import json
- import re
- import sys
- from pathlib import Path
- # 996box Electron app's userData path
- APPDATA = Path.home() / "AppData" / "Roaming"
- USER_DATA = APPDATA / "996传奇盒子"
- LEVELDB_DIR = USER_DATA / "Local Storage" / "leveldb"
- # JWT pattern: header.payload.signature (3 base64url segments)
- JWT_PATTERN = re.compile(r"eyJ[A-Za-z0-9_\-]{20,}\.eyJ[A-Za-z0-9_\-]{20,}\.[A-Za-z0-9_\-]{20,}")
- # Also try base64-without-dots (in case storage truncates dots)
- LOOSE_PATTERN = re.compile(r'"token"\s*:\s*"(eyJ[^"]{50,})"')
- # Loose: just look for any long eyJ string
- LONG_EYJ = re.compile(rb"eyJ[A-Za-z0-9_\-\.]{80,}")
- def find_tokens_in_ldbs() -> list[dict]:
- """Scan all .ldb files, return candidate tokens."""
- candidates = []
- for ldb_file in sorted(LEVELDB_DIR.glob("*.ldb")):
- try:
- data = ldb_file.read_bytes()
- except Exception as e:
- print(f" [warn] can't read {ldb_file.name}: {e}")
- continue
- # Search for JWT-shaped strings
- for m in JWT_PATTERN.finditer(data.decode("utf-8", errors="ignore")):
- candidates.append({"file": ldb_file.name, "token": m.group(0), "kind": "jwt-3part"})
- # Also try loose match (in case of escaping)
- for m in LONG_EYJ.finditer(data):
- t = m.group(0).decode("utf-8", errors="ignore")
- # Filter to JWT-shaped (3 dots) or plausibly long single
- if t.count(".") == 2 and len(t) > 100:
- if not any(c["token"] == t for c in candidates):
- candidates.append({"file": ldb_file.name, "token": t, "kind": "loose-3dot"})
- elif len(t) > 200:
- # Very long base64 - could be truncated JWT
- if not any(c["token"].startswith(t[:50]) for c in candidates):
- candidates.append({"file": ldb_file.name, "token": t, "kind": "long-eyJ"})
- return candidates
- def main() -> int:
- print(f"=== Scanning {LEVELDB_DIR} for JWT tokens ===\n")
- if not LEVELDB_DIR.exists():
- print(f"[FAIL] {LEVELDB_DIR} does not exist")
- return 1
- candidates = find_tokens_in_ldbs()
- if not candidates:
- print("[!] No JWT-like strings found in LevelDB files")
- print(" Possible reasons:")
- print(" - 996box has never been logged in successfully on this machine")
- print(" - localStorage was cleared")
- print(" - Files are DPAPI-encrypted (Chrome v80+ with --enable-encryption)")
- return 0
- print(f"Found {len(candidates)} candidate(s):\n")
- for i, c in enumerate(candidates[:5], 1):
- t = c["token"]
- kind = c["kind"]
- print(f" [{i}] kind={kind} file={c['file']}")
- print(f" token[:40] = {t[:40]}...")
- print(f" length = {len(t)}")
- print(f" 3-part JWT = {'YES' if t.count('.') == 2 else 'NO'}")
- print()
- return 0
- if __name__ == "__main__":
- sys.exit(main())
|