mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-18 14:55:25 +00:00
Merge branch 'dev' into cli-variables-as-payload
This commit is contained in:
commit
e79c6262b9
@ -71,6 +71,7 @@ on extensive configurability, massive extensibility and ease of use.`)
|
|||||||
flagSet.StringSliceVarP(&options.ExcludedTemplates, "exclude", "exclude-templates", []string{}, "template or template directory paths to exclude"),
|
flagSet.StringSliceVarP(&options.ExcludedTemplates, "exclude", "exclude-templates", []string{}, "template or template directory paths to exclude"),
|
||||||
|
|
||||||
flagSet.VarP(&options.Severities, "impact", "severity", fmt.Sprintf("Templates to run based on severity. Possible values: %s", severity.GetSupportedSeverities().String())),
|
flagSet.VarP(&options.Severities, "impact", "severity", fmt.Sprintf("Templates to run based on severity. Possible values: %s", severity.GetSupportedSeverities().String())),
|
||||||
|
flagSet.VarP(&options.ExcludeSeverities, "exclude-impact", "exclude-severity", fmt.Sprintf("Templates to exclude based on severity. Possible values: %s", severity.GetSupportedSeverities().String())),
|
||||||
flagSet.NormalizedStringSliceVar(&options.Author, "author", []string{}, "execute templates that are (co-)created by the specified authors"),
|
flagSet.NormalizedStringSliceVar(&options.Author, "author", []string{}, "execute templates that are (co-)created by the specified authors"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -100,6 +100,7 @@ require (
|
|||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/projectdiscovery/blackrock v0.0.0-20210415162320-b38689ae3a2e // indirect
|
github.com/projectdiscovery/blackrock v0.0.0-20210415162320-b38689ae3a2e // indirect
|
||||||
github.com/projectdiscovery/cryptoutil v0.0.0-20210805184155-b5d2512f9345 // indirect
|
github.com/projectdiscovery/cryptoutil v0.0.0-20210805184155-b5d2512f9345 // indirect
|
||||||
|
github.com/projectdiscovery/fileutil v0.0.0-20210804142714-ebba15fa53ca // indirect
|
||||||
github.com/projectdiscovery/iputil v0.0.0-20210804143329-3a30fcde43f3 // indirect
|
github.com/projectdiscovery/iputil v0.0.0-20210804143329-3a30fcde43f3 // indirect
|
||||||
github.com/projectdiscovery/mapcidr v0.0.8 // indirect
|
github.com/projectdiscovery/mapcidr v0.0.8 // indirect
|
||||||
github.com/projectdiscovery/networkpolicy v0.0.1 // indirect
|
github.com/projectdiscovery/networkpolicy v0.0.1 // indirect
|
||||||
|
|||||||
@ -562,9 +562,9 @@ github.com/projectdiscovery/fastdialer v0.0.12/go.mod h1:RkRbxqDCcCFhfNUbkzBIz/i
|
|||||||
github.com/projectdiscovery/fastdialer v0.0.13-0.20210824195254-0113c1406542/go.mod h1:TuapmLiqtunJOxpM7g0tpTy/TUF/0S+XFyx0B0Wx0DQ=
|
github.com/projectdiscovery/fastdialer v0.0.13-0.20210824195254-0113c1406542/go.mod h1:TuapmLiqtunJOxpM7g0tpTy/TUF/0S+XFyx0B0Wx0DQ=
|
||||||
github.com/projectdiscovery/fastdialer v0.0.13-0.20210917073912-cad93d88e69e h1:xMAFYJgRxopAwKrj7HDwMBKJGCGDbHqopS8f959xges=
|
github.com/projectdiscovery/fastdialer v0.0.13-0.20210917073912-cad93d88e69e h1:xMAFYJgRxopAwKrj7HDwMBKJGCGDbHqopS8f959xges=
|
||||||
github.com/projectdiscovery/fastdialer v0.0.13-0.20210917073912-cad93d88e69e/go.mod h1:O1l6+vAQy1QRo9FqyuyJ57W3CwpIXXg7oGo14Le6ZYQ=
|
github.com/projectdiscovery/fastdialer v0.0.13-0.20210917073912-cad93d88e69e/go.mod h1:O1l6+vAQy1QRo9FqyuyJ57W3CwpIXXg7oGo14Le6ZYQ=
|
||||||
|
github.com/projectdiscovery/fileutil v0.0.0-20210804142714-ebba15fa53ca h1:xT//ApxoeQRbt9GgL/122688bhGy9hur8YG0Qh69k5I=
|
||||||
github.com/projectdiscovery/fileutil v0.0.0-20210804142714-ebba15fa53ca/go.mod h1:U+QCpQnX8o2N2w0VUGyAzjM3yBAe4BKedVElxiImsx0=
|
github.com/projectdiscovery/fileutil v0.0.0-20210804142714-ebba15fa53ca/go.mod h1:U+QCpQnX8o2N2w0VUGyAzjM3yBAe4BKedVElxiImsx0=
|
||||||
github.com/projectdiscovery/fileutil v0.0.0-20210928100737-cab279c5d4b5 h1:2dbm7UhrAKnccZttr78CAmG768sSCd+MBn4ayLVDeqA=
|
github.com/projectdiscovery/goflags v0.0.7 h1:aykmRkrOgDyRwcvGrK3qp+9aqcjGfAMs/+LtRmtyxwk=
|
||||||
github.com/projectdiscovery/fileutil v0.0.0-20210928100737-cab279c5d4b5/go.mod h1:U+QCpQnX8o2N2w0VUGyAzjM3yBAe4BKedVElxiImsx0=
|
|
||||||
github.com/projectdiscovery/goflags v0.0.7/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY=
|
github.com/projectdiscovery/goflags v0.0.7/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY=
|
||||||
github.com/projectdiscovery/goflags v0.0.8-0.20211007103353-9b9229e8a240 h1:b7zDUSsgN5f4/IlhKF6RVGsp/NkHIuty0o1YjzAMKUs=
|
github.com/projectdiscovery/goflags v0.0.8-0.20211007103353-9b9229e8a240 h1:b7zDUSsgN5f4/IlhKF6RVGsp/NkHIuty0o1YjzAMKUs=
|
||||||
github.com/projectdiscovery/goflags v0.0.8-0.20211007103353-9b9229e8a240/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY=
|
github.com/projectdiscovery/goflags v0.0.8-0.20211007103353-9b9229e8a240/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY=
|
||||||
|
|||||||
@ -344,6 +344,7 @@ func (r *Runner) RunEnumeration() error {
|
|||||||
IncludeTemplates: r.options.IncludeTemplates,
|
IncludeTemplates: r.options.IncludeTemplates,
|
||||||
Authors: r.options.Author,
|
Authors: r.options.Author,
|
||||||
Severities: r.options.Severities,
|
Severities: r.options.Severities,
|
||||||
|
ExcludeSeverities: r.options.ExcludeSeverities,
|
||||||
IncludeTags: r.options.IncludeTags,
|
IncludeTags: r.options.IncludeTags,
|
||||||
TemplatesDirectory: r.options.TemplatesDirectory,
|
TemplatesDirectory: r.options.TemplatesDirectory,
|
||||||
Catalog: r.catalog,
|
Catalog: r.catalog,
|
||||||
|
|||||||
@ -9,11 +9,12 @@ import (
|
|||||||
|
|
||||||
// TagFilter is used to filter nuclei templates for tag based execution
|
// TagFilter is used to filter nuclei templates for tag based execution
|
||||||
type TagFilter struct {
|
type TagFilter struct {
|
||||||
allowedTags map[string]struct{}
|
allowedTags map[string]struct{}
|
||||||
severities map[severity.Severity]struct{}
|
severities map[severity.Severity]struct{}
|
||||||
authors map[string]struct{}
|
excludeSeverities map[severity.Severity]struct{}
|
||||||
block map[string]struct{}
|
authors map[string]struct{}
|
||||||
matchAllows map[string]struct{}
|
block map[string]struct{}
|
||||||
|
matchAllows map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrExcluded is returned for excluded templates
|
// ErrExcluded is returned for excluded templates
|
||||||
@ -54,15 +55,21 @@ func (tagFilter *TagFilter) Match(templateTags, templateAuthors []string, templa
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isSeverityMatch(tagFilter *TagFilter, templateSeverity severity.Severity) bool {
|
func isSeverityMatch(tagFilter *TagFilter, templateSeverity severity.Severity) bool {
|
||||||
if len(tagFilter.severities) == 0 || templateSeverity == severity.Undefined {
|
if (len(tagFilter.excludeSeverities) == 0 && len(tagFilter.severities) == 0) || templateSeverity == severity.Undefined {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := tagFilter.severities[templateSeverity]; ok {
|
included := true
|
||||||
return true
|
if len(tagFilter.severities) > 0 {
|
||||||
|
_, included = tagFilter.severities[templateSeverity]
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
excluded := false
|
||||||
|
if len(tagFilter.excludeSeverities) > 0 {
|
||||||
|
_, excluded = tagFilter.excludeSeverities[templateSeverity]
|
||||||
|
}
|
||||||
|
|
||||||
|
return included && !excluded
|
||||||
}
|
}
|
||||||
|
|
||||||
func isAuthorMatch(tagFilter *TagFilter, templateAuthors []string) bool {
|
func isAuthorMatch(tagFilter *TagFilter, templateAuthors []string) bool {
|
||||||
@ -110,23 +117,25 @@ func isTagMatch(tagFilter *TagFilter, templateTags []string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Tags []string
|
Tags []string
|
||||||
ExcludeTags []string
|
ExcludeTags []string
|
||||||
Authors []string
|
Authors []string
|
||||||
Severities severity.Severities
|
Severities severity.Severities
|
||||||
IncludeTags []string
|
ExcludeSeverities severity.Severities
|
||||||
|
IncludeTags []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a tag filter for nuclei tag based execution
|
// New returns a tag filter for nuclei tag based execution
|
||||||
//
|
//
|
||||||
// It takes into account Tags, Severities, Authors, IncludeTags, ExcludeTags.
|
// It takes into account Tags, Severities, ExcludeSeverities, Authors, IncludeTags, ExcludeTags.
|
||||||
func New(config *Config) *TagFilter {
|
func New(config *Config) *TagFilter {
|
||||||
filter := &TagFilter{
|
filter := &TagFilter{
|
||||||
allowedTags: make(map[string]struct{}),
|
allowedTags: make(map[string]struct{}),
|
||||||
authors: make(map[string]struct{}),
|
authors: make(map[string]struct{}),
|
||||||
severities: make(map[severity.Severity]struct{}),
|
severities: make(map[severity.Severity]struct{}),
|
||||||
block: make(map[string]struct{}),
|
excludeSeverities: make(map[severity.Severity]struct{}),
|
||||||
matchAllows: make(map[string]struct{}),
|
block: make(map[string]struct{}),
|
||||||
|
matchAllows: make(map[string]struct{}),
|
||||||
}
|
}
|
||||||
for _, tag := range config.ExcludeTags {
|
for _, tag := range config.ExcludeTags {
|
||||||
for _, val := range splitCommaTrim(tag) {
|
for _, val := range splitCommaTrim(tag) {
|
||||||
@ -140,6 +149,11 @@ func New(config *Config) *TagFilter {
|
|||||||
filter.severities[tag] = struct{}{}
|
filter.severities[tag] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, tag := range config.ExcludeSeverities {
|
||||||
|
if _, ok := filter.excludeSeverities[tag]; !ok {
|
||||||
|
filter.excludeSeverities[tag] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, tag := range config.Authors {
|
for _, tag := range config.Authors {
|
||||||
for _, val := range splitCommaTrim(tag) {
|
for _, val := range splitCommaTrim(tag) {
|
||||||
if _, ok := filter.authors[val]; !ok {
|
if _, ok := filter.authors[val]; !ok {
|
||||||
|
|||||||
@ -73,6 +73,16 @@ func TestTagBasedFilter(t *testing.T) {
|
|||||||
matched, _ := filter.Match([]string{"fuzz"}, []string{"pdteam"}, severity.High, nil)
|
matched, _ := filter.Match([]string{"fuzz"}, []string{"pdteam"}, severity.High, nil)
|
||||||
require.True(t, matched, "could not get correct match")
|
require.True(t, matched, "could not get correct match")
|
||||||
})
|
})
|
||||||
|
t.Run("match-exclude-severity", func(t *testing.T) {
|
||||||
|
filter := New(&Config{
|
||||||
|
ExcludeSeverities: severity.Severities{severity.Low},
|
||||||
|
})
|
||||||
|
matched, _ := filter.Match([]string{"fuzz"}, []string{"pdteam"}, severity.High, nil)
|
||||||
|
require.True(t, matched, "could not get correct match")
|
||||||
|
|
||||||
|
matched, _ = filter.Match([]string{"fuzz"}, []string{"pdteam"}, severity.Low, nil)
|
||||||
|
require.False(t, matched, "could not get correct match")
|
||||||
|
})
|
||||||
t.Run("match-exclude-with-tags", func(t *testing.T) {
|
t.Run("match-exclude-with-tags", func(t *testing.T) {
|
||||||
filter := New(&Config{
|
filter := New(&Config{
|
||||||
Tags: []string{"tag"},
|
Tags: []string{"tag"},
|
||||||
|
|||||||
@ -19,11 +19,12 @@ type Config struct {
|
|||||||
ExcludeTemplates []string
|
ExcludeTemplates []string
|
||||||
IncludeTemplates []string
|
IncludeTemplates []string
|
||||||
|
|
||||||
Tags []string
|
Tags []string
|
||||||
ExcludeTags []string
|
ExcludeTags []string
|
||||||
Authors []string
|
Authors []string
|
||||||
Severities severity.Severities
|
Severities severity.Severities
|
||||||
IncludeTags []string
|
ExcludeSeverities severity.Severities
|
||||||
|
IncludeTags []string
|
||||||
|
|
||||||
Catalog *catalog.Catalog
|
Catalog *catalog.Catalog
|
||||||
ExecutorOptions protocols.ExecuterOptions
|
ExecutorOptions protocols.ExecuterOptions
|
||||||
@ -49,11 +50,12 @@ func New(config *Config) (*Store, error) {
|
|||||||
store := &Store{
|
store := &Store{
|
||||||
config: config,
|
config: config,
|
||||||
tagFilter: filter.New(&filter.Config{
|
tagFilter: filter.New(&filter.Config{
|
||||||
Tags: config.Tags,
|
Tags: config.Tags,
|
||||||
ExcludeTags: config.ExcludeTags,
|
ExcludeTags: config.ExcludeTags,
|
||||||
Authors: config.Authors,
|
Authors: config.Authors,
|
||||||
Severities: config.Severities,
|
Severities: config.Severities,
|
||||||
IncludeTags: config.IncludeTags,
|
ExcludeSeverities: config.ExcludeSeverities,
|
||||||
|
IncludeTags: config.IncludeTags,
|
||||||
}),
|
}),
|
||||||
pathFilter: filter.NewPathFilter(&filter.PathFilterConfig{
|
pathFilter: filter.NewPathFilter(&filter.PathFilterConfig{
|
||||||
IncludedTemplates: config.IncludeTemplates,
|
IncludedTemplates: config.IncludeTemplates,
|
||||||
|
|||||||
@ -18,6 +18,21 @@ var templateExpressionRegex = regexp.MustCompile(`(?m)\{\{[^}]+\}\}["'\)\}]*`)
|
|||||||
// The provided keys from finalValues will be used as variable names
|
// The provided keys from finalValues will be used as variable names
|
||||||
// for substitution inside the expression.
|
// for substitution inside the expression.
|
||||||
func Evaluate(data string, base map[string]interface{}) (string, error) {
|
func Evaluate(data string, base map[string]interface{}) (string, error) {
|
||||||
|
return evaluate(data, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvaluateByte checks if the match contains a dynamic variable, for each
|
||||||
|
// found one we will check if it's an expression and can
|
||||||
|
// be compiled, it will be evaluated and the results will be returned.
|
||||||
|
//
|
||||||
|
// The provided keys from finalValues will be used as variable names
|
||||||
|
// for substitution inside the expression.
|
||||||
|
func EvaluateByte(data []byte, base map[string]interface{}) ([]byte, error) {
|
||||||
|
finalData, err := evaluate(string(data), base)
|
||||||
|
return []byte(finalData), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluate(data string, base map[string]interface{}) (string, error) {
|
||||||
data = replacer.Replace(data, base)
|
data = replacer.Replace(data, base)
|
||||||
|
|
||||||
dynamicValues := make(map[string]interface{})
|
dynamicValues := make(map[string]interface{})
|
||||||
@ -37,30 +52,3 @@ func Evaluate(data string, base map[string]interface{}) (string, error) {
|
|||||||
// Replacer dynamic values if any in raw request and parse it
|
// Replacer dynamic values if any in raw request and parse it
|
||||||
return replacer.Replace(data, dynamicValues), nil
|
return replacer.Replace(data, dynamicValues), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EvaluateByte checks if the match contains a dynamic variable, for each
|
|
||||||
// found one we will check if it's an expression and can
|
|
||||||
// be compiled, it will be evaluated and the results will be returned.
|
|
||||||
//
|
|
||||||
// The provided keys from finalValues will be used as variable names
|
|
||||||
// for substitution inside the expression.
|
|
||||||
func EvaluateByte(data []byte, base map[string]interface{}) ([]byte, error) {
|
|
||||||
final := replacer.Replace(string(data), base)
|
|
||||||
|
|
||||||
dynamicValues := make(map[string]interface{})
|
|
||||||
for _, match := range templateExpressionRegex.FindAllString(final, -1) {
|
|
||||||
expr := generators.TrimDelimiters(match)
|
|
||||||
|
|
||||||
compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expr, dsl.HelperFunctions())
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
result, err := compiled.Evaluate(base)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dynamicValues[expr] = result
|
|
||||||
}
|
|
||||||
// Replacer dynamic values if any in raw request and parse it
|
|
||||||
return []byte(replacer.Replace(final, dynamicValues)), nil
|
|
||||||
}
|
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
package generators
|
package generators
|
||||||
|
|
||||||
|
import "github.com/pkg/errors"
|
||||||
|
|
||||||
// Generator is the generator struct for generating payloads
|
// Generator is the generator struct for generating payloads
|
||||||
type Generator struct {
|
type Generator struct {
|
||||||
Type Type
|
Type Type
|
||||||
@ -12,8 +14,8 @@ type Generator struct {
|
|||||||
type Type int
|
type Type int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Sniper replaces each variable with values at a time.
|
// Sniper replaces one iteration of the payload with a value.
|
||||||
Sniper Type = iota + 1
|
BatteringRam Type = iota + 1
|
||||||
// PitchFork replaces variables with positional value from multiple wordlists
|
// PitchFork replaces variables with positional value from multiple wordlists
|
||||||
PitchFork
|
PitchFork
|
||||||
// ClusterBomb replaces variables with all possible combinations of values
|
// ClusterBomb replaces variables with all possible combinations of values
|
||||||
@ -22,9 +24,9 @@ const (
|
|||||||
|
|
||||||
// StringToType is a table for conversion of attack type from string.
|
// StringToType is a table for conversion of attack type from string.
|
||||||
var StringToType = map[string]Type{
|
var StringToType = map[string]Type{
|
||||||
"sniper": Sniper,
|
"batteringram": BatteringRam,
|
||||||
"pitchfork": PitchFork,
|
"pitchfork": PitchFork,
|
||||||
"clusterbomb": ClusterBomb,
|
"clusterbomb": ClusterBomb,
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new generator structure for payload generation
|
// New creates a new generator structure for payload generation
|
||||||
@ -41,6 +43,12 @@ func New(payloads map[string]interface{}, payloadType Type, templatePath string)
|
|||||||
generator.Type = payloadType
|
generator.Type = payloadType
|
||||||
generator.payloads = compiled
|
generator.payloads = compiled
|
||||||
|
|
||||||
|
// Validate the sniper/batteringram payload set
|
||||||
|
if payloadType == BatteringRam {
|
||||||
|
if len(payloads) != 1 {
|
||||||
|
return nil, errors.New("sniper/batteringram must have single payload set")
|
||||||
|
}
|
||||||
|
}
|
||||||
return generator, nil
|
return generator, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +95,7 @@ func (i *Iterator) Remaining() int {
|
|||||||
func (i *Iterator) Total() int {
|
func (i *Iterator) Total() int {
|
||||||
count := 0
|
count := 0
|
||||||
switch i.Type {
|
switch i.Type {
|
||||||
case Sniper:
|
case BatteringRam:
|
||||||
for _, p := range i.payloads {
|
for _, p := range i.payloads {
|
||||||
count += len(p.values)
|
count += len(p.values)
|
||||||
}
|
}
|
||||||
@ -110,19 +118,19 @@ func (i *Iterator) Total() int {
|
|||||||
// Value returns the next value for an iterator
|
// Value returns the next value for an iterator
|
||||||
func (i *Iterator) Value() (map[string]interface{}, bool) {
|
func (i *Iterator) Value() (map[string]interface{}, bool) {
|
||||||
switch i.Type {
|
switch i.Type {
|
||||||
case Sniper:
|
case BatteringRam:
|
||||||
return i.sniperValue()
|
return i.batteringRamValue()
|
||||||
case PitchFork:
|
case PitchFork:
|
||||||
return i.pitchforkValue()
|
return i.pitchforkValue()
|
||||||
case ClusterBomb:
|
case ClusterBomb:
|
||||||
return i.clusterbombValue()
|
return i.clusterbombValue()
|
||||||
default:
|
default:
|
||||||
return i.sniperValue()
|
return i.batteringRamValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sniperValue returns a list of all payloads for the iterator
|
// batteringRamValue returns a list of all payloads for the iterator
|
||||||
func (i *Iterator) sniperValue() (map[string]interface{}, bool) {
|
func (i *Iterator) batteringRamValue() (map[string]interface{}, bool) {
|
||||||
values := make(map[string]interface{}, 1)
|
values := make(map[string]interface{}, 1)
|
||||||
|
|
||||||
currentIndex := i.msbIterator
|
currentIndex := i.msbIterator
|
||||||
@ -132,7 +140,7 @@ func (i *Iterator) sniperValue() (map[string]interface{}, bool) {
|
|||||||
if i.msbIterator == len(i.payloads) {
|
if i.msbIterator == len(i.payloads) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
return i.sniperValue()
|
return i.batteringRamValue()
|
||||||
}
|
}
|
||||||
values[payload.name] = payload.value()
|
values[payload.name] = payload.value()
|
||||||
payload.incrementPosition()
|
payload.incrementPosition()
|
||||||
|
|||||||
@ -6,11 +6,10 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSniperGenerator(t *testing.T) {
|
func TestBatteringRamGenerator(t *testing.T) {
|
||||||
usernames := []string{"admin", "password"}
|
usernames := []string{"admin", "password"}
|
||||||
moreUsernames := []string{"login", "test"}
|
|
||||||
|
|
||||||
generator, err := New(map[string]interface{}{"username": usernames, "aliases": moreUsernames}, Sniper, "")
|
generator, err := New(map[string]interface{}{"username": usernames}, BatteringRam, "")
|
||||||
require.Nil(t, err, "could not create generator")
|
require.Nil(t, err, "could not create generator")
|
||||||
|
|
||||||
iterator := generator.NewIterator()
|
iterator := generator.NewIterator()
|
||||||
@ -22,7 +21,7 @@ func TestSniperGenerator(t *testing.T) {
|
|||||||
}
|
}
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
require.Equal(t, len(usernames)+len(moreUsernames), count, "could not get correct sniper counts")
|
require.Equal(t, len(usernames), count, "could not get correct batteringram counts")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPitchforkGenerator(t *testing.T) {
|
func TestPitchforkGenerator(t *testing.T) {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/projectdiscovery/gologger"
|
"github.com/projectdiscovery/gologger"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/expressions"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ protocols.Request = &Request{}
|
var _ protocols.Request = &Request{}
|
||||||
@ -29,9 +30,14 @@ func (r *Request) ExecuteWithResults(input string, metadata /*TODO review unused
|
|||||||
return errors.Wrap(err, "could not build request")
|
return errors.Wrap(err, "could not build request")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestString := compiledRequest.String()
|
||||||
|
if varErr := expressions.ContainsUnresolvedVariables(requestString); varErr != nil {
|
||||||
|
gologger.Warning().Msgf("[%s] Could not make dns request for %s: %v\n", r.options.TemplateID, domain, varErr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if r.options.Options.Debug || r.options.Options.DebugRequests {
|
if r.options.Options.Debug || r.options.Options.DebugRequests {
|
||||||
gologger.Info().Str("domain", domain).Msgf("[%s] Dumped DNS request for %s", r.options.TemplateID, domain)
|
gologger.Info().Str("domain", domain).Msgf("[%s] Dumped DNS request for %s", r.options.TemplateID, domain)
|
||||||
gologger.Print().Msgf("%s", compiledRequest.String())
|
gologger.Print().Msgf("%s", requestString)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the request to the target servers
|
// Send the request to the target servers
|
||||||
|
|||||||
@ -261,9 +261,13 @@ func (r *Request) Compile(options *protocols.ExecuterOptions) error {
|
|||||||
if len(r.Payloads) > 0 {
|
if len(r.Payloads) > 0 {
|
||||||
attackType := r.AttackType
|
attackType := r.AttackType
|
||||||
if attackType == "" {
|
if attackType == "" {
|
||||||
attackType = "sniper"
|
attackType = "batteringram"
|
||||||
|
}
|
||||||
|
var ok bool
|
||||||
|
r.attackType, ok = generators.StringToType[attackType]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid attack type provided: %s", attackType)
|
||||||
}
|
}
|
||||||
r.attackType = generators.StringToType[attackType]
|
|
||||||
|
|
||||||
// Resolve payload paths if they are files.
|
// Resolve payload paths if they are files.
|
||||||
for name, payload := range r.Payloads {
|
for name, payload := range r.Payloads {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -176,9 +177,13 @@ func (r *Request) Compile(options *protocols.ExecuterOptions) error {
|
|||||||
if len(r.Payloads) > 0 {
|
if len(r.Payloads) > 0 {
|
||||||
attackType := r.AttackType
|
attackType := r.AttackType
|
||||||
if attackType == "" {
|
if attackType == "" {
|
||||||
attackType = "sniper"
|
attackType = "batteringram"
|
||||||
|
}
|
||||||
|
var ok bool
|
||||||
|
r.attackType, ok = generators.StringToType[attackType]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid attack type provided: %s", attackType)
|
||||||
}
|
}
|
||||||
r.attackType = generators.StringToType[attackType]
|
|
||||||
|
|
||||||
// Resolve payload paths if they are files.
|
// Resolve payload paths if they are files.
|
||||||
for name, payload := range r.Payloads {
|
for name, payload := range r.Payloads {
|
||||||
|
|||||||
@ -4,20 +4,41 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var noMinor = regexp.MustCompile(`HTTP\/([0-9]) `)
|
||||||
|
|
||||||
// readResponseFromString reads a raw http response from a string.
|
// readResponseFromString reads a raw http response from a string.
|
||||||
func readResponseFromString(data string) (*http.Response, error) {
|
func readResponseFromString(data string) (*http.Response, error) {
|
||||||
var final string
|
var final string
|
||||||
|
|
||||||
if strings.HasPrefix(data, "HTTP/") {
|
if strings.HasPrefix(data, "HTTP/") {
|
||||||
final = data
|
final = addMinorVersionToHTTP(data)
|
||||||
} else {
|
} else {
|
||||||
lastIndex := strings.LastIndex(data, "HTTP/")
|
lastIndex := strings.LastIndex(data, "HTTP/")
|
||||||
if lastIndex == -1 {
|
if lastIndex == -1 {
|
||||||
return nil, errors.New("malformed raw http response")
|
return nil, errors.New("malformed raw http response")
|
||||||
}
|
}
|
||||||
final = data[lastIndex:] // choose last http/ in case of it being later.
|
final = data[lastIndex:] // choose last http/ in case of it being later.
|
||||||
|
|
||||||
|
final = addMinorVersionToHTTP(final)
|
||||||
}
|
}
|
||||||
return http.ReadResponse(bufio.NewReader(strings.NewReader(final)), nil)
|
return http.ReadResponse(bufio.NewReader(strings.NewReader(final)), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addMinorVersionToHTTP tries to add a minor version to http status header
|
||||||
|
// fixing the compatibility issue with go standard library.
|
||||||
|
func addMinorVersionToHTTP(data string) string {
|
||||||
|
matches := noMinor.FindAllStringSubmatch(data, 1)
|
||||||
|
if len(matches) == 0 {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
if len(matches[0]) < 2 {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
replacedVersion := strings.Replace(matches[0][0], matches[0][1], matches[0][1]+".0", 1)
|
||||||
|
data = strings.Replace(data, matches[0][0], replacedVersion, 1)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|||||||
@ -2,7 +2,10 @@ package offlinehttp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@ -18,11 +21,15 @@ func TestReadResponseFromString(t *testing.T) {
|
|||||||
<h1>What is the Firing Range?</h1>
|
<h1>What is the Firing Range?</h1>
|
||||||
<p>
|
<p>
|
||||||
</body>
|
</body>
|
||||||
</body>
|
|
||||||
</html>`
|
</html>`
|
||||||
|
|
||||||
t.Run("response", func(t *testing.T) {
|
tests := []struct {
|
||||||
data := `HTTP/1.1 200 OK
|
name string
|
||||||
|
data string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "response",
|
||||||
|
data: `HTTP/1.1 200 OK
|
||||||
Age: 0
|
Age: 0
|
||||||
Cache-Control: public, max-age=600
|
Cache-Control: public, max-age=600
|
||||||
Content-Type: text/html
|
Content-Type: text/html
|
||||||
@ -38,19 +45,51 @@ Server: Google Frontend
|
|||||||
<h1>What is the Firing Range?</h1>
|
<h1>What is the Firing Range?</h1>
|
||||||
<p>
|
<p>
|
||||||
</body>
|
</body>
|
||||||
|
</html>`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "response-http2-without-minor-version",
|
||||||
|
data: `HTTP/2 200 OK
|
||||||
|
Age: 0
|
||||||
|
Cache-Control: public, max-age=600
|
||||||
|
Content-Type: text/html
|
||||||
|
Server: Google Frontend
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Firing Range</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Version 0.48</h1>
|
||||||
|
<h1>What is the Firing Range?</h1>
|
||||||
|
<p>
|
||||||
</body>
|
</body>
|
||||||
</html>`
|
</html>`,
|
||||||
resp, err := readResponseFromString(data)
|
},
|
||||||
require.Nil(t, err, "could not read response from string")
|
{
|
||||||
|
name: "response-http2-with-minor-version",
|
||||||
|
data: `HTTP/2.0 200 OK
|
||||||
|
Age: 0
|
||||||
|
Cache-Control: public, max-age=600
|
||||||
|
Content-Type: text/html
|
||||||
|
Server: Google Frontend
|
||||||
|
|
||||||
respData, err := ioutil.ReadAll(resp.Body)
|
<!DOCTYPE html>
|
||||||
require.Nil(t, err, "could not read response body")
|
<html>
|
||||||
require.Equal(t, expectedBody, string(respData), "could not get correct parsed body")
|
<head>
|
||||||
require.Equal(t, "Google Frontend", resp.Header.Get("Server"), "could not get correct headers")
|
<title>Firing Range</title>
|
||||||
})
|
</head>
|
||||||
|
<body>
|
||||||
t.Run("request-response", func(t *testing.T) {
|
<h1>Version 0.48</h1>
|
||||||
data := `GET http://public-firing-range.appspot.com/ HTTP/1.1
|
<h1>What is the Firing Range?</h1>
|
||||||
|
<p>
|
||||||
|
</body>
|
||||||
|
</html>`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "request-response",
|
||||||
|
data: `GET http://public-firing-range.appspot.com/ HTTP/1.1
|
||||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
|
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
|
||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
Upgrade-Insecure-Requests: 1
|
Upgrade-Insecure-Requests: 1
|
||||||
@ -72,14 +111,67 @@ Server: Google Frontend
|
|||||||
<h1>What is the Firing Range?</h1>
|
<h1>What is the Firing Range?</h1>
|
||||||
<p>
|
<p>
|
||||||
</body>
|
</body>
|
||||||
|
</html>`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "request-response-without-minor-version",
|
||||||
|
data: `GET http://public-firing-range.appspot.com/ HTTP/1.1
|
||||||
|
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
|
||||||
|
Accept-Encoding: gzip, deflate
|
||||||
|
Upgrade-Insecure-Requests: 1
|
||||||
|
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36
|
||||||
|
|
||||||
|
HTTP/2 200 OK
|
||||||
|
Age: 0
|
||||||
|
Cache-Control: public, max-age=600
|
||||||
|
Content-Type: text/html
|
||||||
|
Server: Google Frontend
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Firing Range</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Version 0.48</h1>
|
||||||
|
<h1>What is the Firing Range?</h1>
|
||||||
|
<p>
|
||||||
</body>
|
</body>
|
||||||
</html>`
|
</html>`,
|
||||||
resp, err := readResponseFromString(data)
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
resp, err := readResponseFromString(tt.data)
|
||||||
|
require.Nil(t, err, "could not read response from string")
|
||||||
|
|
||||||
|
respData, err := ioutil.ReadAll(resp.Body)
|
||||||
|
require.Nil(t, err, "could not read response body")
|
||||||
|
require.Equal(t, expectedBody, string(respData), "could not get correct parsed body")
|
||||||
|
require.Equal(t, "Google Frontend", resp.Header.Get("Server"), "could not get correct headers")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("test-live-response-with-content-length", func(t *testing.T) {
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := client.Get("https://golang.org/doc/install")
|
||||||
|
require.Nil(t, err, "could not dial url")
|
||||||
|
defer data.Body.Close()
|
||||||
|
|
||||||
|
b, err := httputil.DumpResponse(data, true)
|
||||||
|
require.Nil(t, err, "could not dump response")
|
||||||
|
|
||||||
|
respData, err := readResponseFromString(string(b))
|
||||||
require.Nil(t, err, "could not read response from string")
|
require.Nil(t, err, "could not read response from string")
|
||||||
|
|
||||||
respData, err := ioutil.ReadAll(resp.Body)
|
_, err = ioutil.ReadAll(respData.Body)
|
||||||
require.Nil(t, err, "could not read response body")
|
require.Nil(t, err, "could not read response body")
|
||||||
require.Equal(t, expectedBody, string(respData), "could not get correct parsed body")
|
|
||||||
require.Equal(t, "Google Frontend", resp.Header.Get("Server"), "could not get correct headers")
|
require.Equal(t, "Google Frontend", respData.Header.Get("Server"), "could not get correct headers")
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,8 @@ type Options struct {
|
|||||||
varsPayload map[string]interface{}
|
varsPayload map[string]interface{}
|
||||||
// Severities filters templates based on their severity and only run the matching ones.
|
// Severities filters templates based on their severity and only run the matching ones.
|
||||||
Severities severity.Severities
|
Severities severity.Severities
|
||||||
|
// ExcludeSeverities specifies severities to exclude
|
||||||
|
ExcludeSeverities severity.Severities
|
||||||
// Author filters templates based on their author and only run the matching ones.
|
// Author filters templates based on their author and only run the matching ones.
|
||||||
Author goflags.NormalizedStringSlice
|
Author goflags.NormalizedStringSlice
|
||||||
// IncludeTags includes specified tags to be run even while being in denylist
|
// IncludeTags includes specified tags to be run even while being in denylist
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user