Skip to content
Back to projects
Personal — In Progress

Vulnerability Assessment Tool (Python + CVSS)

Vulnerability Assessment Tool (Python + CVSS)

CLI tool for structured vulnerability assessments with CVSS v3.1 scoring and HTML report generation

Context

The initial plan called for a report based on a simulated Nessus scan against a fictional network. In practice, I built my own scanner instead of replaying an existing tool's output — a stronger signal for a SOC/Pentest/AppSec role: the ability to design and code a vulnerability assessment tool (check architecture, CVSS scoring engine, report generation), not just read Nessus output.

Methodology — 3 check families

  • Network: concurrent port scan (ThreadPoolExecutor, 1-1024), risky-service detection (SMB/445, Telnet/23, unauthenticated Redis/6379, RDP/3389), DNS zone transfer attempt (AXFR)
  • System: inspects the machine running the tool (not the target) — OS version, SSH configuration (sshd_config), world-writable files in /tmp
  • Web: missing security headers (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy), TLS certificate validity

The CVSS v3.1 engine

Each finding carries a CVSS vector (attack vector, complexity, privileges required, user interaction, confidentiality/integrity/availability impact). The score isn't a hand-picked constant — it's computed via the standard's official formula (Exploitability × Impact, with the CVSS spec's Roundup algorithm). Real example — an exposed, unauthenticated SMB service computes CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8 (Critical). Severity is then derived from the computed score, not assigned separately, which eliminates any risk of a mismatch between score and displayed severity.

Bugs found and fixed during code review

  • TLS check never triggered: getpeercert() returns {} when verify_mode=CERT_NONE, so expired-certificate detection silently never worked. Fixed by reading the certificate in DER form (binary_form=True) and parsing via cryptography — verified against expired.badssl.com, correctly detecting a certificate expired since 2015
  • Sequential port scan: 1024 ports × 0.5s timeout, up to 8-9 minutes per scan. Fixed with ThreadPoolExecutor (concurrent scan) — 8.5 min → 6.4 sec measured live against localhost
  • Hardcoded CVSS score: the README advertised CVSS v3.1 scoring but the scores were hand-picked constants. Implemented the real formula (Exploitability, Impact, Roundup)
  • Scope confusion: system checks were analyzing the local machine, not the --target host, undocumented anywhere. IDs renamed to LOCAL-*, with explicit docstrings, README notes, and a console banner
  • No authorization guard: the tool scanned any target without confirmation. Added an interactive confirmation prompt plus an --i-am-authorized flag for non-interactive/CI use

Real test results

localhost scan
6.4 sec(SMB/445 → Critical (9.8), MSRPC/135 → Medium (5.3) — down from 8.5 min before the concurrency fix)
Expired TLS certificate
Correctly detected(expired.badssl.com — expired since 2015-04-12)
Security headers
5 missing headers detected(example.com — HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy)

Outcomes

Stack
Python 3.11+, Jinja2, cryptography(No external scanner binaries (nmap, Nessus...))
Tests
12 pytest tests(CVSS engine + data models)
Output
Self-contained HTML report + JSON export(SIEM/ticketing-compatible)
Legal framework
Built-in authorization guard(Confirmation required, or --i-am-authorized)