2020-04-03 18:46:27 +00:00
|
|
|
package matchers
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
2021-09-15 12:32:22 +00:00
|
|
|
|
2021-11-24 16:49:42 +00:00
|
|
|
"github.com/Knetic/govaluate"
|
2021-12-07 16:17:34 +00:00
|
|
|
|
2021-12-07 15:34:36 +00:00
|
|
|
"github.com/projectdiscovery/gologger"
|
2021-11-24 16:49:42 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/operators/common/dsl"
|
2021-09-15 12:32:22 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/expressions"
|
2020-04-03 18:46:27 +00:00
|
|
|
)
|
|
|
|
|
2020-12-24 15:17:41 +00:00
|
|
|
// MatchStatusCode matches a status code check against a corpus
|
2021-11-25 14:57:43 +00:00
|
|
|
func (matcher *Matcher) MatchStatusCode(statusCode int) bool {
|
2020-04-03 18:46:27 +00:00
|
|
|
// Iterate over all the status codes accepted as valid
|
2020-04-03 19:02:03 +00:00
|
|
|
//
|
|
|
|
// Status codes don't support AND conditions.
|
2021-11-25 14:57:43 +00:00
|
|
|
for _, status := range matcher.Status {
|
2020-04-03 18:46:27 +00:00
|
|
|
// Continue if the status codes don't match
|
|
|
|
if statusCode != status {
|
|
|
|
continue
|
|
|
|
}
|
2020-04-03 19:02:03 +00:00
|
|
|
// Return on the first match.
|
|
|
|
return true
|
2020-04-03 18:46:27 +00:00
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-12-24 15:17:41 +00:00
|
|
|
// MatchSize matches a size check against a corpus
|
2021-11-25 14:57:43 +00:00
|
|
|
func (matcher *Matcher) MatchSize(length int) bool {
|
2020-04-03 18:46:27 +00:00
|
|
|
// Iterate over all the sizes accepted as valid
|
2020-04-03 19:02:03 +00:00
|
|
|
//
|
|
|
|
// Sizes codes don't support AND conditions.
|
2021-11-25 14:57:43 +00:00
|
|
|
for _, size := range matcher.Size {
|
2020-04-03 18:46:27 +00:00
|
|
|
// Continue if the size doesn't match
|
|
|
|
if length != size {
|
|
|
|
continue
|
|
|
|
}
|
2020-04-03 19:02:03 +00:00
|
|
|
// Return on the first match.
|
|
|
|
return true
|
2020-04-03 18:46:27 +00:00
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-12-24 15:17:41 +00:00
|
|
|
// MatchWords matches a word check against a corpus.
|
2021-11-29 08:06:09 +00:00
|
|
|
func (matcher *Matcher) MatchWords(corpus string, data map[string]interface{}) (bool, []string) {
|
2021-11-25 14:57:43 +00:00
|
|
|
if matcher.CaseInsensitive {
|
2021-10-29 16:08:18 +00:00
|
|
|
corpus = strings.ToLower(corpus)
|
|
|
|
}
|
|
|
|
|
2021-09-29 16:43:46 +00:00
|
|
|
var matchedWords []string
|
2020-04-03 18:46:27 +00:00
|
|
|
// Iterate over all the words accepted as valid
|
2021-11-25 14:57:43 +00:00
|
|
|
for i, word := range matcher.Words {
|
2021-11-24 16:49:42 +00:00
|
|
|
if data == nil {
|
|
|
|
data = make(map[string]interface{})
|
2021-09-15 12:32:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
2021-11-24 16:49:42 +00:00
|
|
|
word, err = expressions.Evaluate(word, data)
|
2021-09-15 12:32:22 +00:00
|
|
|
if err != nil {
|
2021-12-07 15:34:36 +00:00
|
|
|
gologger.Warning().Msgf("Error while evaluating word matcher: %q", word)
|
2021-09-15 12:32:22 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-04-03 18:46:27 +00:00
|
|
|
// Continue if the word doesn't match
|
2020-08-25 21:24:31 +00:00
|
|
|
if !strings.Contains(corpus, word) {
|
2020-04-03 18:46:27 +00:00
|
|
|
// If we are in an AND request and a match failed,
|
|
|
|
// return false as the AND condition fails on any single mismatch.
|
2021-12-07 16:17:34 +00:00
|
|
|
switch matcher.condition {
|
2021-12-07 15:34:36 +00:00
|
|
|
case ANDCondition:
|
2021-09-29 16:43:46 +00:00
|
|
|
return false, []string{}
|
2021-12-07 15:34:36 +00:00
|
|
|
case ORCondition:
|
|
|
|
continue
|
2020-04-03 18:46:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the condition was an OR, return on the first match.
|
2022-02-10 10:29:05 +00:00
|
|
|
if matcher.condition == ORCondition && !matcher.MatchAll {
|
2021-09-29 16:43:46 +00:00
|
|
|
return true, []string{word}
|
2020-04-03 18:46:27 +00:00
|
|
|
}
|
2021-09-29 16:43:46 +00:00
|
|
|
matchedWords = append(matchedWords, word)
|
|
|
|
|
2020-04-03 19:02:03 +00:00
|
|
|
// If we are at the end of the words, return with true
|
2022-02-10 10:29:05 +00:00
|
|
|
if len(matcher.Words)-1 == i && !matcher.MatchAll {
|
2021-09-29 16:43:46 +00:00
|
|
|
return true, matchedWords
|
2020-04-03 19:02:03 +00:00
|
|
|
}
|
2020-04-03 18:46:27 +00:00
|
|
|
}
|
2022-02-10 10:29:05 +00:00
|
|
|
if len(matchedWords) > 0 && matcher.MatchAll {
|
|
|
|
return true, matchedWords
|
|
|
|
}
|
2021-09-29 16:43:46 +00:00
|
|
|
return false, []string{}
|
2020-04-03 18:46:27 +00:00
|
|
|
}
|
|
|
|
|
2020-12-24 15:17:41 +00:00
|
|
|
// MatchRegex matches a regex check against a corpus
|
2021-11-25 14:57:43 +00:00
|
|
|
func (matcher *Matcher) MatchRegex(corpus string) (bool, []string) {
|
2021-09-30 17:36:39 +00:00
|
|
|
var matchedRegexes []string
|
2020-04-03 18:46:27 +00:00
|
|
|
// Iterate over all the regexes accepted as valid
|
2021-11-25 14:57:43 +00:00
|
|
|
for i, regex := range matcher.regexCompiled {
|
2020-04-03 18:46:27 +00:00
|
|
|
// Continue if the regex doesn't match
|
|
|
|
if !regex.MatchString(corpus) {
|
|
|
|
// If we are in an AND request and a match failed,
|
|
|
|
// return false as the AND condition fails on any single mismatch.
|
2021-12-07 16:17:34 +00:00
|
|
|
switch matcher.condition {
|
2021-12-07 15:34:36 +00:00
|
|
|
case ANDCondition:
|
2021-09-29 16:43:46 +00:00
|
|
|
return false, []string{}
|
2021-12-07 15:34:36 +00:00
|
|
|
case ORCondition:
|
|
|
|
continue
|
2020-04-03 18:46:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-30 17:36:39 +00:00
|
|
|
currentMatches := regex.FindAllString(corpus, -1)
|
2020-04-03 18:46:27 +00:00
|
|
|
// If the condition was an OR, return on the first match.
|
2022-02-10 10:29:05 +00:00
|
|
|
if matcher.condition == ORCondition && !matcher.MatchAll {
|
2021-09-30 17:36:39 +00:00
|
|
|
return true, currentMatches
|
2020-04-03 18:46:27 +00:00
|
|
|
}
|
2020-04-03 19:02:03 +00:00
|
|
|
|
2021-09-30 17:36:39 +00:00
|
|
|
matchedRegexes = append(matchedRegexes, currentMatches...)
|
|
|
|
|
2020-04-03 19:02:03 +00:00
|
|
|
// If we are at the end of the regex, return with true
|
2022-02-10 10:29:05 +00:00
|
|
|
if len(matcher.regexCompiled)-1 == i && !matcher.MatchAll {
|
2021-09-30 17:36:39 +00:00
|
|
|
return true, matchedRegexes
|
2020-04-03 19:02:03 +00:00
|
|
|
}
|
2020-04-03 18:46:27 +00:00
|
|
|
}
|
2022-02-10 10:29:05 +00:00
|
|
|
if len(matchedRegexes) > 0 && matcher.MatchAll {
|
|
|
|
return true, matchedRegexes
|
|
|
|
}
|
2021-09-29 16:43:46 +00:00
|
|
|
return false, []string{}
|
2020-04-03 18:46:27 +00:00
|
|
|
}
|
2020-04-21 18:50:35 +00:00
|
|
|
|
2020-12-24 15:17:41 +00:00
|
|
|
// MatchBinary matches a binary check against a corpus
|
2021-11-25 14:57:43 +00:00
|
|
|
func (matcher *Matcher) MatchBinary(corpus string) (bool, []string) {
|
2021-09-30 17:36:39 +00:00
|
|
|
var matchedBinary []string
|
2020-04-21 18:50:35 +00:00
|
|
|
// Iterate over all the words accepted as valid
|
2021-11-25 14:57:43 +00:00
|
|
|
for i, binary := range matcher.binaryDecoded {
|
2021-11-16 20:34:27 +00:00
|
|
|
if !strings.Contains(corpus, binary) {
|
2020-04-21 18:50:35 +00:00
|
|
|
// If we are in an AND request and a match failed,
|
|
|
|
// return false as the AND condition fails on any single mismatch.
|
2021-12-07 16:17:34 +00:00
|
|
|
switch matcher.condition {
|
2021-12-07 15:34:36 +00:00
|
|
|
case ANDCondition:
|
2021-09-29 16:43:46 +00:00
|
|
|
return false, []string{}
|
2021-12-07 15:34:36 +00:00
|
|
|
case ORCondition:
|
|
|
|
continue
|
2020-04-21 18:50:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the condition was an OR, return on the first match.
|
2021-11-25 14:57:43 +00:00
|
|
|
if matcher.condition == ORCondition {
|
2021-11-16 20:34:27 +00:00
|
|
|
return true, []string{binary}
|
2020-04-21 18:50:35 +00:00
|
|
|
}
|
|
|
|
|
2021-11-16 20:34:27 +00:00
|
|
|
matchedBinary = append(matchedBinary, binary)
|
2021-09-30 17:36:39 +00:00
|
|
|
|
2020-04-21 18:50:35 +00:00
|
|
|
// If we are at the end of the words, return with true
|
2021-11-25 14:57:43 +00:00
|
|
|
if len(matcher.Binary)-1 == i {
|
2021-09-30 17:36:39 +00:00
|
|
|
return true, matchedBinary
|
2020-04-21 18:50:35 +00:00
|
|
|
}
|
|
|
|
}
|
2021-09-29 16:43:46 +00:00
|
|
|
return false, []string{}
|
2020-04-21 18:50:35 +00:00
|
|
|
}
|
2020-04-26 21:32:58 +00:00
|
|
|
|
2020-12-24 15:17:41 +00:00
|
|
|
// MatchDSL matches on a generic map result
|
2021-11-25 14:57:43 +00:00
|
|
|
func (matcher *Matcher) MatchDSL(data map[string]interface{}) bool {
|
2021-12-16 11:38:02 +00:00
|
|
|
logExpressionEvaluationFailure := func(matcherName string, err error) {
|
2021-12-07 16:17:34 +00:00
|
|
|
gologger.Warning().Msgf("Could not evaluate expression: %s, error: %s", matcherName, err.Error())
|
|
|
|
}
|
|
|
|
|
2020-11-25 17:27:14 +00:00
|
|
|
// Iterate over all the expressions accepted as valid
|
2021-11-25 14:57:43 +00:00
|
|
|
for i, expression := range matcher.dslCompiled {
|
2021-11-24 16:49:42 +00:00
|
|
|
if varErr := expressions.ContainsUnresolvedVariables(expression.String()); varErr != nil {
|
|
|
|
resolvedExpression, err := expressions.Evaluate(expression.String(), data)
|
|
|
|
if err != nil {
|
2021-12-07 16:17:34 +00:00
|
|
|
logExpressionEvaluationFailure(matcher.Name, err)
|
2021-11-24 16:49:42 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
expression, err = govaluate.NewEvaluableExpressionWithFunctions(resolvedExpression, dsl.HelperFunctions())
|
|
|
|
if err != nil {
|
2021-12-07 16:17:34 +00:00
|
|
|
logExpressionEvaluationFailure(matcher.Name, err)
|
2021-11-24 16:49:42 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2021-12-07 16:17:34 +00:00
|
|
|
|
2020-12-24 15:17:41 +00:00
|
|
|
result, err := expression.Evaluate(data)
|
2020-04-26 21:32:58 +00:00
|
|
|
if err != nil {
|
2021-12-07 15:34:36 +00:00
|
|
|
gologger.Warning().Msgf(err.Error())
|
2020-04-26 21:32:58 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-08-25 21:24:31 +00:00
|
|
|
|
2021-12-07 15:34:36 +00:00
|
|
|
if boolResult, ok := result.(bool); !ok {
|
|
|
|
gologger.Warning().Msgf("The return value of a DSL statement must return a boolean value.")
|
|
|
|
continue
|
|
|
|
} else if !boolResult {
|
2020-04-26 21:32:58 +00:00
|
|
|
// If we are in an AND request and a match failed,
|
|
|
|
// return false as the AND condition fails on any single mismatch.
|
2021-12-07 16:17:34 +00:00
|
|
|
switch matcher.condition {
|
2021-12-07 15:34:36 +00:00
|
|
|
case ANDCondition:
|
2020-04-26 21:32:58 +00:00
|
|
|
return false
|
2021-12-07 15:34:36 +00:00
|
|
|
case ORCondition:
|
|
|
|
continue
|
2020-04-26 21:32:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the condition was an OR, return on the first match.
|
2021-11-25 14:57:43 +00:00
|
|
|
if matcher.condition == ORCondition {
|
2020-04-26 21:32:58 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we are at the end of the dsl, return with true
|
2021-11-25 14:57:43 +00:00
|
|
|
if len(matcher.dslCompiled)-1 == i {
|
2020-04-26 21:32:58 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|