github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/network/network.go (about)

     1  package network
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/pkg/errors"
     7  
     8  	"github.com/projectdiscovery/fastdialer/fastdialer"
     9  	"github.com/projectdiscovery/nuclei/v2/pkg/operators"
    10  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
    11  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/expressions"
    12  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
    13  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols/network/networkclientpool"
    14  	fileutil "github.com/projectdiscovery/utils/file"
    15  )
    16  
    17  // Request contains a Network protocol request to be made from a template
    18  type Request struct {
    19  	// ID is the optional id of the request
    20  	ID string `yaml:"id,omitempty" json:"id,omitempty" jsonschema:"title=id of the request,description=ID of the network request"`
    21  
    22  	// description: |
    23  	//   Host to send network requests to.
    24  	//
    25  	//   Usually it's set to `{{Hostname}}`. If you want to enable TLS for
    26  	//   TCP Connection, you can use `tls://{{Hostname}}`.
    27  	// examples:
    28  	//   - value: |
    29  	//       []string{"{{Hostname}}"}
    30  	Address   []string `yaml:"host,omitempty" json:"host,omitempty" jsonschema:"title=host to send requests to,description=Host to send network requests to"`
    31  	addresses []addressKV
    32  
    33  	// description: |
    34  	//   Attack is the type of payload combinations to perform.
    35  	//
    36  	//   Batteringram is inserts the same payload into all defined payload positions at once, pitchfork combines multiple payload sets and clusterbomb generates
    37  	//   permutations and combinations for all payloads.
    38  	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"`
    39  	// description: |
    40  	//   Payloads contains any payloads for the current request.
    41  	//
    42  	//   Payloads support both key-values combinations where a list
    43  	//   of payloads is provided, or optionally a single file can also
    44  	//   be provided as payload which will be read on run-time.
    45  	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"`
    46  
    47  	// description: |
    48  	//   Inputs contains inputs for the network socket
    49  	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"`
    50  	// description: |
    51  	//   Port is the port to send network requests to. this acts as default port but is overriden if target/input contains
    52  	// non-http(s) ports like 80,8080,8081 etc
    53  	Port string `yaml:"port,omitempty" json:"port,omitempty" jsonschema:"title=port to send requests to,description=Port to send network requests to"`
    54  
    55  	// description:	|
    56  	//	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
    57  	ExcludePorts string `yaml:"exclude-ports,omitempty" json:"exclude-ports,omitempty" jsonschema:"title=exclude ports from being scanned,description=Exclude ports from being scanned"`
    58  	// description: |
    59  	//   ReadSize is the size of response to read at the end
    60  	//
    61  	//   Default value for read-size is 1024.
    62  	// examples:
    63  	//   - value: "2048"
    64  	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"`
    65  	// description: |
    66  	//   ReadAll determines if the data stream should be read till the end regardless of the size
    67  	//
    68  	//   Default value for read-all is false.
    69  	// examples:
    70  	//   - value: false
    71  	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"`
    72  
    73  	// description: |
    74  	//   SelfContained specifies if the request is self-contained.
    75  	SelfContained bool `yaml:"-" json:"-"`
    76  
    77  	// Operators for the current request go here.
    78  	operators.Operators `yaml:",inline,omitempty"`
    79  	CompiledOperators   *operators.Operators `yaml:"-"`
    80  
    81  	generator *generators.PayloadGenerator
    82  	// cache any variables that may be needed for operation.
    83  	dialer  *fastdialer.Dialer
    84  	options *protocols.ExecutorOptions
    85  }
    86  
    87  // RequestPartDefinitions contains a mapping of request part definitions and their
    88  // description. Multiple definitions are separated by commas.
    89  // Definitions not having a name (generated on runtime) are prefixed & suffixed by <>.
    90  var RequestPartDefinitions = map[string]string{
    91  	"template-id":   "ID of the template executed",
    92  	"template-info": "Info Block of the template executed",
    93  	"template-path": "Path of the template executed",
    94  	"host":          "Host is the input to the template",
    95  	"matched":       "Matched is the input which was matched upon",
    96  	"type":          "Type is the type of request made",
    97  	"request":       "Network request made from the client",
    98  	"body,all,data": "Network response received from server (default)",
    99  	"raw":           "Full Network protocol data",
   100  }
   101  
   102  type addressKV struct {
   103  	address string
   104  	tls     bool
   105  }
   106  
   107  // Input is the input to send on the network
   108  type Input struct {
   109  	// description: |
   110  	//   Data is the data to send as the input.
   111  	//
   112  	//   It supports DSL Helper Functions as well as normal expressions.
   113  	// examples:
   114  	//   - value: "\"TEST\""
   115  	//   - value: "\"hex_decode('50494e47')\""
   116  	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"`
   117  	// description: |
   118  	//   Type is the type of input specified in `data` field.
   119  	//
   120  	//   Default value is text, but hex can be used for hex formatted data.
   121  	// values:
   122  	//   - "hex"
   123  	//   - "text"
   124  	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"`
   125  	// description: |
   126  	//   Read is the number of bytes to read from socket.
   127  	//
   128  	//   This can be used for protocols which expect an immediate response. You can
   129  	//   read and write responses one after another and eventually perform matching
   130  	//   on every data captured with `name` attribute.
   131  	//
   132  	//   The [network docs](https://nuclei.projectdiscovery.io/templating-guide/protocols/network/) highlight more on how to do this.
   133  	// examples:
   134  	//   - value: "1024"
   135  	Read int `yaml:"read,omitempty" json:"read,omitempty" jsonschema:"title=bytes to read from socket,description=Number of bytes to read from socket"`
   136  	// description: |
   137  	//   Name is the optional name of the data read to provide matching on.
   138  	// examples:
   139  	//   - value: "\"prefix\""
   140  	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"`
   141  }
   142  
   143  // GetID returns the unique ID of the request if any.
   144  func (request *Request) GetID() string {
   145  	return request.ID
   146  }
   147  
   148  // Compile compiles the protocol request for further execution.
   149  func (request *Request) Compile(options *protocols.ExecutorOptions) error {
   150  	var shouldUseTLS bool
   151  	var err error
   152  
   153  	request.options = options
   154  	for _, address := range request.Address {
   155  		// check if the connection should be encrypted
   156  		if strings.HasPrefix(address, "tls://") {
   157  			shouldUseTLS = true
   158  			address = strings.TrimPrefix(address, "tls://")
   159  		}
   160  		request.addresses = append(request.addresses, addressKV{address: address, tls: shouldUseTLS})
   161  	}
   162  	// Pre-compile any input dsl functions before executing the request.
   163  	for _, input := range request.Inputs {
   164  		if input.Type.String() != "" {
   165  			continue
   166  		}
   167  		if compiled, evalErr := expressions.Evaluate(input.Data, map[string]interface{}{}); evalErr == nil {
   168  			input.Data = compiled
   169  		}
   170  	}
   171  
   172  	// Resolve payload paths from vars if they exists
   173  	for name, payload := range request.options.Options.Vars.AsMap() {
   174  		payloadStr, ok := payload.(string)
   175  		// check if inputs contains the payload
   176  		var hasPayloadName bool
   177  		for _, input := range request.Inputs {
   178  			if input.Type.String() != "" {
   179  				continue
   180  			}
   181  			if expressions.ContainsVariablesWithNames(map[string]interface{}{name: payload}, input.Data) == nil {
   182  				hasPayloadName = true
   183  				break
   184  			}
   185  		}
   186  		if ok && hasPayloadName && fileutil.FileExists(payloadStr) {
   187  			if request.Payloads == nil {
   188  				request.Payloads = make(map[string]interface{})
   189  			}
   190  			request.Payloads[name] = payloadStr
   191  		}
   192  	}
   193  
   194  	if len(request.Payloads) > 0 {
   195  		request.generator, err = generators.New(request.Payloads, request.AttackType.Value, request.options.TemplatePath, request.options.Options.AllowLocalFileAccess, request.options.Catalog, request.options.Options.AttackType)
   196  		if err != nil {
   197  			return errors.Wrap(err, "could not parse payloads")
   198  		}
   199  	}
   200  
   201  	// Create a client for the class
   202  	client, err := networkclientpool.Get(options.Options, &networkclientpool.Configuration{})
   203  	if err != nil {
   204  		return errors.Wrap(err, "could not get network client")
   205  	}
   206  	request.dialer = client
   207  
   208  	if len(request.Matchers) > 0 || len(request.Extractors) > 0 {
   209  		compiled := &request.Operators
   210  		compiled.ExcludeMatchers = options.ExcludeMatchers
   211  		compiled.TemplateID = options.TemplateID
   212  		if err := compiled.Compile(); err != nil {
   213  			return errors.Wrap(err, "could not compile operators")
   214  		}
   215  		request.CompiledOperators = compiled
   216  	}
   217  	return nil
   218  }
   219  
   220  // Requests returns the total number of requests the YAML rule will perform
   221  func (request *Request) Requests() int {
   222  	return len(request.Address)
   223  }