
Analysez et suivez les jetons OAuth 2.0, OIDC et Microsoft Entra ID à partir des captures de Burp, mitmproxy ou Chrome DevTools. Visualisez les cycles de vie des jetons, détectez les scopes à risque et exportez les jetons pour rejouer via un tableau de bord interactif.
Suivez les jetons OAuth 2.0, OIDC et Microsoft Entra ID à travers le trafic réseau capturé. Ingère les exports XML de Burp Suite, les fichiers de flux mitmproxy ou les flux live du Chrome DevTools Protocol dans une base de données SQLite unique, puis sert un tableau de bord web interactif pour filtrer les jetons, parcourir les échanges, repérer les scopes à risque, exporter les jetons pour rejeu et visualiser les cycles de vie des jetons sous forme de graphes Mermaid.
Statut : TATS est stable pour un usage personnel / en engagement. Optimisé pour l'écosystème Microsoft 365 / Entra (FOCI, BroCI/NAA, cookies de session ESTSAUTH, enrichissement entrascopes.com) mais fonctionne contre tout trafic OAuth/OIDC à peu près standard.
Lorsque vous proxifiez une longue session Microsoft 365 ou Azure via Burp / mitmproxy, la capture résultante est énorme et la plupart des outils soit :
Cet outil extrait chaque jeton access / refresh / id observé, les empreinte pour pouvoir corréler le même jeton entre les sources, décode les claims JWT, résout les GUID de client / ressource Microsoft contre entrascopes.com, et restitue l'ensemble du tableau sous forme d'un seul tableau de bord — y compris une vue des chaînes de refresh-token qui suit les échanges inter-applications FOCI et l'émission de jetons d'applications imbriquées BroCI.
Ce projet est destiné principalement à des fins de recherche et d'éducation mais fournit des options telles que l'aperçu de commandes et les fonctionnalités d'export de jetons qui peuvent soutenir certains outils offensifs.
ingest — export XML « Save items » de Burp Suitemitm — fichier de flux .mitm de mitmproxy (HTTP et trames WebSocket)cdp — attachement live à Chrome / Edge via le DevTools Protocol
(temps réel, capture le HTTP déchiffré TLS et les trames WebSocket
sans CA de proxy ; suit chaque onglet existant ET chaque onglet ouvert
pendant l'exécution via l'auto-attachement au niveau du navigateur)--append pour fusionner dans une base de données existante ; les jetons
sont upsertés (le nombre d'utilisations + la durée de vie observée s'accumulent), les événements et
les échanges sont ajoutés, et le source_tag de la ligne enregistre chaque passe
qui a vu le jeton.pip install mitmproxy).access_token, refresh_token, id_token) et
les heuristiques sur les noms de cookies déterminent le type de jeton.ESTSAUTH, ESTSAUTHPERSISTENT,
ESTSAUTHLIGHT, SignInStateCookie) sont explicitement reconnus comme
des jetons équivalents à des refresh (ils seraient sinon mal classés par
l'indice générique de cookie « auth »).foci
dans les réponses du point de terminaison de jeton.brk_client_id, brk_redirect_uri, et les schémas de redirection brk-<guid>://
dans le corps de la requête.--enrich récupère firstpartyscopes.json et
resources.json depuis https://entrascopes.com/ et résout les GUID appid /
azp / aud en noms conviviaux avec des liens cliquables.upn / preferred_username /
unique_name / email / name, avec repli sur sub@iss ou oid,
et exposition séparée des compartiments app-only et identité inconnue. Chaque
ligne d'identité affiche un badge captures lorsque l'utilisateur apparaît dans
≥2 source_tags (survie inter-captures, le signal de recherche phare de --append)
plus une plage first_seen → last_seen et un
bouton timeline qui met en évidence chaque jeton pour cet utilisateur sur
l'onglet Diagramme de séquence.appid / azp / client_id du corps de formulaire
/ brk_client_id / brk_nested_id) apparue dans
les échanges, avec badges FOCI / brokerable / broker / nested.aud observé, résolu en noms de
ressources entrascopes lorsque possible.tid distinctes avec comptages de jetons / utilisateurs / applications.scp / scope /
roles de chaque jeton contre une liste de surveillance curatée de permissions
Microsoft Graph à fort impact et de scopes de ressources Azure.(token, host) où
le jeton a été utilisé sur un hôte qui ne correspond pas à son claim aud
(suggère une fuite d'identifiants ou un usage abusif).amr) — distribution pwd / mfa / pop / smartcard.xms_cc=CP1),
la liaison proof-of-possession (claim cnf, avec détection de kid partagé
entre audiences), les exigences d'authentification step-up (acrs), et le niveau
de contexte d'authentification acr. Chaque ligne est cliquable et filtre l'
onglet Tokens pour ne montrer que les jetons portant ce marqueur.⚠ priv — le
signal de recherche d'expansion de privilèges de style FOCI / BroCI.source_tag pour voir combien de
lignes proviennent de chaque passe d'ingestion.roadtx describe,
roadtx auth, curl, Python requests, et PowerShell
Invoke-RestMethod.ws-frame-sent /
ws-frame-received, la source ws[body_json[<key>]], et un
ws_session_id qui regroupe toutes les trames d'une même connexion WebSocket.La base de données stocke des empreintes SHA-256 (12 premiers caractères hex) et un préfixe de 12 caractères de chaque jeton observé. Les chaînes de jetons complètes ne quittent jamais le fichier d'entrée.
Le contenu des claims JWT décodés (en-tête + payload, y compris oid, sub,
upn, email, tid, listes de scopes, etc.) est stocké verbatim par
défaut car c'est tout l'intérêt de l'analyse. Traitez la
base de données et toute URL de tableau de bord partagée comme sensibles dès que des JWT sont
présents.
--redact-claims (disponible sur ingest, mitm, et cdp)
remplace les valeurs de claims listées par des placeholders de hachage stables avant qu'elles
n'atteignent la base de données. La liste de champs par défaut couvre sub, oid,
upn, email, name, unique_name, preferred_username, emails,
mail, ipaddr, given_name, family_name. Passez une liste explicite
séparée par des virgules (par ex. --redact-claims sub,upn,oid) pour remplacer
la valeur par défaut. La même entrée correspond toujours au même placeholder, donc le
regroupement Utilisateurs / Tenants du tableau de bord fonctionne toujours sans révéler
l'utilisateur.
--store-tokens (disponible sur ingest, mitm, et cdp,
désactivé par défaut) opte pour l'écriture de la chaîne de jeton complète dans la
base de données afin que le tableau de bord puisse offrir :
.roadtools_auth et n'importe quelle sous-commande roadtx le récupère).roadtx describe, roadtx auth, curl,
Python requests, PowerShell Invoke-RestMethod — en utilisant les
claims tid, , et réels du jeton.dataclasses modernes). Testé sur 3.12.python -m tats directement.| Besoin | Installation |
|---|---|
Sous-commande mitm | pip install mitmproxy |
| Capture live depuis Chrome / Edge | Aucune — utilise un client WebSocket de la stdlib |
--enrich (entrascopes.com) | Aucune — utilise urllib.request |
Exécuter depuis un checkout (sans installation) :```bash git clone tats cd tats python -m tats --help
Le HTML / CSS / JS du tableau de bord se trouvent dans `tats/static/` et sont chargés
lors de la première importation, donc aucune étape de build n'est requise — exécutez simplement le module
directement depuis le checkout.
**Installer en tant que package (vous donne le script console `tats`) :**```bash
pip install . # core only
pip install .[mitm] # + mitmproxy flow file support
pip install .[test] # + pytest for the test suite
pip install .[all] # everything
Après l'installation, vous pouvez appeler l'outil par son nom court :```bash tats ingest engagement.xml -o tokens.db --enrich tats serve tokens.db
Si vous n'avez besoin que des chemins Burp / CDP, le fichier est entièrement autonome
avec la bibliothèque standard Python — aucune installation ni extras requis.
---
## Démarrage rapide
**Analyser un export XML Burp et ouvrir le tableau de bord :**```bash
tats ingest examples/fixture.xml -o tokens.db --enrich
tats serve tokens.db
Combiner une capture Burp avec un fichier de flux mitmproxy dans une seule base de données :```bash tats ingest engagement.xml -o tokens.db --enrich tats mitm chat-session.mitm -o tokens.db --enrich --append tats serve tokens.db
**Capture en direct depuis un navigateur Chrome (voit le HTTP déchiffré TLS + les trames WebSocket, aucun proxy CA nécessaire) — en laissant l'outil lancer le navigateur :**```bash
# Terminal 1 — auto-launch Chrome / Edge / Chromium / Brave
tats cdp -o tokens.db --enrich --launch-chrome
# Terminal 2 — open the dashboard (auto-refreshes every 5 s)
tats serve tokens.db
Le navigateur lancé est terminé et son profil temporaire est supprimé
lorsque vous faites Ctrl-C sur la commande cdp.
Si vous préférez vous attacher à un navigateur déjà en cours d'exécution, démarrez-le avec
--remote-debugging-port=9222 --user-data-dir=/tmp/cdp-profile et exécutez
cdp sans --launch-chrome.
Assainir une base de données avant de la partager (anonymisation des données personnelles) :```bash
tats ingest engagement.xml -o tokens.db
--enrich --redact-claims
La rédaction est stable en termes de contenu : des valeurs identiques correspondent à des espaces réservés identiques, de sorte que le regroupement par utilisateur du tableau de bord fonctionne toujours sans afficher l'utilisateur.
L'en-tête du tableau de bord affiche `live · updated <time>` une fois que les données commencent à affluer.
---
## Sous-commandes
Chaque sous-commande accepte `--help` pour obtenir la liste canonique des options. Les notes ci-dessous expliquent *quand* et *comment* vous utiliseriez chacune d'elles.
### Options globales
Elles s'appliquent à chaque sous-commande et se placent *avant* le nom de la sous-commande :
* `-v` / `--verbose` — ajoute des lignes de journal INFO (statut d'enrichissement, compteurs de rédaction à l'ingestion). `-vv` ajoute DEBUG (chaque requête au serveur).
* `-q` / `--quiet` — supprime les lignes de journal INFO ; seuls les WARNING et ERROR apparaissent. La ligne de sortie finale destinée à l'utilisateur (par ex. `wrote tokens.db (...)`) et tout diagnostic `error: …` ne sont pas affectés, de sorte que vous verrez toujours ce qui compte depuis un script.
* `--version` — affiche la version de l'outil et quitte.
### `ingest` — export XML de Burp Suite
Lit un XML « Save items » (Proxy → HTTP history → clic droit → Save items). Les fichiers de projet binaires `.burp` ne sont **pas** pris en charge — le format est propriétaire et instable selon les versions de Burp ; exporter les éléments qui vous intéressent est le flux de travail pris en charge.```bash
tats [-v|-q] ingest <burp_items.xml> -o tokens.db \
[--enrich] [--enrich-cache-dir DIR] [--no-enrich-cache] \
[--append] [--source-tag TAG] [--no-progress] \
[--redact-claims [CLAIMS]] [--no-serve-hint]
Exemples :```bash
tats ingest burp.xml -o tokens.db --enrich
tats ingest day2.xml -o tokens.db --append
--source-tag burp:day2
### `mitm` — fichier de flux `.mitm` de mitmproxy
Lit un fichier de flux produit par `mitmdump`, `mitmproxy` ou `mitmweb`. C'est
le seul chemin d'ingestion qui capture les **trames WebSocket** sans session
navigateur active — les fichiers de flux préservent chaque charge utile de
trame texte / binaire.```bash
tats [-v|-q] mitm <flow_file.mitm> -o tokens.db \
[--enrich] [--enrich-cache-dir DIR] [--no-enrich-cache] \
[--append] [--source-tag TAG] [--no-progress] \
[--redact-claims [CLAIMS]] [--no-serve-hint]
Nécessite pip install mitmproxy. L'outil émettra une erreur claire si le
paquet est manquant.
Capturez un fichier de flux avec mitmproxy :```bash mitmdump -w session.mitm
tats mitm session.mitm -o tokens.db --enrich
### `cdp` — attachement en direct à Chrome / Edge
Se connecte à un navigateur de la famille Chromium en cours d'exécution via le DevTools Protocol
et diffuse les événements `Network.*` dans la base de données. Capture les requêtes /
réponses HTTP (avec les corps récupérés via `Network.getResponseBody`), les upgrades
WebSocket, et chaque trame WebSocket dans les deux sens. Le tampon est vidé vers
la base de données tous les N événements (25 par défaut), de sorte que le polling de 5 secondes
du tableau de bord récupère les nouveaux jetons quelques secondes après que le navigateur a effectué la
requête.```bash
tats [-v|-q] cdp [-o tokens.db] \
[--host 127.0.0.1] [--port 9222] [--target ID] \
[--launch-chrome [PATH]] [--flush-every N] \
[--enrich] [--append] [--redact-claims [CLAIMS]]
--launch-chrome)```bashtats cdp -o tokens.db --launch-chrome
tats cdp -o tokens.db
--launch-chrome /opt/google/chrome-canary/chrome
Le navigateur lancé s'exécute avec `--remote-debugging-port=<port>` et un
répertoire de données utilisateur temporaire fraîchement créé. Lorsque vous arrêtez la commande `cdp` (Ctrl-C),
le navigateur est terminé et le profil temporaire est supprimé.
### S'attacher à un navigateur déjà en cours d'exécution
Démarrez le navigateur vous-même, avec un profil vierge, puis exécutez `cdp` sans
`--launch-chrome` :```bash
# Windows
"C:\Program Files\Google\Chrome\Application\chrome.exe" ^
--remote-debugging-port=9222 ^
--user-data-dir="%TEMP%\cdp-profile"
# macOS
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--remote-debugging-port=9222 --user-data-dir=/tmp/cdp-profile
# Linux
google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/cdp-profile
Un user-data-dir distinct évite de s'attacher à un profil personnel et
empêche le navigateur en cours d'exécution de refuser le drapeau de débogage.
Par défaut, cdp s'attache au niveau du navigateur et suit chaque onglet
qui existe au démarrage AINSI que chaque onglet ouvert pendant l'exécution
(window.open, Ctrl-clic, bouton nouvel onglet). Tous les onglets partagent
l'unique WebSocket via le multiplexeur de session du protocole plat CDP, donc
ouvrir ou fermer des onglets pendant la capture est entièrement pris en charge.
Chaque attachement / détachement d'onglet affiche une note d'une ligne sur
stderr au niveau INFO.
Si vous préférez vous fixer sur un seul onglet et que l'attachement se termine lorsque cet onglet se ferme, listez les cibles disponibles :```bash curl http://127.0.0.1:9222/json/list
…puis passez `--target <id>`.
Appuyez sur Ctrl-C pour arrêter. La fin de tout tampon en cours est vidée vers la
base de données avant que le processus ne se termine.
### `serve` — tableau de bord web
Lit une base de données existante et sert une interface web monopage sur
`127.0.0.1:8765`. Le serveur est en lecture seule ; il n'écrit jamais dans la
base de données, il est donc sûr de l'exécuter en parallèle d'une ingestion
`cdp` ou `mitm` en cours.```bash
tats serve <tokens.db> \
[--host 127.0.0.1] [--port 8765] [--no-browser]
Exemples :```bash
tats serve tokens.db
tats serve tokens.db --port 9000 --no-browser
tats serve tokens.db --host 0.0.0.0
> **Avertissement :** l'interface web expose les charges utiles JWT décodées (claims), les empreintes de jetons, la chronologie d'activité et les graphiques Mermaid à quiconque peut atteindre l'adresse d'écoute. Si vous avez ingéré avec `--store-tokens`, elle expose également les **jetons bruts complets** via `/api/token/<fp>` et `/api/export?fps=...`. Il n'y a **aucune authentification**. Gardez `--host` sur `127.0.0.1` sauf si vous en décidez spécifiquement autrement.
#### Export prêt pour le rejeu
Lorsque la base de données a été construite avec `--store-tokens`, chaque jeton développé dans l'onglet Tokens obtient une rangée d'actions en un clic :
* **Copier le brut** — la chaîne de jeton complète dans le presse-papiers.
* **Copier l'en-tête Bearer** — `Authorization: Bearer <token>`, prêt à coller.
* **Copier l'exemple curl** — une commande d'une ligne qui cible l'`aud` du jeton (ou son hôte émetteur) avec l'en-tête bearer attaché.
* **Télécharger le JSON** — un fichier JSON à jeton unique contenant le brut, les claims, les événements observés et les échanges.
* **Copier en roadtx** — la forme JSON d'un cache de jetons roadtools (`tokenType`, `accessToken` / `refreshToken` / `idToken`, `expiresOn`, `tenantId`, `_clientId`, `resource`, `foci`, `scope`). Collez directement dans un fichier `.roadtools_auth`.
* **Télécharger .roadtools_auth** — même charge utile, téléchargée sous forme de fichier. Renommez-le en `.roadtools_auth` (ou passez-le via `roadtx <cmd> --tokens-file`) et n'importe quelle sous-commande roadtx le récupère.
La barre d'outils de l'onglet Tokens dispose également de **Exporter la sélection pour le rejeu**, qui appelle `/api/export?fps=fp1,fp2,...` et télécharge un document JSON unique contenant jusqu'à 200 jetons (brut, claims, événements) en un seul lot. Sans `--store-tokens`, les mêmes boutons affichent une indication pour réingérer avant que l'export prêt pour le rejeu ne devienne possible.
#### Aperçu des commandes
Chaque jeton développé dispose également d'un bloc **Aperçu des commandes** repliable qui pré-remplit les invocations de rejeu / d'inspection les plus courantes en utilisant les claims réels du jeton (et la valeur brute complète lorsque `--store-tokens` est activé). Chaque extrait dispose d'un bouton Copier en un clic. Le mélange exact dépend du type de jeton :
* **Tout JWT :** `roadtx describe -t '<token>'` (décoder sans réseau).
* **Jetons d'actualisation :**
* `roadtx auth --refresh-token '...' -c <client_id> -t <tenant_id>` — échanger un jeton d'actualisation contre de nouveaux jetons d'accès.
* `curl -X POST .../oauth2/v2.0/token` — l'équivalent OAuth pour les utilisateurs qui n'utilisent pas roadtx.
* **Jetons d'accès / id / inconnus :**
* `curl -H 'Authorization: Bearer ...' '<aud>'`
* Python `requests.get(...)` avec l'en-tête bearer défini.
* PowerShell `Invoke-RestMethod` avec le même en-tête.
* **Toujours :** l'objet JSON à déposer dans `.roadtools_auth`.
Lorsque `--store-tokens` est désactivé, les extraits s'affichent avec `<TOKEN>` comme espace réservé afin que le panneau reste utile comme référence de documentation.
---
## L'interface web en détail
### Navigation supérieure
`Summary | Tokens | Exchanges | FOCI | BroCI | Graph | Sequence`
Chaque onglet est rendu indépendamment à partir du même instantané en mémoire de `/api/data`. Le changement d'onglet est instantané ; les diagrammes de graphe et de séquence sont re-rendus à la demande et respectent la sélection en cours dans l'onglet Tokens.
### Summary
Des tuiles de statistiques en haut (tokens / access / refresh / id / unknown / used / unused / events / exchanges / FOCI exchanges / BroCI exchanges / hosts) suivies d'une grille de cartes décrites dans
[Fonctionnalités → Cartes du tableau de bord](#dashboard-cards).
Cliquez sur n'importe quelle ligne de n'importe quelle carte pour accéder à un onglet Tokens pré-filtré — par exemple, cliquer sur une ligne de locataire filtre l'inventaire sur les jetons portant ce `tid`.
### Tokens
Inventaire filtrable et triable. La sélection multiple pilote les boutons de mise en surbrillance / isolement / séquence. Le développement d'une ligne affiche le JWT décodé complet (en-tête + charge utile en JSON brut), chaque événement impliquant ce jeton, et chaque échange où il était une entrée ou une sortie.
### Exchanges
Liste triable de chaque échange jeton-contre-jeton détecté — rotations de jetons d'actualisation, échanges croisés FOCI et échanges d'applications imbriquées BroCI. La colonne BroCI affiche les ID client broker + imbriqué côte à côte avec la preuve qui a déclenché la détection.
### FOCI
Deux tableaux : chaque jeton d'actualisation étiqueté avec une famille FOCI (actuellement Microsoft n'émet que `"1"`), et chaque échange dont la réponse portait le champ `foci`.
### BroCI
Les échanges d'authentification d'application imbriquée. Pour chacun : l'application broker (`brk_client_id`), le client imbriqué (`client_id`), la preuve qui a déclenché la détection (`brk_client_id`, `brk_redirect_uri`, URI de redirection `brk-<guid>://`), et les empreintes des jetons d'entrée / sortie.
### Graph
`flowchart LR` Mermaid des relations jeton ↔ service. Les jetons d'actualisation sont dessinés comme des cylindres, les jetons d'accès / id comme des stades. Les arêtes montrent l'émission, la présentation, l'échange et la rotation. La mise en surbrillance (depuis l'onglet Tokens) ajoute un accent jaune ; l'isolement re-rend le graphe avec uniquement les jetons sélectionnés et les jetons avec lesquels ils échangent.
### Sequence
Diagramme de séquence Mermaid de chaque événement dans l'ordre de capture. Sélectionner un seul jeton n'affiche que sa séquence ; en sélectionner plusieurs conserve la vue complète mais met en vedette les jetons sélectionnés. Plafond d'événements maximum configurable (par défaut 200 ; les diagrammes de séquence Mermaid deviennent illisibles au-delà de quelques centaines de messages).
---
## Prise en charge spécifique à Microsoft
### Family of Client IDs (FOCI)
Microsoft permet qu'un jeton d'actualisation émis pour une application d'une « famille » soit échangé au point de terminaison de jeton par **n'importe quelle autre application** de la même famille. L'outil détecte FOCI sur le réseau en analysant le JSON de réponse du point de terminaison de jeton pour un champ `foci` (actuellement toujours `"1"` pour la seule famille connue). Les jetons d'actualisation émis dans une telle réponse sont étiquetés avec l'identifiant de famille et exposés dans l'onglet **FOCI** dédié.
Si `--enrich` est activé, la colonne app de l'inventaire expose également l'indicateur `foci: true/false` de `firstpartyscopes.json` — notez que cela peut être en désaccord avec la détection sur le réseau (le jeu de données entrascopes est parfois conservateur). Le champ `foci` sur le réseau est toujours le signal faisant autorité.
### Brokered Client Init / Nested App Authentication (BroCI / NAA)
Les compléments Office, les applications Teams et le portail Azure utilisent NAA pour acquérir des jetons pour un client imbriqué via une application broker. L'outil détecte cela côté requête via :
* le paramètre de formulaire `brk_client_id` (GUID de l'application broker),
* le paramètre de formulaire `brk_redirect_uri` (URI de redirection réelle du broker),
* un `redirect_uri` de la forme `brk-<guid>://...` (où `<guid>` est le broker).
Le claim `appid` / `azp` du jeton d'accès résultant est le client imbriqué ; le broker n'apparaît que sur le réseau — jamais comme claim JWT. Le tableau de bord expose clairement les deux côtés.
### Cookies de session `ESTSAUTH`
`ESTSAUTH`, `ESTSAUTHPERSISTENT`, `ESTSAUTHLIGHT` et `SignInStateCookie` sont des cookies de session Microsoft Entra qui ne voyagent pas dans `Authorization: Bearer` mais sont utilisés par le navigateur pour générer de nouveaux jetons d'accès via des flux d'authentification silencieuse. L'outil les étiquette comme `refresh` (leur rôle fonctionnel) au lieu de laisser la règle générique de sous-chaîne `auth` les mal classer comme `access`.
### Enrichissement entrascopes.com (`--enrich`)
Récupère et met en cache `firstpartyscopes.json` (~2,8 Mo ; 504 applications first-party avec leur indicateur FOCI, URI de redirection, scopes et capacité broker) et `resources.json` (~170 Ko ; plus de 1 750 mappages ressource → nom d'affichage) depuis <https://entrascopes.com/>. Le cache réside dans :
| Variable | Défaut |
|---|---|
| `$TATS_CACHE` | (priorité la plus élevée ; `$BURP_TOKEN_TRACKER_CACHE` est honoré comme solution de repli pour une migration d'une version) |
| `$XDG_CACHE_HOME/tats` | (Linux/macOS) |
| `%LOCALAPPDATA%\tats\cache` | (Windows) |
| `~/.cache/tats` | (repli) |
Le TTL est de 7 jours. Utilisez `--no-enrich-cache` pour forcer une nouvelle récupération. Le cache est réutilisé comme repli obsolète lorsque l'outil est exécuté hors ligne.
Lorsque `--enrich` est activé, chaque GUID `appid` / `azp` / `client_id` et chaque claim `aud` GUID-ou-URL est résolu en un nom convivial avec un lien cliquable `https://entrascopes.com/?appId=<guid>`.
---
## Architecture
### One-shot : fichier → DB → interface web```
burp.xml ─┐
.mitm ─┼─→ Tracker ─→ ingest_to_db ─→ tokens.db ─→ Store ─→ /api/data ─→ dashboard
CDP WS ─┘ ▲ │
(live, repeated) └───── --append upserts on every flush ─┘
Chaque chemin source produit le même objet Tracker. ingest_to_db
le transforme en lignes dans la base de données. Store lit la base de données pour le
serveur HTTP, qui expose du JSON via /api/data, /api/meta,
/api/token/<fp>, /api/export, /api/graph et /api/sequence.
tokens (clé primaire fp) — empreinte, préfixe d'échantillon, type,
format, durée de vie observée, en-tête / payload JWT en JSON, champs
d'enrichissement, champs dérivés (user_identity, exp_unix, tenant_id,
scopes_text), source_tag séparé par des virgules, raw (chaîne de jeton
complète, NULL sauf si ingéré avec --store-tokens), et
security_features (JSON compact décrivant les marqueurs CAE / PoP /
step-up détectés — voir la carte Security features).
Les bases de données v2 / v3 plus anciennes migrent automatiquement lorsqu'elles sont rouvertes en mode append :
v2 → v3 ajoute la colonne nullable raw ; v3 → v4 ajoute la colonne nullable
security_features et la remplit à partir du jwt_payload_json stocké de chaque jeton
lors de la première ouverture. Les lignes préexistantes conservent les deux
colonnes à leurs valeurs précédentes.events — chaque interaction de jeton observée : requête /
réponse HTTP ou trame WebSocket. Rôles : issued / returned /
presented / used / exchanged-in / ws-frame-sent /
ws-frame-received. Porte ws_session_id pour regrouper les trames
au sein d'une connexion.exchanges — lorsqu'une requête portant un jeton vers un endpoint de jeton
a produit de nouveaux jetons dans sa réponse. Enregistre les métadonnées FOCI / BroCI.exchange_inputs, exchange_outputs — empreintes de jetons de
chaque côté de chaque échange.hosts — libellés host:port distincts.meta — version du schéma, liste des sources, generated_at, last_modified
(utilisé par le polling en direct du tableau de bord), compteurs.Chaque ligne écrite dans la base de données porte un source_tag — par défaut
burp:<filename>, mitm:<filename>, ou cdp:<host>:<port>, mais
surchargeable via --source-tag. Lorsque la même empreinte est vue par
plus d'une passe d'ingestion, le champ source_tag s'accumule sous forme de
liste séparée par des virgules, afin que la carte Sources du tableau de bord puisse afficher
la provenance de chaque jeton.
--append conserve une base de données existante et y fusionne via UPSERT pour
les jetons (les compteurs d'utilisation + la durée de vie observée s'accumulent, les types inconnus
sont mis à niveau) et INSERT pour les événements / échanges (avec leurs numéros seq
décalés au-delà du maximum existant, afin que la chronologie d'activité reste
monotone). Une incompatibilité de version de schéma refuse la fusion pour prévenir une perte
silencieuse de données.
L'endpoint /api/meta du serveur web renvoie la table meta (~200
octets). Le tableau de bord l'interroge toutes les 5 secondes et ne récupère à nouveau
l'intégralité de /api/data que lorsque last_modified change. Le chemin d'ingestion cdp vide son tracker en mémoire vers la base de données tous les 25 événements par défaut, afin
que la latence en temps réel entre une requête du navigateur et une mise à jour du tableau de bord soit
généralement < 10 secondes.
.burp ne sont pas pris en charge. Utilisez Save items pour
produire le XML que l'outil consomme.alg=none et les attaques de confusion de clé
sont hors périmètre. Utilisez un auditeur JWT dédié pour cela.unknown et sont masqués par défaut
sauf si --include-unknown est défini sur l'ancien flag (Burp uniquement).--enrich effectue des requêtes HTTP sortantes vers
https://entrascopes.com/. Ignorez le flag si votre environnement ne
le permet pas.| Symptôme | Cause probable | Correctif |
|---|---|---|
error: could not parse <file> as XML | Tentative d'ingestion d'un fichier de projet .burp binaire | Dans Burp : Proxy → HTTP history → sélectionner les éléments → clic droit → Save items |
error: no <item> elements found | Le XML n'a pas été produit par Save items de Burp | Réexportez depuis Burp ; l'élément racine doit être <items> |
error: cannot append to DB with schema_version 1 | La base de données a été créée par une version antérieure | Supprimez la base de données et réingérez les sources d'origine ; la migration de schéma n'est intentionnellement pas automatique |
error: the 'mitm' source needs the mitmproxy Python package | mitmproxy n'est pas installé | pip install mitmproxy |
error: cannot reach Chrome at 127.0.0.1:9222 | Chrome n'a pas été démarré avec --remote-debugging-port | Voir l'incantation de lancement dans cdp subcommand |
| CDP s'attache mais aucun événement ne circule | La page n'a pas encore effectué de requêtes réseau, ou toute l'activité est dans un OOPIF / worker (non auto-attaché) | Rechargez la page ; confirmez que les onglets ont été enregistrés (cherchez les lignes de log tab attached: … sur stderr) |
no browser-level webSocketDebuggerUrl at /json/version | La version de Chrome est trop ancienne pour le CDP au niveau navigateur, ou il a renvoyé une forme incorrecte | Mettez à jour Chrome, ou passez --target <id> pour utiliser l'attachement mono-onglet hérité |
target … has no webSocketDebuggerUrl | Un autre débogueur (par ex. la fenêtre DevTools) est déjà attaché | Fermez DevTools, ou attachez-vous à une autre cible |
Le tableau de bord affiche Failed to load /api/data | Le serveur ne peut pas lire le fichier de base de données | Vérifiez que le chemin de la base de données est correct, que le fichier est lisible et que la version du schéma correspond |
| Les mises à jour en direct cessent d'arriver | Le processus cdp s'est arrêté ou le vidage du tampon réseau ne s'est pas encore déclenché |
Deux constructeurs de fixtures se trouvent dans examples/ :```bash
python examples/make_fixture.py examples/fixture.xml tats ingest examples/fixture.xml -o tokens.db --enrich
python examples/make_mitm_fixture.py examples/fixture.mitm tats mitm examples/fixture.mitm -o tokens.db --enrich --append
Après les deux exécutions, `tokens.db` contient 15 jetons (11 de Burp + 4 de
mitmproxy), 23 événements dont un événement de trame WebSocket, et 3
échanges.
### Suite de tests```bash
pip install .[test]
pytest
La suite couvre l'extraction de jetons, l'analyse JWT, la classification des cookies de session Microsoft, la détection FOCI / BroCI, le résumé des revendications, la rédaction des PII, le chemin d'ingestion XML Burp avec la sémantique UPSERT en mode ajout, et le chemin d'ingestion des trames WebSocket mitmproxy (automatiquement ignoré lorsque la dépendance optionnelle mitmproxy est absente).```text
$ pytest tests/
============================= test session starts =============================
…
======================== 62 passed in 1.4s =================================
### Exécution du serveur au premier plan```bash
tats serve tokens.db --no-browser
…et ouvrez http://127.0.0.1:8765 manuellement. Le serveur journalise chaque
requête et toute erreur de gestionnaire sur stderr.
| Chemin | Rôle |
|---|---|
tats/__init__.py | L'outil complet — parseurs, couche BD, serveur HTTP, client CDP ; charge le tableau de bord depuis tats/static/ |
tats/__main__.py | Point d'entrée pour python -m tats ; même logique que le script console tats installé |
tats/static/index.html | Squelette HTML du tableau de bord avec les espaces réservés {{CSS}} / {{JS}} |
tats/static/style.css | Style du tableau de bord — à modifier avec vos outils CSS habituels |
tats/static/app.js | Logique du tableau de bord — à modifier avec vos outils JS habituels (LSP / lint / formateur) |
pyproject.toml | Métadonnées d'empaquetage, extras optionnels ([mitm], [test], [all]), point d'entrée console |
LICENSE | GNU General Public License v3 |
README.md | Ce fichier |
examples/ | Captures synthétiques + scripts de construction de fixtures (voir examples/README.md) |
examples/make_fixture.py | Générateur XML Burp synthétique |
examples/make_mitm_fixture.py | Générateur de fichier de flux mitmproxy synthétique |
examples/fixture.xml | Fixture XML Burp pré-construite |
examples/fixture.mitm | Fixture de flux mitmproxy pré-construite |
tests/ | Suite pytest (à exécuter avec pytest) |
La structure en fichier unique est délibérée : l'outil est conçu pour être lu, audité et intégré dans des investigations par toute personne disposant de Python installé. Il n'y a pas de configuration cachée, pas d'arbre de dépendances à évaluer, et aucune surface autre que le fichier lui-même.
Si vous modifiez les sous-commandes, le schéma, les cartes du tableau de bord ou la surface
d'API publique (options CLI, points de terminaison /api/*), mettez à jour les sections pertinentes
de ce fichier dans la même modification. Les sections les plus susceptibles de dériver :
GNU General Public License v3.0 ou ultérieure — texte intégral dans le
fichier LICENSE à la racine du dépôt. Le code source du script porte l'en-tête court
standard pointant vers le même texte.
Vous pouvez redistribuer et/ou modifier l'outil selon les termes de la GPL v3 (ou toute version ultérieure, à votre choix). Il est distribué sans aucune garantie ; consultez la LICENSE pour les termes complets.
appidaud/api/export?fps=... renvoyant jusqu'à 200 jetons (raw, claims,
événements, échanges) dans un seul bundle JSON pour l'outillage en aval.Activer ceci transforme la base de données en un identifiant en gros — chaque
octet nécessaire pour rejouer n'importe quelle session capturée s'y trouve. Combinez avec
--redact-claims pour nettoyer la vue JWT décodée, mais sachez que le
jeton brut porte toujours les claims non expurgés encodés à l'intérieur.
Lorsque l'option est désactivée, le bloc d'aperçu de commandes du tableau de bord
s'affiche toujours, juste avec <TOKEN> comme placeholder pour qu'il fonctionne comme une
référence de syntaxe ; les boutons d'export affichent un indice pour ré-ingérer.
Vérifiez le terminal cdp pour les erreurs ; réduisez --flush-every pour des mises à jour plus réactives |