Merge pull request #2856 from projectdiscovery/sandbox-pr

Added sandboxing for payload files and requests
dev
Ice3man 2022-11-24 14:07:33 +05:30 committed by GitHub
commit 291a0fea94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 85 additions and 12 deletions

View File

@ -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

View File

@ -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"),

View File

@ -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
}

View File

@ -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()

View File

@ -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")

View 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")
})
}

View File

@ -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

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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)

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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.