A high-throughput network reconnaissance and vulnerability detection tool built in Python. Scans large IP ranges, fingerprints services via SSL certificates and HTTP responses, detects CVEs from response headers, and stores everything in a PostgreSQL database with a live dashboard.
Built as a personal research project to understand internet-scale attack surface mapping — the same technique used by Shodan, Censys, and professional red teams.
IP ranges (GCP, AWS, Azure...)
│
▼
masscan sweep → finds open ports at scale (10,000 pkt/s)
│
▼
SSL cert fetch → extracts Common Name / domain from each IP
│
▼
HTTP/HTTPS requests → grabs title, headers, body preview
(with SSL fallback chain)
│
▼
CVE detection → matches response headers against signature DB
│
▼
PostgreSQL + dashboard → live results, filterable by CVE, severity, IP
vulnerability-scanner/
├── scanner/
│ └── scannerfinal.py Async scanning engine (aiohttp + asyncio)
│
├── backend/
│ ├── main.py FastAPI app — all routes + live dashboard UI
│ ├── database.py Async SQLAlchemy engine (asyncpg driver)
│ ├── models.py PostgreSQL ORM — scans, findings, scan_changes
│ ├── schemas.py Pydantic request/response validation
│ ├── crud.py All DB operations — insert, query, export
│ ├── detector.py CVE detection engine + inline scoring
│ └── cve_signatures.py CVE knowledge base (27 signatures)
│
├── requirements.txt
└── README.md
The scanner uses asyncio.gather() to fetch thousands of SSL certificates and HTTP responses concurrently. The backend uses FastAPI + async SQLAlchemy 2.0 + asyncpg — database queries never block the event loop. The scanner and dashboard can run simultaneously without contention.
Many servers on the internet run outdated TLS configurations. Rather than dropping them silently, the scanner tries four SSL contexts in order:
1. Modern (TLS 1.2/1.3, standard)
2. Legacy TLS (allows TLS 1.0 / 1.1 — found on IoT, embedded devices)
3. Permissive (ALL:@SECLEVEL=0 — weak ciphers, legacy appliances)
4. Legacy + Permissive (worst-case configs)
5. Plain HTTP on port 443 (rare but real)
This recovers roughly 30–40% of hosts that would otherwise be silently dropped.
Every scan row gets its CVEs computed at insert time — no separate detection pass needed. Signatures are split into three categories:
| Category | Meaning | Effect on row |
|---|---|---|
cve |
Signature backed by a real CVE ID | Sets cve_ids[] + highest_severity |
m-sec-h |
Missing security header (HSTS, X-Frame-Options, etc.) | Sets missing_sec_headers = true |
info |
Version disclosure, fingerprinting | Stored in findings only |
The m-sec-h flag is stored separately so missing headers never inflate the CVE-based severity rating of a host. A server with no CVEs but missing HSTS shows severity=null, missing_sec_headers=true — not severity=medium.
On rescan, if a host's headers, CVEs or severity changed since the last scan, a ScanChange row is written with old and new snapshots. This lets you track when a server gets patched, changes software, or becomes newly vulnerable.
| ID | CVE | Severity | Trigger |
|---|---|---|---|
| apache-path-traversal-rce | CVE-2021-41773, CVE-2021-42013 | Critical | Server: Apache/2.4.49 |
| apache-log4shell-exposure | CVE-2021-44228 | Critical | Any Apache server |
| php-eol-5x | CVE-2019-11043 | Critical | X-Powered-By: PHP/5.* |
| laravel-debug-exposed | CVE-2021-3129 | Critical | X-Powered-By: Laravel |
| springboot-actuator | CVE-2022-22965 | Critical | X-Application-Context |
| tomcat-ghostcat | CVE-2020-1938 | Critical | Server: Apache-Coyote |
| cors-wildcard | — | High | Access-Control-Allow-Origin: * |
| missing-hsts | — | m-sec-h | Strict-Transport-Security absent |
| missing-xframe-options | — | m-sec-h | X-Frame-Options absent |
Scanning GCP and AWS IP ranges surfaces several recurring patterns that are valuable from a security research perspective:
Exposed internal systems
Staging APIs, internal dashboards, and enterprise tools (CRMs, ticketing systems, VPN portals) accessible directly from the public internet without authentication walls. These represent misconfigured cloud deployments where 0.0.0.0/0 firewall rules were left open.
Ghost servers / subdomain takeover candidates Servers still running and accepting connections, but whose domains have expired or been decommissioned. The SSL certificate references a domain that no longer resolves to that IP, creating potential subdomain takeover opportunities.
Default installation pages
Apache2 Ubuntu Default Page: It works, Welcome to nginx!, This is the default server vhost — fresh deployments where the application was never properly installed or the vhost configuration was removed while the server kept running.
End-of-life software stacks PHP 5.x, Apache 2.4.49, outdated nginx versions — software that stopped receiving security patches years ago, still actively serving traffic on cloud infrastructure.
Note: All scanning was conducted on public IP ranges for research purposes. No systems were exploited or accessed beyond reading publicly served HTTP responses.
- Python 3.9+
- PostgreSQL 13+
- masscan
git clone https://github.com/kilma98/vulnerability-scanner
cd vulnerability-scanner
pip install -r requirements.txtpsql -U postgres -c "CREATE DATABASE scanner;"cd backend
uvicorn main:app --host 0.0.0.0 --port 8000 --reloadTables are created automatically on first startup.
cd scanner
python scannerfinal.pyThe scanner checks the backend is reachable before starting. Results appear in the dashboard in real time at http://localhost:8000.
Override the database URL via environment variable:
export DATABASE_URL="postgresql+asyncpg://user:password@host:5432/scanner"Key scanner parameters (in scannerfinal.py):
SSLChecker(
masscan_rate=10000, # packets/sec
chunkSize=2000, # IPs per batch
semaphore_limit=70, # max concurrent connections
timeout=3, # seconds per request
)| Method | Path | Description |
|---|---|---|
POST |
/insert |
Ingest scanner results (bulk insert with inline CVE detection) |
GET |
/api/scans |
Paginated scan list — filter by IP, domain, CVE, severity, port |
GET |
/api/findings |
CVE findings with false-positive management |
GET |
/api/findings/stats |
Severity breakdown, top vulnerable IPs, top CVEs |
GET |
/api/detail/{ip} |
Full detail for one IP including headers and body |
GET |
/api/changes/{ip} |
Rescan change history for one IP |
GET |
/api/stats |
Aggregate stats — total scans, unique IPs, CVE host count |
GET |
/api/export |
Download all results as CSV |
POST |
/api/detect |
Run full CVE detection pass (writes findings table) |
GET |
/ |
Live dashboard |
GET |
/docs |
Swagger UI |
| Layer | Technology |
|---|---|
| Scanner | Python 3.9, aiohttp, asyncio, BeautifulSoup, pyOpenSSL |
| Backend | FastAPI, SQLAlchemy 2.0 (async), asyncpg |
| Database | PostgreSQL 13+ |
| Port scanning | masscan |
- Bug bounty mode — subdomain enumeration via crt.sh + DNS bruteforce
- ASN lookup integration — get all IP ranges for a target company
- Exposed paths scanner (
/.env,/.git/config,/actuator/env) - PDF report generator for bug bounty submissions
- Port expansion — Redis (6379), Elasticsearch (9200), MongoDB (27017)