mirror of
https://github.com/lunchcat/sif.git
synced 2026-07-28 14:37:01 -07:00
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.
135 lines
5.4 KiB
Go
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)), ""
|
|
}
|