diff --git a/cmd/docgen/docgen.go b/cmd/docgen/docgen.go index c589b98b0e..accbc2aee1 100644 --- a/cmd/docgen/docgen.go +++ b/cmd/docgen/docgen.go @@ -5,6 +5,7 @@ import ( "encoding/json" "log" "os" + "reflect" "regexp" "strings" @@ -33,6 +34,12 @@ func main() { // Generate jsonschema r := &jsonschema.Reflector{} + r.Namer = func(r reflect.Type) string { + if r.Kind() == reflect.Slice { + return "" + } + return r.String() + } jsonschemaData := r.Reflect(&templates.Template{}) var buf bytes.Buffer diff --git a/cmd/tmc/main.go b/cmd/tmc/main.go index 4f65a8b763..cd552acf49 100644 --- a/cmd/tmc/main.go +++ b/cmd/tmc/main.go @@ -231,7 +231,7 @@ func logErrMsg(path string, err error, debug bool, errFile *os.File) string { return msg } -// enhanceTemplateData enhances template data using templateman +// enhanceTemplate enhances template data using templateman // ref: https://github.com/projectdiscovery/templateman/blob/main/templateman-rest-api/README.md#enhance-api func enhanceTemplate(data string) (string, bool, error) { resp, err := retryablehttp.DefaultClient().Post(fmt.Sprintf("%s/enhance", tmBaseUrl), "application/x-yaml", strings.NewReader(data)) diff --git a/go.mod b/go.mod index 7eeaf57386..d2bec7816f 100644 --- a/go.mod +++ b/go.mod @@ -20,12 +20,12 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/pkg/errors v0.9.1 github.com/projectdiscovery/clistats v0.0.20 - github.com/projectdiscovery/fastdialer v0.0.64 + github.com/projectdiscovery/fastdialer v0.0.66 github.com/projectdiscovery/hmap v0.0.41 github.com/projectdiscovery/interactsh v1.1.9 - github.com/projectdiscovery/rawhttp v0.1.41 + github.com/projectdiscovery/rawhttp v0.1.44 github.com/projectdiscovery/retryabledns v1.0.58 - github.com/projectdiscovery/retryablehttp-go v1.0.54 + github.com/projectdiscovery/retryablehttp-go v1.0.55 github.com/projectdiscovery/yamldoc-go v1.0.4 github.com/remeh/sizedwaitgroup v1.0.0 github.com/rs/xid v1.5.0 @@ -78,7 +78,7 @@ require ( github.com/mholt/archiver v3.1.1+incompatible github.com/ory/dockertest/v3 v3.10.0 github.com/praetorian-inc/fingerprintx v1.1.9 - github.com/projectdiscovery/dsl v0.0.50 + github.com/projectdiscovery/dsl v0.0.51 github.com/projectdiscovery/fasttemplate v0.0.2 github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb github.com/projectdiscovery/goflags v0.1.46 @@ -94,7 +94,7 @@ require ( github.com/projectdiscovery/tlsx v1.1.6 github.com/projectdiscovery/uncover v1.0.7 github.com/projectdiscovery/useragent v0.0.40 - github.com/projectdiscovery/utils v0.0.87 + github.com/projectdiscovery/utils v0.0.88 github.com/projectdiscovery/wappalyzergo v0.0.116 github.com/redis/go-redis/v9 v9.1.0 github.com/seh-msft/burpxml v1.0.1 diff --git a/go.sum b/go.sum index cefd9ae445..1570efb97d 100644 --- a/go.sum +++ b/go.sum @@ -829,10 +829,10 @@ github.com/projectdiscovery/cdncheck v1.0.9 h1:BS15gzj9gb5AVSKqTDzPamfSgStu7nJQO github.com/projectdiscovery/cdncheck v1.0.9/go.mod h1:18SSl1w7rMj53CGeRIZTbDoa286a6xZIxGbaiEo4Fxs= github.com/projectdiscovery/clistats v0.0.20 h1:5jO5SLiRJ7f0nDV0ndBNmBeesbROouPooH+DGMgoWq4= github.com/projectdiscovery/clistats v0.0.20/go.mod h1:GJ2av0KnOvK0AISQnP8hyDclYIji1LVkx2l0pwnzAu4= -github.com/projectdiscovery/dsl v0.0.50 h1:4SuAwTS9l6o1tqlIC/79+EcUwTM6CjaU7MpY/nDlFaM= -github.com/projectdiscovery/dsl v0.0.50/go.mod h1:6g740l4tH4d2j9UYtIchtxudb0Dphkq4o+VatpR4M6g= -github.com/projectdiscovery/fastdialer v0.0.64 h1:xivkA4g14nwQElOVsxPkGMWsdcYPcp7DPhVjvI6yQkw= -github.com/projectdiscovery/fastdialer v0.0.64/go.mod h1:S/7PAQRmVDYRaU7u4xXD0qA5a48NAZq2JcpcVoEVrlo= +github.com/projectdiscovery/dsl v0.0.51 h1:7OQPumOrrUCFnCA7Y0nchhPvRo3IJGMIJ2Oy4DVTQsc= +github.com/projectdiscovery/dsl v0.0.51/go.mod h1:GYhusn+T9EL7t+iJ8zN/GXlp8ohLGU+Yv/nevAPlJZg= +github.com/projectdiscovery/fastdialer v0.0.66 h1:DRpmok9TArLyQKaSjRWSzikt2N2Qyzx/z0BmTmDyJvI= +github.com/projectdiscovery/fastdialer v0.0.66/go.mod h1:7uPrwFsIBhtUBkXd72K4VSo9lvcwqOzOGXIZ9UZXFYw= github.com/projectdiscovery/fasttemplate v0.0.2 h1:h2cISk5xDhlJEinlBQS6RRx0vOlOirB2y3Yu4PJzpiA= github.com/projectdiscovery/fasttemplate v0.0.2/go.mod h1:XYWWVMxnItd+r0GbjA1GCsUopMw1/XusuQxdyAIHMCw= github.com/projectdiscovery/freeport v0.0.5 h1:jnd3Oqsl4S8n0KuFkE5Hm8WGDP24ITBvmyw5pFTHS8Q= @@ -865,14 +865,14 @@ github.com/projectdiscovery/networkpolicy v0.0.8 h1:XvfBaBwSDNTesSfNQP9VLk3HX9I7 github.com/projectdiscovery/networkpolicy v0.0.8/go.mod h1:xnjNqhemxUPxU+UD5Jgsc3+K8IVmcqT1SJeo6UzMtkI= github.com/projectdiscovery/ratelimit v0.0.35 h1:epEzFATOcXZ4tssV4Hax5Op9lrbUnQMEGMV5PoUpTKc= github.com/projectdiscovery/ratelimit v0.0.35/go.mod h1:mPqa8UpV5I7eAN5/ZcsjLiXMhjtVvZRrHtpBRsTPuyA= -github.com/projectdiscovery/rawhttp v0.1.41 h1:0n6CohOf0Aq7dsXv+ozznhlYr4ANDKLwvPmdzTet3qU= -github.com/projectdiscovery/rawhttp v0.1.41/go.mod h1:TyVfCwNbAsQSwrMOKu8o1g80AO3t1OnlJx+flgSV/CQ= +github.com/projectdiscovery/rawhttp v0.1.44 h1:mkXTTUR65TTNisQGpLo5y5PRYRgNwZLW15KZNhNpsO8= +github.com/projectdiscovery/rawhttp v0.1.44/go.mod h1:jaldbYYP0QihgQKk6Ar9ym9NPLAz5QkXp5TPET0sjYM= github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917 h1:m03X4gBVSorSzvmm0bFa7gDV4QNSOWPL/fgZ4kTXBxk= github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917/go.mod h1:JxXtZC9e195awe7EynrcnBJmFoad/BNDzW9mzFkK8Sg= github.com/projectdiscovery/retryabledns v1.0.58 h1:ut1FSB9+GZ6zQIlKJFLqIz2RZs81EmkbsHTuIrWfYLE= github.com/projectdiscovery/retryabledns v1.0.58/go.mod h1:RobmKoNBgngAVE4H9REQtaLP1pa4TCyypHy1MWHT1mY= -github.com/projectdiscovery/retryablehttp-go v1.0.54 h1:lUmQA3obq3Ya3xU1vouKf+hVjbLFKzJCK6FcNKPZ8vQ= -github.com/projectdiscovery/retryablehttp-go v1.0.54/go.mod h1:J+pg00bYLEgWOZJISi16icHUDbsnkjnA1PmSa2kSMYs= +github.com/projectdiscovery/retryablehttp-go v1.0.55 h1:ADgugnl9jKkNXn5m/Zd8TGPq1P7GplYlqUNKm/qTmls= +github.com/projectdiscovery/retryablehttp-go v1.0.55/go.mod h1:Kpvh4ruFPOEPYaYxgbFmlvBJr4lJKqpcbGvx1j0r/Ng= github.com/projectdiscovery/sarif v0.0.1 h1:C2Tyj0SGOKbCLgHrx83vaE6YkzXEVrMXYRGLkKCr/us= github.com/projectdiscovery/sarif v0.0.1/go.mod h1:cEYlDu8amcPf6b9dSakcz2nNnJsoz4aR6peERwV+wuQ= github.com/projectdiscovery/stringsutil v0.0.2 h1:uzmw3IVLJSMW1kEg8eCStG/cGbYYZAja8BH3LqqJXMA= @@ -883,8 +883,8 @@ github.com/projectdiscovery/uncover v1.0.7 h1:ut+2lTuvmftmveqF5RTjMWAgyLj8ltPQC7 github.com/projectdiscovery/uncover v1.0.7/go.mod h1:HFXgm1sRPuoN0D4oATljPIdmbo/EEh1wVuxQqo/dwFE= github.com/projectdiscovery/useragent v0.0.40 h1:1LUhReSGPkhqsM5n40OOC9dIoNqMGs1dyGFJcOmg2Fo= github.com/projectdiscovery/useragent v0.0.40/go.mod h1:EvK1x3s948Gtqb/XOahXcauyejCL/rSgy5d1IAvsKT4= -github.com/projectdiscovery/utils v0.0.87 h1:9+RiTEhpUB/vk6XJUVpysNWJ2aCTD7WuyoyAcNnbIzk= -github.com/projectdiscovery/utils v0.0.87/go.mod h1:jGK450sL9AVDTjaPwEs9za8NVeEC9xE97IWNoK138kI= +github.com/projectdiscovery/utils v0.0.88 h1:oYfCXM+8VHNLyH/H6cOibkuDUwHUAOBAMRNPFX6NPrs= +github.com/projectdiscovery/utils v0.0.88/go.mod h1:lAWzFdGXtJRPKdhUu1Z46d8B8JbASTk1Z69WY6H/3kA= github.com/projectdiscovery/wappalyzergo v0.0.116 h1:xy+mBpwbYo/0PSzmJOQ/RXHomEh0D3nDBcbCxsW69m8= github.com/projectdiscovery/wappalyzergo v0.0.116/go.mod h1:hc/o+fgM8KtdpFesjfBTmHTwsR+yBd+4kYZW/DGy/x8= github.com/projectdiscovery/yamldoc-go v1.0.4 h1:eZoESapnMw6WAHiVgRwNqvbJEfNHEH148uthhFbG5jE= diff --git a/internal/runner/lazy.go b/internal/runner/lazy.go index 193b22ff6c..b61dd5515a 100644 --- a/internal/runner/lazy.go +++ b/internal/runner/lazy.go @@ -21,7 +21,7 @@ type AuthLazyFetchOptions struct { OnError func(error) } -// GetAuthTemlStore create new loader for loading auth templates +// GetAuthTmplStore create new loader for loading auth templates func GetAuthTmplStore(opts types.Options, catalog catalog.Catalog, execOpts protocols.ExecutorOptions) (*loader.Store, error) { tmpls := []string{} for _, file := range opts.SecretsFile { diff --git a/nuclei-jsonschema.json b/nuclei-jsonschema.json index 0f4c2a8f65..9e14e531e0 100644 --- a/nuclei-jsonschema.json +++ b/nuclei-jsonschema.json @@ -1,77 +1,269 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://template", - "$ref": "#/$defs/Template", + "$id": "https://templates.-template", + "$ref": "#/$defs/templates.Template", "$defs": { - "AttackTypeHolder": { + "code.Request": { "properties": { - "Value": { - "type": "integer" + "matchers": { + "items": { + "$ref": "#/$defs/matchers.Matcher" + }, + "type": "array", + "title": "matchers to run on response", + "description": "Detection mechanism to identify whether the request was successful by doing pattern matching" + }, + "extractors": { + "items": { + "$ref": "#/$defs/extractors.Extractor" + }, + "type": "array", + "title": "extractors to run on response", + "description": "Extractors contains the extraction mechanism for the request to identify and extract parts of the response" + }, + "matchers-condition": { + "type": "string", + "enum": [ + "and", + "or" + ], + "title": "condition between the matchers", + "description": "Conditions between the matchers" + }, + "id": { + "type": "string", + "title": "id of the request", + "description": "ID is the optional ID of the Request" + }, + "engine": { + "items": { + "type": "string" + }, + "type": "array", + "title": "engine", + "description": "Engine" + }, + "pre-condition": { + "type": "string", + "title": "pre-condition for the request", + "description": "PreCondition is a condition which is evaluated before sending the request" + }, + "args": { + "items": { + "type": "string" + }, + "type": "array", + "title": "args", + "description": "Args" + }, + "pattern": { + "type": "string", + "title": "pattern", + "description": "Pattern" + }, + "source": { + "type": "string", + "title": "source file/snippet", + "description": "Source snippet" } }, "additionalProperties": false, - "type": "object", - "required": [ - "Value" - ] + "type": "object" + }, + "dns.DNSRequestTypeHolder": { + "type": "string", + "enum": [ + "A", + "NS", + "DS", + "CNAME", + "SOA", + "PTR", + "MX", + "TXT", + "AAAA", + "CAA", + "TLSA", + "ANY" + ], + "title": "type of DNS request to make", + "description": "Type is the type of DNS request to make" }, - "Classification": { + "dns.Request": { "properties": { - "cve-id": { - "$ref": "#/$defs/StringOrSlice", - "title": "cve ids for the template", - "description": "CVE IDs for the template" + "matchers": { + "items": { + "$ref": "#/$defs/matchers.Matcher" + }, + "type": "array", + "title": "matchers to run on response", + "description": "Detection mechanism to identify whether the request was successful by doing pattern matching" }, - "cwe-id": { - "$ref": "#/$defs/StringOrSlice", - "title": "cwe ids for the template", - "description": "CWE IDs for the template" + "extractors": { + "items": { + "$ref": "#/$defs/extractors.Extractor" + }, + "type": "array", + "title": "extractors to run on response", + "description": "Extractors contains the extraction mechanism for the request to identify and extract parts of the response" }, - "cvss-metrics": { + "matchers-condition": { "type": "string", - "title": "cvss metrics for the template", - "description": "CVSS Metrics for the template", - "examples": [ - "3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" - ] + "enum": [ + "and", + "or" + ], + "title": "condition between the matchers", + "description": "Conditions between the matchers" }, - "cvss-score": { - "type": "number", - "title": "cvss score for the template", - "description": "CVSS Score for the template", - "examples": [ - 9.8 - ] + "id": { + "type": "string", + "title": "id of the dns request", + "description": "ID is the optional ID of the DNS Request" }, - "epss-score": { - "type": "number", - "title": "epss score for the template", - "description": "EPSS Score for the template", - "examples": [ - 0.42509 - ] + "name": { + "type": "string", + "title": "hostname to make dns request for", + "description": "Name is the Hostname to make DNS request for" }, - "epss-percentile": { - "type": "number", - "title": "epss percentile for the template", - "description": "EPSS Percentile for the template", - "examples": [ - 0.42509 - ] + "type": { + "$ref": "#/$defs/dns.DNSRequestTypeHolder", + "title": "type of dns request to make", + "description": "Type is the type of DNS request to make" }, - "cpe": { + "class": { "type": "string", - "title": "cpe for the template", - "description": "CPE for the template", - "examples": [ - "cpe:/a:vendor:product:version" - ] + "enum": [ + "inet", + "csnet", + "chaos", + "hesiod", + "none", + "any" + ], + "title": "class of DNS request", + "description": "Class is the class of the DNS request" + }, + "retries": { + "type": "integer", + "title": "retries for dns request", + "description": "Retries is the number of retries for the DNS request" + }, + "trace": { + "type": "boolean", + "title": "trace operation", + "description": "Trace performs a trace operation for the target." + }, + "trace-max-recursion": { + "type": "integer", + "title": "trace-max-recursion level for dns request", + "description": "TraceMaxRecursion is the number of max recursion allowed for trace operations" + }, + "attack": { + "$ref": "#/$defs/generators.AttackTypeHolder", + "title": "attack is the payload combination", + "description": "Attack is the type of payload combinations to perform" + }, + "payloads": { + "$ref": "#/$defs/map[string]interface {}", + "title": "payloads for the network request", + "description": "Payloads contains any payloads for the current request" + }, + "threads": { + "type": "integer", + "title": "threads for sending requests", + "description": "Threads specifies number of threads to use sending requests. This enables Connection Pooling" + }, + "recursion": { + "type": "boolean", + "title": "recurse all servers", + "description": "Recursion determines if resolver should recurse all records to get fresh results" + }, + "resolvers": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Resolvers", + "description": "Define resolvers to use within the template" } }, "additionalProperties": false, "type": "object" }, - "Extractor": { + "engine.Action": { + "properties": { + "args": { + "patternProperties": { + ".*": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "boolean" + } + ] + } + }, + "title": "arguments for headless action", + "description": "Args contain arguments for the headless action" + }, + "name": { + "type": "string", + "title": "name for headless action", + "description": "Name is the name assigned to the headless action" + }, + "description": { + "type": "string", + "title": "description for headless action", + "description": "Description of the headless action" + }, + "action": { + "$ref": "#/$defs/engine.ActionTypeHolder", + "title": "action to perform", + "description": "Type of actions to perform" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "action" + ] + }, + "engine.ActionTypeHolder": { + "type": "string", + "enum": [ + "navigate", + "script", + "click", + "rightclick", + "text", + "screenshot", + "time", + "select", + "files", + "waitload", + "getresource", + "extract", + "setmethod", + "addheader", + "setheader", + "deleteheader", + "setbody", + "waitevent", + "keyboard", + "debug", + "sleep", + "waitvisible" + ], + "title": "action to perform", + "description": "Type of actions to perform" + }, + "extractors.Extractor": { "properties": { "name": { "type": "string", @@ -79,7 +271,7 @@ "description": "Name of the extractor" }, "type": { - "$ref": "#/$defs/ExtractorTypeHolder" + "$ref": "#/$defs/extractors.ExtractorTypeHolder" }, "regex": { "items": { @@ -153,267 +345,299 @@ "type" ] }, - "ExtractorTypeHolder": { - "properties": { - "ExtractorType": { - "type": "integer" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "ExtractorType" - ] - }, - "HTTPMethodTypeHolder": { - "properties": { - "MethodType": { - "type": "integer" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "MethodType" - ] - }, - "Holder": { + "extractors.ExtractorTypeHolder": { "type": "string", "enum": [ - "info", - "low", - "medium", - "high", - "critical", - "unknown" + "regex", + "kval", + "xpath", + "json", + "dsl" ], - "title": "severity of the template", - "description": "Seriousness of the implications of the template" + "title": "type of the extractor", + "description": "Type of the extractor" }, - "Info": { + "file.Request": { "properties": { - "name": { - "type": "string", - "title": "name of the template", - "description": "Name is a short summary of what the template does", - "examples": [ - "Nagios Default Credentials Check" - ] + "matchers": { + "items": { + "$ref": "#/$defs/matchers.Matcher" + }, + "type": "array", + "title": "matchers to run on response", + "description": "Detection mechanism to identify whether the request was successful by doing pattern matching" }, - "author": { - "$ref": "#/$defs/StringOrSlice", - "oneOf": [ - { - "type": "string", - "examples": [ - "pdteam" - ] - }, - { - "type": "array", - "examples": [ - "pdteam,mr.robot" - ] - } + "extractors": { + "items": { + "$ref": "#/$defs/extractors.Extractor" + }, + "type": "array", + "title": "extractors to run on response", + "description": "Extractors contains the extraction mechanism for the request to identify and extract parts of the response" + }, + "matchers-condition": { + "type": "string", + "enum": [ + "and", + "or" ], - "title": "author of the template", - "description": "Author is the author of the template" + "title": "condition between the matchers", + "description": "Conditions between the matchers" }, - "tags": { - "$ref": "#/$defs/StringOrSlice", - "title": "tags of the template", - "description": "Any tags for the template" + "extensions": { + "items": { + "type": "string" + }, + "type": "array", + "title": "extensions to match", + "description": "List of extensions to perform matching on" }, - "description": { + "denylist": { + "items": { + "type": "string" + }, + "type": "array", + "title": "denylist", + "description": "List of files" + }, + "id": { "type": "string", - "title": "description of the template", - "description": "In-depth explanation on what the template does", - "examples": [ - "Bower is a package manager which stores package information in the bower.json file" - ] + "title": "id of the request", + "description": "ID is the optional ID for the request" }, - "impact": { + "max-size": { "type": "string", - "title": "impact of the template", - "description": "In-depth explanation on the impact of the issue found by the template", - "examples": [ - "Successful exploitation of this vulnerability could allow an attacker to execute arbitrary SQL queries" - ] + "title": "max size data to run request on", + "description": "Maximum size of the file to run request on" }, - "reference": { - "$ref": "#/$defs/StringOrSlice", - "title": "references for the template", - "description": "Links relevant to the template" - }, - "severity": { - "$ref": "#/$defs/Holder" - }, - "metadata": { - "type": "object", - "title": "additional metadata for the template", - "description": "Additional metadata fields for the template" + "archive": { + "type": "boolean", + "title": "enable archives", + "description": "Process compressed archives without unpacking" }, - "classification": { - "$ref": "#/$defs/Classification", - "type": "object", - "title": "classification info for the template", - "description": "Classification information for the template" + "mime-type": { + "type": "boolean", + "title": "enable filtering by mime-type", + "description": "Filter files by mime-type" }, - "remediation": { - "type": "string", - "title": "remediation steps for the template", - "description": "In-depth explanation on how to fix the issues found by the template", - "examples": [ - "Change the default administrative username and password of Apache ActiveMQ by editing the file jetty-realm.properties" - ] + "no-recursive": { + "type": "boolean", + "title": "do not perform recursion", + "description": "Specifies whether to not do recursive checks if folders are provided" } }, "additionalProperties": false, - "type": "object", - "required": [ - "name", - "author" - ] + "type": "object" }, - "Matcher": { + "fuzz.Rule": { "properties": { "type": { - "$ref": "#/$defs/MatcherTypeHolder", - "title": "type of matcher", - "description": "Type of the matcher" - }, - "condition": { "type": "string", "enum": [ - "and", - "or" + "replace", + "prefix", + "postfix", + "infix", + "replace-regex" ], - "title": "condition between matcher variables", - "description": "Condition between the matcher variables" + "title": "type of rule", + "description": "Type of fuzzing rule to perform" }, "part": { "type": "string", - "title": "part of response to match", - "description": "Part of response to match data from" - }, - "negative": { - "type": "boolean", - "title": "negative specifies if match reversed", - "description": "Negative specifies if the match should be reversed. It will only match if the condition is not true" + "enum": [ + "query", + "header", + "path", + "body", + "cookie", + "request" + ], + "title": "part of rule", + "description": "Part of request rule to fuzz" }, - "name": { + "mode": { "type": "string", - "title": "name of the matcher", - "description": "Name of the matcher" + "enum": [ + "single", + "multiple" + ], + "title": "mode of rule", + "description": "Mode of request rule to fuzz" }, - "status": { + "keys": { "items": { - "type": "integer" + "type": "string" }, "type": "array", - "title": "status to match", - "description": "Status to match for the response" + "title": "keys of parameters to fuzz", + "description": "Keys of parameters to fuzz" }, - "size": { + "keys-regex": { "items": { - "type": "integer" + "type": "string" }, "type": "array", - "title": "acceptable size for response", - "description": "Size is the acceptable size for the response" + "title": "keys regex to fuzz", + "description": "Regex of parameter keys to fuzz" }, - "words": { + "values": { "items": { "type": "string" }, "type": "array", - "title": "words to match in response", - "description": " Words contains word patterns required to be present in the response part" + "title": "values regex to fuzz", + "description": "Regex of parameter values to fuzz" }, - "regex": { - "items": { + "fuzz": { + "$ref": "#/$defs/fuzz.SliceOrMapSlice", + "title": "payloads of fuzz rule", + "description": "Payloads to perform fuzzing substitutions with" + }, + "replace-regex": { + "type": "string", + "title": "replace regex of rule", + "description": "Regex for regex-replace rule type" + } + }, + "additionalProperties": false, + "type": "object" + }, + "fuzz.SliceOrMapSlice": { + "items": { + "oneOf": [ + { "type": "string" }, - "type": "array", - "title": "regex to match in response", - "description": "Regex contains regex patterns required to be present in the response part" + { + "type": "object" + } + ] + }, + "type": "array", + "title": "Payloads of Fuzz Rule", + "description": "Payloads to perform fuzzing substitutions with." + }, + "generators.AttackTypeHolder": { + "type": "string", + "enum": [ + "batteringram", + "pitchfork", + "clusterbomb" + ], + "title": "type of the attack", + "description": "Type of the attack" + }, + "headless.Request": { + "properties": { + "id": { + "type": "string", + "title": "id of the request", + "description": "Optional ID of the headless request" }, - "binary": { + "attack": { + "$ref": "#/$defs/generators.AttackTypeHolder", + "title": "attack is the payload combination", + "description": "Attack is the type of payload combinations to perform" + }, + "payloads": { + "$ref": "#/$defs/map[string]interface {}", + "title": "payloads for the headless request", + "description": "Payloads contains any payloads for the current request" + }, + "steps": { "items": { - "type": "string" + "$ref": "#/$defs/engine.Action" }, "type": "array", - "title": "binary patterns to match in response", - "description": "Binary are the binary patterns required to be present in the response part" + "title": "list of actions for headless request", + "description": "List of actions to run for headless request" }, - "dsl": { + "user_agent": { + "$ref": "#/$defs/userAgent.UserAgentHolder", + "title": "user agent for the headless request", + "description": "User agent for the headless request" + }, + "custom_user_agent": { + "type": "string", + "title": "custom user agent for the headless request", + "description": "Custom user agent for the headless request" + }, + "stop-at-first-match": { + "type": "boolean", + "title": "stop at first match", + "description": "Stop the execution after a match is found" + }, + "matchers": { "items": { - "type": "string" + "$ref": "#/$defs/matchers.Matcher" }, "type": "array", - "title": "dsl expressions to match in response", - "description": "DSL are the dsl expressions that will be evaluated as part of nuclei matching rules" + "title": "matchers to run on response", + "description": "Detection mechanism to identify whether the request was successful by doing pattern matching" }, - "xpath": { + "extractors": { "items": { - "type": "string" + "$ref": "#/$defs/extractors.Extractor" }, "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" + "title": "extractors to run on response", + "description": "Extractors contains the extraction mechanism for the request to identify and extract parts of the response" }, - "encoding": { + "matchers-condition": { "type": "string", "enum": [ - "hex" + "and", + "or" ], - "title": "encoding for word field", - "description": "Optional encoding for the word fields" + "title": "condition between the matchers", + "description": "Conditions between the matchers" }, - "case-insensitive": { - "type": "boolean", - "title": "use case insensitive match", - "description": "use case insensitive match" + "fuzzing": { + "items": { + "$ref": "#/$defs/fuzz.Rule" + }, + "type": "array", + "title": "fuzzin rules for http fuzzing", + "description": "Fuzzing describes rule schema to fuzz headless requests" }, - "match-all": { + "cookie-reuse": { "type": "boolean", - "title": "match all values", - "description": "match all matcher values ignoring condition" + "title": "optional cookie reuse enable", + "description": "Optional setting that enables cookie reuse" }, - "internal": { + "disable-cookie": { "type": "boolean", - "title": "hide matcher from output", - "description": "hide matcher from output" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "type" - ] - }, - "MatcherTypeHolder": { - "properties": { - "MatcherType": { - "type": "integer" + "title": "optional disable cookie reuse", + "description": "Optional setting that disables cookie reuse" } }, "additionalProperties": false, - "type": "object", - "required": [ - "MatcherType" - ] - }, - "OrderedMap[string,string]": { - "properties": {}, - "additionalProperties": false, "type": "object" }, - "Request": { + "http.HTTPMethodTypeHolder": { + "type": "string", + "enum": [ + "GET", + "HEAD", + "POST", + "PUT", + "DELETE", + "CONNECT", + "OPTIONS", + "TRACE", + "PATCH", + "PURGE", + "DEBUG" + ], + "title": "method is the HTTP request method", + "description": "Method is the HTTP Request Method" + }, + "http.Request": { "properties": { "matchers": { "items": { - "$ref": "#/$defs/Matcher" + "$ref": "#/$defs/matchers.Matcher" }, "type": "array", "title": "matchers to run on response", @@ -421,7 +645,7 @@ }, "extractors": { "items": { - "$ref": "#/$defs/Extractor" + "$ref": "#/$defs/extractors.Extractor" }, "type": "array", "title": "extractors to run on response", @@ -462,12 +686,12 @@ "description": "Optional name for the HTTP Request" }, "attack": { - "$ref": "#/$defs/AttackTypeHolder", + "$ref": "#/$defs/generators.AttackTypeHolder", "title": "attack is the payload combination", "description": "Attack is the type of payload combinations to perform" }, "method": { - "$ref": "#/$defs/HTTPMethodTypeHolder", + "$ref": "#/$defs/http.HTTPMethodTypeHolder", "title": "method is the http request method", "description": "Method is the HTTP Request Method" }, @@ -477,15 +701,26 @@ "description": "Body is an optional parameter which contains HTTP Request body" }, "payloads": { - "type": "object", + "$ref": "#/$defs/map[string]interface {}", "title": "payloads for the http request", "description": "Payloads contains any payloads for the current request" }, "headers": { - "additionalProperties": { - "type": "string" + "patternProperties": { + ".*": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "boolean" + } + ] + } }, - "type": "object", "title": "headers to send with the http request", "description": "Headers contains HTTP Headers to send with the request" }, @@ -521,7 +756,7 @@ }, "fuzzing": { "items": { - "$ref": "#/$defs/Rule" + "$ref": "#/$defs/fuzz.Rule" }, "type": "array", "title": "fuzzin rules for http fuzzing", @@ -531,7 +766,7 @@ "type": "boolean" }, "signature": { - "$ref": "#/$defs/SignatureTypeHolder", + "$ref": "#/$defs/http.SignatureTypeHolder", "title": "signature is the http request signature method", "description": "Signature is the HTTP Request signature Method" }, @@ -595,148 +830,666 @@ "title": "iterate all the values", "description": "Iterates all the values extracted from internal extractors" }, - "digest-username": { + "digest-username": { + "type": "string", + "title": "specifies the username for digest authentication", + "description": "Optional parameter which specifies the username for digest auth" + }, + "digest-password": { + "type": "string", + "title": "specifies the password for digest authentication", + "description": "Optional parameter which specifies the password for digest auth" + }, + "disable-path-automerge": { + "type": "boolean", + "title": "disable auto merging of path", + "description": "Disable merging target url path with raw request path" + }, + "pre-condition": { + "items": { + "$ref": "#/$defs/matchers.Matcher" + }, + "type": "array", + "title": "pre-condition for fuzzing/dast", + "description": "PreCondition is matcher-like field to check if fuzzing should be performed on this request or not" + }, + "pre-condition-operator": { + "type": "string", + "enum": [ + "and", + "or" + ], + "title": "condition between the filters", + "description": "Operator to use between multiple per-conditions" + } + }, + "additionalProperties": false, + "type": "object" + }, + "http.SignatureTypeHolder": { + "type": "string", + "enum": [ + "AWS" + ], + "title": "type of the signature", + "description": "Type of the signature" + }, + "javascript.Request": { + "properties": { + "matchers": { + "items": { + "$ref": "#/$defs/matchers.Matcher" + }, + "type": "array", + "title": "matchers to run on response", + "description": "Detection mechanism to identify whether the request was successful by doing pattern matching" + }, + "extractors": { + "items": { + "$ref": "#/$defs/extractors.Extractor" + }, + "type": "array", + "title": "extractors to run on response", + "description": "Extractors contains the extraction mechanism for the request to identify and extract parts of the response" + }, + "matchers-condition": { + "type": "string", + "enum": [ + "and", + "or" + ], + "title": "condition between the matchers", + "description": "Conditions between the matchers" + }, + "id": { + "type": "string", + "title": "id of the request", + "description": "ID is the optional ID of the Request" + }, + "init": { + "type": "string", + "title": "init javascript code", + "description": "Init is the javascript code to execute after compiling template" + }, + "pre-condition": { + "type": "string", + "title": "pre-condition for the request", + "description": "PreCondition is a condition which is evaluated before sending the request" + }, + "args": { + "$ref": "#/$defs/map[string]interface {}" + }, + "code": { + "type": "string", + "title": "code to execute in javascript", + "description": "Executes inline javascript code for the request" + }, + "timeout": { + "type": "integer", + "title": "timeout for javascript execution", + "description": "Timeout in seconds is optional timeout for entire javascript script execution" + }, + "stop-at-first-match": { + "type": "boolean", + "title": "stop at first match", + "description": "Stop the execution after a match is found" + }, + "attack": { + "$ref": "#/$defs/generators.AttackTypeHolder", + "title": "attack is the payload combination", + "description": "Attack is the type of payload combinations to perform" + }, + "threads": { + "type": "integer", + "title": "threads for sending requests", + "description": "Threads specifies number of threads to use sending requests. This enables Connection Pooling" + }, + "payloads": { + "$ref": "#/$defs/map[string]interface {}", + "title": "payloads for the webosocket request", + "description": "Payloads contains any payloads for the current request" + } + }, + "additionalProperties": false, + "type": "object" + }, + "map[string]interface {}": { + "type": "object" + }, + "map[string]string": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "matchers.Matcher": { + "properties": { + "type": { + "$ref": "#/$defs/matchers.MatcherTypeHolder", + "title": "type of matcher", + "description": "Type of the matcher" + }, + "condition": { + "type": "string", + "enum": [ + "and", + "or" + ], + "title": "condition between matcher variables", + "description": "Condition between the matcher variables" + }, + "part": { + "type": "string", + "title": "part of response to match", + "description": "Part of response to match data from" + }, + "negative": { + "type": "boolean", + "title": "negative specifies if match reversed", + "description": "Negative specifies if the match should be reversed. It will only match if the condition is not true" + }, + "name": { + "type": "string", + "title": "name of the matcher", + "description": "Name of the matcher" + }, + "status": { + "items": { + "type": "integer" + }, + "type": "array", + "title": "status to match", + "description": "Status to match for the response" + }, + "size": { + "items": { + "type": "integer" + }, + "type": "array", + "title": "acceptable size for response", + "description": "Size is the acceptable size for the response" + }, + "words": { + "items": { + "type": "string" + }, + "type": "array", + "title": "words to match in response", + "description": " Words contains word patterns required to be present in the response part" + }, + "regex": { + "items": { + "type": "string" + }, + "type": "array", + "title": "regex to match in response", + "description": "Regex contains regex patterns required to be present in the response part" + }, + "binary": { + "items": { + "type": "string" + }, + "type": "array", + "title": "binary patterns to match in response", + "description": "Binary are the binary patterns required to be present in the response part" + }, + "dsl": { + "items": { + "type": "string" + }, + "type": "array", + "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": { + "type": "string", + "enum": [ + "hex" + ], + "title": "encoding for word field", + "description": "Optional encoding for the word fields" + }, + "case-insensitive": { + "type": "boolean", + "title": "use case insensitive match", + "description": "use case insensitive match" + }, + "match-all": { + "type": "boolean", + "title": "match all values", + "description": "match all matcher values ignoring condition" + }, + "internal": { + "type": "boolean", + "title": "hide matcher from output", + "description": "hide matcher from output" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "type" + ] + }, + "matchers.MatcherTypeHolder": { + "type": "string", + "enum": [ + "word", + "regex", + "binary", + "status", + "size", + "dsl", + "xpath" + ], + "title": "type of the matcher", + "description": "Type of the matcher" + }, + "model.Classification": { + "properties": { + "cve-id": { + "$ref": "#/$defs/stringslice.StringOrSlice", + "title": "cve ids for the template", + "description": "CVE IDs for the template" + }, + "cwe-id": { + "$ref": "#/$defs/stringslice.StringOrSlice", + "title": "cwe ids for the template", + "description": "CWE IDs for the template" + }, + "cvss-metrics": { + "type": "string", + "title": "cvss metrics for the template", + "description": "CVSS Metrics for the template", + "examples": [ + "3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" + ] + }, + "cvss-score": { + "type": "number", + "title": "cvss score for the template", + "description": "CVSS Score for the template", + "examples": [ + 9.8 + ] + }, + "epss-score": { + "type": "number", + "title": "epss score for the template", + "description": "EPSS Score for the template", + "examples": [ + 0.42509 + ] + }, + "epss-percentile": { + "type": "number", + "title": "epss percentile for the template", + "description": "EPSS Percentile for the template", + "examples": [ + 0.42509 + ] + }, + "cpe": { + "type": "string", + "title": "cpe for the template", + "description": "CPE for the template", + "examples": [ + "cpe:/a:vendor:product:version" + ] + } + }, + "additionalProperties": false, + "type": "object" + }, + "model.Info": { + "properties": { + "name": { + "type": "string", + "title": "name of the template", + "description": "Name is a short summary of what the template does", + "examples": [ + "Nagios Default Credentials Check" + ] + }, + "author": { + "$ref": "#/$defs/stringslice.StringOrSlice", + "oneOf": [ + { + "type": "string", + "examples": [ + "pdteam" + ] + }, + { + "type": "array", + "examples": [ + "pdteam,mr.robot" + ] + } + ], + "title": "author of the template", + "description": "Author is the author of the template" + }, + "tags": { + "$ref": "#/$defs/stringslice.StringOrSlice", + "title": "tags of the template", + "description": "Any tags for the template" + }, + "description": { + "type": "string", + "title": "description of the template", + "description": "In-depth explanation on what the template does", + "examples": [ + "Bower is a package manager which stores package information in the bower.json file" + ] + }, + "impact": { + "type": "string", + "title": "impact of the template", + "description": "In-depth explanation on the impact of the issue found by the template", + "examples": [ + "Successful exploitation of this vulnerability could allow an attacker to execute arbitrary SQL queries" + ] + }, + "reference": { + "$ref": "#/$defs/stringslice.StringOrSlice", + "title": "references for the template", + "description": "Links relevant to the template" + }, + "severity": { + "$ref": "#/$defs/severity.Holder" + }, + "metadata": { + "$ref": "#/$defs/map[string]interface {}", + "type": "object", + "title": "additional metadata for the template", + "description": "Additional metadata fields for the template" + }, + "classification": { + "$ref": "#/$defs/model.Classification", + "type": "object", + "title": "classification info for the template", + "description": "Classification information for the template" + }, + "remediation": { + "type": "string", + "title": "remediation steps for the template", + "description": "In-depth explanation on how to fix the issues found by the template", + "examples": [ + "Change the default administrative username and password of Apache ActiveMQ by editing the file jetty-realm.properties" + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "name", + "author" + ] + }, + "network.Input": { + "properties": { + "data": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ], + "title": "data to send as input", + "description": "Data is the data to send as the input" + }, + "type": { + "$ref": "#/$defs/network.NetworkInputTypeHolder", + "title": "type is the type of input data", + "description": "Type of input specified in data field" + }, + "read": { + "type": "integer", + "title": "bytes to read from socket", + "description": "Number of bytes to read from socket" + }, + "name": { + "type": "string", + "title": "optional name for data read", + "description": "Optional name of the data read to provide matching on" + } + }, + "additionalProperties": false, + "type": "object" + }, + "network.NetworkInputTypeHolder": { + "type": "string", + "enum": [ + "hex", + "text" + ], + "title": "type is the type of input data", + "description": "description=Type of input specified in data field" + }, + "network.Request": { + "properties": { + "id": { + "type": "string", + "title": "id of the request", + "description": "ID of the network request" + }, + "host": { + "items": { + "type": "string" + }, + "type": "array", + "title": "host to send requests to", + "description": "Host to send network requests to" + }, + "attack": { + "$ref": "#/$defs/generators.AttackTypeHolder", + "title": "attack is the payload combination", + "description": "Attack is the type of payload combinations to perform" + }, + "payloads": { + "$ref": "#/$defs/map[string]interface {}", + "title": "payloads for the network request", + "description": "Payloads contains any payloads for the current request" + }, + "threads": { + "type": "integer", + "title": "threads for sending requests", + "description": "Threads specifies number of threads to use sending requests. This enables Connection Pooling" + }, + "inputs": { + "items": { + "$ref": "#/$defs/network.Input" + }, + "type": "array", + "title": "inputs for the network request", + "description": "Inputs contains any input/output for the current request" + }, + "port": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ], + "title": "port to send requests to", + "description": "Port to send network requests to" + }, + "exclude-ports": { "type": "string", - "title": "specifies the username for digest authentication", - "description": "Optional parameter which specifies the username for digest auth" + "title": "exclude ports from being scanned", + "description": "Exclude ports from being scanned" }, - "digest-password": { - "type": "string", - "title": "specifies the password for digest authentication", - "description": "Optional parameter which specifies the password for digest auth" + "read-size": { + "type": "integer", + "title": "size of network response to read", + "description": "Size of response to read at the end. Default is 1024 bytes" }, - "disable-path-automerge": { + "read-all": { "type": "boolean", - "title": "disable auto merging of path", - "description": "Disable merging target url path with raw request path" + "title": "read all response stream", + "description": "Read all response stream till the server stops sending" }, - "pre-condition": { + "matchers": { "items": { - "$ref": "#/$defs/Matcher" + "$ref": "#/$defs/matchers.Matcher" }, "type": "array", - "title": "pre-condition for fuzzing/dast", - "description": "PreCondition is matcher-like field to check if fuzzing should be performed on this request or not" + "title": "matchers to run on response", + "description": "Detection mechanism to identify whether the request was successful by doing pattern matching" }, - "pre-condition-operator": { + "extractors": { + "items": { + "$ref": "#/$defs/extractors.Extractor" + }, + "type": "array", + "title": "extractors to run on response", + "description": "Extractors contains the extraction mechanism for the request to identify and extract parts of the response" + }, + "matchers-condition": { "type": "string", "enum": [ "and", "or" ], - "title": "condition between the filters", - "description": "Operator to use between multiple per-conditions" + "title": "condition between the matchers", + "description": "Conditions between the matchers" } }, "additionalProperties": false, "type": "object" }, - "Rule": { + "severity.Holder": { + "type": "string", + "enum": [ + "info", + "low", + "medium", + "high", + "critical", + "unknown" + ], + "title": "severity of the template", + "description": "Seriousness of the implications of the template" + }, + "ssl.Request": { "properties": { - "type": { + "matchers": { + "items": { + "$ref": "#/$defs/matchers.Matcher" + }, + "type": "array", + "title": "matchers to run on response", + "description": "Detection mechanism to identify whether the request was successful by doing pattern matching" + }, + "extractors": { + "items": { + "$ref": "#/$defs/extractors.Extractor" + }, + "type": "array", + "title": "extractors to run on response", + "description": "Extractors contains the extraction mechanism for the request to identify and extract parts of the response" + }, + "matchers-condition": { "type": "string", "enum": [ - "replace", - "prefix", - "postfix", - "infix", - "replace-regex" + "and", + "or" ], - "title": "type of rule", - "description": "Type of fuzzing rule to perform" + "title": "condition between the matchers", + "description": "Conditions between the matchers" }, - "part": { + "id": { + "type": "string", + "title": "id of the request", + "description": "ID of the request" + }, + "address": { + "type": "string", + "title": "address for the ssl request", + "description": "Address contains address for the request" + }, + "min_version": { "type": "string", "enum": [ - "query", - "header", - "path", - "body", - "cookie", - "request" + "sslv3", + "tls10", + "tls11", + "tls12", + "tls13" ], - "title": "part of rule", - "description": "Part of request rule to fuzz" + "title": "Min. TLS version", + "description": "Minimum tls version - automatic if not specified." }, - "mode": { + "max_version": { "type": "string", "enum": [ - "single", - "multiple" + "sslv3", + "tls10", + "tls11", + "tls12", + "tls13" ], - "title": "mode of rule", - "description": "Mode of request rule to fuzz" + "title": "Max. TLS version", + "description": "Max tls version - automatic if not specified." }, - "keys": { + "cipher_suites": { "items": { "type": "string" }, - "type": "array", - "title": "keys of parameters to fuzz", - "description": "Keys of parameters to fuzz" + "type": "array" }, - "keys-regex": { - "items": { - "type": "string" - }, - "type": "array", - "title": "keys regex to fuzz", - "description": "Regex of parameter keys to fuzz" + "scan_mode": { + "type": "string", + "enum": [ + "ctls", + "ztls", + "auto" + ], + "title": "Scan Mode", + "description": "Scan Mode - auto if not specified." }, - "values": { - "items": { - "type": "string" - }, - "type": "array", - "title": "values regex to fuzz", - "description": "Regex of parameter values to fuzz" + "tls_version_enum": { + "type": "boolean", + "title": "Enumerate Versions", + "description": "Enumerate Version - false if not specified" }, - "fuzz": { - "$ref": "#/$defs/SliceOrMapSlice", - "title": "payloads of fuzz rule", - "description": "Payloads to perform fuzzing substitutions with" + "tls_cipher_enum": { + "type": "boolean", + "title": "Enumerate Ciphers", + "description": "Enumerate Ciphers - false if not specified" }, - "replace-regex": { - "type": "string", - "title": "replace regex of rule", - "description": "Regex for regex-replace rule type" - } - }, - "additionalProperties": false, - "type": "object" - }, - "SignatureTypeHolder": { - "properties": { - "Value": { - "type": "integer" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "Value" - ] - }, - "SliceOrMapSlice": { - "properties": { - "Value": { + "tls_cipher_types": { "items": { - "type": "string" + "type": "string", + "enum": [ + "weak", + "secure", + "insecure", + "all" + ] }, - "type": "array" - }, - "KV": { - "$ref": "#/$defs/OrderedMap[string,string]" + "type": "array", + "title": "TLS Cipher Types", + "description": "TLS Cipher Types to enumerate" } }, "additionalProperties": false, - "type": "object", - "required": [ - "Value", - "KV" - ] + "type": "object" }, - "StringOrSlice": { + "stringslice.StringOrSlice": { "oneOf": [ { "type": "string" @@ -746,7 +1499,7 @@ } ] }, - "Template": { + "templates.Template": { "properties": { "id": { "type": "string", @@ -758,7 +1511,7 @@ ] }, "info": { - "$ref": "#/$defs/Info", + "$ref": "#/$defs/model.Info", "type": "object", "title": "info for the template", "description": "Info contains metadata for the template" @@ -773,7 +1526,7 @@ }, "requests": { "items": { - "$ref": "#/$defs/Request" + "$ref": "#/$defs/http.Request" }, "type": "array", "title": "http requests to make", @@ -781,7 +1534,7 @@ }, "http": { "items": { - "$ref": "#/$defs/Request" + "$ref": "#/$defs/http.Request" }, "type": "array", "title": "http requests to make", @@ -789,7 +1542,7 @@ }, "dns": { "items": { - "$ref": "#/$defs/Request" + "$ref": "#/$defs/dns.Request" }, "type": "array", "title": "dns requests to make", @@ -797,7 +1550,7 @@ }, "file": { "items": { - "$ref": "#/$defs/Request" + "$ref": "#/$defs/file.Request" }, "type": "array", "title": "file requests to make", @@ -805,7 +1558,7 @@ }, "network": { "items": { - "$ref": "#/$defs/Request" + "$ref": "#/$defs/network.Request" }, "type": "array", "title": "network requests to make", @@ -813,7 +1566,7 @@ }, "tcp": { "items": { - "$ref": "#/$defs/Request" + "$ref": "#/$defs/network.Request" }, "type": "array", "title": "network(tcp) requests to make", @@ -821,7 +1574,7 @@ }, "headless": { "items": { - "$ref": "#/$defs/Request" + "$ref": "#/$defs/headless.Request" }, "type": "array", "title": "headless requests to make", @@ -829,7 +1582,7 @@ }, "ssl": { "items": { - "$ref": "#/$defs/Request" + "$ref": "#/$defs/ssl.Request" }, "type": "array", "title": "ssl requests to make", @@ -837,7 +1590,7 @@ }, "websocket": { "items": { - "$ref": "#/$defs/Request" + "$ref": "#/$defs/websocket.Request" }, "type": "array", "title": "websocket requests to make", @@ -845,7 +1598,7 @@ }, "whois": { "items": { - "$ref": "#/$defs/Request" + "$ref": "#/$defs/whois.Request" }, "type": "array", "title": "whois requests to make", @@ -853,7 +1606,7 @@ }, "code": { "items": { - "$ref": "#/$defs/Request" + "$ref": "#/$defs/code.Request" }, "type": "array", "title": "code snippets to make", @@ -861,7 +1614,7 @@ }, "javascript": { "items": { - "$ref": "#/$defs/Request" + "$ref": "#/$defs/javascript.Request" }, "type": "array", "title": "javascript requests to make", @@ -869,7 +1622,7 @@ }, "workflows": { "items": { - "$ref": "#/$defs/WorkflowTemplate" + "$ref": "#/$defs/workflows.WorkflowTemplate" }, "type": "array", "title": "list of workflows to execute", @@ -886,17 +1639,18 @@ "description": "Stop at first match for the template" }, "signature": { - "$ref": "#/$defs/SignatureTypeHolder", + "$ref": "#/$defs/http.SignatureTypeHolder", "title": "signature is the http request signature method", "description": "Signature is the HTTP Request signature Method" }, "variables": { - "$ref": "#/$defs/Variable", + "$ref": "#/$defs/variables.Variable", "type": "object", "title": "variables for the http request", "description": "Variables contains any variables for the current request" }, "constants": { + "$ref": "#/$defs/map[string]interface {}", "type": "object", "title": "constant for the template", "description": "constants contains any constant for the template" @@ -909,12 +1663,177 @@ "info" ] }, - "Variable": { - "properties": {}, + "userAgent.UserAgentHolder": { + "type": "string", + "enum": [ + "off", + "default", + "custom" + ], + "title": "userAgent for the headless", + "description": "userAgent for the headless http request" + }, + "variables.Variable": { + "additionalProperties": true, + "type": "object", + "title": "variables for the request", + "description": "Additional variables for the request" + }, + "websocket.Input": { + "properties": { + "data": { + "type": "string", + "title": "data to send as input", + "description": "Data is the data to send as the input" + }, + "name": { + "type": "string", + "title": "optional name for data read", + "description": "Optional name of the data read to provide matching on" + } + }, + "additionalProperties": false, + "type": "object" + }, + "websocket.Request": { + "properties": { + "matchers": { + "items": { + "$ref": "#/$defs/matchers.Matcher" + }, + "type": "array", + "title": "matchers to run on response", + "description": "Detection mechanism to identify whether the request was successful by doing pattern matching" + }, + "extractors": { + "items": { + "$ref": "#/$defs/extractors.Extractor" + }, + "type": "array", + "title": "extractors to run on response", + "description": "Extractors contains the extraction mechanism for the request to identify and extract parts of the response" + }, + "matchers-condition": { + "type": "string", + "enum": [ + "and", + "or" + ], + "title": "condition between the matchers", + "description": "Conditions between the matchers" + }, + "id": { + "type": "string", + "title": "id of the request", + "description": "ID of the network request" + }, + "address": { + "type": "string", + "title": "address for the websocket request", + "description": "Address contains address for the request" + }, + "inputs": { + "items": { + "$ref": "#/$defs/websocket.Input" + }, + "type": "array", + "title": "inputs for the websocket request", + "description": "Inputs contains any input/output for the current request" + }, + "headers": { + "$ref": "#/$defs/map[string]string", + "title": "headers contains the request headers", + "description": "Headers contains headers for the request" + }, + "attack": { + "$ref": "#/$defs/generators.AttackTypeHolder", + "title": "attack is the payload combination", + "description": "Attack is the type of payload combinations to perform" + }, + "payloads": { + "$ref": "#/$defs/map[string]interface {}", + "title": "payloads for the websocket request", + "description": "Payloads contains any payloads for the current request" + } + }, + "additionalProperties": false, + "type": "object" + }, + "whois.Request": { + "properties": { + "matchers": { + "items": { + "$ref": "#/$defs/matchers.Matcher" + }, + "type": "array", + "title": "matchers to run on response", + "description": "Detection mechanism to identify whether the request was successful by doing pattern matching" + }, + "extractors": { + "items": { + "$ref": "#/$defs/extractors.Extractor" + }, + "type": "array", + "title": "extractors to run on response", + "description": "Extractors contains the extraction mechanism for the request to identify and extract parts of the response" + }, + "matchers-condition": { + "type": "string", + "enum": [ + "and", + "or" + ], + "title": "condition between the matchers", + "description": "Conditions between the matchers" + }, + "id": { + "type": "string", + "title": "id of the request", + "description": "ID of the network request" + }, + "query": { + "type": "string", + "title": "query for the WHOIS request", + "description": "Query contains query for the request" + }, + "server": { + "type": "string", + "title": "server url to execute the WHOIS request on", + "description": "Server contains the server url to execute the WHOIS request on" + } + }, + "additionalProperties": false, + "type": "object" + }, + "workflows.Matcher": { + "properties": { + "name": { + "$ref": "#/$defs/stringslice.StringOrSlice", + "title": "name of items to match", + "description": "Name of items to match" + }, + "condition": { + "type": "string", + "enum": [ + "and", + "or" + ], + "title": "condition between names", + "description": "Condition between the names" + }, + "subtemplates": { + "items": { + "$ref": "#/$defs/workflows.WorkflowTemplate" + }, + "type": "array", + "title": "templates to run after match", + "description": "Templates to run after match" + } + }, "additionalProperties": false, "type": "object" }, - "WorkflowTemplate": { + "workflows.WorkflowTemplate": { "properties": { "template": { "type": "string", @@ -922,13 +1841,13 @@ "description": "Template or directory to execute as part of workflow" }, "tags": { - "$ref": "#/$defs/StringOrSlice", + "$ref": "#/$defs/stringslice.StringOrSlice", "title": "tags to execute", "description": "Tags to run template based on" }, "matchers": { "items": { - "$ref": "#/$defs/Matcher" + "$ref": "#/$defs/workflows.Matcher" }, "type": "array", "title": "name based template result matchers", @@ -936,7 +1855,7 @@ }, "subtemplates": { "items": { - "$ref": "#/$defs/WorkflowTemplate" + "$ref": "#/$defs/workflows.WorkflowTemplate" }, "type": "array", "title": "subtemplate based result matchers", diff --git a/pkg/catalog/config/constants.go b/pkg/catalog/config/constants.go index 9c1bacdca9..3dca0be63b 100644 --- a/pkg/catalog/config/constants.go +++ b/pkg/catalog/config/constants.go @@ -31,7 +31,7 @@ const ( CLIConfigFileName = "config.yaml" ReportingConfigFilename = "reporting-config.yaml" // Version is the current version of nuclei - Version = `v3.2.3` + Version = `v3.2.4` // Directory Names of custom templates CustomS3TemplatesDirName = "s3" CustomGitHubTemplatesDirName = "github" diff --git a/pkg/fuzz/dataformat/form.go b/pkg/fuzz/dataformat/form.go index ab335299f7..a8e59c0b4d 100644 --- a/pkg/fuzz/dataformat/form.go +++ b/pkg/fuzz/dataformat/form.go @@ -94,18 +94,33 @@ func (f *Form) Encode(data KV) (string, error) { } } } - data := make([]string, maxIndex+1) // Ensure the slice is large enough - for key, value := range v { - matches := reNormalized.FindStringSubmatch(key) - if len(matches) == 2 { - dataIdx, _ := strconv.Atoi(matches[1]) // Error already checked above - data[dataIdx-1] = value // Use dataIdx-1 since slice is 0-indexed + if maxIndex >= 0 { // Ensure the slice is only created if maxIndex is valid + data := make([]string, maxIndex+1) // Ensure the slice is large enough + for key, value := range v { + matches := reNormalized.FindStringSubmatch(key) + if len(matches) == 2 { + dataIdx, err := strconv.Atoi(matches[1]) // Error already checked above + if err != nil { + gologger.Verbose().Msgf("error converting data index to integer: %v", err) + continue + } + // Validate dataIdx to avoid index out of range errors + if dataIdx > 0 && dataIdx <= len(data) { + data[dataIdx-1] = value // Use dataIdx-1 since slice is 0-indexed + } else { + gologger.Verbose().Msgf("data index out of range: %d", dataIdx) + } + } + } + if len(params.Get(k)) > 0 { + data[maxIndex] = fmt.Sprint(params.Get(k)) // Use maxIndex which is the last index + } + // remove existing + params.Del(k) + if len(data) > 0 { + params.Add(k, data...) } } - data[maxIndex] = fmt.Sprint(params.Get(k)) // Use maxIndex which is the last index - // remove existing - params.Del(k) - params.Add(k, data...) } } diff --git a/pkg/fuzz/execute.go b/pkg/fuzz/execute.go index 6e1a7a6a23..c8054bf80b 100644 --- a/pkg/fuzz/execute.go +++ b/pkg/fuzz/execute.go @@ -11,6 +11,7 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/fuzz/component" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" "github.com/projectdiscovery/retryablehttp-go" errorutil "github.com/projectdiscovery/utils/errors" @@ -100,8 +101,11 @@ func (rule *Rule) Execute(input *ExecuteRuleInput) (err error) { baseValues := input.Values if rule.generator == nil { for _, component := range finalComponentList { + // get vars from variables while replacing interactsh urls evaluatedValues, interactURLs := rule.options.Variables.EvaluateWithInteractsh(baseValues, rule.options.Interactsh) - input.Values = generators.MergeMaps(evaluatedValues, baseValues, rule.options.Constants) + input.Values = generators.MergeMaps(evaluatedValues, baseValues, rule.options.Options.Vars.AsMap(), rule.options.Constants) + // evaluate all vars with interactsh + input.Values, interactURLs = rule.evaluateVarsWithInteractsh(input.Values, interactURLs) input.InteractURLs = interactURLs err := rule.executeRuleValues(input, component) if err != nil { @@ -118,9 +122,12 @@ mainLoop: if !next { continue mainLoop } + // get vars from variables while replacing interactsh urls evaluatedValues, interactURLs := rule.options.Variables.EvaluateWithInteractsh(generators.MergeMaps(values, baseValues), rule.options.Interactsh) + input.Values = generators.MergeMaps(values, evaluatedValues, baseValues, rule.options.Options.Vars.AsMap(), rule.options.Constants) + // evaluate all vars with interactsh + input.Values, interactURLs = rule.evaluateVarsWithInteractsh(input.Values, interactURLs) input.InteractURLs = interactURLs - input.Values = generators.MergeMaps(values, evaluatedValues, baseValues, rule.options.Constants) if err := rule.executeRuleValues(input, component); err != nil { if err == io.EOF { @@ -134,6 +141,33 @@ mainLoop: return nil } +// evaluateVarsWithInteractsh evaluates the variables with Interactsh URLs and updates them accordingly. +func (rule *Rule) evaluateVarsWithInteractsh(data map[string]interface{}, interactshUrls []string) (map[string]interface{}, []string) { + // Check if Interactsh options are configured + if rule.options.Interactsh != nil { + // Iterate through the data to replace and evaluate variables with Interactsh URLs + for k, v := range data { + // Replace variables with Interactsh URLs and collect new URLs + got, oastUrls := rule.options.Interactsh.Replace(fmt.Sprint(v), interactshUrls) + + // Append new OAST URLs if any + if len(oastUrls) > 0 { + interactshUrls = append(interactshUrls, oastUrls...) + } + // Evaluate the replaced data + evaluatedData, err := expressions.Evaluate(got, data) + if err == nil { + // Update the data if there is a change after evaluation + if evaluatedData != got { + data[k] = evaluatedData + } + } + } + } + // Return the updated data and Interactsh URLs without any error + return data, interactshUrls +} + // isInputURLValid returns true if url is valid after parsing it func (rule *Rule) isInputURLValid(input *contextargs.Context) bool { if input == nil || input.MetaInput == nil || input.MetaInput.Input == "" { diff --git a/pkg/fuzz/type.go b/pkg/fuzz/type.go index 6110b2cdfd..b8f6f3beef 100644 --- a/pkg/fuzz/type.go +++ b/pkg/fuzz/type.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" + "github.com/invopop/jsonschema" mapsutil "github.com/projectdiscovery/utils/maps" "gopkg.in/yaml.v2" ) @@ -29,6 +30,44 @@ type SliceOrMapSlice struct { KV *mapsutil.OrderedMap[string, string] } +func (v SliceOrMapSlice) JSONSchemaExtend(schema *jsonschema.Schema) *jsonschema.Schema { + schema = &jsonschema.Schema{ + Title: schema.Title, + Description: schema.Description, + Type: "array", + Items: &jsonschema.Schema{ + OneOf: []*jsonschema.Schema{ + { + Type: "string", + }, + { + Type: "object", + }, + }, + }, + } + return schema +} + +func (v SliceOrMapSlice) JSONSchema() *jsonschema.Schema { + gotType := &jsonschema.Schema{ + Title: "Payloads of Fuzz Rule", + Description: "Payloads to perform fuzzing substitutions with.", + Type: "array", + Items: &jsonschema.Schema{ + OneOf: []*jsonschema.Schema{ + { + Type: "string", + }, + { + Type: "object", + }, + }, + }, + } + return gotType +} + // UnmarshalJSON implements json.Unmarshaler interface. func (v *SliceOrMapSlice) UnmarshalJSON(data []byte) error { // try to unmashal as a string and fallback to map diff --git a/pkg/js/generated/ts/kerberos.ts b/pkg/js/generated/ts/kerberos.ts index a142e536bb..8f9ce98f99 100755 --- a/pkg/js/generated/ts/kerberos.ts +++ b/pkg/js/generated/ts/kerberos.ts @@ -200,9 +200,9 @@ export interface AuthorizationDataEntry { */ export interface BitString { - Bytes?: Uint8Array, - BitLength?: number, + + Bytes?: Uint8Array, } @@ -212,9 +212,9 @@ export interface BitString { */ export interface BitString { - Bytes?: Uint8Array, - BitLength?: number, + + Bytes?: Uint8Array, } @@ -236,17 +236,15 @@ export interface Config { */ export interface EncTicketPart { - RenewTill?: Date, - - CRealm?: string, - AuthTime?: Date, StartTime?: Date, EndTime?: Date, - Transited?: TransitedEncoding, + RenewTill?: Date, + + CRealm?: string, CAddr?: HostAddress, @@ -257,6 +255,8 @@ export interface EncTicketPart { Key?: EncryptionKey, CName?: PrincipalName, + + Transited?: TransitedEncoding, } @@ -266,11 +266,11 @@ export interface EncTicketPart { */ export interface EncryptedData { + KVNO?: number, + Cipher?: Uint8Array, EType?: number, - - KVNO?: number, } @@ -318,67 +318,73 @@ export interface HostAddress { */ export interface LibDefaults { - /** - * time in nanoseconds - */ + NoAddresses?: boolean, - Clockskew?: number, + RealmTryDomains?: number, - KDCTimeSync?: number, + DNSLookupKDC?: boolean, + + DefaultRealm?: string, SafeChecksumType?: number, - /** - * time in nanoseconds - */ + VerifyAPReqNofail?: boolean, - TicketLifetime?: number, + AllowWeakCrypto?: boolean, - Forwardable?: boolean, + DefaultTGSEnctypes?: string[], - K5LoginAuthoritative?: boolean, + DefaultTktEnctypeIDs?: number[], - AllowWeakCrypto?: boolean, + IgnoreAcceptorHostname?: boolean, - DefaultClientKeytabName?: string, + K5LoginAuthoritative?: boolean, - DefaultTktEnctypes?: string[], + PermittedEnctypes?: string[], - ExtraAddresses?: Uint8Array, + /** + * time in nanoseconds + */ - K5LoginDirectory?: string, + Clockskew?: number, - PreferredPreauthTypes?: number[], + DNSCanonicalizeHostname?: boolean, + + Proxiable?: boolean, RDNS?: boolean, - DefaultKeytabName?: string, + /** + * time in nanoseconds + */ - DefaultRealm?: string, + TicketLifetime?: number, - DefaultTGSEnctypeIDs?: number[], + DefaultClientKeytabName?: string, - DNSCanonicalizeHostname?: boolean, + PermittedEnctypeIDs?: number[], - PermittedEnctypes?: string[], + UDPPreferenceLimit?: number, - VerifyAPReqNofail?: boolean, + DefaultTGSEnctypeIDs?: number[], - DNSLookupRealm?: boolean, + DefaultTktEnctypes?: string[], - UDPPreferenceLimit?: number, + CCacheType?: number, - Canonicalize?: boolean, + DNSLookupRealm?: boolean, - CCacheType?: number, + ExtraAddresses?: Uint8Array, - DefaultTGSEnctypes?: string[], + PreferredPreauthTypes?: number[], - Proxiable?: boolean, + Canonicalize?: boolean, - DNSLookupKDC?: boolean, + Forwardable?: boolean, - RealmTryDomains?: number, + K5LoginDirectory?: string, + + KDCTimeSync?: number, /** * time in nanoseconds @@ -386,13 +392,7 @@ export interface LibDefaults { RenewLifetime?: number, - DefaultTktEnctypeIDs?: number[], - - IgnoreAcceptorHostname?: boolean, - - NoAddresses?: boolean, - - PermittedEnctypeIDs?: number[], + DefaultKeytabName?: string, KDCDefaultOptions?: BitString, } @@ -404,9 +404,9 @@ export interface LibDefaults { */ export interface PrincipalName { - NameType?: number, - NameString?: string[], + + NameType?: number, } @@ -416,8 +416,6 @@ export interface PrincipalName { */ export interface Realm { - Realm?: string, - AdminServer?: string[], DefaultDomain?: string, @@ -427,6 +425,8 @@ export interface Realm { KPasswdServer?: string[], MasterKDC?: string[], + + Realm?: string, } @@ -450,10 +450,10 @@ export interface TGS { */ export interface Ticket { - Realm?: string, - TktVNO?: number, + Realm?: string, + SName?: PrincipalName, EncPart?: EncryptedData, diff --git a/pkg/js/generated/ts/ldap.ts b/pkg/js/generated/ts/ldap.ts index 5d25faa1c8..daca4f86b9 100755 --- a/pkg/js/generated/ts/ldap.ts +++ b/pkg/js/generated/ts/ldap.ts @@ -358,6 +358,22 @@ export class Client { } + /** + * GetADUserAsRepRoastable returns all AD users that are AsRepRoastable + * using FilterIsPerson, and FilterDontRequirePreauth filter query + * @example + * ```javascript + * const ldap = require('nuclei/ldap'); + * const client = new ldap.Client('ldap://ldap.example.com', 'acme.com'); + * const AsRepRoastable = client.GetADUserAsRepRoastable(); + * log(to_json(AsRepRoastable)); + * ``` + */ + public GetADUserAsRepRoastable(): ADObject[] { + return []; + } + + /** * GetADDomainSID returns the SID of the AD domain * @example diff --git a/pkg/js/generated/ts/mysql.ts b/pkg/js/generated/ts/mysql.ts index a8c331cde0..bf5c881241 100755 --- a/pkg/js/generated/ts/mysql.ts +++ b/pkg/js/generated/ts/mysql.ts @@ -209,9 +209,9 @@ export interface MySQLOptions { */ export interface SQLResult { - Columns?: string[], - Count?: number, + + Columns?: string[], } diff --git a/pkg/js/generated/ts/smb.ts b/pkg/js/generated/ts/smb.ts index b2bcb08f95..b0ca997ac5 100755 --- a/pkg/js/generated/ts/smb.ts +++ b/pkg/js/generated/ts/smb.ts @@ -137,10 +137,6 @@ export interface NegotiationLog { */ export interface SMBCapabilities { - DFSSupport?: boolean, - - Leasing?: boolean, - LargeMTU?: boolean, MultiChan?: boolean, @@ -150,6 +146,10 @@ export interface SMBCapabilities { DirLeasing?: boolean, Encryption?: boolean, + + DFSSupport?: boolean, + + Leasing?: boolean, } @@ -159,6 +159,8 @@ export interface SMBCapabilities { */ export interface SMBLog { + SupportV1?: boolean, + NativeOs?: string, NTLM?: string, @@ -167,15 +169,13 @@ export interface SMBLog { HasNTLM?: boolean, - SupportV1?: boolean, + Version?: SMBVersions, Capabilities?: SMBCapabilities, NegotiationLog?: NegotiationLog, SessionSetupLog?: SessionSetupLog, - - Version?: SMBVersions, } @@ -185,13 +185,13 @@ export interface SMBLog { */ export interface SMBVersions { + VerString?: string, + Major?: number, Minor?: number, Revision?: number, - - VerString?: string, } @@ -201,10 +201,6 @@ export interface SMBVersions { */ export interface ServiceSMB { - SigningEnabled?: boolean, - - SigningRequired?: boolean, - OSVersion?: string, NetBIOSComputerName?: string, @@ -216,6 +212,10 @@ export interface ServiceSMB { DNSDomainName?: string, ForestName?: string, + + SigningEnabled?: boolean, + + SigningRequired?: boolean, } @@ -225,12 +225,12 @@ export interface ServiceSMB { */ export interface SessionSetupLog { + NegotiateFlags?: number, + SetupFlags?: number, TargetName?: string, - NegotiateFlags?: number, - HeaderLog?: HeaderLog, } diff --git a/pkg/js/generated/ts/ssh.ts b/pkg/js/generated/ts/ssh.ts index 37d04ae670..21dacf4054 100755 --- a/pkg/js/generated/ts/ssh.ts +++ b/pkg/js/generated/ts/ssh.ts @@ -133,9 +133,9 @@ export interface Algorithms { HostKey?: string, - W?: DirectionAlgorithms, - R?: DirectionAlgorithms, + + W?: DirectionAlgorithms, } @@ -197,33 +197,33 @@ export interface HandshakeLog { */ export interface KexInitMsg { - CiphersServerClient?: string[], - - MACsClientServer?: string[], - - MACsServerClient?: string[], + Reserved?: number, - LanguagesClientServer?: string[], + /** + * fixed size array of length: [16] + */ - KexAlgos?: string[], + Cookie?: Uint8Array, CiphersClientServer?: string[], - Reserved?: number, + MACsClientServer?: string[], - CompressionClientServer?: string[], + MACsServerClient?: string[], CompressionServerClient?: string[], - LanguagesServerClient?: string[], + LanguagesClientServer?: string[], FirstKexFollows?: boolean, - /** - * fixed size array of length: [16] - */ + KexAlgos?: string[], - Cookie?: Uint8Array, + CiphersServerClient?: string[], + + CompressionClientServer?: string[], + + LanguagesServerClient?: string[], ServerHostKeyAlgos?: string[], } diff --git a/pkg/js/libs/ldap/adenum.go b/pkg/js/libs/ldap/adenum.go index 23d2a4fc11..9aea98be97 100644 --- a/pkg/js/libs/ldap/adenum.go +++ b/pkg/js/libs/ldap/adenum.go @@ -253,6 +253,19 @@ func (c *Client) GetADUserKerberoastable() []ADObject { return c.FindADObjects(JoinFilters(FilterIsPerson, FilterAccountEnabled, FilterHasServicePrincipalName)) } +// GetADUserAsRepRoastable returns all AD users that are AsRepRoastable +// using FilterIsPerson, and FilterDontRequirePreauth filter query +// @example +// ```javascript +// const ldap = require('nuclei/ldap'); +// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com'); +// const AsRepRoastable = client.GetADUserAsRepRoastable(); +// log(to_json(AsRepRoastable)); +// ``` +func (c *Client) GetADUserAsRepRoastable() []ADObject { + return c.FindADObjects(JoinFilters(FilterIsPerson, FilterDontRequirePreauth)) +} + // GetADDomainSID returns the SID of the AD domain // @example // ```javascript diff --git a/pkg/model/types/userAgent/user_agent.go b/pkg/model/types/userAgent/user_agent.go index 4a187d06a1..4554d6b241 100644 --- a/pkg/model/types/userAgent/user_agent.go +++ b/pkg/model/types/userAgent/user_agent.go @@ -61,7 +61,7 @@ type UserAgentHolder struct { Value UserAgent `mapping:"true"` } -func (userAgentHolder UserAgentHolder) JSONSchemaType() *jsonschema.Schema { +func (userAgentHolder UserAgentHolder) JSONSchema() *jsonschema.Schema { gotType := &jsonschema.Schema{ Type: "string", Title: "userAgent for the headless", diff --git a/pkg/operators/extractors/extractor_types.go b/pkg/operators/extractors/extractor_types.go index 79fa191f8b..34a640e134 100644 --- a/pkg/operators/extractors/extractor_types.go +++ b/pkg/operators/extractors/extractor_types.go @@ -72,7 +72,7 @@ type ExtractorTypeHolder struct { ExtractorType ExtractorType `mapping:"true"` } -func (holder ExtractorTypeHolder) JSONSchemaType() *jsonschema.Schema { +func (holder ExtractorTypeHolder) JSONSchema() *jsonschema.Schema { gotType := &jsonschema.Schema{ Type: "string", Title: "type of the extractor", diff --git a/pkg/operators/matchers/matchers_types.go b/pkg/operators/matchers/matchers_types.go index 5bcbc67628..d67ae112d1 100644 --- a/pkg/operators/matchers/matchers_types.go +++ b/pkg/operators/matchers/matchers_types.go @@ -82,7 +82,7 @@ func (t MatcherTypeHolder) String() string { return t.MatcherType.String() } -func (holder MatcherTypeHolder) JSONSchemaType() *jsonschema.Schema { +func (holder MatcherTypeHolder) JSONSchema() *jsonschema.Schema { gotType := &jsonschema.Schema{ Type: "string", Title: "type of the matcher", diff --git a/pkg/protocols/code/code.go b/pkg/protocols/code/code.go index 5193344ab6..43c6721c59 100644 --- a/pkg/protocols/code/code.go +++ b/pkg/protocols/code/code.go @@ -48,25 +48,25 @@ var ( type Request struct { // Operators for the current request go here. operators.Operators `yaml:",inline,omitempty"` - CompiledOperators *operators.Operators `yaml:"-"` + CompiledOperators *operators.Operators `yaml:"-" json:"-"` // ID is the optional id of the request ID string `yaml:"id,omitempty" json:"id,omitempty" jsonschema:"title=id of the request,description=ID is the optional ID of the Request"` // description: | // Engine type - Engine []string `yaml:"engine,omitempty" jsonschema:"title=engine,description=Engine"` + Engine []string `yaml:"engine,omitempty" json:"engine,omitempty" jsonschema:"title=engine,description=Engine"` // description: | // PreCondition is a condition which is evaluated before sending the request. PreCondition string `yaml:"pre-condition,omitempty" json:"pre-condition,omitempty" jsonschema:"title=pre-condition for the request,description=PreCondition is a condition which is evaluated before sending the request"` // description: | // Engine Arguments - Args []string `yaml:"args,omitempty" jsonschema:"title=args,description=Args"` + Args []string `yaml:"args,omitempty" json:"args,omitempty" jsonschema:"title=args,description=Args"` // description: | // Pattern preferred for file name - Pattern string `yaml:"pattern,omitempty" jsonschema:"title=pattern,description=Pattern"` + Pattern string `yaml:"pattern,omitempty" json:"pattern,omitempty" jsonschema:"title=pattern,description=Pattern"` // description: | // Source File/Snippet - Source string `yaml:"source,omitempty" jsonschema:"title=source file/snippet,description=Source snippet"` + Source string `yaml:"source,omitempty" json:"source,omitempty" jsonschema:"title=source file/snippet,description=Source snippet"` options *protocols.ExecutorOptions `yaml:"-" json:"-"` preConditionCompiled *goja.Program `yaml:"-" json:"-"` diff --git a/pkg/protocols/common/generators/attack_types.go b/pkg/protocols/common/generators/attack_types.go index c0ad882f81..6071abeba6 100644 --- a/pkg/protocols/common/generators/attack_types.go +++ b/pkg/protocols/common/generators/attack_types.go @@ -61,7 +61,7 @@ type AttackTypeHolder struct { Value AttackType `mapping:"true"` } -func (holder AttackTypeHolder) JSONSchemaType() *jsonschema.Schema { +func (holder AttackTypeHolder) JSONSchema() *jsonschema.Schema { gotType := &jsonschema.Schema{ Type: "string", Title: "type of the attack", diff --git a/pkg/protocols/common/protocolstate/state.go b/pkg/protocols/common/protocolstate/state.go index 09d1a66eb0..e6bacb4915 100644 --- a/pkg/protocols/common/protocolstate/state.go +++ b/pkg/protocols/common/protocolstate/state.go @@ -132,6 +132,9 @@ func Init(options *types.Options) error { opts.WithDialerHistory = true opts.SNIName = options.SNI + // this instance is used in javascript protocol libraries and + // dial history is required to get dialed ip of a host + opts.WithDialerHistory = true // fastdialer now by default fallbacks to ztls when there are tls related errors dialer, err := fastdialer.NewDialer(opts) diff --git a/pkg/protocols/common/variables/variables.go b/pkg/protocols/common/variables/variables.go index 131098f9c9..0ba4680edb 100644 --- a/pkg/protocols/common/variables/variables.go +++ b/pkg/protocols/common/variables/variables.go @@ -21,7 +21,7 @@ type Variable struct { utils.InsertionOrderedStringMap `yaml:"-" json:"-"` } -func (variables Variable) JSONSchemaType() *jsonschema.Schema { +func (variables Variable) JSONSchema() *jsonschema.Schema { gotType := &jsonschema.Schema{ Type: "object", Title: "variables for the request", diff --git a/pkg/protocols/dns/dns.go b/pkg/protocols/dns/dns.go index 6f4f06714a..80c14d234f 100644 --- a/pkg/protocols/dns/dns.go +++ b/pkg/protocols/dns/dns.go @@ -60,7 +60,7 @@ type Request struct { // examples: // - name: Use a retry of 100 to 150 generally // value: 100 - TraceMaxRecursion int `yaml:"trace-max-recursion,omitempty" jsonschema:"title=trace-max-recursion level for dns request,description=TraceMaxRecursion is the number of max recursion allowed for trace operations"` + TraceMaxRecursion int `yaml:"trace-max-recursion,omitempty" json:"trace-max-recursion,omitempty" jsonschema:"title=trace-max-recursion level for dns request,description=TraceMaxRecursion is the number of max recursion allowed for trace operations"` // description: | // Attack is the type of payload combinations to perform. @@ -83,7 +83,7 @@ type Request struct { Threads int `yaml:"threads,omitempty" json:"threads,omitempty" jsonschema:"title=threads for sending requests,description=Threads specifies number of threads to use sending requests. This enables Connection Pooling"` generator *generators.PayloadGenerator - CompiledOperators *operators.Operators `yaml:"-"` + CompiledOperators *operators.Operators `yaml:"-" json:"-"` dnsClient *retryabledns.Client options *protocols.ExecutorOptions diff --git a/pkg/protocols/dns/dns_types.go b/pkg/protocols/dns/dns_types.go index 1c90c5836d..034f69b326 100644 --- a/pkg/protocols/dns/dns_types.go +++ b/pkg/protocols/dns/dns_types.go @@ -92,7 +92,7 @@ func (holder DNSRequestTypeHolder) String() string { return holder.DNSRequestType.String() } -func (holder DNSRequestTypeHolder) JSONSchemaType() *jsonschema.Schema { +func (holder DNSRequestTypeHolder) JSONSchema() *jsonschema.Schema { gotType := &jsonschema.Schema{ Type: "string", Title: "type of DNS request to make", diff --git a/pkg/protocols/headless/engine/action.go b/pkg/protocols/headless/engine/action.go index 905652812d..3e2ce4e010 100644 --- a/pkg/protocols/headless/engine/action.go +++ b/pkg/protocols/headless/engine/action.go @@ -1,6 +1,10 @@ package engine -import "strings" +import ( + "strings" + + "github.com/invopop/jsonschema" +) // Action is an action taken by the browser to reach a navigation // @@ -29,6 +33,29 @@ type Action struct { ActionType ActionTypeHolder `yaml:"action" json:"action" jsonschema:"title=action to perform,description=Type of actions to perform,enum=navigate,enum=script,enum=click,enum=rightclick,enum=text,enum=screenshot,enum=time,enum=select,enum=files,enum=waitload,enum=getresource,enum=extract,enum=setmethod,enum=addheader,enum=setheader,enum=deleteheader,enum=setbody,enum=waitevent,enum=keyboard,enum=debug,enum=sleep"` } +func (a Action) JSONSchemaExtend(schema *jsonschema.Schema) { + argsSchema, ok := schema.Properties.Get("args") + if !ok { + return + } + argsSchema.PatternProperties = map[string]*jsonschema.Schema{ + ".*": { + OneOf: []*jsonschema.Schema{ + { + Type: "string", + }, + { + Type: "integer", + }, + { + Type: "boolean", + }, + }, + }, + } + argsSchema.Ref = "" +} + // String returns the string representation of an action func (a *Action) String() string { builder := &strings.Builder{} diff --git a/pkg/protocols/headless/engine/action_types.go b/pkg/protocols/headless/engine/action_types.go index 52873e45de..c9cd39ad2a 100644 --- a/pkg/protocols/headless/engine/action_types.go +++ b/pkg/protocols/headless/engine/action_types.go @@ -171,7 +171,7 @@ type ActionTypeHolder struct { func (holder ActionTypeHolder) String() string { return holder.ActionType.String() } -func (holder ActionTypeHolder) JSONSchemaType() *jsonschema.Schema { +func (holder ActionTypeHolder) JSONSchema() *jsonschema.Schema { gotType := &jsonschema.Schema{ Type: "string", Title: "action to perform", diff --git a/pkg/protocols/http/http.go b/pkg/protocols/http/http.go index 51e5a36dfc..b98fd917a5 100644 --- a/pkg/protocols/http/http.go +++ b/pkg/protocols/http/http.go @@ -5,9 +5,11 @@ import ( "fmt" "strings" + "github.com/invopop/jsonschema" json "github.com/json-iterator/go" "github.com/pkg/errors" + "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/nuclei/v3/pkg/fuzz" "github.com/projectdiscovery/nuclei/v3/pkg/operators" "github.com/projectdiscovery/nuclei/v3/pkg/operators/matchers" @@ -219,6 +221,29 @@ type Request struct { fuzzPreConditionOperator matchers.ConditionType `yaml:"-" json:"-"` } +func (e Request) JSONSchemaExtend(schema *jsonschema.Schema) { + headersSchema, ok := schema.Properties.Get("headers") + if !ok { + return + } + headersSchema.PatternProperties = map[string]*jsonschema.Schema{ + ".*": { + OneOf: []*jsonschema.Schema{ + { + Type: "string", + }, + { + Type: "integer", + }, + { + Type: "boolean", + }, + }, + }, + } + headersSchema.Ref = "" +} + // Options returns executer options for http request func (r *Request) Options() *protocols.ExecutorOptions { return r.options @@ -411,12 +436,34 @@ func (request *Request) Compile(options *protocols.ExecutorOptions) error { } } if len(request.Payloads) > 0 { - // specifically for http requests high concurrency and and threads will lead to memory exausthion, hence reduce the maximum parallelism - if protocolstate.IsLowOnMemory() { - request.Threads = protocolstate.GuardThreadsOrDefault(request.Threads) + // Due to a known issue (https://github.com/projectdiscovery/nuclei/issues/5015), + // dynamic extractors cannot be used with payloads. To address this, + // execution is handled by the standard engine without concurrency, + // achieved by setting the thread count to 0. + + // this limitation will be removed once we have a better way to handle dynamic extractors with payloads + hasMultipleRequests := false + if len(request.Raw)+len(request.Path) > 1 { + hasMultipleRequests = true + } + // look for dynamic extractor ( internal: true with named extractor) + hasNamedInternalExtractor := false + for _, extractor := range request.Extractors { + if extractor.Internal && extractor.Name != "" { + hasNamedInternalExtractor = true + break + } + } + if hasNamedInternalExtractor && hasMultipleRequests { + gologger.Warning().Label(options.TemplateID).Msgf("Setting thread count to 0 because dynamic extractors are not supported with payloads yet") + request.Threads = 0 + } else { + // specifically for http requests high concurrency and and threads will lead to memory exausthion, hence reduce the maximum parallelism + if protocolstate.IsLowOnMemory() { + request.Threads = protocolstate.GuardThreadsOrDefault(request.Threads) + } + request.Threads = options.GetThreadsForNPayloadRequests(request.Requests(), request.Threads) } - // if we have payloads, adjust threads if none specified - request.Threads = options.GetThreadsForNPayloadRequests(request.Requests(), request.Threads) } return nil diff --git a/pkg/protocols/http/http_method_types.go b/pkg/protocols/http/http_method_types.go index 9ef78911b0..315a5090fc 100644 --- a/pkg/protocols/http/http_method_types.go +++ b/pkg/protocols/http/http_method_types.go @@ -89,7 +89,7 @@ func (holder HTTPMethodTypeHolder) String() string { return holder.MethodType.String() } -func (holder HTTPMethodTypeHolder) JSONSchemaType() *jsonschema.Schema { +func (holder HTTPMethodTypeHolder) JSONSchema() *jsonschema.Schema { gotType := &jsonschema.Schema{ Type: "string", Title: "method is the HTTP request method", diff --git a/pkg/protocols/http/signature.go b/pkg/protocols/http/signature.go index c28ecf6827..10fcf0a38a 100644 --- a/pkg/protocols/http/signature.go +++ b/pkg/protocols/http/signature.go @@ -51,7 +51,7 @@ type SignatureTypeHolder struct { Value SignatureType } -func (holder SignatureTypeHolder) JSONSchemaType() *jsonschema.Schema { +func (holder SignatureTypeHolder) JSONSchema() *jsonschema.Schema { gotType := &jsonschema.Schema{ Type: "string", Title: "type of the signature", diff --git a/pkg/protocols/javascript/js.go b/pkg/protocols/javascript/js.go index fc32486937..dee3ff35e8 100644 --- a/pkg/protocols/javascript/js.go +++ b/pkg/protocols/javascript/js.go @@ -27,11 +27,13 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/eventcreator" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/utils/vardump" protocolutils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils" templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types" "github.com/projectdiscovery/nuclei/v3/pkg/types" errorutil "github.com/projectdiscovery/utils/errors" + iputil "github.com/projectdiscovery/utils/ip" urlutil "github.com/projectdiscovery/utils/url" "github.com/remeh/sizedwaitgroup" ) @@ -520,6 +522,46 @@ func (request *Request) executeRequestWithPayloads(hostPort string, input *conte data["stop-at-first-match"] = true } + // add ip address to data + if input.MetaInput.CustomIP != "" { + data["ip"] = input.MetaInput.CustomIP + } else { + // context: https://github.com/projectdiscovery/nuclei/issues/5021 + hostname := input.MetaInput.Input + if strings.Contains(hostname, ":") { + host, _, err := net.SplitHostPort(hostname) + if err == nil { + hostname = host + } else { + // naive way + if !strings.Contains(hostname, "]") { + hostname = hostname[:strings.LastIndex(hostname, ":")] + } + } + } + data["ip"] = protocolstate.Dialer.GetDialedIP(hostname) + // if input itself was an ip, use it + if iputil.IsIP(hostname) { + data["ip"] = hostname + } + + // if ip is not found,this is because ssh and other protocols do not use fastdialer + // although its not perfect due to its use case dial and get ip + dnsData, err := protocolstate.Dialer.GetDNSData(hostname) + if err == nil { + for _, v := range dnsData.A { + data["ip"] = v + break + } + if data["ip"] == "" { + for _, v := range dnsData.AAAA { + data["ip"] = v + break + } + } + } + } + // add and get values from templatectx request.options.AddTemplateVars(input.MetaInput, request.Type(), request.GetID(), data) data = generators.MergeMaps(data, request.options.GetTemplateCtx(input.MetaInput).GetAll()) diff --git a/pkg/protocols/network/network.go b/pkg/protocols/network/network.go index 296770ffdc..70d618dcb5 100644 --- a/pkg/protocols/network/network.go +++ b/pkg/protocols/network/network.go @@ -61,7 +61,7 @@ type Request struct { // description: | // Port is the port to send network requests to. this acts as default port but is overriden if target/input contains // non-http(s) ports like 80,8080,8081 etc - Port string `yaml:"port,omitempty" json:"port,omitempty" jsonschema:"title=port to send requests to,description=Port to send network requests to"` + Port string `yaml:"port,omitempty" json:"port,omitempty" jsonschema:"title=port to send requests to,description=Port to send network requests to,oneof_type=string;integer"` // description: | // ExcludePorts is the list of ports to exclude from being scanned . It is intended to be used with `Port` field and contains a list of ports which are ignored/skipped @@ -91,7 +91,7 @@ type Request struct { // Operators for the current request go here. operators.Operators `yaml:",inline,omitempty"` - CompiledOperators *operators.Operators `yaml:"-"` + CompiledOperators *operators.Operators `yaml:"-" json:"-"` generator *generators.PayloadGenerator // cache any variables that may be needed for operation. @@ -128,7 +128,7 @@ type Input struct { // examples: // - value: "\"TEST\"" // - value: "\"hex_decode('50494e47')\"" - Data string `yaml:"data,omitempty" json:"data,omitempty" jsonschema:"title=data to send as input,description=Data is the data to send as the input"` + Data string `yaml:"data,omitempty" json:"data,omitempty" jsonschema:"title=data to send as input,description=Data is the data to send as the input,oneof_type=string;integer"` // description: | // Type is the type of input specified in `data` field. // diff --git a/pkg/protocols/network/network_input_types.go b/pkg/protocols/network/network_input_types.go index e8b294eaab..2c663896e8 100644 --- a/pkg/protocols/network/network_input_types.go +++ b/pkg/protocols/network/network_input_types.go @@ -66,7 +66,7 @@ func (holder NetworkInputTypeHolder) String() string { return holder.NetworkInputType.String() } -func (holder NetworkInputTypeHolder) JSONSchemaType() *jsonschema.Schema { +func (holder NetworkInputTypeHolder) JSONSchema() *jsonschema.Schema { gotType := &jsonschema.Schema{ Type: "string", Title: "type is the type of input data", diff --git a/pkg/protocols/ssl/ssl.go b/pkg/protocols/ssl/ssl.go index 9b185a4f4c..49d612b882 100644 --- a/pkg/protocols/ssl/ssl.go +++ b/pkg/protocols/ssl/ssl.go @@ -282,6 +282,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa } else { data["ip"] = request.dialer.GetDialedIP(hostname) } + data["Port"] = port data["template-path"] = requestOptions.TemplatePath data["template-id"] = requestOptions.TemplateID data["template-info"] = requestOptions.TemplateInfo @@ -405,6 +406,9 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent if fields.Port == "80" { fields.Port = "443" } + if types.ToString(wrapped.InternalEvent["Port"]) != "" { + fields.Port = types.ToString(wrapped.InternalEvent["Port"]) + } data := &output.ResultEvent{ TemplateID: types.ToString(wrapped.InternalEvent["template-id"]), TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]), diff --git a/pkg/templates/types/types.go b/pkg/templates/types/types.go index f1195dc74d..f51c444f8b 100644 --- a/pkg/templates/types/types.go +++ b/pkg/templates/types/types.go @@ -92,7 +92,7 @@ type TypeHolder struct { ProtocolType ProtocolType `mapping:"true"` } -func (holder TypeHolder) JSONSchemaType() *jsonschema.Schema { +func (holder TypeHolder) JSONSchema() *jsonschema.Schema { gotType := &jsonschema.Schema{ Type: "string", Title: "type of the protocol",