mirror of https://github.com/daffainfo/nuclei.git
Merge pull request #2856 from projectdiscovery/sandbox-pr
Added sandboxing for payload files and requestsdev
commit
291a0fea94
|
@ -160,6 +160,7 @@ CONFIGURATIONS:
|
|||
-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
|
||||
-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
|
||||
-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
|
||||
|
|
|
@ -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.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.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.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"),
|
||||
|
|
|
@ -16,7 +16,7 @@ type PayloadGenerator struct {
|
|||
}
|
||||
|
||||
// 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() == "" {
|
||||
attackType = BatteringRamAttack
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ func New(payloads map[string]interface{}, attackType AttackType, templatePath st
|
|||
return nil, err
|
||||
}
|
||||
|
||||
compiled, err := generator.loadPayloads(payloadsFinal)
|
||||
compiled, err := generator.loadPayloads(payloadsFinal, templatePath, templateDirectory, sandbox)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ func TestBatteringRamGenerator(t *testing.T) {
|
|||
usernames := []string{"admin", "password"}
|
||||
|
||||
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")
|
||||
|
||||
iterator := generator.NewIterator()
|
||||
|
@ -32,7 +32,7 @@ func TestPitchforkGenerator(t *testing.T) {
|
|||
passwords := []string{"password1", "password2", "password3"}
|
||||
|
||||
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")
|
||||
|
||||
iterator := generator.NewIterator()
|
||||
|
@ -54,7 +54,7 @@ func TestClusterbombGenerator(t *testing.T) {
|
|||
passwords := []string{"admin", "password", "token"}
|
||||
|
||||
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")
|
||||
|
||||
iterator := generator.NewIterator()
|
||||
|
|
|
@ -3,6 +3,7 @@ package generators
|
|||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
@ -10,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
// 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)
|
||||
|
||||
for name, payload := range payloads {
|
||||
|
@ -21,6 +22,13 @@ func (generator *PayloadGenerator) loadPayloads(payloads map[string]interface{})
|
|||
if len(elements) >= 2 {
|
||||
loadedPayloads[name] = elements
|
||||
} 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)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not load payloads")
|
||||
|
|
|
@ -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"
|
||||
|
||||
"github.com/projectdiscovery/fastdialer/fastdialer"
|
||||
"github.com/projectdiscovery/networkpolicy"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
)
|
||||
|
||||
|
@ -90,6 +91,9 @@ func Init(options *types.Options) error {
|
|||
if options.ResolversFile != "" {
|
||||
opts.BaseResolvers = options.InternalResolversList
|
||||
}
|
||||
if options.Sandbox {
|
||||
opts.Deny = append(networkpolicy.DefaultIPv4DenylistRanges, networkpolicy.DefaultIPv6DenylistRanges...)
|
||||
}
|
||||
opts.WithDialerHistory = true
|
||||
opts.WithZTLS = options.ZTLS
|
||||
opts.SNIName = options.SNI
|
||||
|
|
|
@ -95,7 +95,7 @@ func (request *Request) Compile(options *protocols.ExecuterOptions) error {
|
|||
|
||||
if len(request.Payloads) > 0 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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`},
|
||||
}
|
||||
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")
|
||||
|
||||
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`},
|
||||
}
|
||||
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")
|
||||
|
||||
generator := req.newGenerator(false)
|
||||
|
|
|
@ -184,7 +184,7 @@ func (request *Request) Compile(options *protocols.ExecuterOptions) error {
|
|||
}
|
||||
|
||||
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 {
|
||||
return errors.Wrap(err, "could not parse payloads")
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ func (request *Request) Compile(options *protocols.ExecuterOptions) error {
|
|||
request.dialer = client
|
||||
|
||||
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 {
|
||||
return errors.Wrap(err, "could not parse payloads")
|
||||
}
|
||||
|
|
|
@ -240,6 +240,8 @@ type Options struct {
|
|||
ClientCAFile string
|
||||
// Use ZTLS library
|
||||
ZTLS bool
|
||||
// Sandbox enables sandboxed nuclei template execution
|
||||
Sandbox bool
|
||||
// ShowMatchLine enables display of match line number
|
||||
ShowMatchLine bool
|
||||
// EnablePprof enables exposing pprof runtime information with a webserver.
|
||||
|
|
Loading…
Reference in New Issue