Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add new field 'port' in network protocol #4123

Merged
merged 5 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions integration_tests/network/network-port.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
id: network-port-example

info:
name: Example Template with Network Port
author: pdteam
severity: high
description: This is an updated description for the network port example.
reference: https://updated-reference-link

tcp:
- host:
- "{{Hostname}}"
port: 23846
inputs:
- data: "PING\r\n"
read-size: 4
matchers:
- type: word
part: data
words:
- "PONG"
61 changes: 61 additions & 0 deletions v2/cmd/integration-test/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"net"
"strings"

"github.com/projectdiscovery/nuclei/v2/pkg/testutils"
osutils "github.com/projectdiscovery/utils/os"
Expand All @@ -14,6 +15,7 @@ var networkTestcases = []TestCaseInfo{
{Path: "network/self-contained.yaml", TestCase: &networkRequestSelContained{}},
{Path: "network/variables.yaml", TestCase: &networkVariables{}},
{Path: "network/same-address.yaml", TestCase: &networkBasic{}},
{Path: "network/network-port.yaml", TestCase: &networkPort{}},
}

const defaultStaticPort = 5431
Expand Down Expand Up @@ -145,3 +147,62 @@ func (h *networkVariables) Execute(filePath string) error {

return expectResultsCount(results, 1)
}

type networkPort struct{}

func (n *networkPort) Execute(filePath string) error {
ts := testutils.NewTCPServer(nil, 23846, func(conn net.Conn) {
defer conn.Close()

data := make([]byte, 4)
if _, err := conn.Read(data); err != nil {
return
}
if string(data) == "PING" {
_, _ = conn.Write([]byte("PONG"))
}
})
defer ts.Close()

results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}

if err := expectResultsCount(results, 1); err != nil {
return err
}

// even though we passed port 443 in url it is ignored and port 23846 is used
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, strings.ReplaceAll(ts.URL, "23846", "443"), debug)
if err != nil {
return err
}

if err := expectResultsCount(results, 1); err != nil {
return err
}

// this is positive test case where we expect port to be overridden and 34567 to be used
ts2 := testutils.NewTCPServer(nil, 34567, func(conn net.Conn) {
defer conn.Close()

data := make([]byte, 4)
if _, err := conn.Read(data); err != nil {
return
}
if string(data) == "PING" {
_, _ = conn.Write([]byte("PONG"))
}
})
defer ts2.Close()

// even though we passed port 443 in url it is ignored and port 23846 is used
// instead of hardcoded port 23846 in template
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, ts2.URL, debug)
if err != nil {
return err
}

return expectResultsCount(results, 1)
}
27 changes: 27 additions & 0 deletions v2/pkg/protocols/common/contextargs/contextargs.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import (
"net/http/cookiejar"

mapsutil "github.com/projectdiscovery/utils/maps"
stringsutil "github.com/projectdiscovery/utils/strings"
urlutil "github.com/projectdiscovery/utils/url"
)

var (
// reservedPorts contains list of reserved ports for non-network requests in nuclei
reservedPorts = []string{"80", "443", "8080", "8443", "8081", "53"}
)

// Context implements a shared context struct to share information across multiple templates within a workflow
Expand Down Expand Up @@ -49,6 +56,26 @@ func (ctx *Context) hasArgs() bool {
return ctx.isInitialized() && !ctx.args.IsEmpty()
}

// UseNetworkPort updates input with required/default network port for that template
// but is ignored if input/target contains non-http ports like 80,8080,8081 etc
func (ctx *Context) UseNetworkPort(port string) error {
if port == "" {
// if template does not contain port, do nothing
return nil
}
target, err := urlutil.Parse(ctx.MetaInput.Input)
if err != nil {
return err
}
inputPort := target.Port()
if inputPort == "" || stringsutil.EqualFoldAny(inputPort, reservedPorts...) {
// replace port with networkPort
target.UpdatePort(port)
ctx.MetaInput.Input = target.Host
}
return nil
}

// Get the value with specific key if exists
func (ctx *Context) Get(key string) (interface{}, bool) {
if !ctx.hasArgs() {
Expand Down
4 changes: 4 additions & 0 deletions v2/pkg/protocols/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ type Request struct {
// Inputs contains inputs for the network socket
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"`
// 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"`
// description: |
// ReadSize is the size of response to read at the end
//
// Default value for read-size is 1024.
Expand Down
10 changes: 9 additions & 1 deletion v2/pkg/protocols/network/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,18 @@ func (request *Request) Type() templateTypes.ProtocolType {
}

// ExecuteWithResults executes the protocol requests and returns results instead of writing them.
func (request *Request) ExecuteWithResults(input *contextargs.Context, metadata, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
func (request *Request) ExecuteWithResults(target *contextargs.Context, metadata, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
var address string
var err error

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); err != nil {
gologger.Debug().Msgf("Could not network port from constants: %s\n", err)
}

if request.SelfContained {
address = ""
} else {
Expand Down
Loading