diff --git a/README.md b/README.md index 7007930a03..8c770a234a 100644 --- a/README.md +++ b/README.md @@ -126,9 +126,9 @@ TEMPLATES: -ntv, -new-templates-version string[] run new templates added in specific version -as, -automatic-scan automatic web scan using wappalyzer technology detection to tags mapping -t, -templates string[] list of template or template directory to run (comma-separated, file) - -tu, -template-url string[] list of template urls to run (comma-separated, file) + -turl, -template-url string[] template url or list containing template urls to run (comma-separated, file) -w, -workflows string[] list of workflow or workflow directory to run (comma-separated, file) - -wu, -workflow-url string[] list of workflow urls to run (comma-separated, file) + -wurl, -workflow-url string[] workflow url or list containing workflow urls to run (comma-separated, file) -validate validate the passed templates to nuclei -nss, -no-strict-syntax disable strict syntax check on templates -td, -template-display displays the templates content diff --git a/docs/template-guide/operators/matchers.mdx b/docs/template-guide/operators/matchers.mdx index 8a87ac4b3a..4a9da9cea4 100644 --- a/docs/template-guide/operators/matchers.mdx +++ b/docs/template-guide/operators/matchers.mdx @@ -8,7 +8,7 @@ Matchers allow different type of flexible comparisons on protocol responses. The ### Types -Multiple matchers can be specified in a request. There are basically 6 types of matchers: +Multiple matchers can be specified in a request. There are basically 7 types of matchers: | Matcher Type | Part Matched | |--------------|-----------------------------| @@ -18,6 +18,7 @@ Multiple matchers can be specified in a request. There are basically 6 types of | regex | Part for a protocol | | binary | Part for a protocol | | dsl | Part for a protocol | +| xpath | Part for a protocol | To match status codes for responses, you can use the following syntax. @@ -57,6 +58,16 @@ matchers: **Word** and **Regex** matchers can be further configured depending on the needs of the users. +**XPath** matchers use XPath queries to match XML and HTML responses. If the XPath query returns any results, it's considered a match. + +```yaml +matchers: + - type: xpath + part: body + xpath: + - "/html/head/title[contains(text(), 'Example Domain')]" +``` + Complex matchers of type **dsl** allows building more elaborate expressions with helper functions. These function allow access to Protocol Response which contains variety of data based on each protocol. See protocol specific documentation to learn about different returned results. diff --git a/integration_tests/headless/file-upload-negative.yaml b/integration_tests/headless/file-upload-negative.yaml new file mode 100644 index 0000000000..3d8c2bf4a0 --- /dev/null +++ b/integration_tests/headless/file-upload-negative.yaml @@ -0,0 +1,29 @@ +id: file-upload +# template for testing when file upload is disabled +info: + name: Basic File Upload + author: pdteam + severity: info + +headless: + - steps: + - action: navigate + args: + url: "{{BaseURL}}" + - action: waitload + - action: files + args: + by: xpath + xpath: /html/body/form/input[1] + value: headless/file-upload.yaml + - action: sleep + args: + duration: 2 + - action: click + args: + by: x + xpath: /html/body/form/input[2] + matchers: + - type: word + words: + - "Basic File Upload" \ No newline at end of file diff --git a/integration_tests/headless/headless-local.yaml b/integration_tests/headless/headless-local.yaml new file mode 100644 index 0000000000..385859d0a1 --- /dev/null +++ b/integration_tests/headless/headless-local.yaml @@ -0,0 +1,15 @@ +id: nuclei-headless-local + +info: + name: Nuclei Headless Local + author: pdteam + severity: high + +headless: + - steps: + - action: navigate + args: + url: "{{BaseURL}}" + + - action: waitload + \ No newline at end of file diff --git a/nuclei-jsonschema.json b/nuclei-jsonschema.json index 579db7082f..ff8291eb12 100644 --- a/nuclei-jsonschema.json +++ b/nuclei-jsonschema.json @@ -342,6 +342,14 @@ "title": "dsl expressions to match in response", "description": "DSL are the dsl expressions that will be evaluated as part of nuclei matching rules" }, + "xpath": { + "items": { + "type": "string" + }, + "type": "array", + "title": "xpath queries to match in response", + "description": "xpath are the XPath queries that will be evaluated against the response part of nuclei matching rules" + }, "encoding": { "enum": [ "hex" @@ -371,7 +379,8 @@ "binary", "status", "size", - "dsl" + "dsl", + "xpath" ], "type": "string", "title": "type of the matcher", diff --git a/v2/cmd/integration-test/headless.go b/v2/cmd/integration-test/headless.go index 185cdc87f5..b30c464cc7 100644 --- a/v2/cmd/integration-test/headless.go +++ b/v2/cmd/integration-test/headless.go @@ -16,7 +16,9 @@ var headlessTestcases = []TestCaseInfo{ {Path: "headless/headless-extract-values.yaml", TestCase: &headlessExtractValues{}}, {Path: "headless/headless-payloads.yaml", TestCase: &headlessPayloads{}}, {Path: "headless/variables.yaml", TestCase: &headlessVariables{}}, + {Path: "headless/headless-local.yaml", TestCase: &headlessLocal{}}, {Path: "headless/file-upload.yaml", TestCase: &headlessFileUpload{}}, + {Path: "headless/file-upload-negative.yaml", TestCase: &headlessFileUploadNegative{}}, {Path: "headless/headless-header-status-test.yaml", TestCase: &headlessHeaderStatus{}}, } @@ -39,6 +41,27 @@ func (h *headlessBasic) Execute(filePath string) error { return expectResultsCount(results, 1) } +type headlessLocal struct{} + +// Execute executes a test case and returns an error if occurred +// in this testcases local network access is disabled +func (h *headlessLocal) Execute(filePath string) error { + router := httprouter.New() + router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + _, _ = w.Write([]byte("
")) + }) + ts := httptest.NewServer(router) + defer ts.Close() + + args := []string{"-t", filePath, "-u", ts.URL, "-headless", "-lna"} + + results, err := testutils.RunNucleiWithArgsAndGetResults(debug, args...) + if err != nil { + return err + } + return expectResultsCount(results, 0) +} + type headlessHeaderActions struct{} // Execute executes a test case and returns an error if occurred @@ -171,3 +194,48 @@ func (h *headlessHeaderStatus) Execute(filePath string) error { return expectResultsCount(results, 1) } + +type headlessFileUploadNegative struct{} + +// Execute executes a test case and returns an error if occurred +func (h *headlessFileUploadNegative) Execute(filePath string) error { + router := httprouter.New() + router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + _, _ = w.Write([]byte(` + + + + +