nuclei/v2/pkg/operators/operators.go

192 lines
6.5 KiB
Go
Raw Normal View History

package operators
import (
"github.com/pkg/errors"
"github.com/projectdiscovery/nuclei/v2/pkg/operators/extractors"
"github.com/projectdiscovery/nuclei/v2/pkg/operators/matchers"
)
// Operators contains the operators that can be applied on protocols
type Operators struct {
2021-07-27 16:03:56 +05:30
// description: |
// Matchers contains the detection mechanism for the request to identify
// whether the request was successful by doing pattern matching
// on request/responses.
//
2021-09-07 17:31:46 +03:00
// Multiple matchers can be combined with `matcher-condition` flag
2021-07-27 16:03:56 +05:30
// which accepts either `and` or `or` as argument.
Matchers []*matchers.Matcher `yaml:"matchers,omitempty" jsonschema:"title=matchers to run on response,description=Detection mechanism to identify whether the request was successful by doing pattern matching"`
2021-07-27 16:03:56 +05:30
// description: |
// Extractors contains the extraction mechanism for the request to identify
// and extract parts of the response.
Extractors []*extractors.Extractor `yaml:"extractors,omitempty" jsonschema:"title=extractors to run on response,description=Extractors contains the extraction mechanism for the request to identify and extract parts of the response"`
2021-07-27 16:03:56 +05:30
// description: |
// MatchersCondition is the condition between the matchers. Default is OR.
// values:
// - "and"
// - "or"
MatchersCondition string `yaml:"matchers-condition,omitempty" jsonschema:"title=condition between the matchers,description=Conditions between the matchers,enum=and,enum=or"`
// cached variables that may be used along with request.
matchersCondition matchers.ConditionType
}
// Compile compiles the operators as well as their corresponding matchers and extractors
func (r *Operators) Compile() error {
if r.MatchersCondition != "" {
r.matchersCondition = matchers.ConditionTypes[r.MatchersCondition]
} else {
2020-12-30 13:26:55 +05:30
r.matchersCondition = matchers.ORCondition
}
for _, matcher := range r.Matchers {
if err := matcher.CompileMatchers(); err != nil {
return errors.Wrap(err, "could not compile matcher")
}
}
for _, extractor := range r.Extractors {
if err := extractor.CompileExtractors(); err != nil {
return errors.Wrap(err, "could not compile extractor")
}
}
return nil
}
// GetMatchersCondition returns the condition for the matchers
func (r *Operators) GetMatchersCondition() matchers.ConditionType {
return r.matchersCondition
}
2020-12-24 12:56:28 +05:30
// Result is a result structure created from operators running on data.
type Result struct {
// Matched is true if any matchers matched
Matched bool
// Extracted is true if any result type values were extracted
Extracted bool
2020-12-24 12:56:28 +05:30
// Matches is a map of matcher names that we matched
Matches map[string][]string
2020-12-24 12:56:28 +05:30
// Extracts contains all the data extracted from inputs
Extracts map[string][]string
2020-12-24 20:47:41 +05:30
// OutputExtracts is the list of extracts to be displayed on screen.
OutputExtracts []string
2020-12-24 12:56:28 +05:30
// DynamicValues contains any dynamic values to be templated
DynamicValues map[string]interface{}
// PayloadValues contains payload values provided by user. (Optional)
PayloadValues map[string]interface{}
2020-12-24 12:56:28 +05:30
}
2021-04-18 16:10:10 +05:30
// Merge merges a result structure into the other.
func (r *Result) Merge(result *Result) {
if !r.Matched && result.Matched {
r.Matched = result.Matched
}
if !r.Extracted && result.Extracted {
r.Extracted = result.Extracted
}
for k, v := range result.Matches {
r.Matches[k] = v
}
for k, v := range result.Extracts {
r.Extracts[k] = v
}
2021-05-01 18:28:24 +05:30
r.OutputExtracts = append(r.OutputExtracts, result.OutputExtracts...)
2021-04-18 16:10:10 +05:30
for k, v := range result.DynamicValues {
r.DynamicValues[k] = v
}
for k, v := range result.PayloadValues {
r.PayloadValues[k] = v
}
}
2020-12-24 20:47:41 +05:30
// MatchFunc performs matching operation for a matcher on model and returns true or false.
type MatchFunc func(data map[string]interface{}, matcher *matchers.Matcher) (bool, []string)
2020-12-24 20:47:41 +05:30
2021-09-07 17:31:46 +03:00
// ExtractFunc performs extracting operation for an extractor on model and returns true or false.
2020-12-24 20:47:41 +05:30
type ExtractFunc func(data map[string]interface{}, matcher *extractors.Extractor) map[string]struct{}
2020-12-24 12:56:28 +05:30
// Execute executes the operators on data and returns a result structure
func (r *Operators) Execute(data map[string]interface{}, match MatchFunc, extract ExtractFunc) (*Result, bool) {
2020-12-24 12:56:28 +05:30
matcherCondition := r.GetMatchersCondition()
2020-12-30 13:26:55 +05:30
var matches bool
2020-12-24 12:56:28 +05:30
result := &Result{
Matches: make(map[string][]string),
2020-12-24 12:56:28 +05:30
Extracts: make(map[string][]string),
DynamicValues: make(map[string]interface{}),
2020-12-24 12:56:28 +05:30
}
2021-04-18 16:10:10 +05:30
2021-03-09 16:35:53 +05:30
// Start with the extractors first and evaluate them.
2020-12-24 12:56:28 +05:30
for _, extractor := range r.Extractors {
2020-12-24 20:47:41 +05:30
var extractorResults []string
for match := range extract(data, extractor) {
2020-12-24 12:56:28 +05:30
extractorResults = append(extractorResults, match)
if extractor.Internal {
if _, ok := result.DynamicValues[extractor.Name]; !ok {
result.DynamicValues[extractor.Name] = match
}
} else {
2020-12-24 20:47:41 +05:30
result.OutputExtracts = append(result.OutputExtracts, match)
2020-12-24 12:56:28 +05:30
}
}
2021-01-11 21:11:35 +05:30
if len(extractorResults) > 0 && !extractor.Internal && extractor.Name != "" {
2020-12-30 13:26:55 +05:30
result.Extracts[extractor.Name] = extractorResults
}
2020-12-24 12:56:28 +05:30
}
2021-03-09 16:35:53 +05:30
for _, matcher := range r.Matchers {
// Check if the matcher matched
if isMatch, matched := match(data, matcher); isMatch {
2021-09-07 17:31:46 +03:00
// If the matcher has matched, and it's an OR
2021-03-09 16:35:53 +05:30
// write the first output then move to next matcher.
if matcherCondition == matchers.ORCondition && matcher.Name != "" {
result.Matches[matcher.Name] = matched
2021-03-09 16:35:53 +05:30
}
2021-03-09 16:35:53 +05:30
matches = true
} else if matcherCondition == matchers.ANDCondition {
if len(result.DynamicValues) > 0 {
return result, true
}
return nil, false
2021-03-09 16:35:53 +05:30
}
}
result.Matched = matches
result.Extracted = len(result.OutputExtracts) > 0
if len(result.DynamicValues) > 0 {
return result, true
}
2021-09-16 20:35:43 +03:00
// Don't print if we have matchers, and they have not matched, regardless of extractor
2020-12-30 13:26:55 +05:30
if len(r.Matchers) > 0 && !matches {
return nil, false
}
2020-12-24 12:56:28 +05:30
// Write a final string of output if matcher type is
// AND or if we have extractors for the mechanism too.
if len(result.Extracts) > 0 || len(result.OutputExtracts) > 0 || matches {
2020-12-24 12:56:28 +05:30
return result, true
}
return nil, false
}
// ExecuteInternalExtractors executes internal dynamic extractors
func (r *Operators) ExecuteInternalExtractors(data map[string]interface{}, extract ExtractFunc) map[string]interface{} {
dynamicValues := make(map[string]interface{})
// Start with the extractors first and evaluate them.
for _, extractor := range r.Extractors {
if !extractor.Internal {
continue
}
for match := range extract(data, extractor) {
if _, ok := dynamicValues[extractor.Name]; !ok {
dynamicValues[extractor.Name] = match
}
}
}
return dynamicValues
}