Files
sif/internal/scan/frameworks/detectors/waf.go
T
TigahandGitHub bd91f7f590 feat(frameworks): add web application firewall detectors (#360)
detect cloudflare waf, sucuri, incapsula, f5 big-ip asm and modsecurity
from the headers, cookies and block-page strings the waf itself sets, so
a site that merely names the vendor in prose does not trip them. grouped
in one file so the category can be reviewed or excluded as a unit.
2026-07-22 12:57:18 -07:00

135 lines
5.4 KiB
Go

/*
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
: :
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
: ▄█ █ █▀ · BSD 3-Clause License :
: :
: (c) 2022-2026 vmfunc, xyzeva, :
: lunchcat alumni & contributors :
: :
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
*/
/*
BSD 3-Clause License
(c) 2022-2026 vmfunc, xyzeva & contributors
*/
package detectors
import (
"net/http"
fw "github.com/vmfunc/sif/internal/scan/frameworks"
)
// The detectors here identify the WAF in front of a target. Each keys on a
// header, Set-Cookie name, or block-page string the WAF itself sets, so a page
// that merely names the vendor in prose does not trip it.
func init() {
fw.Register(&cloudflareWAFDetector{})
fw.Register(&sucuriDetector{})
fw.Register(&incapsulaDetector{})
fw.Register(&bigipASMDetector{})
fw.Register(&modSecurityDetector{})
}
// cloudflareWAFDetector detects an active Cloudflare WAF or bot challenge via the
// cf-mitigated header and __cf_chl_ tokens, which appear only on an interception
// response (plain proxying is covered by the cf-ray CDN marker).
type cloudflareWAFDetector struct{}
func (d *cloudflareWAFDetector) Name() string { return "Cloudflare WAF" }
func (d *cloudflareWAFDetector) Signatures() []fw.Signature {
return []fw.Signature{
{Pattern: "cf-mitigated", Weight: 0.6, HeaderOnly: true},
{Pattern: "__cf_chl_", Weight: 0.5},
{Pattern: "Attention Required! | Cloudflare", Weight: 0.4},
}
}
func (d *cloudflareWAFDetector) Detect(body string, headers http.Header) (float32, string) {
base := fw.NewBaseDetector(d.Name(), d.Signatures())
return sigmoidConfidence(base.MatchSignatures(body, headers)), ""
}
// sucuriDetector detects the Sucuri (GoDaddy) cloud WAF via its x-sucuri-id and
// x-sucuri-cache headers, backed by the block-page string.
type sucuriDetector struct{}
func (d *sucuriDetector) Name() string { return "Sucuri WAF" }
func (d *sucuriDetector) Signatures() []fw.Signature {
return []fw.Signature{
{Pattern: "x-sucuri-id", Weight: 0.6, HeaderOnly: true},
{Pattern: "x-sucuri-cache", Weight: 0.4, HeaderOnly: true},
{Pattern: "Sucuri WebSite Firewall", Weight: 0.5},
}
}
func (d *sucuriDetector) Detect(body string, headers http.Header) (float32, string) {
base := fw.NewBaseDetector(d.Name(), d.Signatures())
return sigmoidConfidence(base.MatchSignatures(body, headers)), ""
}
// incapsulaDetector detects the Imperva Incapsula cloud WAF via its x-iinfo
// header, incap_ses_ Set-Cookie name, and block-page incident string. Each is
// Incapsula-exclusive and clears the threshold on its own.
type incapsulaDetector struct{}
func (d *incapsulaDetector) Name() string { return "Imperva Incapsula" }
func (d *incapsulaDetector) Signatures() []fw.Signature {
return []fw.Signature{
{Pattern: "x-iinfo", Weight: 0.6, HeaderOnly: true},
{Pattern: "incap_ses_", Weight: 0.5, HeaderOnly: true},
{Pattern: "Incapsula incident ID", Weight: 0.5},
}
}
func (d *incapsulaDetector) Detect(body string, headers http.Header) (float32, string) {
base := fw.NewBaseDetector(d.Name(), d.Signatures())
return sigmoidConfidence(base.MatchSignatures(body, headers)), ""
}
// bigipASMDetector detects the F5 BIG-IP Application Security Manager via its
// block-page rejection sentence and support-id line. Block-page only, so it never
// fires on a passing response.
type bigipASMDetector struct{}
func (d *bigipASMDetector) Name() string { return "F5 BIG-IP ASM" }
func (d *bigipASMDetector) Signatures() []fw.Signature {
return []fw.Signature{
{Pattern: "The requested URL was rejected. Please consult with your administrator.", Weight: 0.6},
{Pattern: "Your support ID is:", Weight: 0.3},
}
}
func (d *bigipASMDetector) Detect(body string, headers http.Header) (float32, string) {
base := fw.NewBaseDetector(d.Name(), d.Signatures())
return sigmoidConfidence(base.MatchSignatures(body, headers)), ""
}
// modSecurityDetector detects ModSecurity via its generated block page (primary)
// and the Mod_Security/NOYB server-token values (supporting, since suppressible).
type modSecurityDetector struct{}
func (d *modSecurityDetector) Name() string { return "ModSecurity" }
func (d *modSecurityDetector) Signatures() []fw.Signature {
return []fw.Signature{
{Pattern: "This error was generated by Mod_Security", Weight: 0.6},
{Pattern: "Mod_Security", Weight: 0.3, HeaderOnly: true},
{Pattern: "NOYB", Weight: 0.2, HeaderOnly: true},
}
}
func (d *modSecurityDetector) Detect(body string, headers http.Header) (float32, string) {
base := fw.NewBaseDetector(d.Name(), d.Signatures())
return sigmoidConfidence(base.MatchSignatures(body, headers)), ""
}