Choose between 'and' and 'or' matching and filtering (#20)

This commit is contained in:
Joona Hoikkala 2022-04-03 23:20:53 +03:00 committed by Joona Hoikkala
parent 9fa0a5d20a
commit 21a19a1f3d
7 changed files with 57 additions and 4 deletions

View file

@ -1,6 +1,8 @@
## Changelog
- master
- New
- New autocalibration options: `-ach`, `-ack` and `-acs`. Revamped the whole autocalibration process
- Configurable modes for matchers and filters (CLI flags: `fmode` and `mmode`): "and" and "or"
- Changed
- v1.4.1
@ -16,7 +18,6 @@
- Added full line colors
- Added `-json` to emit newline delimited JSON output
- Added 500 Internal Server Error to list of status codes matched by default
- New autocalibration options: `-ach`, `-ack` and `-acs`. Revamped the whole autocalibration process
- Changed
- Fixed an issue where output file was created regardless of `-or`
- Fixed an issue where output (often a lot of it) would be printed after entering interactive mode

View file

@ -69,6 +69,7 @@
outputcreateemptyfile = false
[filter]
mode = "or"
lines = ""
regexp = ""
size = ""
@ -77,6 +78,7 @@
words = ""
[matcher]
mode = "or"
lines = ""
regexp = ""
size = ""

View file

@ -75,14 +75,14 @@ func Usage() {
Description: "Matchers for the response filtering.",
Flags: make([]UsageFlag, 0),
Hidden: false,
ExpectedFlags: []string{"mc", "ml", "mr", "ms", "mt", "mw"},
ExpectedFlags: []string{"mmode", "mc", "ml", "mr", "ms", "mt", "mw"},
}
u_filter := UsageSection{
Name: "FILTER OPTIONS",
Description: "Filters for the response filtering.",
Flags: make([]UsageFlag, 0),
Hidden: false,
ExpectedFlags: []string{"fc", "fl", "fr", "fs", "ft", "fw"},
ExpectedFlags: []string{"fmode", "fc", "fl", "fr", "fs", "ft", "fw"},
}
u_input := UsageSection{
Name: "INPUT OPTIONS",

View file

@ -88,6 +88,7 @@ func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions {
flag.StringVar(&opts.General.AutoCalibrationKeyword, "ack", opts.General.AutoCalibrationKeyword, "Autocalibration keyword")
flag.StringVar(&opts.General.AutoCalibrationStrategy, "acs", opts.General.AutoCalibrationStrategy, "Autocalibration strategy: \"basic\" or \"advanced\"")
flag.StringVar(&opts.General.ConfigFile, "config", "", "Load configuration from a file")
flag.StringVar(&opts.Filter.Mode, "fmode", opts.Filter.Mode, "Filter set operator. Either of: and, or")
flag.StringVar(&opts.Filter.Lines, "fl", opts.Filter.Lines, "Filter by amount of lines in response. Comma separated list of line counts and ranges")
flag.StringVar(&opts.Filter.Regexp, "fr", opts.Filter.Regexp, "Filter regexp")
flag.StringVar(&opts.Filter.Size, "fs", opts.Filter.Size, "Filter HTTP response size. Comma separated list of sizes and ranges")
@ -110,6 +111,7 @@ func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions {
flag.StringVar(&opts.Input.InputShell, "input-shell", opts.Input.InputShell, "Shell to be used for running command")
flag.StringVar(&opts.Input.Request, "request", opts.Input.Request, "File containing the raw http request")
flag.StringVar(&opts.Input.RequestProto, "request-proto", opts.Input.RequestProto, "Protocol to use along with raw request")
flag.StringVar(&opts.Matcher.Mode, "mmode", opts.Matcher.Mode, "Matcher set operator. Either of: and, or")
flag.StringVar(&opts.Matcher.Lines, "ml", opts.Matcher.Lines, "Match amount of lines in response")
flag.StringVar(&opts.Matcher.Regexp, "mr", opts.Matcher.Regexp, "Match regexp")
flag.StringVar(&opts.Matcher.Size, "ms", opts.Matcher.Size, "Match HTTP response size")

View file

@ -20,6 +20,7 @@ type Config struct {
Delay optRange `json:"delay"`
DirSearchCompat bool `json:"dirsearch_compatibility"`
Extensions []string `json:"extensions"`
FilterMode string `json:"fmode"`
FollowRedirects bool `json:"follow_redirects"`
Headers map[string]string `json:"headers"`
IgnoreBody bool `json:"ignorebody"`
@ -30,6 +31,7 @@ type Config struct {
InputShell string `json:"inputshell"`
Json bool `json:"json"`
MatcherManager MatcherManager `json:"matchers"`
MatcherMode string `json:"mmode"`
MaxTime int `json:"maxtime"`
MaxTimeJob int `json:"maxtime_job"`
Method string `json:"method"`
@ -76,6 +78,7 @@ func NewConfig(ctx context.Context, cancel context.CancelFunc) Config {
conf.Delay = optRange{0, 0, false, false}
conf.DirSearchCompat = false
conf.Extensions = make([]string, 0)
conf.FilterMode = "or"
conf.FollowRedirects = false
conf.Headers = make(map[string]string)
conf.IgnoreWordlistComments = false
@ -84,6 +87,7 @@ func NewConfig(ctx context.Context, cancel context.CancelFunc) Config {
conf.InputShell = ""
conf.InputProviders = make([]InputProviderConfig, 0)
conf.Json = false
conf.MatcherMode = "or"
conf.MaxTime = 0
conf.MaxTimeJob = 0
conf.Method = "GET"

View file

@ -341,6 +341,10 @@ func (j *Job) isMatch(resp Response) bool {
}
if match {
matched = true
} else if j.Config.MatcherMode == "and" {
// we already know this isn't "and" match
return false
}
}
// The response was not matched, return before running filters
@ -353,8 +357,21 @@ func (j *Job) isMatch(resp Response) bool {
continue
}
if fv {
// return false
if j.Config.FilterMode == "or" {
// return early, as filter matched
return false
}
} else {
if j.Config.FilterMode == "and" {
// return early as not all filters matched in "and" mode
return true
}
}
}
if len(filters) > 0 && j.Config.FilterMode == "and" {
// we did not return early, so all filters were matched
return false
}
return true
}

View file

@ -88,6 +88,7 @@ type OutputOptions struct {
}
type FilterOptions struct {
Mode string
Lines string
Regexp string
Size string
@ -97,6 +98,7 @@ type FilterOptions struct {
}
type MatcherOptions struct {
Mode string
Lines string
Regexp string
Size string
@ -108,6 +110,7 @@ type MatcherOptions struct {
//NewConfigOptions returns a newly created ConfigOptions struct with default values
func NewConfigOptions() *ConfigOptions {
c := &ConfigOptions{}
c.Filter.Mode = "or"
c.Filter.Lines = ""
c.Filter.Regexp = ""
c.Filter.Size = ""
@ -151,6 +154,7 @@ func NewConfigOptions() *ConfigOptions {
c.Input.InputNum = 100
c.Input.Request = ""
c.Input.RequestProto = "https"
c.Matcher.Mode = "or"
c.Matcher.Lines = ""
c.Matcher.Regexp = ""
c.Matcher.Size = ""
@ -461,6 +465,29 @@ func ConfigFromOptions(parseOpts *ConfigOptions, ctx context.Context, cancel con
conf.Json = parseOpts.General.Json
conf.Http2 = parseOpts.HTTP.Http2
// Check that fmode and mmode have sane values
valid_opmodes := []string{"and", "or"}
fmode_found := false
mmode_found := false
for _, v := range valid_opmodes {
if v == parseOpts.Filter.Mode {
fmode_found = true
}
if v == parseOpts.Matcher.Mode {
mmode_found = true
}
}
if !fmode_found {
errmsg := fmt.Sprintf("Unrecognized value for parameter fmode: %s, valid values are: and, or", parseOpts.Filter.Mode)
errs.Add(fmt.Errorf(errmsg))
}
if !mmode_found {
errmsg := fmt.Sprintf("Unrecognized value for parameter mmode: %s, valid values are: and, or", parseOpts.Matcher.Mode)
errs.Add(fmt.Errorf(errmsg))
}
conf.FilterMode = parseOpts.Filter.Mode
conf.MatcherMode = parseOpts.Matcher.Mode
if conf.AutoCalibrationPerHost {
// AutoCalibrationPerHost implies AutoCalibration
conf.AutoCalibration = true