
Kali365 - EvilTokens Replica
Un server di phishing basato su device-code per l'emulazione di avversari. Cattura i token OAuth di Microsoft 365 tramite il flusso Device Authorization Grant. Ispirato da GraphSpy.
Avviso — Solo per uso autorizzato. Questo strumento è destinato a penetration tester e red teamer che operano con esplicita autorizzazione scritta. Un uso improprio può violare le leggi contro le frodi informatiche.
microsoft.com/devicelogin.Backend Python (una porta per campagna) + front-end nginx + persistenza SQLite.
main_server.py ← punto di ingresso unificato (esegui questo)
db.py ← strato di persistenza SQLite
html_enroll.py ← costruttore pagina di iscrizione vittima
html_search.py ← costruttore pagina di ricerca post-autenticazione vittima
css_shared.py ← CSS condiviso per interfaccia operatore
nginx_helper.py ← genera e ricarica automaticamente la configurazione del proxy inverso nginx
nginx.conf ← configurazione nginx di base (include /etc/nginx/conf.d/*.conf)
Quando una campagna viene creata con un dominio impostato, nginx_helper.py scrive automaticamente un blocco server per dominio in NGINX_CONF (/etc/nginx/conf.d/easytokens.conf di default) e ricarica nginx. Il traffico delle vittime in arrivo sulla porta 80 per quel dominio viene proxyzato all'istanza Python corretta.
nginx.conf rifiuta qualsiasi richiesta il cui header Host non corrisponda a una campagna nota (return 444), impedendo l'esposizione accidentale del backend Python.NGINX_CONF='' o NGINX_RELOAD_CMD='' per disabilitare la gestione automatica di nginx.docker compose up --build
Porte esposte da Docker:
| Porta | Scopo |
|---|---|
80 | nginx — traffico vittime basato su dominio |
3000 | Python — interfaccia operatore + accesso diretto vittima |
http://localhost:3000/http://localhost:3000/apphttp://<campagna-dominio>/ (instradate tramite nginx sulla porta 80)I token catturati e i log sono persistenti su volumi Docker:
| Volume | Percorso nel container | Scopo |
|---|---|---|
easytokens-data | /data/easytokens.db | Database SQLite |
./logs | /logs/ | File di log per richiesta |
Requisiti: Python 3.12+, nessun pacchetto di terze parti.
python main_server.py
La directory php-relay/ contiene un contenitore Apache/PHP autonomo che funge da nodo distribuito rivolto alla vittima. Serve la stessa pagina di iscrizione device-code del server principale, effettua poll diretto di Microsoft e inoltra i token catturati al pannello principale tramite POST /webhook.
checkin.php esegue un POST su POST /relay/checkin del pannello principale, autenticandosi con RELAY_SECRET. Il pannello registra il nodo e restituisce un node_id.index.php), che avvia un nuovo flusso device-code.poll.php. Quando un token viene catturato, poll.php lo inoltra a POST /webhook del pannello principale, etichettato con il node_id.Configura in php-relay/.env:
Entrambi i contenitori contemporaneamente (consigliato):
# da EasyTokens/
docker compose -f docker-compose.yml -f php-relay/docker-compose.yml up --build
Eseguire entrambi con un singolo merge -f li mette sulla stessa rete Docker, quindi MAIN_SERVER_URL=http://easytokens:3000 viene risolto correttamente.
Accesso:
| URL | Scopo |
|---|---|
http://localhost:8082/ | Pagina di iscrizione vittima del relay |
http://localhost:3000/app/nodes | Vista operatore dei nodi relay registrati |
Solo relay (pannello remoto):
cd php-relay
# Imposta MAIN_SERVER_URL sull'URL pubblico del pannello in .env
docker compose up --build
openssl rand -hex 32RELAY_SECRET=<segreto> in EasyTokens/.env (viene raccolto dal pannello principale tramite docker-compose.yml)php-relay/.envDue tabelle nel database SQLite:
tokens — ogni token di accesso/refresh catturato (UPN, access token, refresh token, id token, scope, origine, timestamp cattura)device_codes — campagne avviate dall'operatore (codice utente, codice dispositivo, URL di verifica, stato, ID token collegato, porta, dominio)MIT — Copyright (c) 2026 Casey Smith
| Metodo | Percorso | Destinatario | Descrizione |
|---|
GET | / | Vittima | Pagina di iscrizione device-code (tema Dunder Mifflin) |
POST | /poll | Vittima (JS) | Poll dell'endpoint token di Microsoft; salva il token in caso di successo |
GET | /search?s=<id> | Vittima | Pagina di ricerca OneDrive/email post-autenticazione |
GET | /app | Operatore | Dashboard — statistiche + catture recenti |
GET | /app/device-codes | Operatore | Tutte le campagne con stato del poll in tempo reale |
POST | /app/device-codes/new | Operatore | Crea una nuova campagna device-code |
GET | /app/tokens | Operatore | Tutti i token catturati |
GET | /app/search?id=<id> | Operatore | Ricerca OneDrive + email per un token specifico |
POST | /webhook | Nodo relay | Ingestisce un token da un nodo relay autenticato |
POST | /relay/checkin | Nodo relay | Registra un nodo relay; restituisce node_id |
| Variabile | Default | Descrizione |
|---|
PORT | 3000 | Porta di ascolto per il server Python |
PORT_MAX | 3010 | Limite superiore dell'intervallo di porte esposte da Docker |
DB_PATH | /data/easytokens.db | Percorso del database SQLite |
LOG_DIR | /logs | Directory dei log per richiesta |
NGINX_CONF | /etc/nginx/conf.d/easytokens.conf | Percorso in cui nginx_helper scrive la configurazione generata; impostare a '' per disabilitare |
NGINX_RELOAD_CMD | nginx -s reload | Comando shell per ricaricare nginx dopo modifiche alla configurazione; impostare a '' per disabilitare |
RELAY_SECRET | (non impostato) | Segreto condiviso per l'autenticazione del nodo relay. Generalo con openssl rand -hex 32. Deve corrispondere a RELAY_SECRET in php-relay/.env. Lascialo non impostato per disabilitare il supporto relay. |
| Variabile | Default | Descrizione |
|---|
MAIN_SERVER_URL | http://easytokens:3000 | URL del pannello principale raggiungibile dall'interno del contenitore relay |
RELAY_SECRET | (obbligatorio) | Deve corrispondere a RELAY_SECRET sul pannello principale |
RELAY_PORT | 8082 | Porta host su cui esporre il relay |
RELAY_LABEL | hostname | Nome leggibile visualizzato nel pannello per questo nodo |
CHECKIN_RETRIES | 10 | Tentativi di check-in prima di arrendersi |
CHECKIN_DELAY | 5 | Secondi tra i tentativi di check-in |
NODE_ID_FILE | /tmp/relay_node_id | Percorso in cui il node_id viene memorizzato dopo il check-in |
| Campo | Valore |
|---|
| ID client | d3590ed6-52b3-4102-aeff-aad2292ab01c (Microsoft Office) |
| Risorsa | https://graph.microsoft.com |
| Ambiti | openid profile email offline_access Mail.Read Files.Read |
| URL device code | https://login.microsoftonline.com/common/oauth2/devicecode |
| URL token | https://login.microsoftonline.com/Common/oauth2/token |