mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-17 23:05:26 +00:00
add support for multiple ports in network template (#4401)
* add support for multiple ports in network template * backwords compatibility for templates without 'port' field * fix nil panic in compile
This commit is contained in:
parent
8d7bbdd978
commit
a09b8afd0f
@ -1,6 +1,7 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@ -11,6 +12,7 @@ import (
|
||||
"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"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
)
|
||||
|
||||
@ -74,6 +76,10 @@ type Request struct {
|
||||
// SelfContained specifies if the request is self-contained.
|
||||
SelfContained bool `yaml:"-" json:"-"`
|
||||
|
||||
// description: |
|
||||
// ports is post processed list of ports to scan (obtained from Port)
|
||||
ports []string `yaml:"-" json:"-"`
|
||||
|
||||
// Operators for the current request go here.
|
||||
operators.Operators `yaml:",inline,omitempty"`
|
||||
CompiledOperators *operators.Operators `yaml:"-"`
|
||||
@ -169,6 +175,23 @@ func (request *Request) Compile(options *protocols.ExecutorOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve payload paths from vars if they exists
|
||||
for name, payload := range request.options.Options.Vars.AsMap() {
|
||||
payloadStr, ok := payload.(string)
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/multierr"
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
@ -22,6 +23,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/eventcreator"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/responsehighlighter"
|
||||
"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/replacer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/utils/vardump"
|
||||
protocolutils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
|
||||
@ -44,18 +46,82 @@ func (request *Request) Type() templateTypes.ProtocolType {
|
||||
return templateTypes.NetworkProtocol
|
||||
}
|
||||
|
||||
// getOpenPorts returns all open ports from list of ports provided in template
|
||||
// if only 1 port is provided, no need to check if port is open or not
|
||||
func (request *Request) getOpenPorts(target *contextargs.Context) ([]string, error) {
|
||||
if len(request.ports) == 1 {
|
||||
// no need to check if port is open or not
|
||||
return request.ports, nil
|
||||
}
|
||||
errs := []error{}
|
||||
// if more than 1 port is provided, check if port is open or not
|
||||
openPorts := make([]string, 0)
|
||||
for _, port := range request.ports {
|
||||
cloned := target.Clone()
|
||||
if err := cloned.UseNetworkPort(port, request.ExcludePorts); err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
addr, err := getAddress(cloned.MetaInput.Input)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
conn, err := protocolstate.Dialer.Dial(context.TODO(), "tcp", addr)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
_ = conn.Close()
|
||||
openPorts = append(openPorts, port)
|
||||
}
|
||||
if len(openPorts) == 0 {
|
||||
return nil, multierr.Combine(errs...)
|
||||
}
|
||||
return openPorts, nil
|
||||
}
|
||||
|
||||
// ExecuteWithResults executes the protocol requests and returns results instead of writing them.
|
||||
func (request *Request) ExecuteWithResults(target *contextargs.Context, metadata, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
|
||||
var address string
|
||||
var err error
|
||||
visitedAddresses := make(mapsutil.Map[string, struct{}])
|
||||
|
||||
if request.Port == "" {
|
||||
// backwords compatibility or for other use cases
|
||||
// where port is not provided in template
|
||||
if err := request.executeOnTarget(target, visitedAddresses, metadata, previous, callback); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// get open ports from list of ports provided in template
|
||||
ports, err := request.getOpenPorts(target)
|
||||
if len(ports) == 0 {
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
// TODO: replace this after scan context is implemented
|
||||
gologger.Verbose().Msgf("[%v] got errors while checking open ports: %s\n", request.options.TemplateID, err)
|
||||
}
|
||||
|
||||
for _, port := range ports {
|
||||
input := target.Clone()
|
||||
// use network port updates input with new port requested in template file
|
||||
// and it is ignored if input port is not standard http(s) ports like 80,8080,8081 etc
|
||||
// idea is to reduce redundant dials to http ports
|
||||
if err := input.UseNetworkPort(request.Port, request.ExcludePorts); err != nil {
|
||||
if err := input.UseNetworkPort(port, request.ExcludePorts); err != nil {
|
||||
gologger.Debug().Msgf("Could not network port from constants: %s\n", err)
|
||||
}
|
||||
if err := request.executeOnTarget(input, visitedAddresses, metadata, previous, callback); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (request *Request) executeOnTarget(input *contextargs.Context, visited mapsutil.Map[string, struct{}], metadata, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
|
||||
var address string
|
||||
var err error
|
||||
|
||||
if request.SelfContained {
|
||||
address = ""
|
||||
@ -73,15 +139,13 @@ func (request *Request) ExecuteWithResults(target *contextargs.Context, metadata
|
||||
variablesMap := request.options.Variables.Evaluate(variables)
|
||||
variables = generators.MergeMaps(variablesMap, variables, request.options.Constants)
|
||||
|
||||
visitedAddresses := make(mapsutil.Map[string, struct{}])
|
||||
|
||||
for _, kv := range request.addresses {
|
||||
actualAddress := replacer.Replace(kv.address, variables)
|
||||
|
||||
if visitedAddresses.Has(actualAddress) && !request.options.Options.DisableClustering {
|
||||
if visited.Has(actualAddress) && !request.options.Options.DisableClustering {
|
||||
continue
|
||||
}
|
||||
visitedAddresses.Set(actualAddress, struct{}{})
|
||||
visited.Set(actualAddress, struct{}{})
|
||||
|
||||
if err := request.executeAddress(variables, actualAddress, address, input, kv.tls, previous, callback); err != nil {
|
||||
outputEvent := request.responseToDSLMap("", "", "", address, "")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user