2020-12-30 14:54:20 +05:30
package network
import (
2023-11-23 16:34:26 +05:30
"strconv"
2020-12-30 14:54:20 +05:30
"strings"
"github.com/pkg/errors"
2021-09-07 17:31:46 +03:00
2020-12-30 14:54:20 +05:30
"github.com/projectdiscovery/fastdialer/fastdialer"
2023-10-17 17:44:13 +05:30
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/network/networkclientpool"
2023-11-23 16:34:26 +05:30
errorutil "github.com/projectdiscovery/utils/errors"
2022-11-06 21:24:23 +01:00
fileutil "github.com/projectdiscovery/utils/file"
2020-12-30 14:54:20 +05:30
)
// Request contains a Network protocol request to be made from a template
type Request struct {
2021-09-07 17:31:46 +03:00
// ID is the optional id of the request
2023-02-07 16:10:40 +08:00
ID string ` yaml:"id,omitempty" json:"id,omitempty" jsonschema:"title=id of the request,description=ID of the network request" `
2021-01-16 14:10:24 +05:30
2021-07-27 16:03:56 +05:30
// description: |
2021-08-23 23:50:45 +05:30
// Host to send network requests to.
2021-07-27 16:03:56 +05:30
//
// Usually it's set to `{{Hostname}}`. If you want to enable TLS for
// TCP Connection, you can use `tls://{{Hostname}}`.
// examples:
// - value: |
// []string{"{{Hostname}}"}
2023-02-07 16:10:40 +08:00
Address [ ] string ` yaml:"host,omitempty" json:"host,omitempty" jsonschema:"title=host to send requests to,description=Host to send network requests to" `
2021-02-19 00:10:06 +01:00
addresses [ ] addressKV
2020-12-30 14:54:20 +05:30
2021-07-27 16:03:56 +05:30
// description: |
// Attack is the type of payload combinations to perform.
//
2021-11-25 18:54:16 +02:00
// Batteringram is inserts the same payload into all defined payload positions at once, pitchfork combines multiple payload sets and clusterbomb generates
2021-07-27 16:03:56 +05:30
// permutations and combinations for all payloads.
2023-02-07 16:10:40 +08:00
AttackType generators . AttackTypeHolder ` yaml:"attack,omitempty" json:"attack,omitempty" jsonschema:"title=attack is the payload combination,description=Attack is the type of payload combinations to perform,enum=batteringram,enum=pitchfork,enum=clusterbomb" `
2021-07-27 16:03:56 +05:30
// description: |
// Payloads contains any payloads for the current request.
//
// Payloads support both key-values combinations where a list
// of payloads is provided, or optionally a single file can also
// be provided as payload which will be read on run-time.
2023-02-07 16:10:40 +08:00
Payloads map [ string ] interface { } ` yaml:"payloads,omitempty" json:"payloads,omitempty" jsonschema:"title=payloads for the network request,description=Payloads contains any payloads for the current request" `
2024-02-02 02:05:30 +05:30
// description: |
// Threads specifies number of threads to use sending requests. This enables Connection Pooling.
//
// Connection: Close attribute must not be used in request while using threads flag, otherwise
// pooling will fail and engine will continue to close connections after requests.
// examples:
// - name: Send requests using 10 concurrent threads
// value: 10
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" `
2021-07-06 18:27:30 +05:30
2021-07-27 16:03:56 +05:30
// description: |
// Inputs contains inputs for the network socket
2023-02-07 16:10:40 +08:00
Inputs [ ] * Input ` yaml:"inputs,omitempty" json:"inputs,omitempty" jsonschema:"title=inputs for the network request,description=Inputs contains any input/output for the current request" `
2021-07-27 16:03:56 +05:30
// description: |
2023-09-12 04:34:15 +05:30
// 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
2024-04-08 03:29:42 +05:30
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" `
2023-09-12 04:34:15 +05:30
// 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
ExcludePorts string ` yaml:"exclude-ports,omitempty" json:"exclude-ports,omitempty" jsonschema:"title=exclude ports from being scanned,description=Exclude ports from being scanned" `
// description: |
2021-07-27 16:03:56 +05:30
// ReadSize is the size of response to read at the end
//
// Default value for read-size is 1024.
// examples:
// - value: "2048"
2023-02-07 16:10:40 +08:00
ReadSize int ` yaml:"read-size,omitempty" json:"read-size,omitempty" jsonschema:"title=size of network response to read,description=Size of response to read at the end. Default is 1024 bytes" `
2021-10-12 23:28:24 +02:00
// description: |
// ReadAll determines if the data stream should be read till the end regardless of the size
//
// Default value for read-all is false.
// examples:
// - value: false
2023-02-07 16:10:40 +08:00
ReadAll bool ` yaml:"read-all,omitempty" json:"read-all,omitempty" jsonschema:"title=read all response stream,description=Read all response stream till the server stops sending" `
2021-10-19 21:29:18 +05:30
2021-10-17 18:50:07 +05:30
// description: |
2021-11-25 18:54:16 +02:00
// SelfContained specifies if the request is self-contained.
2021-10-19 21:29:18 +05:30
SelfContained bool ` yaml:"-" json:"-" `
2020-12-30 14:54:20 +05:30
2024-10-14 20:54:58 +05:30
// description: |
// StopAtFirstMatch stops the execution of the requests and template as soon as a match is found.
StopAtFirstMatch bool ` yaml:"stop-at-first-match,omitempty" json:"stop-at-first-match,omitempty" jsonschema:"title=stop at first match,description=Stop the execution after a match is found" `
2023-11-23 16:34:26 +05:30
// description: |
// ports is post processed list of ports to scan (obtained from Port)
ports [ ] string ` yaml:"-" json:"-" `
2020-12-30 14:54:20 +05:30
// Operators for the current request go here.
2021-02-21 16:31:34 +05:30
operators . Operators ` yaml:",inline,omitempty" `
2024-04-08 03:29:42 +05:30
CompiledOperators * operators . Operators ` yaml:"-" json:"-" `
2020-12-30 14:54:20 +05:30
2021-11-05 03:01:41 +05:30
generator * generators . PayloadGenerator
2020-12-30 14:54:20 +05:30
// cache any variables that may be needed for operation.
2021-11-24 22:19:42 +05:30
dialer * fastdialer . Dialer
2023-05-31 16:58:10 -04:00
options * protocols . ExecutorOptions
2020-12-30 14:54:20 +05:30
}
2021-11-26 16:23:54 +05:30
// RequestPartDefinitions contains a mapping of request part definitions and their
// description. Multiple definitions are separated by commas.
// Definitions not having a name (generated on runtime) are prefixed & suffixed by <>.
var RequestPartDefinitions = map [ string ] string {
"template-id" : "ID of the template executed" ,
"template-info" : "Info Block of the template executed" ,
"template-path" : "Path of the template executed" ,
"host" : "Host is the input to the template" ,
"matched" : "Matched is the input which was matched upon" ,
"type" : "Type is the type of request made" ,
"request" : "Network request made from the client" ,
2022-02-07 16:41:55 +02:00
"body,all,data" : "Network response received from server (default)" ,
2021-11-26 16:23:54 +05:30
"raw" : "Full Network protocol data" ,
}
2021-02-19 00:10:06 +01:00
type addressKV struct {
2021-11-23 10:44:31 +05:30
address string
tls bool
2020-12-30 21:14:04 +05:30
}
2020-12-30 16:47:22 +05:30
// Input is the input to send on the network
type Input struct {
2021-07-27 16:03:56 +05:30
// description: |
// Data is the data to send as the input.
//
// It supports DSL Helper Functions as well as normal expressions.
// examples:
// - value: "\"TEST\""
// - value: "\"hex_decode('50494e47')\""
2024-04-08 03:29:42 +05:30
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" `
2021-07-27 16:03:56 +05:30
// description: |
// Type is the type of input specified in `data` field.
//
// Default value is text, but hex can be used for hex formatted data.
// values:
// - "hex"
// - "text"
2023-02-07 16:10:40 +08:00
Type NetworkInputTypeHolder ` yaml:"type,omitempty" json:"type,omitempty" jsonschema:"title=type is the type of input data,description=Type of input specified in data field,enum=hex,enum=text" `
2021-07-27 16:03:56 +05:30
// description: |
// Read is the number of bytes to read from socket.
//
2021-09-16 11:33:20 -05:00
// This can be used for protocols which expect an immediate response. You can
2021-11-25 18:54:16 +02:00
// read and write responses one after another and eventually perform matching
2021-07-27 16:03:56 +05:30
// on every data captured with `name` attribute.
//
// The [network docs](https://nuclei.projectdiscovery.io/templating-guide/protocols/network/) highlight more on how to do this.
// examples:
// - value: "1024"
2023-02-07 16:10:40 +08:00
Read int ` yaml:"read,omitempty" json:"read,omitempty" jsonschema:"title=bytes to read from socket,description=Number of bytes to read from socket" `
2021-07-27 16:03:56 +05:30
// description: |
// Name is the optional name of the data read to provide matching on.
// examples:
// - value: "\"prefix\""
2023-02-07 16:10:40 +08:00
Name string ` yaml:"name,omitempty" json:"name,omitempty" jsonschema:"title=optional name for data read,description=Optional name of the data read to provide matching on" `
2020-12-30 16:47:22 +05:30
}
2021-01-16 14:10:24 +05:30
// GetID returns the unique ID of the request if any.
2021-10-01 14:30:04 +03:00
func ( request * Request ) GetID ( ) string {
return request . ID
2021-01-16 14:10:24 +05:30
}
2020-12-30 14:54:20 +05:30
// Compile compiles the protocol request for further execution.
2023-05-31 16:58:10 -04:00
func ( request * Request ) Compile ( options * protocols . ExecutorOptions ) error {
2021-02-19 00:10:06 +01:00
var shouldUseTLS bool
2021-02-23 18:13:00 +05:30
var err error
2021-10-01 14:30:04 +03:00
request . options = options
for _ , address := range request . Address {
2021-02-19 00:10:06 +01:00
// check if the connection should be encrypted
2021-02-23 18:13:00 +05:30
if strings . HasPrefix ( address , "tls://" ) {
2021-02-19 00:10:06 +01:00
shouldUseTLS = true
2021-02-23 18:13:00 +05:30
address = strings . TrimPrefix ( address , "tls://" )
2021-02-19 00:10:06 +01:00
}
2021-11-23 10:44:31 +05:30
request . addresses = append ( request . addresses , addressKV { address : address , tls : shouldUseTLS } )
2020-12-30 14:54:20 +05:30
}
2021-02-24 12:07:16 +05:30
// Pre-compile any input dsl functions before executing the request.
2021-10-01 14:30:04 +03:00
for _ , input := range request . Inputs {
2021-11-18 07:48:47 -06:00
if input . Type . String ( ) != "" {
2021-02-24 12:07:16 +05:30
continue
}
2021-02-26 13:13:11 +05:30
if compiled , evalErr := expressions . Evaluate ( input . Data , map [ string ] interface { } { } ) ; evalErr == nil {
input . Data = compiled
2021-02-24 12:07:16 +05:30
}
}
2020-12-30 14:54:20 +05:30
2023-11-23 16:34:26 +05:30
// parse ports and validate
if request . Port != "" {
for _ , port := range strings . Split ( request . Port , "," ) {
if port == "" {
continue
}
portInt , err := strconv . Atoi ( port )
if err != nil {
return errorutil . NewWithErr ( err ) . Msgf ( "could not parse port %v from '%s'" , port , request . Port )
}
if portInt < 1 || portInt > 65535 {
return errorutil . NewWithTag ( request . TemplateID , "port %v is not in valid range" , portInt )
}
request . ports = append ( request . ports , port )
}
}
2021-10-07 12:36:27 +02:00
// Resolve payload paths from vars if they exists
2023-03-04 04:57:27 +05:30
for name , payload := range request . options . Options . Vars . AsMap ( ) {
2021-10-07 12:36:27 +02:00
payloadStr , ok := payload . ( string )
// check if inputs contains the payload
var hasPayloadName bool
2021-10-11 13:58:20 +03:00
for _ , input := range request . Inputs {
2021-11-18 07:48:47 -06:00
if input . Type . String ( ) != "" {
2021-10-07 12:36:27 +02:00
continue
}
2021-11-18 14:52:11 +01:00
if expressions . ContainsVariablesWithNames ( map [ string ] interface { } { name : payload } , input . Data ) == nil {
2021-10-07 12:36:27 +02:00
hasPayloadName = true
break
}
}
if ok && hasPayloadName && fileutil . FileExists ( payloadStr ) {
2021-10-11 13:58:20 +03:00
if request . Payloads == nil {
request . Payloads = make ( map [ string ] interface { } )
2021-10-07 12:36:27 +02:00
}
2021-10-11 13:58:20 +03:00
request . Payloads [ name ] = payloadStr
2021-10-07 12:36:27 +02:00
}
}
2021-10-01 14:30:04 +03:00
if len ( request . Payloads ) > 0 {
2023-08-31 18:03:01 +05:30
request . generator , err = generators . New ( request . Payloads , request . AttackType . Value , request . options . TemplatePath , request . options . Catalog , request . options . Options . AttackType , request . options . Options )
2021-07-06 18:27:30 +05:30
if err != nil {
return errors . Wrap ( err , "could not parse payloads" )
}
2024-02-02 02:05:30 +05:30
// if we have payloads, adjust threads if none specified
request . Threads = options . GetThreadsForNPayloadRequests ( request . Requests ( ) , request . Threads )
2021-07-06 18:27:30 +05:30
}
2020-12-30 14:54:20 +05:30
// Create a client for the class
2025-06-16 22:24:52 +05:30
client , err := networkclientpool . Get ( options . Options , & networkclientpool . Configuration {
CustomDialer : options . CustomFastdialer ,
} )
2020-12-30 14:54:20 +05:30
if err != nil {
return errors . Wrap ( err , "could not get network client" )
}
2021-10-01 14:30:04 +03:00
request . dialer = client
2020-12-30 14:54:20 +05:30
2021-10-01 14:30:04 +03:00
if len ( request . Matchers ) > 0 || len ( request . Extractors ) > 0 {
compiled := & request . Operators
2022-06-24 23:09:27 +05:30
compiled . ExcludeMatchers = options . ExcludeMatchers
compiled . TemplateID = options . TemplateID
2020-12-30 14:54:20 +05:30
if err := compiled . Compile ( ) ; err != nil {
return errors . Wrap ( err , "could not compile operators" )
}
2021-10-01 14:30:04 +03:00
request . CompiledOperators = compiled
2020-12-30 14:54:20 +05:30
}
return nil
}
// Requests returns the total number of requests the YAML rule will perform
2021-10-01 14:30:04 +03:00
func ( request * Request ) Requests ( ) int {
return len ( request . Address )
2020-12-30 14:54:20 +05:30
}
2025-07-09 14:47:26 -05:00
func ( request * Request ) SetDialer ( dialer * fastdialer . Dialer ) {
request . dialer = dialer
}
// UpdateOptions replaces this request's options with a new copy
func ( r * Request ) UpdateOptions ( opts * protocols . ExecutorOptions ) {
r . options . ApplyNewEngineOptions ( opts )
}