Sn1per Deep Dive Part 3: Web Mode, Sc0pe, and the Vulnerability Scoring Engine
Part 2 traced how Sn1per builds a target list before it ever touches a web port. This post covers what happens once it does: web mode, and the scoring engine underneath it that turns a pile of raw scan output into a single number per host. The engine is called sc0pe internally, and it’s a far simpler piece of design than “vulnerability scanner” usually implies, which is worth showing directly rather than describing.
Read from modes/web.sh, modes/webscan.sh, modes/sc0pe.sh, and the template files under templates/passive/web/ and templates/passive/network/ in the Community Edition source.
Sc0pe is a directory of one-fact-per-file templates
Every check sc0pe runs is a standalone file. Here’s Server_Header_Disclosure.sh, in full:
if [ "$SSL" = "false" ]; then
AUTHOR='@xer0dayz'
VULN_NAME='Server Header Disclosure - HTTP'
FILENAME="$LOOT_DIR/web/headers-http-$TARGET-*.txt"
MATCH="Server\:"
SEVERITY='P5 - INFO'
GREP_OPTIONS='-i'
SEARCH='positive'
SECONDARY_COMMANDS=''
else
...(same thing against the https headers file)...
fi
That’s the whole check. SEARCH='positive' means: if MATCH is found in FILENAME with GREP_OPTIONS applied, it’s a finding. CSP_Not_Enforced.sh is the mirror image, SEARCH='negative', meaning the absence of content-security-policy in the response headers is itself the finding. Strict_Tranposrt_Security_Not_Enforced.sh does the same for HSTS (the misspelling in that filename is in the real source, not a transcription error). The Community Edition ships 18 of these under templates/passive/web/, covering things like autocomplete left on for password fields, wildcard CORS origins, cookies missing HttpOnly or Secure, expired certificates, clickjacking (missing X-Frame-Options), and the TRACE HTTP method still being enabled. A separate templates/passive/network/ set applies the identical positive/negative pattern-match model to non-web findings.
The engine that runs all of this, in sc0pe.sh, is a loop:
for file in `ls $INSTALL_DIR/templates/passive/web/*.sh 2> /dev/null`; do
source $file
# ... apply $MATCH against $FILENAME with $GREP_OPTIONS, per $SEARCH ...
done
Every template gets sourced and evaluated against whatever nmap, curl, and the header-capture stage already saved to the loot directory. There’s no live re-request per check; sc0pe reads the artifacts other stages already produced. Adding a new check to Sn1per, at least at this layer, means writing one more file with those same seven variables, not touching any scanning logic.
The severity scale is a priority label, not a CVSS score
Notice the actual SEVERITY strings above: 'P5 - INFO', 'P4 - LOW'. Not “INFO” or “LOW” alone, but a P1-P5 priority prefix, and the aggregator downstream only cares about the tail end of that string. Here’s the actual scoring logic from sc0pe.sh:
CRITICAL_VULNS=$(egrep CRITICAL $LOOT_DIR/vulnerabilities/sc0pe-$TARGET-*.txt | wc -l)
HIGH_VULNS=$(egrep HIGH ... | wc -l)
MEDIUM_VULNS=$(egrep MEDIUM ... | wc -l)
LOW_VULNS=$(egrep LOW ... | wc -l)
INFO_VULNS=$(egrep INFO ... | wc -l)
VULN_SCORE=$(($CRITICAL_VULNS*5 + $HIGH_VULNS*4 + $MEDIUM_VULNS*3 + $LOW_VULNS*2 + $INFO_VULNS*1))
A weighted count, not a CVSS-style calculation with attack vector, complexity, or scope in it anywhere. One CRITICAL finding is worth exactly five INFO findings. This has a real consequence: a host with one missing X-Frame-Options header (INFO or LOW depending on the template), one disclosed Server: banner (INFO), one missing CSP (INFO), and a wildcard CORS policy (LOW) racks up a score of roughly 6 to 8 from entirely cosmetic findings, in the same numeric range as a single MEDIUM finding. Anyone consuming this score without reading the underlying findings list would have no way to tell those two situations apart. It’s a genuinely useful triage signal across a large batch of hosts (which is the point, when you’re running massvulnscan against hundreds of targets and need to know where to look first), and a poor substitute for actually reading the report on any individual host that matters. Real root-verified testing in Part 7 checked this exact formula by hand against more than a dozen separate reports, at scores ranging from single digits up to the high 70s, and the arithmetic matched every single time.
Running against Northbridge Freight
Part 1 introduced the small local lab this series scans against: Northbridge Freight Co., a Python http.server app on loopback. Its real captured response headers:
HTTP/1.0 200 OK
Server: Werkzeug/2.2.3 Python/3.10
Date: Sun, 26 Jul 2026 08:19:00 GMT
Content-Type: text/html
Content-Length: 335
Run that through the actual templates above and the hits are immediate and entirely mechanical: Server_Header_Disclosure.sh matches on Server: (the full Werkzeug/2.2.3 Python/3.10 string, disclosing exact framework and Python version). CSP_Not_Enforced.sh matches because there’s no content-security-policy header anywhere in that response. Strict_Tranposrt_Security_Not_Enforced.sh would match too, if this were being served over HTTPS. None of these are wrong findings. All three are real, all three are the kind of thing that should get fixed on anything customer-facing. None of them is remotely as urgent as, say, the /internal-notes.txt file robots.txt inadvertently points at (which sc0pe’s template model has no way to notice at all, since finding it depends on reading robots.txt as a hint and requesting the disallowed path, a different kind of check than pattern-matching a header value).
That gap, between what a template-driven scanner can mechanically confirm and what a person reading the same output would flag first, is exactly why Part 6’s full walkthrough treats the sc0pe score as a starting point for triage rather than a verdict.
Two sc0pe bugs, confirmed against real targets
Reading the templates above only shows intent; running them against real targets in Part 7 surfaced two bugs worth knowing before trusting the output at face value. First, Clickjacking_HTTP.sh and its HTTPS counterpart flag a target regardless of the actual header value present. A target correctly serving x-frame-options: DENY, the secure setting, still gets logged as a P4-LOW “Clickjacking” finding, indistinguishable from a target with no header at all. The template checks for the header’s role in the response rather than distinguishing a secure value from a missing one. Second, every real nuclei invocation across every mode and target tested in Part 7 failed identically: [FTL] Could not run nuclei: no templates provided for scan, a packaging or configuration gap in how Community Edition bundles the nuclei template set rather than anything target-specific. Neither bug invalidates the tool; the other sc0pe checks and everything else in this series held up. Both are worth knowing before treating a clickjacking line item or an empty nuclei section as meaning what it appears to mean.
Web mode’s paid-tier ceiling
webscan.sh also shows, plainly, where Community Edition’s web scanning stops. The same script has fully built-out branches for Burp Suite (BURP_SCAN), OWASP ZAP (ZAP_SCAN), Arachni (ARACHNI_SCAN), and Nuclei (NUCLEI), every one of them gated behind a config flag that’s off by default and, for Burp and ZAP specifically, requires pointing Sn1per at an already-running instance of a commercial or separately-installed scanner. The free tier’s own vulnerability detection is entirely the sc0pe template engine described above. Everything past that is integration glue for tools you have to bring yourself, or pay for the Professional tier to bundle.
Part 4 covers what that Professional tier actually adds architecturally, which has to be described from Sn1per’s own release documentation since the paid engine isn’t in the public source tree the way everything in this post is.