Strumenti di sicurezza
Il toolkit del bug bounty e la NotHoly CLI
Un toolkit di bug bounty per chi inizia, più la NotHoly CLI nel terminale con lo scope registrato così i comandi restano dentro il programma.
Esegui la scansione passiva baseline di ZAP contro Juice Shop sulla tua macchina, leggi il report e controlla a mano un alert prima di fidarti.
Zed Attack Proxy, di solito chiamato ZAP, è uno strumento gratuito per trovare problemi nelle applicazioni web. Per questa guida contano due cose che fa. Funziona da proxy di intercettazione, così puoi leggere ogni richiesta che il browser invia e ogni risposta che riceve. Esegue anche scanner che controllano quel traffico cercando pattern deboli.
Un proxy è un intermediario. Di solito il browser invia la richiesta direttamente a 127.0.0.1:3000. Con ZAP in mezzo, il browser invia la richiesta a ZAP. ZAP la inoltra all'applicazione, riceve la risposta, salva la coppia nella cronologia e restituisce la risposta al browser. L'applicazione non si accorge di nulla. Tu ottieni soltanto una copia della conversazione.
Lo strumento è utile proprio per questa posizione. Uno scanner vede quello che il server rimanda indietro. Un proxy ti mostra cosa ha fatto davvero il browser, compresi header, cookie, redirect e il JavaScript caricato dalla pagina. Molti problemi web emergono solo confrontando queste due viste.
ZAP ha due tipi di scansione, e la differenza decide cosa ti è permesso eseguire e dove.
La scansione passiva legge il traffico già passato da ZAP. In ogni risposta cerca cose come un header Content-Security-Policy mancante, cookie senza il flag HttpOnly o stack trace nelle pagine di errore. Le regole passive non inviano stringhe d'attacco. Guardano solo ciò che è tornato.
La scansione attiva invia nuove richieste costruite per provocare una reazione. Cambia i parametri, invia input strani e osserva come risponde l'applicazione. Da questo tipo di test arriva la maggior parte dei finding seri, ed è anche quello che si comporta come un attacco. Eseguilo solo contro un lab tuo, o contro un sistema per cui hai un permesso scritto e un perimetro definito.
La scansione baseline di questa guida è passiva. La documentazione di ZAP la descrive come uno script che esegue lo spider sul target per un minuto (il valore predefinito), aspetta che le regole passive finiscano e poi scrive il report. Non lancia payload d'attacco, quindi è un primo passo ragionevole sul tuo lab.
Ogni alert di ZAP è una regola che ha trovato una corrispondenza. Una regola può corrispondere a un header mancante, a un pattern nel corpo di una risposta o a una combinazione dei due. Non può sapere a cosa serve la tua applicazione, quali dati protegge o se un attaccante può raggiungere il percorso che conta. Un alert ti dice dove guardare. Non conferma che una debolezza sia sfruttabile.
Due tipi di errore compaiono di continuo. Alcuni alert sono falsi positivi: la regola ha trovato qualcosa di innocuo. Altri problemi sfuggono del tutto, perché lo scanner non ha mai raggiunto la pagina o non ha mai inviato l'input che fa scattare il difetto. Una pista diventa un finding con un passaggio che puoi ripetere tu: una richiesta che hai inviato, la risposta che hai visto e una spiegazione semplice del perché conta. Finché non hai tutte e tre le cose, l'alert resta in un elenco con la nota "da verificare".
Tieni a mente questa regola per il resto della guida. Lo scanner ti dà un punto di partenza, e la verifica tocca a te.
Ti servono quattro cose:
docker version.Il lab resta sulla tua macchina. Pubblica la sua porta solo su 127.0.0.1. Una porta pubblicata senza quel prefisso può restare in ascolto su ogni interfaccia di rete, e Juice Shop è fatto per essere violato.
Avvia Juice Shop in background con un nome che potrai riusare:
docker run -d --name juice-local -p 127.0.0.1:3000:3000 bkimminich/juice-shopIl flag -d esegue il container in modalità detached. Ho tolto --rm di proposito, così il container resta finché non lo fermi e lo rimuovi per nome. Il prefisso 127.0.0.1: tiene la porta fuori dalla tua rete.
Controlla che risponda:
curl -i http://127.0.0.1:3000/Sulla mia macchina le prime righe della risposta erano queste. Ho tolto l'indicatore di avanzamento che curl stampa quando non lo lanci con -s:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Feature-Policy: payment 'self'
X-Recruiting: /#/jobs
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Sun, 08 Nov 2026 09:06:18 GMT
ETag: W/"2700-1a1ba6896a5"
Content-Type: text/html; charset=UTF-8
Content-Length: 9984Sulla tua macchina header e date saranno diversi. Registra anche la versione di Juice Shop, perché l'applicazione cambia da una release all'altra. Questo endpoint la restituisce:
curl -s http://127.0.0.1:3000/rest/admin/application-versionSulla mia macchina ha stampato {"version":"20.3.0"}. Metti quel numero negli appunti accanto ai risultati della scansione.
Lancia lo scanner dalla cartella in cui vuoi che compaia il report. Questo è il comando per Docker Desktop:
docker run --rm -t -v "$(pwd)":/zap/wrk ghcr.io/zaproxy/zaproxy:stable zap-baseline.py -t http://host.docker.internal:3000 -r zap-report.htmlOgni parte ha un compito:
--rm rimuove il container dello scanner quando la scansione finisce.-v "$(pwd)":/zap/wrk monta la cartella corrente dentro il container, così il report viene scritto sul tuo disco.-t http://host.docker.internal:3000 è il target. Da dentro un container, host.docker.internal punta al tuo computer, dove il lab è in ascolto.-r zap-report.html scrive il report HTML completo nella cartella montata.Su Linux l'alias host di Docker Desktop non esiste per impostazione predefinita. Aggiungi una mappatura host allo stesso comando:
docker run --rm -t --add-host=host.docker.internal:host-gateway -v "$(pwd)":/zap/wrk ghcr.io/zaproxy/zaproxy:stable zap-baseline.py -t http://host.docker.internal:3000 -r zap-report.htmlHo testato solo la forma per Docker Desktop. Su Linux, una porta pubblicata su 127.0.0.1 potrebbe non accettare le connessioni che arrivano dall'indirizzo del bridge Docker. Se lo scanner segnala errori di connessione, leggi le prime righe del suo output prima di cambiare altro.
Il primo tentativo è fallito senza scansionare nulla. La console ha scritto A file based option has been specified but the directory '/zap/wrk' is not mounted, poi ha stampato il testo d'uso ed è uscita con codice 3. Il mount non funzionava nella mia sessione Git Bash, e non ho scoperto perché. Ho risolto lanciando il comando con MSYS_NO_PATHCONV=1 e il percorso Windows completo nel flag -v:
MSYS_NO_PATHCONV=1 docker run --rm -t -v "D:/path/to/your/folder:/zap/wrk" ghcr.io/zaproxy/zaproxy:stable zap-baseline.py -t http://host.docker.internal:3000 -r zap-report.htmlSostituisci il percorso con la tua cartella. La prima esecuzione scarica anche l'immagine di ZAP, e con la mia connessione ha richiesto quasi dodici minuti. Le esecuzioni successive partono in pochi secondi.
Lo scanner stampa una riga per regola. Questa è la parte che conta della mia esecuzione, con il lungo elenco delle regole superate accorciato:
Using the Automation Framework
Total of 116 URLs
PASS: Vulnerable JS Library (Powered by Retire.js) [10003]
PASS: Cookie No HttpOnly Flag [10010]
...
PASS: CSP [10055]
...
WARN-NEW: Content Security Policy (CSP) Header Not Set [10038] x 5
WARN-NEW: Cross-Domain Misconfiguration [10098] x 5
WARN-NEW: Modern Web Application [10109] x 5
FAIL-NEW: 0 FAIL-INPROG: 0 WARN-NEW: 3 WARN-INPROG: 0 INFO: 0 IGNORE: 0 PASS: 59WARN-NEW significa che una regola ha segnalato qualcosa che la scansione non aveva visto prima. PASS significa che la regola è stata eseguita e non ha trovato nulla. Il numero dopo il nome di ogni regola è l'identificatore di ZAP, che puoi cercare nella documentazione.
Nella mia esecuzione il comando è uscito con codice 2. La documentazione della baseline elenca i codici di uscita: 0 è successo, 1 significa almeno un FAIL, 2 significa almeno un WARN e nessun FAIL, 3 significa qualsiasi altro errore. Qui un codice 2 è normale. Vuol dire solo che lo scanner ha trovato qualcosa da leggere.
Apri zap-report.html in un browser. Il riepilogo in cima al mio report mostrava High 0, Medium 2, Low 0 e Informational 1. Tre alert formavano l'intero elenco:
Content-Security-Policy, quello che permette al browser di decidere quali script e frame caricare.Access-Control-Allow-Origin: *, che permette a qualsiasi sito web di leggere la risposta in un browser.Ogni alert ha le stesse parti: una descrizione, gli URL dove è scattato, l'evidenza vista da ZAP, alcune note aggiuntive e una correzione suggerita. Leggi per prime l'evidenza e l'URL. La descrizione ti dice cosa significa la regola in generale. L'evidenza ti dice cosa è successo in questa risposta.
Non ordinare il report solo in base all'etichetta di rischio. ZAP assegna il livello di rischio in base alla regola, non ai dati della tua applicazione. Un Medium su una pagina senza dati sensibili può contare meno di un Low su un endpoint che gestisce gli account.
Prendi l'alert CSP. La sua evidenza è un'assenza, quindi il test è semplice: richiedi la pagina, cerca l'header e registra il risultato. Questo comando stampa il numero di righe che corrispondono:
curl -s -i http://127.0.0.1:3000/ -o /dev/null -D - | grep -i -c '^content-security-policy'Sulla mia macchina ha stampato 0. Neanche l'elenco completo degli header catturato prima aveva una riga Content-Security-Policy. L'alert corrisponde a ciò che il server invia.
Passa al secondo alert. L'evidenza è la riga Access-Control-Allow-Origin: * che hai già visto nell'output di curl -i. Il tuo controllo deve rispondere a una domanda diversa: questa risposta contiene qualcosa che un sito di terze parti non dovrebbe leggere? La pagina principale del lab è HTML pubblico, quindi l'header in sé non è il problema. Il testo di ZAP dice la stessa cosa. I browser non permettono a una pagina di terze parti di leggere così le risposte autenticate. L'alert diventa serio solo quando un endpoint che restituisce dati privati invia anche un'origine wildcard. Se trovi un endpoint del genere, testalo con il tuo account del lab e registra cosa restituisce.
Per il terzo alert non c'è nulla da dimostrare. Descrive il tipo di applicazione. Lascialo nel report come contesto e vai avanti.
Scrivi ogni risultato verificato in una tabella o in un file di testo con quattro colonne: l'alert, la richiesta che hai inviato, cosa hai osservato e il tuo verdetto. Un verdetto può essere "confermato", "non riprodotto" o "serve più contesto". Queste tre risposte coprono la maggior parte di quello che farai con l'output di uno scanner.
Questo passaggio ti fa osservare richieste reali, ed è la parte che rende il proxy utile al di là della scansione. Puoi farlo con l'applicazione desktop o con un container che esegue ZAP in modalità daemon. Li descrivo entrambi. Io ho eseguito la versione daemon. Non ho eseguito il percorso desktop, quindi più sotto non troverai il suo output.
Installa ZAP dal sito ufficiale. La pagina getting-started rimanda all'installer attuale. Aprilo e scegli Manual Explore, che avvia un browser già configurato per inviare il traffico attraverso ZAP. Inserisci http://127.0.0.1:3000 e naviga nella home page. Poi apri la scheda History. Ogni riga è una coppia richiesta e risposta, con metodo, URL e codice di stato. Confronta quelle righe con ciò che vedi negli strumenti per sviluppatori del tuo browser. Le differenze mostrano cosa ha inviato il browser che non ti aspettavi.
Avvia ZAP come proxy in background sulla porta 8080:
docker run -d --name zap-proxy-lab -p 127.0.0.1:8080:8080 ghcr.io/zaproxy/zaproxy:stable zap.sh -daemon -host 0.0.0.0 -port 8080 -config api.disablekey=true -config api.addrs.addr.name=.* -config api.addrs.addr.regex=trueL'opzione api.disablekey=true toglie il controllo della chiave API, così i comandi qui sotto funzionano senza altra configurazione. Tieni la porta pubblicata su 127.0.0.1 e rimuovi il container quando hai finito. Chiunque possa raggiungere l'API può controllare ZAP.
Controlla che l'API risponda:
curl -s http://127.0.0.1:8080/JSON/core/view/version/Output sulla mia macchina:
{"version":"2.17.0"}Invia due richieste attraverso il proxy. Il flag -x dice a curl di usare ZAP. Il nome del target è host.docker.internal perché ZAP gira dentro Docker, e il nome della tua macchina va risolto da lì:
curl -s -x http://127.0.0.1:8080 -o /dev/null -w 'proxied root http=%{http_code}\n' http://host.docker.internal:3000/
curl -s -x http://127.0.0.1:8080 -o /dev/null -w 'proxied api http=%{http_code}\n' "http://host.docker.internal:3000/rest/products/search?q=apple"Output:
proxied root http=200
proxied api http=200Chiedi a ZAP quanti messaggi ha registrato:
curl -s "http://127.0.0.1:8080/JSON/core/view/numberOfMessages/"Sulla mia macchina ha restituito {"numberOfMessages":"5"}. Poi elencali con una riga di Python che stampa la request line e la status line di ogni messaggio:
curl -s "http://127.0.0.1:8080/JSON/core/view/messages/?start=1&count=5" | python -c "import json,sys; d=json.load(sys.stdin); [print(m['requestHeader'].splitlines()[0], '->', m['responseHeader'].splitlines()[0]) for m in d['messages']]"Output:
GET http://host.docker.internal:3000/ HTTP/1.1 -> HTTP/1.1 200 OK
GET http://host.docker.internal:3000 HTTP/1.1 -> HTTP/1.0 0
GET http://host.docker.internal:3000/rest/products/search?q=apple HTTP/1.1 -> HTTP/1.1 200 OK
GET http://host.docker.internal:3000/rest HTTP/1.1 -> HTTP/1.0 0
GET http://host.docker.internal:3000/rest/products HTTP/1.1 -> HTTP/1.0 0Ho inviato due richieste attraverso il proxy, ma la cronologia contiene cinque voci. Tre non hanno risposta, e compaiono con lo stato 0. Non ho indagato da dove arrivino. Non leggerle come prove su Juice Shop. Per questo conviene controllare la cronologia di persona invece di fidarsi di un conteggio. Quando il tuo elenco è diverso, scrivi cosa vedi e chiediti perché prima di trarre conclusioni.
Ferma e rimuovi il proxy quando hai finito:
docker rm -f zap-proxy-labPuntare lo scanner su un target che non è tuo. Il flag -t accetta qualsiasi URL, e lo scanner gli invierà richieste. Tieni il target sull'alias del lab per la tua macchina.
Pubblicare il lab su tutte le interfacce. -p 3000:3000 senza indirizzo ascolta su ogni interfaccia. Scrivi sempre 127.0.0.1:3000:3000.
Trattare gli avvisi come un punteggio. Un report con tre avvisi ti dice che tre regole hanno trovato una corrispondenza. Verifica ciascuno prima di contarlo.
Saltare la versione. Juice Shop cambia da una release all'altra, e lo stesso vale per le regole di ZAP. Annota entrambe le versioni con il report. Io ho usato Juice Shop 20.3.0 e ZAP 2.17.0.
Lasciare il proxy in esecuzione. Il daemon ha la chiave API disattivata. Rimuovi il container dopo ogni sessione.
Leggere la colonna di stato come una prova. Una riga con stato 0 significa che non è stata registrata alcuna risposta. Non è un finding.
Montare la cartella sbagliata. Se il report non c'è, controlla per prima cosa il mount -v. Lo scanner può terminare con successo e comunque non scrivere nulla dove te lo aspetti.
Tieni un solo file di appunti per il lab. Su ogni riga metti un alert, la richiesta che l'ha testato, cosa hai visto e il tuo verdetto. Dopo qualche scansione, il file diventa un registro di ciò che lo strumento ha trovato, di ciò che hai confermato e di dove ha sbagliato.
Leggi la guida all'installazione di Juice Shop se l'hai saltata, perché questo articolo dà per scontato lo stesso lab. L'articolo successivo usa lo stesso lab per spiegare la SQL injection. Lì si vede meglio perché un alert dello scanner richiede un controllo manuale: il difetto riguarda il modo in cui l'input arriva a una query, e uno strumento può solo farlo intuire.
La documentazione di ZAP tratta contesti, autenticazione e scansione attiva. Questi argomenti contano quando sai distinguere un problema confermato da una regola che ha trovato una corrispondenza. Studiali sul lab, con il permesso, e non portarli su altri sistemi.
Il progetto Juice Shop tiene l'elenco delle sfide e le note di installazione nel suo repository GitHub. Controlla la pagina delle release per la versione attuale prima di scaricare l'immagine.
Strumenti di sicurezza
Un toolkit di bug bounty per chi inizia, più la NotHoly CLI nel terminale con lo scope registrato così i comandi restano dentro il programma.
Strumenti di sicurezza
L'agente gira sulla tua macchina con la tua approvazione, il modello accetta il lavoro di sicurezza, e un engagement controlla ogni comando.
Strumenti di sicurezza
Review del file o del git diff nel tuo editor, con tutto il contesto. Il modello accetta la domanda di sicurezza e crea da un reperto un test o una regola.