mirror of https://github.com/daffainfo/nuclei.git
Added per protocol responseToDSL function + misc cleanup with operators
parent
d631074e35
commit
ed84bb187b
|
@ -47,16 +47,5 @@ func (m *Matcher) CompileMatchers() error {
|
|||
} else {
|
||||
m.condition = ORCondition
|
||||
}
|
||||
|
||||
// Setup the part of the request to match, if any.
|
||||
if m.Part != "" {
|
||||
m.part, ok = PartTypes[m.Part]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown matcher part specified: %s", m.Part)
|
||||
}
|
||||
} else {
|
||||
m.part = BodyPart
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -19,27 +19,27 @@ func (m *Matcher) Match(resp *http.Response, body, headers string, duration time
|
|||
return m.isNegative(m.matchSizeCode(len(body)))
|
||||
case WordsMatcher:
|
||||
// Match the parts as required for word check
|
||||
if m.part == BodyPart {
|
||||
if m.Part == "body" {
|
||||
return m.isNegative(m.matchWords(body))
|
||||
} else if m.part == HeaderPart {
|
||||
} else if m.Part == "header" {
|
||||
return m.isNegative(m.matchWords(headers))
|
||||
} else {
|
||||
return m.isNegative(m.matchWords(headers) || m.matchWords(body))
|
||||
}
|
||||
case RegexMatcher:
|
||||
// Match the parts as required for regex check
|
||||
if m.part == BodyPart {
|
||||
if m.Part == "body" {
|
||||
return m.isNegative(m.matchRegex(body))
|
||||
} else if m.part == HeaderPart {
|
||||
} else if m.Part == "header" {
|
||||
return m.isNegative(m.matchRegex(headers))
|
||||
} else {
|
||||
return m.isNegative(m.matchRegex(headers) || m.matchRegex(body))
|
||||
}
|
||||
case BinaryMatcher:
|
||||
// Match the parts as required for binary characters check
|
||||
if m.part == BodyPart {
|
||||
if m.Part == "body" {
|
||||
return m.isNegative(m.matchBinary(body))
|
||||
} else if m.part == HeaderPart {
|
||||
} else if m.Part == "header" {
|
||||
return m.isNegative(m.matchBinary(headers))
|
||||
} else {
|
||||
return m.isNegative(m.matchBinary(headers) || m.matchBinary(body))
|
||||
|
@ -55,7 +55,8 @@ func (m *Matcher) Match(resp *http.Response, body, headers string, duration time
|
|||
// MatchDNS matches a dns response against a given matcher
|
||||
func (m *Matcher) MatchDNS(msg *dns.Msg) bool {
|
||||
switch m.matcherType {
|
||||
// [WIP] add dns status code matcher
|
||||
case StatusMatcher:
|
||||
return m.isNegative(m.matchStatusCode(msg.Rcode))
|
||||
case SizeMatcher:
|
||||
return m.matchSizeCode(msg.Len())
|
||||
case WordsMatcher:
|
||||
|
@ -71,7 +72,6 @@ func (m *Matcher) MatchDNS(msg *dns.Msg) bool {
|
|||
// Match complex query
|
||||
return m.matchDSL(DNSToMap(msg, ""))
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,21 @@ import (
|
|||
"github.com/Knetic/govaluate"
|
||||
)
|
||||
|
||||
// Matcher is used to identify whether a template was successful.
|
||||
// Matcher is used to match a part in the output from a protocol.
|
||||
type Matcher struct {
|
||||
// Type is the type of the matcher
|
||||
Type string `yaml:"type"`
|
||||
// matcherType is the internal type of the matcher
|
||||
matcherType MatcherType
|
||||
// Condition is the optional condition between two matcher variables
|
||||
//
|
||||
// By default, the condition is assumed to be OR.
|
||||
Condition string `yaml:"condition,omitempty"`
|
||||
|
||||
// Part is the part of the data to match
|
||||
Part string `yaml:"part,omitempty"`
|
||||
|
||||
// Negative specifies if the match should be reversed
|
||||
// It will only match if the condition is not true.
|
||||
Negative bool `yaml:"negative,omitempty"`
|
||||
|
||||
// Name is matcher Name
|
||||
Name string `yaml:"name,omitempty"`
|
||||
|
@ -23,32 +32,16 @@ type Matcher struct {
|
|||
Words []string `yaml:"words,omitempty"`
|
||||
// Regex are the regex pattern required to be present in the response
|
||||
Regex []string `yaml:"regex,omitempty"`
|
||||
// regexCompiled is the compiled variant
|
||||
regexCompiled []*regexp.Regexp
|
||||
// Binary are the binary characters required to be present in the response
|
||||
Binary []string `yaml:"binary,omitempty"`
|
||||
// DSL are the dsl queries
|
||||
DSL []string `yaml:"dsl,omitempty"`
|
||||
// dslCompiled is the compiled variant
|
||||
dslCompiled []*govaluate.EvaluableExpression
|
||||
|
||||
// Condition is the optional condition between two matcher variables
|
||||
//
|
||||
// By default, the condition is assumed to be OR.
|
||||
Condition string `yaml:"condition,omitempty"`
|
||||
// condition is the condition of the matcher
|
||||
condition ConditionType
|
||||
|
||||
// Part is the part of the request to match
|
||||
//
|
||||
// By default, matching is performed in request body.
|
||||
Part string `yaml:"part,omitempty"`
|
||||
// part is the part of the request to match
|
||||
part Part
|
||||
|
||||
// Negative specifies if the match should be reversed
|
||||
// It will only match if the condition is not true.
|
||||
Negative bool `yaml:"negative,omitempty"`
|
||||
// cached data for the compiled matcher
|
||||
condition ConditionType
|
||||
matcherType MatcherType
|
||||
regexCompiled []*regexp.Regexp
|
||||
dslCompiled []*govaluate.EvaluableExpression
|
||||
}
|
||||
|
||||
// MatcherType is the type of the matcher specified
|
||||
|
@ -95,30 +88,6 @@ var ConditionTypes = map[string]ConditionType{
|
|||
"or": ORCondition,
|
||||
}
|
||||
|
||||
// Part is the part of the request to match
|
||||
type Part int
|
||||
|
||||
const (
|
||||
// BodyPart matches body of the response.
|
||||
BodyPart Part = iota + 1
|
||||
// HeaderPart matches headers of the response.
|
||||
HeaderPart
|
||||
// AllPart matches both response body and headers of the response.
|
||||
AllPart
|
||||
)
|
||||
|
||||
// PartTypes is an table for conversion of part type from string.
|
||||
var PartTypes = map[string]Part{
|
||||
"body": BodyPart,
|
||||
"header": HeaderPart,
|
||||
"all": AllPart,
|
||||
}
|
||||
|
||||
// GetPart returns the part of the matcher
|
||||
func (m *Matcher) GetPart() Part {
|
||||
return m.part
|
||||
}
|
||||
|
||||
// isNegative reverts the results of the match if the matcher
|
||||
// is of type negative.
|
||||
func (m *Matcher) isNegative(data bool) bool {
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
package matchers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
const defaultFormat = "%s"
|
||||
|
||||
// HTTPToMap Converts HTTP to Matcher Map
|
||||
func HTTPToMap(resp *http.Response, body, headers string, duration time.Duration, format string) (m map[string]interface{}) {
|
||||
m = make(map[string]interface{})
|
||||
|
||||
if format == "" {
|
||||
format = defaultFormat
|
||||
}
|
||||
|
||||
m[fmt.Sprintf(format, "content_length")] = resp.ContentLength
|
||||
m[fmt.Sprintf(format, "status_code")] = resp.StatusCode
|
||||
|
||||
for k, v := range resp.Header {
|
||||
k = strings.ToLower(strings.TrimSpace(strings.ReplaceAll(k, "-", "_")))
|
||||
m[fmt.Sprintf(format, k)] = strings.Join(v, " ")
|
||||
}
|
||||
|
||||
m[fmt.Sprintf(format, "all_headers")] = headers
|
||||
m[fmt.Sprintf(format, "body")] = body
|
||||
|
||||
if r, err := httputil.DumpResponse(resp, true); err == nil {
|
||||
m[fmt.Sprintf(format, "raw")] = string(r)
|
||||
}
|
||||
|
||||
// Converts duration to seconds (floating point) for DSL syntax
|
||||
m[fmt.Sprintf(format, "duration")] = duration.Seconds()
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// DNSToMap Converts DNS to Matcher Map
|
||||
func DNSToMap(msg *dns.Msg, format string) (m map[string]interface{}) {
|
||||
m = make(map[string]interface{})
|
||||
|
||||
if format == "" {
|
||||
format = defaultFormat
|
||||
}
|
||||
|
||||
m[fmt.Sprintf(format, "rcode")] = msg.Rcode
|
||||
|
||||
var qs string
|
||||
|
||||
for _, question := range msg.Question {
|
||||
qs += fmt.Sprintln(question.String())
|
||||
}
|
||||
|
||||
m[fmt.Sprintf(format, "question")] = qs
|
||||
|
||||
var exs string
|
||||
for _, extra := range msg.Extra {
|
||||
exs += fmt.Sprintln(extra.String())
|
||||
}
|
||||
|
||||
m[fmt.Sprintf(format, "extra")] = exs
|
||||
|
||||
var ans string
|
||||
for _, answer := range msg.Answer {
|
||||
ans += fmt.Sprintln(answer.String())
|
||||
}
|
||||
|
||||
m[fmt.Sprintf(format, "answer")] = ans
|
||||
|
||||
var nss string
|
||||
for _, ns := range msg.Ns {
|
||||
nss += fmt.Sprintln(ns.String())
|
||||
}
|
||||
|
||||
m[fmt.Sprintf(format, "ns")] = nss
|
||||
m[fmt.Sprintf(format, "raw")] = msg.String()
|
||||
|
||||
return m
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// responseToDSLMap converts a DNS response to a map for use in DSL matching
|
||||
func responseToDSLMap(msg *dns.Msg) map[string]interface{} {
|
||||
data := make(map[string]interface{}, 6)
|
||||
|
||||
data["rcode"] = msg.Rcode
|
||||
|
||||
buffer := &bytes.Buffer{}
|
||||
for _, question := range msg.Question {
|
||||
buffer.WriteString(question.String())
|
||||
}
|
||||
data["question"] = buffer.String()
|
||||
buffer.Reset()
|
||||
|
||||
for _, extra := range msg.Extra {
|
||||
buffer.WriteString(extra.String())
|
||||
}
|
||||
data["extra"] = buffer.String()
|
||||
buffer.Reset()
|
||||
|
||||
for _, answer := range msg.Answer {
|
||||
buffer.WriteString(answer.String())
|
||||
}
|
||||
data["answer"] = buffer.String()
|
||||
buffer.Reset()
|
||||
|
||||
for _, ns := range msg.Ns {
|
||||
buffer.WriteString(ns.String())
|
||||
}
|
||||
data["ns"] = buffer.String()
|
||||
buffer.Reset()
|
||||
|
||||
data["raw"] = msg.String()
|
||||
return data
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// responseToDSLMap converts a HTTP response to a map for use in DSL matching
|
||||
func responseToDSLMap(resp *http.Response, body, headers string, duration time.Duration, extra map[string]interface{}) map[string]interface{} {
|
||||
data := make(map[string]interface{}, len(extra)+6+len(resp.Header))
|
||||
for k, v := range extra {
|
||||
data[k] = v
|
||||
}
|
||||
|
||||
data["content_length"] = resp.ContentLength
|
||||
data["status_code"] = resp.StatusCode
|
||||
|
||||
for k, v := range resp.Header {
|
||||
k = strings.ToLower(strings.TrimSpace(strings.ReplaceAll(k, "-", "_")))
|
||||
data[k] = strings.Join(v, " ")
|
||||
}
|
||||
data["all_headers"] = headers
|
||||
data["body"] = body
|
||||
|
||||
if r, err := httputil.DumpResponse(resp, true); err == nil {
|
||||
data["raw"] = string(r)
|
||||
}
|
||||
data["duration"] = duration.Seconds()
|
||||
return data
|
||||
}
|
Loading…
Reference in New Issue