mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-22 06:15:26 +00:00
Merge pull request #2856 from projectdiscovery/sandbox-pr
Added sandboxing for payload files and requests
This commit is contained in:
commit
291a0fea94
@ -160,6 +160,7 @@ CONFIGURATIONS:
|
|||||||
-sml, -show-match-line show match lines for file templates, works with extractors only
|
-sml, -show-match-line show match lines for file templates, works with extractors only
|
||||||
-ztls use ztls library with autofallback to standard one for tls13
|
-ztls use ztls library with autofallback to standard one for tls13
|
||||||
-sni string tls sni hostname to use (default: input domain name)
|
-sni string tls sni hostname to use (default: input domain name)
|
||||||
|
-sandbox sandbox nuclei for safe templates execution
|
||||||
-i, -interface string network interface to use for network scan
|
-i, -interface string network interface to use for network scan
|
||||||
-at, -attack-type string type of payload combinations to perform (batteringram,pitchfork,clusterbomb)
|
-at, -attack-type string type of payload combinations to perform (batteringram,pitchfork,clusterbomb)
|
||||||
-sip, -source-ip string source ip address to use for network scan
|
-sip, -source-ip string source ip address to use for network scan
|
||||||
|
|||||||
@ -196,6 +196,7 @@ on extensive configurability, massive extensibility and ease of use.`)
|
|||||||
flagSet.BoolVarP(&options.ShowMatchLine, "show-match-line", "sml", false, "show match lines for file templates, works with extractors only"),
|
flagSet.BoolVarP(&options.ShowMatchLine, "show-match-line", "sml", false, "show match lines for file templates, works with extractors only"),
|
||||||
flagSet.BoolVar(&options.ZTLS, "ztls", false, "use ztls library with autofallback to standard one for tls13"),
|
flagSet.BoolVar(&options.ZTLS, "ztls", false, "use ztls library with autofallback to standard one for tls13"),
|
||||||
flagSet.StringVar(&options.SNI, "sni", "", "tls sni hostname to use (default: input domain name)"),
|
flagSet.StringVar(&options.SNI, "sni", "", "tls sni hostname to use (default: input domain name)"),
|
||||||
|
flagSet.BoolVar(&options.Sandbox, "sandbox", false, "sandbox nuclei for safe templates execution"),
|
||||||
flagSet.StringVarP(&options.Interface, "interface", "i", "", "network interface to use for network scan"),
|
flagSet.StringVarP(&options.Interface, "interface", "i", "", "network interface to use for network scan"),
|
||||||
flagSet.StringVarP(&options.AttackType, "attack-type", "at", "", "type of payload combinations to perform (batteringram,pitchfork,clusterbomb)"),
|
flagSet.StringVarP(&options.AttackType, "attack-type", "at", "", "type of payload combinations to perform (batteringram,pitchfork,clusterbomb)"),
|
||||||
flagSet.StringVarP(&options.SourceIP, "source-ip", "sip", "", "source ip address to use for network scan"),
|
flagSet.StringVarP(&options.SourceIP, "source-ip", "sip", "", "source ip address to use for network scan"),
|
||||||
|
|||||||
@ -16,7 +16,7 @@ type PayloadGenerator struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new generator structure for payload generation
|
// New creates a new generator structure for payload generation
|
||||||
func New(payloads map[string]interface{}, attackType AttackType, templatePath string, catalog catalog.Catalog, customAttackType string) (*PayloadGenerator, error) {
|
func New(payloads map[string]interface{}, attackType AttackType, templatePath, templateDirectory string, sandbox bool, catalog catalog.Catalog, customAttackType string) (*PayloadGenerator, error) {
|
||||||
if attackType.String() == "" {
|
if attackType.String() == "" {
|
||||||
attackType = BatteringRamAttack
|
attackType = BatteringRamAttack
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ func New(payloads map[string]interface{}, attackType AttackType, templatePath st
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
compiled, err := generator.loadPayloads(payloadsFinal)
|
compiled, err := generator.loadPayloads(payloadsFinal, templatePath, templateDirectory, sandbox)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ func TestBatteringRamGenerator(t *testing.T) {
|
|||||||
usernames := []string{"admin", "password"}
|
usernames := []string{"admin", "password"}
|
||||||
|
|
||||||
catalogInstance := disk.NewCatalog("")
|
catalogInstance := disk.NewCatalog("")
|
||||||
generator, err := New(map[string]interface{}{"username": usernames}, BatteringRamAttack, "", catalogInstance, "")
|
generator, err := New(map[string]interface{}{"username": usernames}, BatteringRamAttack, "", "", false, catalogInstance, "")
|
||||||
require.Nil(t, err, "could not create generator")
|
require.Nil(t, err, "could not create generator")
|
||||||
|
|
||||||
iterator := generator.NewIterator()
|
iterator := generator.NewIterator()
|
||||||
@ -32,7 +32,7 @@ func TestPitchforkGenerator(t *testing.T) {
|
|||||||
passwords := []string{"password1", "password2", "password3"}
|
passwords := []string{"password1", "password2", "password3"}
|
||||||
|
|
||||||
catalogInstance := disk.NewCatalog("")
|
catalogInstance := disk.NewCatalog("")
|
||||||
generator, err := New(map[string]interface{}{"username": usernames, "password": passwords}, PitchForkAttack, "", catalogInstance, "")
|
generator, err := New(map[string]interface{}{"username": usernames, "password": passwords}, PitchForkAttack, "", "", false, catalogInstance, "")
|
||||||
require.Nil(t, err, "could not create generator")
|
require.Nil(t, err, "could not create generator")
|
||||||
|
|
||||||
iterator := generator.NewIterator()
|
iterator := generator.NewIterator()
|
||||||
@ -54,7 +54,7 @@ func TestClusterbombGenerator(t *testing.T) {
|
|||||||
passwords := []string{"admin", "password", "token"}
|
passwords := []string{"admin", "password", "token"}
|
||||||
|
|
||||||
catalogInstance := disk.NewCatalog("")
|
catalogInstance := disk.NewCatalog("")
|
||||||
generator, err := New(map[string]interface{}{"username": usernames, "password": passwords}, ClusterBombAttack, "", catalogInstance, "")
|
generator, err := New(map[string]interface{}{"username": usernames, "password": passwords}, ClusterBombAttack, "", "", false, catalogInstance, "")
|
||||||
require.Nil(t, err, "could not create generator")
|
require.Nil(t, err, "could not create generator")
|
||||||
|
|
||||||
iterator := generator.NewIterator()
|
iterator := generator.NewIterator()
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package generators
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"io"
|
"io"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -10,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// loadPayloads loads the input payloads from a map to a data map
|
// loadPayloads loads the input payloads from a map to a data map
|
||||||
func (generator *PayloadGenerator) loadPayloads(payloads map[string]interface{}) (map[string][]string, error) {
|
func (generator *PayloadGenerator) loadPayloads(payloads map[string]interface{}, templatePath, templateDirectory string, sandbox bool) (map[string][]string, error) {
|
||||||
loadedPayloads := make(map[string][]string)
|
loadedPayloads := make(map[string][]string)
|
||||||
|
|
||||||
for name, payload := range payloads {
|
for name, payload := range payloads {
|
||||||
@ -21,6 +22,13 @@ func (generator *PayloadGenerator) loadPayloads(payloads map[string]interface{})
|
|||||||
if len(elements) >= 2 {
|
if len(elements) >= 2 {
|
||||||
loadedPayloads[name] = elements
|
loadedPayloads[name] = elements
|
||||||
} else {
|
} else {
|
||||||
|
if sandbox {
|
||||||
|
pt = filepath.Clean(pt)
|
||||||
|
templatePathDir := filepath.Dir(templatePath)
|
||||||
|
if !(templatePathDir != "/" && strings.HasPrefix(pt, templatePathDir)) && !strings.HasPrefix(pt, templateDirectory) {
|
||||||
|
return nil, errors.New("denied payload file path specified")
|
||||||
|
}
|
||||||
|
}
|
||||||
payloads, err := generator.loadPayloadsFromFile(pt)
|
payloads, err := generator.loadPayloadsFromFile(pt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "could not load payloads")
|
return nil, errors.Wrap(err, "could not load payloads")
|
||||||
|
|||||||
57
v2/pkg/protocols/common/generators/load_test.go
Normal file
57
v2/pkg/protocols/common/generators/load_test.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package generators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/disk"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoadPayloads(t *testing.T) {
|
||||||
|
tempdir, err := os.MkdirTemp("", "templates-*")
|
||||||
|
require.NoError(t, err, "could not create temp dir")
|
||||||
|
defer os.RemoveAll(tempdir)
|
||||||
|
|
||||||
|
generator := &PayloadGenerator{catalog: disk.NewCatalog(tempdir)}
|
||||||
|
|
||||||
|
fullpath := filepath.Join(tempdir, "payloads.txt")
|
||||||
|
err = os.WriteFile(fullpath, []byte("test\nanother"), 0777)
|
||||||
|
require.NoError(t, err, "could not write payload")
|
||||||
|
|
||||||
|
// Test sandbox
|
||||||
|
t.Run("templates-directory", func(t *testing.T) {
|
||||||
|
values, err := generator.loadPayloads(map[string]interface{}{
|
||||||
|
"new": fullpath,
|
||||||
|
}, "/test", tempdir, true)
|
||||||
|
require.NoError(t, err, "could not load payloads")
|
||||||
|
require.Equal(t, map[string][]string{"new": {"test", "another"}}, values, "could not get values")
|
||||||
|
})
|
||||||
|
t.Run("template-directory", func(t *testing.T) {
|
||||||
|
values, err := generator.loadPayloads(map[string]interface{}{
|
||||||
|
"new": fullpath,
|
||||||
|
}, filepath.Join(tempdir, "test.yaml"), "/test", true)
|
||||||
|
require.NoError(t, err, "could not load payloads")
|
||||||
|
require.Equal(t, map[string][]string{"new": {"test", "another"}}, values, "could not get values")
|
||||||
|
})
|
||||||
|
t.Run("no-sandbox", func(t *testing.T) {
|
||||||
|
_, err := generator.loadPayloads(map[string]interface{}{
|
||||||
|
"new": "/etc/passwd",
|
||||||
|
}, "/random", "/test", false)
|
||||||
|
require.NoError(t, err, "could load payloads")
|
||||||
|
})
|
||||||
|
t.Run("invalid", func(t *testing.T) {
|
||||||
|
values, err := generator.loadPayloads(map[string]interface{}{
|
||||||
|
"new": "/etc/passwd",
|
||||||
|
}, "/random", "/test", true)
|
||||||
|
require.Error(t, err, "could load payloads")
|
||||||
|
require.Equal(t, 0, len(values), "could get values")
|
||||||
|
|
||||||
|
values, err = generator.loadPayloads(map[string]interface{}{
|
||||||
|
"new": fullpath,
|
||||||
|
}, "/random", "/test", true)
|
||||||
|
require.Error(t, err, "could load payloads")
|
||||||
|
require.Equal(t, 0, len(values), "could get values")
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -9,6 +9,7 @@ import (
|
|||||||
"golang.org/x/net/proxy"
|
"golang.org/x/net/proxy"
|
||||||
|
|
||||||
"github.com/projectdiscovery/fastdialer/fastdialer"
|
"github.com/projectdiscovery/fastdialer/fastdialer"
|
||||||
|
"github.com/projectdiscovery/networkpolicy"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -90,6 +91,9 @@ func Init(options *types.Options) error {
|
|||||||
if options.ResolversFile != "" {
|
if options.ResolversFile != "" {
|
||||||
opts.BaseResolvers = options.InternalResolversList
|
opts.BaseResolvers = options.InternalResolversList
|
||||||
}
|
}
|
||||||
|
if options.Sandbox {
|
||||||
|
opts.Deny = append(networkpolicy.DefaultIPv4DenylistRanges, networkpolicy.DefaultIPv6DenylistRanges...)
|
||||||
|
}
|
||||||
opts.WithDialerHistory = true
|
opts.WithDialerHistory = true
|
||||||
opts.WithZTLS = options.ZTLS
|
opts.WithZTLS = options.ZTLS
|
||||||
opts.SNIName = options.SNI
|
opts.SNIName = options.SNI
|
||||||
|
|||||||
@ -95,7 +95,7 @@ func (request *Request) Compile(options *protocols.ExecuterOptions) error {
|
|||||||
|
|
||||||
if len(request.Payloads) > 0 {
|
if len(request.Payloads) > 0 {
|
||||||
var err error
|
var err error
|
||||||
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, options.TemplatePath, options.Catalog, options.Options.AttackType)
|
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, options.TemplatePath, options.Options.TemplatesDirectory, options.Options.Sandbox, options.Catalog, options.Options.AttackType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not parse payloads")
|
return errors.Wrap(err, "could not parse payloads")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -350,7 +350,7 @@ func (request *Request) Compile(options *protocols.ExecuterOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(request.Payloads) > 0 {
|
if len(request.Payloads) > 0 {
|
||||||
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, request.options.TemplatePath, request.options.Catalog, request.options.Options.AttackType)
|
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, request.options.TemplatePath, request.options.Options.TemplatesDirectory, request.options.Options.Sandbox, request.options.Catalog, request.options.Options.AttackType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not parse payloads")
|
return errors.Wrap(err, "could not parse payloads")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,7 +34,7 @@ func TestRequestGeneratorClusterBombSingle(t *testing.T) {
|
|||||||
Raw: []string{`GET /{{username}}:{{password}} HTTP/1.1`},
|
Raw: []string{`GET /{{username}}:{{password}} HTTP/1.1`},
|
||||||
}
|
}
|
||||||
catalogInstance := disk.NewCatalog("")
|
catalogInstance := disk.NewCatalog("")
|
||||||
req.generator, err = generators.New(req.Payloads, req.AttackType.Value, "", catalogInstance, "")
|
req.generator, err = generators.New(req.Payloads, req.AttackType.Value, "", "", false, catalogInstance, "")
|
||||||
require.Nil(t, err, "could not create generator")
|
require.Nil(t, err, "could not create generator")
|
||||||
|
|
||||||
generator := req.newGenerator(false)
|
generator := req.newGenerator(false)
|
||||||
@ -58,7 +58,7 @@ func TestRequestGeneratorClusterBombMultipleRaw(t *testing.T) {
|
|||||||
Raw: []string{`GET /{{username}}:{{password}} HTTP/1.1`, `GET /{{username}}@{{password}} HTTP/1.1`},
|
Raw: []string{`GET /{{username}}:{{password}} HTTP/1.1`, `GET /{{username}}@{{password}} HTTP/1.1`},
|
||||||
}
|
}
|
||||||
catalogInstance := disk.NewCatalog("")
|
catalogInstance := disk.NewCatalog("")
|
||||||
req.generator, err = generators.New(req.Payloads, req.AttackType.Value, "", catalogInstance, "")
|
req.generator, err = generators.New(req.Payloads, req.AttackType.Value, "", "", false, catalogInstance, "")
|
||||||
require.Nil(t, err, "could not create generator")
|
require.Nil(t, err, "could not create generator")
|
||||||
|
|
||||||
generator := req.newGenerator(false)
|
generator := req.newGenerator(false)
|
||||||
|
|||||||
@ -184,7 +184,7 @@ func (request *Request) Compile(options *protocols.ExecuterOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(request.Payloads) > 0 {
|
if len(request.Payloads) > 0 {
|
||||||
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, request.options.TemplatePath, request.options.Catalog, request.options.Options.AttackType)
|
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, request.options.TemplatePath, request.options.Options.TemplatesDirectory, request.options.Options.Sandbox, request.options.Catalog, request.options.Options.AttackType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not parse payloads")
|
return errors.Wrap(err, "could not parse payloads")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,7 +104,7 @@ func (request *Request) Compile(options *protocols.ExecuterOptions) error {
|
|||||||
request.dialer = client
|
request.dialer = client
|
||||||
|
|
||||||
if len(request.Payloads) > 0 {
|
if len(request.Payloads) > 0 {
|
||||||
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, request.options.TemplatePath, options.Catalog, options.Options.AttackType)
|
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, request.options.TemplatePath, request.options.Options.TemplatesDirectory, request.options.Options.Sandbox, options.Catalog, options.Options.AttackType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not parse payloads")
|
return errors.Wrap(err, "could not parse payloads")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -240,6 +240,8 @@ type Options struct {
|
|||||||
ClientCAFile string
|
ClientCAFile string
|
||||||
// Use ZTLS library
|
// Use ZTLS library
|
||||||
ZTLS bool
|
ZTLS bool
|
||||||
|
// Sandbox enables sandboxed nuclei template execution
|
||||||
|
Sandbox bool
|
||||||
// ShowMatchLine enables display of match line number
|
// ShowMatchLine enables display of match line number
|
||||||
ShowMatchLine bool
|
ShowMatchLine bool
|
||||||
// EnablePprof enables exposing pprof runtime information with a webserver.
|
// EnablePprof enables exposing pprof runtime information with a webserver.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user