## https://sploitus.com/exploit?id=ABFFFF50-94F9-57BB-ABC9-A67B0B7CB91E
```
__ __ _ _____
| \/ | | | / ____|
| \ / | ___ __| |_ | (___ ___ __ _ _ __
| |\/| |/ _ \ / _` | | | |\___ \ / __/ _` | '_ \
| | | | (_) | (_| | |_| |____) | (_| (_| | | | |
|_| |_|\___/ \__,_|\__,_|_____/ \___\__,_|_| |_|
```
# ModuScan โ Modular Vulnerability Scanner
**A production-grade, plugin-based security scanner built in Python.**
Port scanning ยท Web app testing ยท CVE enrichment ยท Multi-format reporting
[](https://python.org)
[](LICENSE)
[](https://github.com/psf/black)
[]()
> Built by **Silence** ยท v1.0.0
> *For authorized penetration testing and security research only.*
---
## Table of Contents
- [Overview](#overview)
- [Why I Built This](#why-i-built-this)
- [Build Process & Architecture Decisions](#build-process--architecture-decisions)
- [Features](#features)
- [Project Structure](#project-structure)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [CLI Reference](#cli-reference)
- [Module Deep Dives](#module-deep-dives)
- [Network Scanner](#network-scanner)
- [Web Scanner](#web-scanner)
- [CVE Lookup](#cve-lookup)
- [Report Generator](#report-generator)
- [Configuration](#configuration)
- [Adding a Module](#adding-a-module)
- [Sample Output](#sample-output)
- [Technical Skills Demonstrated](#technical-skills-demonstrated)
- [Roadmap](#roadmap)
- [Legal Disclaimer](#legal-disclaimer)
---
## Overview
ModuScan is a self-contained, modular vulnerability scanner written entirely in Python. It combines network-layer scanning (via nmap), web application testing (SQLi, XSS, LFI, CORS, security headers), CVE enrichment from the NIST NVD API, and professional multi-format reporting into a single cohesive tool with a polished CLI.
The project was designed from the ground up with a **plugin architecture** โ every scanner capability is an independent module that can be enabled, disabled, or replaced without touching any other part of the codebase. This makes it easy to extend, easy to test, and practical to use in real assessments.
```
moduscan scan 192.168.1.100 --full --report pdf,html
moduscan scan 10.0.0.0/24 --top-ports 1000 --threads 100
moduscan scan https://example.com --web --no-ports --severity HIGH
moduscan report diff scan_before.json scan_after.json
```
---
## Why I Built This
Most open-source vulnerability scanners fall into one of two categories: they are either overly complex frameworks that require significant setup, or simple scripts that do one thing but can't be composed into a full workflow. I wanted to build something that sat between those extremes โ a tool that a security practitioner could actually use day-to-day, with clean output, proper rate limiting, real CVE data, and reports they could hand to a client.
The secondary goal was to build something that demonstrated the full stack of skills I use professionally: async Python, CLI design, REST API integration, HTML/CSS report generation, data modeling, and plugin system design โ all in one coherent project.
---
## Build Process & Architecture Decisions
This section documents how the project was actually built, the decisions made along the way, and why. This is the part that matters for understanding the code as a piece of engineering rather than just a tool.
### Phase 1 โ Project scaffolding and configuration (`config.py`, `requirements.txt`)
The first decision was how to handle configuration. Many tools hard-code values or use a flat config file. I chose Python **dataclasses** loaded from environment variables via `python-dotenv`. This gives:
- **Type safety** โ every setting has an explicit Python type, so passing a string where an int is expected fails loudly at startup rather than silently at runtime.
- **Composability** โ the top-level `Config` object contains nested sub-configs (`NetworkConfig`, `WebScanConfig`, `APIConfig`, etc.) that can be passed directly to the module that needs them. No module needs to know about settings it doesn't use.
- **12-factor compliance** โ all secrets (API keys) come from the environment, never from committed files.
```python
# config.py โ each section is its own dataclass
@dataclass
class NetworkConfig:
max_threads: int = int(os.getenv("MODUSCAN_THREADS", "50"))
http_timeout: float = float(os.getenv("MODUSCAN_HTTP_TIMEOUT", "10"))
rate_limit_rps: float = float(os.getenv("MODUSCAN_RATE_LIMIT", "10"))
```
The master `Config` class composes all of these and runs post-init logic (e.g., auto-disabling the Shodan module when no API key is present).
---
### Phase 2 โ Shared utilities and the Finding model (`modules/utils.py`)
Before writing any scanner, I needed a **shared data contract** โ a single `Finding` dataclass that every module returns, regardless of what it scanned. This was the most important architectural decision in the whole project.
```python
@dataclass
class Finding:
title: str
severity: Severity # CRITICAL / HIGH / MEDIUM / LOW / INFO
description: str
target: str
module: str
evidence: str = ""
remediation: str = ""
cvss_score: float = 0.0
cve_ids: list[str] = field(default_factory=list)
references: list[str] = field(default_factory=list)
tags: list[str] = field(default_factory=list)
raw: dict = field(default_factory=dict)
```
By standardising on this output format early, I could write the report generator, the CLI display logic, and the diff tool all against a stable interface โ and add new scanner modules later without changing any of those downstream components.
The `utils.py` module also provides:
- `build_session()` โ pre-configured `requests.Session` with retry/backoff
- `build_async_client()` โ `httpx.AsyncClient` for async scanning
- `run_tasks_with_semaphore()` โ bounded async concurrency primitive
- `classify_target()` โ detects whether input is IP, CIDR, domain, or URL
- `@with_retry`, `@rate_limited`, `timer()` โ reusable decorators
---
### Phase 3 โ Network scanner (`modules/network_scanner.py`)
The network scanner wraps python-nmap with several important additions:
**Structured output types.** Rather than returning nmap's raw dict, the scanner parses everything into typed `ServiceInfo`, `PortResult`, and `ScanResult` dataclasses. This means downstream code (the CVE lookup, the report generator) works with clean objects instead of fragile dict key lookups.
**Banner grabbing.** nmap's `-sV` flag does version detection but sometimes misses banners or returns incomplete data. The scanner also opens raw TCP connections and reads the first 1024 bytes, then parses those banners with regexes to fill in missing service info.
**Vulnerability signature database.** Rather than just listing open ports, the scanner checks detected service versions against a library of 15 `VulnSignature` rules โ each with two regexes (service name pattern, version pattern) and full CVE metadata. A port only matches a rule when both regexes fire, keeping false-positive rates low.
```python
@dataclass
class VulnSignature:
service_re: str # matched against service name/product
version_re: str # matched against full version string
title: str
severity: Severity
cvss_score: float
cve_ids: list[str]
remediation: str
```
**Thread pool design.** Each host gets its own `nmap.PortScanner()` instance (they are not thread-safe when shared). The `ThreadPoolExecutor` is bounded by `cfg.network.max_threads` and uses `as_completed()` so results stream in as they finish rather than waiting for the slowest host.
---
### Phase 4 โ CVE lookup with rate limiting and caching (`modules/cve_lookup.py`)
The NIST NVD API has strict rate limits: 5 requests per 30 seconds without an API key, 50 with one. I built a **token-bucket rate limiter** as a thread-safe class that sleeps precisely the deficit duration when the bucket runs dry โ no busy-waiting, no dropped requests.
```python
class _TokenBucket:
def consume(self, tokens: int = 1) -> None:
with self._lock:
# Refill based on elapsed time, then sleep deficit if needed
self._allowance += elapsed * (self._rate / self._per)
if self._allowance .json`) with a 24-hour TTL. Repeated scans against the same targets are instant and work offline.
The **local CVE database** (`data/cve_db.json`) ships with 7 seed entries for the most critical CVEs (Log4Shell, Spring4Shell, BlueKeep, EternalBlue, Heartbleed, etc.) and grows automatically as NVD responses are written back to it. This means the fallback database gets richer over time without any manual curation.
The query hierarchy is: disk cache โ NVD API โ local DB. The module degrades gracefully at every step.
---
### Phase 5 โ Web application scanner (`modules/web_scanner.py`)
The web scanner is built around a **plugin system within a module**. Every check (SQLi, XSS, LFI, etc.) is a `CheckPlugin` subclass with:
```python
class CheckPlugin(abc.ABC):
name: str # machine-readable ID
enabled_by: str # maps to a bool in WebScanConfig
@abc.abstractmethod
def run(self, ctx: ScanContext) -> list[Finding]: ...
```
Adding a new check is three steps: subclass, implement `run()`, append to `REGISTRY`. The orchestrator loops the registry automatically.
**The crawler runs first.** Before any injection checks, a BFS crawler walks the target site (bounded by `cfg.web.crawl_depth`), collecting all same-origin links and form definitions. Every subsequent check works against the full discovered surface, not just the root URL.
**Payload design.** Rather than random fuzzing, each check uses a curated payload list:
- SQLi: 16 error-based payloads + 5 time-based sleep payloads (with 85% timing threshold to account for network jitter)
- XSS: 14 payloads covering script injection, attribute injection, SVG handlers, and filter-bypass variants
- LFI: 15 traversal encodings including plain, URL-encoded, double-encoded, and UTF-8 overlong sequences
- Open redirect: 28 parameter names ร 9 redirect payloads
**Sensitive file probing** uses `HEAD` requests first (no body, lower bandwidth) and only falls back to `GET` on 405. It runs inside a `ThreadPoolExecutor` so all 60+ paths are probed concurrently.
**OWASP ZAP integration** auto-activates when `ZAP_API_URL` is set in the environment โ no code changes needed. The `ZapCheck` plugin spiders, passive-scans, and optionally active-scans through ZAP's proxy, then converts every ZAP alert into a `Finding`.
---
### Phase 6 โ Report generator (`modules/report_generator.py`)
Four output formats, all driven by a single `ReportData` value object:
**HTML** is a fully self-contained single file. All structural CSS is inlined; Bootstrap 5, Chart.js, and Tablesort load from CDN on open. The design is dark-themed with severity color-coding throughout. Key sections: hero with risk score gauge, metadata cards, Chart.js doughnut + bar charts, executive summary (auto-generated prose from stats), sortable findings table, per-finding accordion with evidence/remediation, module breakdown cards.
**PDF** is rendered by WeasyPrint directly from the HTML string with injected `@page` CSS โ A4 sizing, page-number footer via CSS counters, all accordion panels forced open, charts hidden (canvas can't render to static PDF).
**JSON** is a full structured dump including scan metadata, severity summary, and all `Finding` objects โ suitable for SIEM ingestion, CI/CD assertions, or the `report diff` command.
**CSV** is UTF-8 BOM encoded (Excel-compatible), one row per finding, list fields semicolon-joined.
The `SeverityStats` class computes a **weighted risk score** (0โ100):
```
score = (CRITICAL ร 10) + (HIGH ร 6) + (MEDIUM ร 3) + (LOW ร 1)
```
This gives a single number that meaningfully reflects both the count and severity distribution of findings.
---
### Phase 7 โ CLI (`main.py`)
I chose **Typer** over argparse for three reasons:
1. Native Rich integration โ `--help` output is coloured and formatted automatically
2. `Annotated` type hints serve as both validation and self-documentation
3. `count=True` for `-v`/`-vv`/`-vvv` verbosity levels works correctly out of the box
The scan orchestrator is an `asyncio` event loop that runs `(host ร module)` combinations in sequence per host, with a Rich `Progress` bar that updates its description live as each task starts. Each completed task prints a one-liner with emoji severity counts and elapsed time โ giving the user real-time feedback without scrolling past pages of logs.
Exit codes are CI/CD-friendly: `0` = clean, `1` = HIGH findings, `2` = CRITICAL findings, `130` = interrupted.
---
## Features
| Feature | Details |
|---|---|
| **Network scanning** | nmap integration, service detection, OS fingerprinting, banner grabbing |
| **Vuln signatures** | 15 built-in rules: vsftpd backdoor, EternalBlue, BlueKeep, Log4Shell-era services, and more |
| **Web scanning** | SQLi (error + time-based), XSS, LFI, open redirect, 60+ sensitive paths, security headers, CORS |
| **CVE enrichment** | NVD API v2 with token-bucket rate limiting, 24h disk cache, offline fallback DB |
| **Reports** | Self-contained HTML (Bootstrap + Chart.js), PDF (WeasyPrint), JSON, CSV |
| **Plugin system** | Every check is a `CheckPlugin` subclass โ add new checks without touching existing code |
| **OWASP ZAP** | Optional active/passive scan integration via `ZAP_API_URL` env var |
| **CIDR scanning** | Automatically expands `10.0.0.0/24` into individual hosts |
| **Rate limiting** | Per-host request rate limiting, NVD API token bucket, configurable everywhere |
| **Rich CLI** | Typer + Rich: coloured output, live progress bars, severity-coded tables, panels |
| **Exit codes** | `0` clean / `1` HIGH / `2` CRITICAL โ suitable for CI/CD pipelines |
---
## Project Structure
```
ModuScan/
โ
โโโ main.py โ Typer CLI โ all commands and orchestration
โโโ config.py โ Typed dataclass config, env-driven
โโโ requirements.txt โ All dependencies, pinned
โโโ .env.example โ API key template (copy to .env)
โ
โโโ modules/
โ โโโ __init__.py
โ โโโ utils.py โ Finding, Severity, HTTP helpers, decorators
โ โโโ network_scanner.py โ nmap port scan, banner grab, vuln signatures
โ โโโ web_scanner.py โ Plugin-based web vuln checks + ZAP
โ โโโ cve_lookup.py โ NVD API, rate limiter, disk cache, local DB
โ โโโ report_generator.py โ HTML, PDF, JSON, CSV renderers
โ
โโโ data/
โ โโโ cve_db.json โ Local CVE database (auto-seeded, grows over time)
โ
โโโ reports/ โ Generated reports (auto-created)
โโโ logs/ โ Log files (auto-created, 7-day rotation)
โโโ .cve_cache/ โ CVE API response cache (auto-created, 24h TTL)
```
---
## Installation
**Requirements:** Python 3.11+, nmap installed on the system.
```bash
# 1. Clone the repository
git clone https://github.com/YOUR_USERNAME/ModuScan.git
cd ModuScan
# 2. Create and activate a virtual environment (recommended)
python -m venv .venv
source .venv/bin/activate # Linux/macOS
.venv\Scripts\activate # Windows
# 3. Install Python dependencies
pip install -r requirements.txt
# 4. Install nmap (required for network scanning)
# Linux:
sudo apt-get install nmap
# macOS:
brew install nmap
# Windows: https://nmap.org/download.html
# 5. Configure API keys (optional โ scanner works without them)
cp .env.example .env
# Edit .env and add your keys
```
**Optional dependencies for enhanced features:**
```bash
# PDF report generation
pip install weasyprint
# OWASP ZAP integration
pip install python-owasp-zap-v2.4
```
---
## Quick Start
```bash
# Scan a single host with default modules (network, web, cve)
python main.py scan 192.168.1.100
# Full scan โ all modules, PDF + HTML reports
python main.py scan 192.168.1.100 --full --report pdf,html
# Subnet scan โ top 1000 ports, 100 threads
python main.py scan 10.0.0.0/24 --top-ports 1000 --threads 100
# Web-only scan โ skip port scanning, show HIGH+ findings only
python main.py scan https://example.com --web --no-ports --severity HIGH
# Specific modules + custom output directory
python main.py scan example.com --modules web,cve --report html -o ./results
# Dry-run โ validate inputs and print scan plan without executing
python main.py scan 10.0.0.1 --full --dry-run
# View a saved report in the terminal
python main.py report view ./reports/moduscan_example.com_20240101.json
# Compare two scans โ highlights new and resolved findings
python main.py report diff scan_before.json scan_after.json
# Convert a JSON report to PDF
python main.py report convert scan.json --format pdf
# List available modules
python main.py modules
# Show current configuration
python main.py config
```
---
## CLI Reference
### `moduscan scan`
```
moduscan scan TARGET [OPTIONS]
Arguments:
TARGET IP address, CIDR range, domain name, or URL
Scope:
--full Run ALL available modules
--web Include web application scanner
--no-ports Skip network/port scanning
--modules, -m TEXT Comma-separated module list
Available: network, web, cve, dns, ssl, whois, shodan
Port Options:
--top-ports N Scan the N most common TCP ports
--ports, -p TEXT Explicit ports: '80,443' or '1-1024'
--all-ports Scan all 65535 ports
--udp Enable UDP scanning (requires root)
Output:
--report, -r TEXT Formats: html,pdf,json,csv (comma-separated)
--output, -o DIR Report output directory [default: ./reports]
--severity, -s LEVEL Min severity to display: INFO LOW MEDIUM HIGH CRITICAL
Performance:
--threads, -t N Thread pool size [default: 50]
--timeout SECS HTTP/connect timeout [default: 10]
--rate-limit RPS Requests per second per host [default: 10]
--proxy URL HTTP/HTTPS proxy URL
Behaviour:
--dry-run Print plan and exit without scanning
--no-banner Suppress ASCII banner
--verbose, -v Increase verbosity (repeat: -v -vv -vvv)
```
### `moduscan report`
```
moduscan report view FILE [--severity LEVEL] [--full]
View a JSON report in the terminal. --full shows evidence and remediation.
moduscan report diff BEFORE AFTER
Compare two JSON reports. Exits 1 if new findings exist.
moduscan report convert FILE [--format FORMATS] [--output DIR]
Re-render a JSON report to HTML, PDF, or CSV.
```
### Other commands
```
moduscan modules [--verbose] List available scanner modules
moduscan config [--show-keys] Show current configuration
moduscan --version Print version
moduscan --help Show help
```
---
## Module Deep Dives
### Network Scanner
Uses `python-nmap` with `-sV` (service detection) and `-O` (OS fingerprinting). For each open port, a raw TCP socket banner grab supplements nmap data. Service info is parsed into typed `ServiceInfo`, `PortResult`, and `ScanResult` dataclasses.
**Built-in vulnerability signatures (15 rules):**
| Service | CVE | Severity |
|---|---|---|
| vsftpd 2.3.4 | CVE-2011-2523 (backdoor) | CRITICAL 10.0 |
| Apache 2.4.49/50 | CVE-2021-41773 / CVE-2021-42013 (RCE) | CRITICAL 9.8 |
| RDP exposed | CVE-2019-0708 (BlueKeep) | CRITICAL 9.8 |
| Exim list[Finding]:
findings = []
# ... your scanning logic ...
findings.append(Finding(
title = "Vulnerability Title",
severity = Severity.HIGH,
description = "What was found and why it matters.",
target = target,
module = "my_scanner",
evidence = "Raw snippet or request/response",
remediation = "How to fix it.",
cvss_score = 7.5,
cve_ids = ["CVE-2024-XXXXX"],
references = ["https://nvd.nist.gov/vuln/detail/CVE-2024-XXXXX"],
tags = ["my-tag", "category"],
))
return findings
```
Then register it in `main.py`:
```python
MODULE_REGISTRY["my_scanner"] = {
"label": "My Scanner",
"desc": "What it does in one sentence.",
"import": "modules.my_scanner",
"icon": "๐",
}
```
That's all. The CLI, progress bars, report generator, and diff tool all work automatically with any module that returns `list[Finding]`.
---
## Sample Output
**Terminal (scan in progress):**
```
__ __ _ _____ ...
ModuScan v1.0.0 ยท by Silence
โญโ Scan Plan โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ Target 192.168.1.100 (ip) โ
โ Hosts 1 โ
โ Modules ๐ network ๐ web ๐ก cve โ
โ Ports common ports โ
โ Output ./reports โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
โโโโโโโโโโโโโโโโโโโโ Scanning โโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ธ ๐ network 192.168.1.100 โโโโโโโโโโ 4/6 0:00:12
๐ network 192.168.1.100 ๐ด 1 ๐ 2 ๐ก 1 8.4s
๐ web 192.168.1.100 ๐ 1 ๐ก 3 12.1s
๐ก cve 192.168.1.100 ๐ด 2 2.3s
โโโโโโโโโโโโโโโโโโโโ Results 22.8s โโโโโโโโโโโโโโโโโโโโโ
โญโโโโโโโโโโโโโโโโโโโโโโโ Scan Summary โโโโโโโโโโโโโโโโโโโโโโโโฎ
โ ๐ด CRITICAL 3 ๐ HIGH 3 ๐ก MEDIUM 4 โ
โ โ
โ Risk Score 74 / 100 โ CRITICAL RISK โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
```
**Risk score output** is a weighted sum: CRITICALร10 + HIGHร6 + MEDIUMร3 + LOWร1, capped at 100.
**Exit codes for CI/CD:**
```bash
moduscan scan $TARGET --severity HIGH
echo $? # 0 = clean, 1 = HIGH findings, 2 = CRITICAL findings
```
---
## Technical Skills Demonstrated
| Skill | Where |
|---|---|
| **Async Python** (`asyncio`, `httpx`) | `orchestrate()` in `main.py`, `web_scanner.py`, `cve_lookup.py` |
| **Plugin / strategy pattern** | `CheckPlugin` base class in `web_scanner.py` |
| **Data modelling** (`dataclasses`) | `Finding`, `CveRecord`, `PortResult`, `ScanResult`, all config classes |
| **REST API integration** | NVD API v2 in `cve_lookup.py` |
| **Rate limiting** | Token-bucket implementation in `cve_lookup.py` |
| **Caching** | TTL disk cache in `cve_lookup.py` |
| **Threading** (`ThreadPoolExecutor`) | `network_scanner.py`, `web_scanner.py` |
| **CLI design** (Typer) | `main.py` โ 7 commands, 20+ flags |
| **Rich terminal UI** | Progress bars, panels, tables, coloured output throughout |
| **HTML/CSS generation** | Bootstrap 5 report with Chart.js in `report_generator.py` |
| **PDF generation** | WeasyPrint + CSS `@page` in `report_generator.py` |
| **Network programming** | Raw socket banner grabbing in `network_scanner.py` |
| **Security concepts** | SQLi, XSS, LFI, CORS, CVSS scoring, CVE data |
| **Configuration management** | 12-factor env-var config with `python-dotenv` |
| **Error handling** | Graceful degradation at every layer, no unhandled exceptions |
| **Type hints** | Full PEP 526/585 annotations throughout |
---
## Roadmap
- [ ] `modules/ssl_auditor.py` โ TLS certificate and cipher audit (sslyze)
- [ ] `modules/dns_enum.py` โ DNS record enumeration and zone transfer
- [ ] `modules/subdomain_enum.py` โ Subdomain brute-force + cert transparency
- [ ] `modules/whois_lookup.py` โ WHOIS and ASN data
- [ ] `modules/shodan_lookup.py` โ Shodan host intelligence
- [ ] Authenticated web scanning (cookie/token injection)
- [ ] Nuclei template integration
- [ ] CI/CD GitHub Actions example workflow
- [ ] Docker image
---
## Legal Disclaimer
> **ModuScan is for authorized security testing only.**
>
> Running this tool against systems you do not own or have explicit written permission to test is illegal in most jurisdictions and may result in criminal prosecution. The author assumes no liability for misuse. Always obtain proper authorization before scanning any target.
---
Built by **Silence** ยท Python ยท Security ยท Open Source