mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-18 22:45:27 +00:00
More tests + tag based execution + misc
This commit is contained in:
parent
a23ca6ee54
commit
f2c20dda12
@ -74,6 +74,7 @@ based on templates offering massive extensibility and ease of use.`)
|
|||||||
set.BoolVarP(&options.NoMeta, "no-meta", "nm", false, "Don't display metadata for the matches")
|
set.BoolVarP(&options.NoMeta, "no-meta", "nm", false, "Don't display metadata for the matches")
|
||||||
set.BoolVarP(&options.TemplatesVersion, "templates-version", "tv", false, "Shows the installed nuclei-templates version")
|
set.BoolVarP(&options.TemplatesVersion, "templates-version", "tv", false, "Shows the installed nuclei-templates version")
|
||||||
set.StringVarP(&options.BurpCollaboratorBiid, "burp-collaborator-biid", "biid", "", "Burp Collaborator BIID")
|
set.StringVarP(&options.BurpCollaboratorBiid, "burp-collaborator-biid", "biid", "", "Burp Collaborator BIID")
|
||||||
|
set.StringSliceVar(&options.Tags, "tags", []string{}, "Tags to execute templates for")
|
||||||
_ = set.Parse()
|
_ = set.Parse()
|
||||||
|
|
||||||
if cfgFile != "" {
|
if cfgFile != "" {
|
||||||
|
|||||||
@ -29,10 +29,10 @@ func (r *Runner) getParsedTemplatesFor(templatePaths []string, severities []stri
|
|||||||
if len(t.Workflows) > 0 {
|
if len(t.Workflows) > 0 {
|
||||||
workflowCount++
|
workflowCount++
|
||||||
}
|
}
|
||||||
sev := strings.ToLower(t.Info["severity"])
|
sev := strings.ToLower(t.Info["severity"].(string))
|
||||||
if !filterBySeverity || hasMatchingSeverity(sev, severities) {
|
if !filterBySeverity || hasMatchingSeverity(sev, severities) {
|
||||||
parsedTemplates[t.ID] = t
|
parsedTemplates[t.ID] = t
|
||||||
gologger.Info().Msgf("%s\n", r.templateLogMsg(t.ID, t.Info["name"], t.Info["author"], t.Info["severity"]))
|
gologger.Info().Msgf("%s\n", r.templateLogMsg(t.ID, t.Info["name"].(string), t.Info["author"].(string), t.Info["severity"].(string)))
|
||||||
} else {
|
} else {
|
||||||
gologger.Error().Msgf("Excluding template %s due to severity filter (%s not in [%s])", t.ID, sev, severities)
|
gologger.Error().Msgf("Excluding template %s due to severity filter (%s not in [%s])", t.ID, sev, severities)
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ func (r *Runner) logAvailableTemplate(tplPath string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
gologger.Error().Msgf("Could not parse file '%s': %s\n", tplPath, err)
|
gologger.Error().Msgf("Could not parse file '%s': %s\n", tplPath, err)
|
||||||
} else {
|
} else {
|
||||||
gologger.Print().Msgf("%s\n", r.templateLogMsg(t.ID, t.Info["name"], t.Info["author"], t.Info["severity"]))
|
gologger.Print().Msgf("%s\n", r.templateLogMsg(t.ID, t.Info["name"].(string), t.Info["author"].(string), t.Info["severity"].(string)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -96,7 +96,7 @@ func (m *MockOutputWriter) Request(templateID, url, requestType string, err erro
|
|||||||
// TemplateInfo contains info for a mock executed template.
|
// TemplateInfo contains info for a mock executed template.
|
||||||
type TemplateInfo struct {
|
type TemplateInfo struct {
|
||||||
ID string
|
ID string
|
||||||
Info map[string]string
|
Info map[string]interface{}
|
||||||
Path string
|
Path string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,7 +27,7 @@ func (w *StandardWriter) formatScreen(output *ResultEvent) ([]byte, error) {
|
|||||||
builder.WriteString("] ")
|
builder.WriteString("] ")
|
||||||
|
|
||||||
builder.WriteString("[")
|
builder.WriteString("[")
|
||||||
builder.WriteString(w.severityColors.Data[output.Info["severity"]])
|
builder.WriteString(w.severityColors.Data[output.Info["severity"].(string)])
|
||||||
builder.WriteString("] ")
|
builder.WriteString("] ")
|
||||||
}
|
}
|
||||||
builder.WriteString(output.Matched)
|
builder.WriteString(output.Matched)
|
||||||
|
|||||||
@ -54,7 +54,7 @@ type ResultEvent struct {
|
|||||||
// TemplateID is the ID of the template for the result.
|
// TemplateID is the ID of the template for the result.
|
||||||
TemplateID string `json:"templateID"`
|
TemplateID string `json:"templateID"`
|
||||||
// Info contains information block of the template for the result.
|
// Info contains information block of the template for the result.
|
||||||
Info map[string]string `json:"info,inline"`
|
Info map[string]interface{} `json:"info,inline"`
|
||||||
// MatcherName is the name of the matcher matched if any.
|
// MatcherName is the name of the matcher matched if any.
|
||||||
MatcherName string `json:"matcher_name,omitempty"`
|
MatcherName string `json:"matcher_name,omitempty"`
|
||||||
// ExtractorName is the name of the extractor matched if any.
|
// ExtractorName is the name of the extractor matched if any.
|
||||||
|
|||||||
@ -21,7 +21,7 @@ type Executer struct {
|
|||||||
|
|
||||||
type clusteredOperator struct {
|
type clusteredOperator struct {
|
||||||
templateID string
|
templateID string
|
||||||
templateInfo map[string]string
|
templateInfo map[string]interface{}
|
||||||
operator *operators.Operators
|
operator *operators.Operators
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@ func TestDNSCompileMake(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile dns request")
|
require.Nil(t, err, "could not compile dns request")
|
||||||
|
|||||||
@ -136,7 +136,7 @@ func (r *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*outpu
|
|||||||
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
|
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
|
||||||
data := &output.ResultEvent{
|
data := &output.ResultEvent{
|
||||||
TemplateID: wrapped.InternalEvent["template-id"].(string),
|
TemplateID: wrapped.InternalEvent["template-id"].(string),
|
||||||
Info: wrapped.InternalEvent["template-info"].(map[string]string),
|
Info: wrapped.InternalEvent["template-info"].(map[string]interface{}),
|
||||||
Type: "dns",
|
Type: "dns",
|
||||||
Host: wrapped.InternalEvent["host"].(string),
|
Host: wrapped.InternalEvent["host"].(string),
|
||||||
Matched: wrapped.InternalEvent["matched"].(string),
|
Matched: wrapped.InternalEvent["matched"].(string),
|
||||||
|
|||||||
@ -29,7 +29,7 @@ func TestResponseToDSLMap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile dns request")
|
require.Nil(t, err, "could not compile dns request")
|
||||||
@ -61,7 +61,7 @@ func TestDNSOperatorMatch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile dns request")
|
require.Nil(t, err, "could not compile dns request")
|
||||||
@ -144,7 +144,7 @@ func TestDNSOperatorExtract(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile dns request")
|
require.Nil(t, err, "could not compile dns request")
|
||||||
@ -214,7 +214,7 @@ func TestDNSMakeResult(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile dns request")
|
require.Nil(t, err, "could not compile dns request")
|
||||||
|
|||||||
@ -39,7 +39,7 @@ func TestDNSExecuteWithResults(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile dns request")
|
require.Nil(t, err, "could not compile dns request")
|
||||||
|
|||||||
@ -22,7 +22,7 @@ func TestFileCompile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile file request")
|
require.Nil(t, err, "could not compile file request")
|
||||||
|
|||||||
@ -25,7 +25,7 @@ func TestFindInputPaths(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile file request")
|
require.Nil(t, err, "could not compile file request")
|
||||||
|
|||||||
@ -103,7 +103,7 @@ func (r *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*outpu
|
|||||||
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
|
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
|
||||||
data := &output.ResultEvent{
|
data := &output.ResultEvent{
|
||||||
TemplateID: wrapped.InternalEvent["template-id"].(string),
|
TemplateID: wrapped.InternalEvent["template-id"].(string),
|
||||||
Info: wrapped.InternalEvent["template-info"].(map[string]string),
|
Info: wrapped.InternalEvent["template-info"].(map[string]interface{}),
|
||||||
Type: "file",
|
Type: "file",
|
||||||
Host: wrapped.InternalEvent["host"].(string),
|
Host: wrapped.InternalEvent["host"].(string),
|
||||||
Matched: wrapped.InternalEvent["matched"].(string),
|
Matched: wrapped.InternalEvent["matched"].(string),
|
||||||
|
|||||||
@ -26,7 +26,7 @@ func TestResponseToDSLMap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile file request")
|
require.Nil(t, err, "could not compile file request")
|
||||||
@ -52,7 +52,7 @@ func TestFileOperatorMatch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile file request")
|
require.Nil(t, err, "could not compile file request")
|
||||||
@ -118,7 +118,7 @@ func TestFileOperatorExtract(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile file request")
|
require.Nil(t, err, "could not compile file request")
|
||||||
@ -184,7 +184,7 @@ func TestFileMakeResult(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile file request")
|
require.Nil(t, err, "could not compile file request")
|
||||||
|
|||||||
@ -42,7 +42,7 @@ func TestFileExecuteWithResults(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile file request")
|
require.Nil(t, err, "could not compile file request")
|
||||||
|
|||||||
@ -27,72 +27,6 @@ var (
|
|||||||
templateExpressionRegex = regexp.MustCompile(`(?m)\{\{[^}]+\}\}`)
|
templateExpressionRegex = regexp.MustCompile(`(?m)\{\{[^}]+\}\}`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// requestGenerator generates requests sequentially based on various
|
|
||||||
// configurations for a http request template.
|
|
||||||
//
|
|
||||||
// If payload values are present, an iterator is created for the payload
|
|
||||||
// values. Paths and Raw requests are supported as base input, so
|
|
||||||
// it will automatically select between them based on the template.
|
|
||||||
type requestGenerator struct {
|
|
||||||
currentIndex int
|
|
||||||
request *Request
|
|
||||||
payloadIterator *generators.Iterator
|
|
||||||
}
|
|
||||||
|
|
||||||
// newGenerator creates a new request generator instance
|
|
||||||
func (r *Request) newGenerator() *requestGenerator {
|
|
||||||
generator := &requestGenerator{request: r}
|
|
||||||
|
|
||||||
if len(r.Payloads) > 0 {
|
|
||||||
generator.payloadIterator = r.generator.NewIterator()
|
|
||||||
}
|
|
||||||
return generator
|
|
||||||
}
|
|
||||||
|
|
||||||
// nextValue returns the next path or the next raw request depending on user input
|
|
||||||
// It returns false if all the inputs have been exhausted by the generator instance.
|
|
||||||
func (r *requestGenerator) nextValue() (string, map[string]interface{}, bool) {
|
|
||||||
// If we have paths, return the next path.
|
|
||||||
if len(r.request.Path) > 0 && r.currentIndex < len(r.request.Path) {
|
|
||||||
if item := r.request.Path[r.currentIndex]; item != "" {
|
|
||||||
r.currentIndex++
|
|
||||||
return item, nil, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have raw requests, start with the request at current index.
|
|
||||||
// If we are not at the start, then check if the iterator for payloads
|
|
||||||
// has finished if there are any.
|
|
||||||
//
|
|
||||||
// If the iterator has finished for the current raw request
|
|
||||||
// then reset it and move on to the next value, otherwise use the last request.
|
|
||||||
if len(r.request.Raw) > 0 && r.currentIndex < len(r.request.Raw) {
|
|
||||||
if r.payloadIterator != nil {
|
|
||||||
payload, ok := r.payloadIterator.Value()
|
|
||||||
if !ok {
|
|
||||||
r.currentIndex++
|
|
||||||
r.payloadIterator.Reset()
|
|
||||||
|
|
||||||
// No more payloads request for us now.
|
|
||||||
if len(r.request.Raw) == r.currentIndex {
|
|
||||||
return "", nil, false
|
|
||||||
}
|
|
||||||
if item := r.request.Raw[r.currentIndex]; item != "" {
|
|
||||||
newPayload, ok := r.payloadIterator.Value()
|
|
||||||
return item, newPayload, ok
|
|
||||||
}
|
|
||||||
return "", nil, false
|
|
||||||
}
|
|
||||||
return r.request.Raw[r.currentIndex], payload, true
|
|
||||||
}
|
|
||||||
if item := r.request.Raw[r.currentIndex]; item != "" {
|
|
||||||
r.currentIndex++
|
|
||||||
return item, nil, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// generatedRequest is a single wrapped generated request for a template request
|
// generatedRequest is a single wrapped generated request for a template request
|
||||||
type generatedRequest struct {
|
type generatedRequest struct {
|
||||||
original *Request
|
original *Request
|
||||||
|
|||||||
@ -1,70 +1 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRequestGeneratorPaths(t *testing.T) {
|
|
||||||
req := &Request{
|
|
||||||
Path: []string{"{{BaseURL}}/test", "{{BaseURL}}/test.php"},
|
|
||||||
}
|
|
||||||
generator := req.newGenerator()
|
|
||||||
var payloads []string
|
|
||||||
for {
|
|
||||||
raw, _, ok := generator.nextValue()
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
payloads = append(payloads, raw)
|
|
||||||
}
|
|
||||||
require.Equal(t, req.Path, payloads, "Could not get correct paths")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRequestGeneratorClusterSingle(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
req := &Request{
|
|
||||||
Payloads: map[string]interface{}{"username": []string{"admin", "tomcat", "manager"}, "password": []string{"password", "test", "secret"}},
|
|
||||||
attackType: generators.ClusterBomb,
|
|
||||||
Raw: []string{`GET /{{username}}:{{password}} HTTP/1.1`},
|
|
||||||
}
|
|
||||||
req.generator, err = generators.New(req.Payloads, req.attackType, "")
|
|
||||||
require.Nil(t, err, "could not create generator")
|
|
||||||
|
|
||||||
generator := req.newGenerator()
|
|
||||||
var payloads []map[string]interface{}
|
|
||||||
for {
|
|
||||||
_, data, ok := generator.nextValue()
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
payloads = append(payloads, data)
|
|
||||||
}
|
|
||||||
require.Equal(t, 9, len(payloads), "Could not get correct number of payloads")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRequestGeneratorClusterMultipleRaw(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
req := &Request{
|
|
||||||
Payloads: map[string]interface{}{"username": []string{"admin", "tomcat", "manager"}, "password": []string{"password", "test", "secret"}},
|
|
||||||
attackType: generators.ClusterBomb,
|
|
||||||
Raw: []string{`GET /{{username}}:{{password}} HTTP/1.1`, `GET /{{username}}@{{password}} HTTP/1.1`},
|
|
||||||
}
|
|
||||||
req.generator, err = generators.New(req.Payloads, req.attackType, "")
|
|
||||||
require.Nil(t, err, "could not create generator")
|
|
||||||
|
|
||||||
generator := req.newGenerator()
|
|
||||||
var payloads []map[string]interface{}
|
|
||||||
for {
|
|
||||||
_, data, ok := generator.nextValue()
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
payloads = append(payloads, data)
|
|
||||||
}
|
|
||||||
require.Equal(t, 18, len(payloads), "Could not get correct number of payloads")
|
|
||||||
}
|
|
||||||
|
|||||||
1
v2/pkg/protocols/http/cluster_test.go
Normal file
1
v2/pkg/protocols/http/cluster_test.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package http
|
||||||
@ -136,7 +136,7 @@ func (r *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*outpu
|
|||||||
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
|
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
|
||||||
data := &output.ResultEvent{
|
data := &output.ResultEvent{
|
||||||
TemplateID: wrapped.InternalEvent["template-id"].(string),
|
TemplateID: wrapped.InternalEvent["template-id"].(string),
|
||||||
Info: wrapped.InternalEvent["template-info"].(map[string]string),
|
Info: wrapped.InternalEvent["template-info"].(map[string]interface{}),
|
||||||
Type: "http",
|
Type: "http",
|
||||||
Host: wrapped.InternalEvent["host"].(string),
|
Host: wrapped.InternalEvent["host"].(string),
|
||||||
Matched: wrapped.InternalEvent["matched"].(string),
|
Matched: wrapped.InternalEvent["matched"].(string),
|
||||||
|
|||||||
69
v2/pkg/protocols/http/request_generator.go
Normal file
69
v2/pkg/protocols/http/request_generator.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
|
||||||
|
|
||||||
|
// requestGenerator generates requests sequentially based on various
|
||||||
|
// configurations for a http request template.
|
||||||
|
//
|
||||||
|
// If payload values are present, an iterator is created for the payload
|
||||||
|
// values. Paths and Raw requests are supported as base input, so
|
||||||
|
// it will automatically select between them based on the template.
|
||||||
|
type requestGenerator struct {
|
||||||
|
currentIndex int
|
||||||
|
request *Request
|
||||||
|
payloadIterator *generators.Iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
// newGenerator creates a new request generator instance
|
||||||
|
func (r *Request) newGenerator() *requestGenerator {
|
||||||
|
generator := &requestGenerator{request: r}
|
||||||
|
|
||||||
|
if len(r.Payloads) > 0 {
|
||||||
|
generator.payloadIterator = r.generator.NewIterator()
|
||||||
|
}
|
||||||
|
return generator
|
||||||
|
}
|
||||||
|
|
||||||
|
// nextValue returns the next path or the next raw request depending on user input
|
||||||
|
// It returns false if all the inputs have been exhausted by the generator instance.
|
||||||
|
func (r *requestGenerator) nextValue() (string, map[string]interface{}, bool) {
|
||||||
|
// If we have paths, return the next path.
|
||||||
|
if len(r.request.Path) > 0 && r.currentIndex < len(r.request.Path) {
|
||||||
|
if item := r.request.Path[r.currentIndex]; item != "" {
|
||||||
|
r.currentIndex++
|
||||||
|
return item, nil, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have raw requests, start with the request at current index.
|
||||||
|
// If we are not at the start, then check if the iterator for payloads
|
||||||
|
// has finished if there are any.
|
||||||
|
//
|
||||||
|
// If the iterator has finished for the current raw request
|
||||||
|
// then reset it and move on to the next value, otherwise use the last request.
|
||||||
|
if len(r.request.Raw) > 0 && r.currentIndex < len(r.request.Raw) {
|
||||||
|
if r.payloadIterator != nil {
|
||||||
|
payload, ok := r.payloadIterator.Value()
|
||||||
|
if !ok {
|
||||||
|
r.currentIndex++
|
||||||
|
r.payloadIterator.Reset()
|
||||||
|
|
||||||
|
// No more payloads request for us now.
|
||||||
|
if len(r.request.Raw) == r.currentIndex {
|
||||||
|
return "", nil, false
|
||||||
|
}
|
||||||
|
if item := r.request.Raw[r.currentIndex]; item != "" {
|
||||||
|
newPayload, ok := r.payloadIterator.Value()
|
||||||
|
return item, newPayload, ok
|
||||||
|
}
|
||||||
|
return "", nil, false
|
||||||
|
}
|
||||||
|
return r.request.Raw[r.currentIndex], payload, true
|
||||||
|
}
|
||||||
|
if item := r.request.Raw[r.currentIndex]; item != "" {
|
||||||
|
r.currentIndex++
|
||||||
|
return item, nil, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil, false
|
||||||
|
}
|
||||||
70
v2/pkg/protocols/http/request_generator_test.go
Normal file
70
v2/pkg/protocols/http/request_generator_test.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRequestGeneratorPaths(t *testing.T) {
|
||||||
|
req := &Request{
|
||||||
|
Path: []string{"{{BaseURL}}/test", "{{BaseURL}}/test.php"},
|
||||||
|
}
|
||||||
|
generator := req.newGenerator()
|
||||||
|
var payloads []string
|
||||||
|
for {
|
||||||
|
raw, _, ok := generator.nextValue()
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
payloads = append(payloads, raw)
|
||||||
|
}
|
||||||
|
require.Equal(t, req.Path, payloads, "Could not get correct paths")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestGeneratorClusterBombSingle(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
req := &Request{
|
||||||
|
Payloads: map[string]interface{}{"username": []string{"admin", "tomcat", "manager"}, "password": []string{"password", "test", "secret"}},
|
||||||
|
attackType: generators.ClusterBomb,
|
||||||
|
Raw: []string{`GET /{{username}}:{{password}} HTTP/1.1`},
|
||||||
|
}
|
||||||
|
req.generator, err = generators.New(req.Payloads, req.attackType, "")
|
||||||
|
require.Nil(t, err, "could not create generator")
|
||||||
|
|
||||||
|
generator := req.newGenerator()
|
||||||
|
var payloads []map[string]interface{}
|
||||||
|
for {
|
||||||
|
_, data, ok := generator.nextValue()
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
payloads = append(payloads, data)
|
||||||
|
}
|
||||||
|
require.Equal(t, 9, len(payloads), "Could not get correct number of payloads")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestGeneratorClusterBombMultipleRaw(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
req := &Request{
|
||||||
|
Payloads: map[string]interface{}{"username": []string{"admin", "tomcat", "manager"}, "password": []string{"password", "test", "secret"}},
|
||||||
|
attackType: generators.ClusterBomb,
|
||||||
|
Raw: []string{`GET /{{username}}:{{password}} HTTP/1.1`, `GET /{{username}}@{{password}} HTTP/1.1`},
|
||||||
|
}
|
||||||
|
req.generator, err = generators.New(req.Payloads, req.attackType, "")
|
||||||
|
require.Nil(t, err, "could not create generator")
|
||||||
|
|
||||||
|
generator := req.newGenerator()
|
||||||
|
var payloads []map[string]interface{}
|
||||||
|
for {
|
||||||
|
_, data, ok := generator.nextValue()
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
payloads = append(payloads, data)
|
||||||
|
}
|
||||||
|
require.Equal(t, 18, len(payloads), "Could not get correct number of payloads")
|
||||||
|
}
|
||||||
@ -20,7 +20,7 @@ func TestNetworkCompileMake(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile network request")
|
require.Nil(t, err, "could not compile network request")
|
||||||
|
|||||||
@ -104,7 +104,7 @@ func (r *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*outpu
|
|||||||
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
|
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
|
||||||
data := &output.ResultEvent{
|
data := &output.ResultEvent{
|
||||||
TemplateID: wrapped.InternalEvent["template-id"].(string),
|
TemplateID: wrapped.InternalEvent["template-id"].(string),
|
||||||
Info: wrapped.InternalEvent["template-info"].(map[string]string),
|
Info: wrapped.InternalEvent["template-info"].(map[string]interface{}),
|
||||||
Type: "network",
|
Type: "network",
|
||||||
Host: wrapped.InternalEvent["host"].(string),
|
Host: wrapped.InternalEvent["host"].(string),
|
||||||
Matched: wrapped.InternalEvent["matched"].(string),
|
Matched: wrapped.InternalEvent["matched"].(string),
|
||||||
|
|||||||
@ -24,7 +24,7 @@ func TestResponseToDSLMap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile network request")
|
require.Nil(t, err, "could not compile network request")
|
||||||
@ -49,7 +49,7 @@ func TestNetworkOperatorMatch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile network request")
|
require.Nil(t, err, "could not compile network request")
|
||||||
@ -112,7 +112,7 @@ func TestNetworkOperatorExtract(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile network request")
|
require.Nil(t, err, "could not compile network request")
|
||||||
@ -175,7 +175,7 @@ func TestNetworkMakeResult(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile network request")
|
require.Nil(t, err, "could not compile network request")
|
||||||
|
|||||||
@ -38,7 +38,7 @@ func TestNetworkExecuteWithResults(t *testing.T) {
|
|||||||
}
|
}
|
||||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||||
ID: templateID,
|
ID: templateID,
|
||||||
Info: map[string]string{"severity": "low", "name": "test"},
|
Info: map[string]interface{}{"severity": "low", "name": "test"},
|
||||||
})
|
})
|
||||||
err := request.Compile(executerOpts)
|
err := request.Compile(executerOpts)
|
||||||
require.Nil(t, err, "could not compile network request")
|
require.Nil(t, err, "could not compile network request")
|
||||||
@ -90,8 +90,4 @@ func TestNetworkExecuteWithResults(t *testing.T) {
|
|||||||
require.Equal(t, "test", finalEvent.Results[0].MatcherName, "could not get correct matcher name of results")
|
require.Equal(t, "test", finalEvent.Results[0].MatcherName, "could not get correct matcher name of results")
|
||||||
require.Equal(t, 1, len(finalEvent.Results[0].ExtractedResults), "could not get correct number of extracted results")
|
require.Equal(t, 1, len(finalEvent.Results[0].ExtractedResults), "could not get correct number of extracted results")
|
||||||
require.Equal(t, "<h1>400 - Bad Request</h1>", finalEvent.Results[0].ExtractedResults[0], "could not get correct extracted results")
|
require.Equal(t, "<h1>400 - Bad Request</h1>", finalEvent.Results[0].ExtractedResults[0], "could not get correct extracted results")
|
||||||
finalEvent = nil
|
|
||||||
|
|
||||||
request.Inputs[0].Type = ""
|
|
||||||
request.Inputs[0].Data = "GET / HTTP/1.1\r\n\r\n"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,7 +30,7 @@ type ExecuterOptions struct {
|
|||||||
// TemplatePath is the path of the template for the request
|
// TemplatePath is the path of the template for the request
|
||||||
TemplatePath string
|
TemplatePath string
|
||||||
// TemplateInfo contains information block of the template request
|
// TemplateInfo contains information block of the template request
|
||||||
TemplateInfo map[string]string
|
TemplateInfo map[string]interface{}
|
||||||
// Output is a writer interface for writing output events from executer.
|
// Output is a writer interface for writing output events from executer.
|
||||||
Output output.Writer
|
Output output.Writer
|
||||||
// Options contains configuration options for the executer.
|
// Options contains configuration options for the executer.
|
||||||
|
|||||||
@ -3,10 +3,12 @@ package templates
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/executer"
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/executer"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/workflows"
|
"github.com/projectdiscovery/nuclei/v2/pkg/workflows"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
@ -26,6 +28,21 @@ func Parse(filePath string, options *protocols.ExecuterOptions) (*Template, erro
|
|||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
|
if _, ok := template.Info["name"]; !ok {
|
||||||
|
return nil, errors.New("no template name field provided")
|
||||||
|
}
|
||||||
|
if _, ok := template.Info["author"]; !ok {
|
||||||
|
return nil, errors.New("no template author field provided")
|
||||||
|
}
|
||||||
|
if _, ok := template.Info["severity"]; !ok {
|
||||||
|
return nil, errors.New("no template severity field provided")
|
||||||
|
}
|
||||||
|
if templateTags, ok := template.Info["tags"]; ok && len(options.Options.Tags) > 0 {
|
||||||
|
if err := matchTemplateWithTags(templateTags.([]interface{}), options.Options); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Setting up variables regarding template metadata
|
// Setting up variables regarding template metadata
|
||||||
options.TemplateID = template.ID
|
options.TemplateID = template.ID
|
||||||
options.TemplateInfo = template.Info
|
options.TemplateInfo = template.Info
|
||||||
@ -140,3 +157,40 @@ func (t *Template) parseWorkflowTemplate(workflow *workflows.WorkflowTemplate, o
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// matchTemplateWithTags matches if the template matches a tag
|
||||||
|
func matchTemplateWithTags(tags []interface{}, options *types.Options) error {
|
||||||
|
matched := false
|
||||||
|
mainLoop:
|
||||||
|
for _, tag := range options.Tags {
|
||||||
|
key, value := getKeyValue(tag)
|
||||||
|
|
||||||
|
for _, templTag := range tags {
|
||||||
|
tKey, tValue := getKeyValue(templTag.(string))
|
||||||
|
if strings.EqualFold(key, tKey) && strings.EqualFold(value, tValue) {
|
||||||
|
matched = true
|
||||||
|
break mainLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
return errors.New("could not match template tags with input")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getKeyValue returns key value pair for a data string
|
||||||
|
func getKeyValue(data string) (string, string) {
|
||||||
|
|
||||||
|
var key, value string
|
||||||
|
if strings.Contains(data, ":") {
|
||||||
|
parts := strings.SplitN(data, ":", 1)
|
||||||
|
if len(parts) > 2 {
|
||||||
|
key, value = parts[0], parts[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if value == "" {
|
||||||
|
value = data
|
||||||
|
}
|
||||||
|
return key, value
|
||||||
|
}
|
||||||
|
|||||||
25
v2/pkg/templates/compile_test.go
Normal file
25
v2/pkg/templates/compile_test.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMatchTemplateWithTags(t *testing.T) {
|
||||||
|
templateTags := []interface{}{"php", "linux", "symfony"}
|
||||||
|
|
||||||
|
err := matchTemplateWithTags(templateTags, &types.Options{Tags: []string{"php"}})
|
||||||
|
require.Nil(t, err, "could not get php tag from input slice")
|
||||||
|
|
||||||
|
templateTags = []interface{}{"lang:php", "os:linux", "cms:symfony"}
|
||||||
|
|
||||||
|
err = matchTemplateWithTags(templateTags, &types.Options{Tags: []string{"cms:symfony"}})
|
||||||
|
require.Nil(t, err, "could not get php tag from input key value")
|
||||||
|
|
||||||
|
templateTags = []interface{}{"lang:php", "os:linux", "symfony"}
|
||||||
|
|
||||||
|
err = matchTemplateWithTags(templateTags, &types.Options{Tags: []string{"cms:symfony"}})
|
||||||
|
require.NotNil(t, err, "could get key value tag from input key value")
|
||||||
|
}
|
||||||
@ -14,7 +14,7 @@ type Template struct {
|
|||||||
// ID is the unique id for the template
|
// ID is the unique id for the template
|
||||||
ID string `yaml:"id"`
|
ID string `yaml:"id"`
|
||||||
// Info contains information about the template
|
// Info contains information about the template
|
||||||
Info map[string]string `yaml:"info"`
|
Info map[string]interface{} `yaml:"info"`
|
||||||
// RequestsHTTP contains the http request to make in the template
|
// RequestsHTTP contains the http request to make in the template
|
||||||
RequestsHTTP []*http.Request `yaml:"requests,omitempty"`
|
RequestsHTTP []*http.Request `yaml:"requests,omitempty"`
|
||||||
// RequestsDNS contains the dns request to make in the template
|
// RequestsDNS contains the dns request to make in the template
|
||||||
|
|||||||
@ -80,4 +80,8 @@ type Options struct {
|
|||||||
ExcludedTemplates goflags.StringSlice
|
ExcludedTemplates goflags.StringSlice
|
||||||
// CustomHeaders is the list of custom global headers to send with each request.
|
// CustomHeaders is the list of custom global headers to send with each request.
|
||||||
CustomHeaders goflags.StringSlice
|
CustomHeaders goflags.StringSlice
|
||||||
|
// Tags contains a list of tags to execute templates for. Multiple paths
|
||||||
|
// can be specified with -l flag and -tags can be used in combination with
|
||||||
|
// the -l flag.
|
||||||
|
Tags goflags.StringSlice
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user