Scanner di vulnerabilita' web. Fa il crawling di un sito, trova form e parametri, e li prova contro SQL injection, XSS riflesso, path traversal e header di sicurezza mancanti. Produce un report a terminale, un PDF e ha una dashboard Flask.
Progetto di apprendimento. L'ho scritto per capire come funzionano quegli
attacchi lato pratico, dopo aver studiato la teoria. Il codice e' generato con
assistenza AI e lo dichiaro: quello che ho fatto io e' il testing e la
correzione dei falsi positivi, che sono documentati in
docs/DESIGN_NOTES.md.
Solo su applicazioni tue o con autorizzazione scritta. Scansionare siti di terzi senza permesso e' illegale nella maggior parte delle giurisdizioni.
Quello che NON fa, anche se i payload sono nei file: SQL injection time-based e XSS stored. Il time-based richiede di misurare la latenza contro una baseline e su rete rumorosa produce falsi positivi troppo facilmente; lo stored richiede di inviare su una pagina e verificare su un'altra, cosa che il crawler attuale non fa. Finche' non c'e' il codice non li dichiaro tra le feature.
git clone https://github.com/torchiachristian/VulnScan
cd VulnScan
pip install -r requirements.txt
python3 src/database.py
# scansione base
python3 src/scanner.py http://localhost:8080
# con report PDF
python3 src/scanner.py http://localhost:8080 --pdf report.pdf
# dashboard web su http://127.0.0.1:5000
python3 src/web/app.py
# DVWA in docker
docker run -p 80:80 vulnerables/web-dvwa
# credenziali admin/password, livello di sicurezza "Low"
Oppure OWASP Juice Shop. Nel repo ci sono anche due app Flask minime in
tests/: una volutamente vulnerabile e una sicura, usate per verificare che il
tool trovi le vulnerabilita' vere senza inventarne di finte.
La prima versione del detector boolean-based faceva cosi':
if len(response_true.text) != len(response_false.text):
# -> SQL Injection, Critical
Bastava un byte di differenza tra la risposta alla condizione vera e quella alla condizione falsa per dichiarare una SQL injection critica.
Il problema e' che due richieste identiche alla stessa pagina quasi mai danno la stessa risposta: token CSRF, timestamp, contatori di visite, banner. Ho scritto un'app Flask senza nessun database e con l'input sempre escapato, con dentro un token casuale come ne ha qualsiasi sito, e il tool ci ha trovato questo:
[6] SQL Injection (Critical)
Evidence: Response length difference: 268 vs 300
Una SQL injection su un'applicazione che non ha un database. Due richieste identiche alla stessa pagina davano 262 e 241 byte, quindi il controllo scattava anche senza payload.
Adesso il detector misura prima quanto varia la pagina da sola a parita' di input, poi considera una differenza solo se supera quel rumore di un margine e se il risultato e' coerente su piu' tentativi, sempre nello stesso verso. Se la pagina e' troppo instabile per misurare restituisce niente invece di indovinare.
Sulla stessa app sicura ora non segnala piu' nulla, e su un'app con una SQL injection vera continua a trovarla.
src/
├── crawler/web_crawler.py scoperta di pagine e form
├── detectors/
│ ├── http_utils.py costruzione richieste e misura del rumore
│ ├── sql_injection.py error-based e boolean-based
│ ├── xss.py XSS riflesso
│ ├── path_traversal.py directory traversal
│ └── security_headers.py header mancanti
├── reporting/pdf_generator.py report PDF
├── web/app.py dashboard Flask
├── scanner.py pipeline principale
└── database.py SQLite
payloads/ payload per tipo di attacco
tests/ app di prova (vulnerabile e sicura) + test
Il crawler non gestisce autenticazione, quindi vede solo la parte pubblica di un'applicazione. Non c'e' rate limiting: contro un sito vero va usato con cautela. I payload sono generici e non provano bypass di WAF. Il boolean-based resta l'euristica piu' fragile delle due tecniche SQL e per questo il report riporta sempre il rumore misurato e la soglia usata, cosi' chi legge puo' valutare.
MIT. Usalo in modo etico.