mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-17 21:55:26 +00:00
* feat: added initial live DAST server implementation * feat: more logging + misc additions * feat: auth file support enhancements for more complex scenarios + misc * feat: added io.Reader support to input providers for http * feat: added stats db to fuzzing + use sdk for dast server + misc * feat: more additions and enhancements * misc changes to live server * misc * use utils pprof server * feat: added simpler stats tracking system * feat: fixed analyzer timeout issue + missing case fix * misc changes fix * feat: changed the logics a bit + misc changes and additions * feat: re-added slope checks + misc * feat: added baseline measurements for time based checks * chore(server): fix typos Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix(templates): potential DOM XSS Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix(authx): potential NIL deref Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * feat: misc review changes * removed debug logging * feat: remove existing cookies only * feat: lint fixes * misc * misc text update * request endpoint update * feat: added tracking for status code, waf-detection & grouped errors (#6028) * feat: added tracking for status code, waf-detection & grouped errors * lint error fixes * feat: review changes + moving to package + misc --------- Co-authored-by: sandeep <8293321+ehsandeep@users.noreply.github.com> * fix var dump (#5921) * fix var dump * fix dump test * Added filename length restriction for debug mode (-srd flag) (#5931) Co-authored-by: Andrey Matveenko <an.matveenko@vkteam.ru> * more updates * Update pkg/output/stats/waf/waf.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: sandeep <8293321+ehsandeep@users.noreply.github.com> Co-authored-by: Dwi Siswanto <25837540+dwisiswant0@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Dogan Can Bakir <65292895+dogancanbakir@users.noreply.github.com> Co-authored-by: 9flowers <51699499+Lercas@users.noreply.github.com> Co-authored-by: Andrey Matveenko <an.matveenko@vkteam.ru> Co-authored-by: Sandeep Singh <sandeep@projectdiscovery.io>
200 lines
6.5 KiB
Go
200 lines
6.5 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
_ "net/http/pprof"
|
|
"strings"
|
|
|
|
"github.com/logrusorgru/aurora"
|
|
"github.com/projectdiscovery/gologger"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/frequency"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/stats"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/input/formats"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/input/provider/http"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/projectfile"
|
|
"gopkg.in/yaml.v3"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/projectdiscovery/ratelimit"
|
|
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/catalog"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/core"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/input"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/loader/parser"
|
|
parsers "github.com/projectdiscovery/nuclei/v3/pkg/loader/workflow"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/globalmatchers"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/hosterrorscache"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/utils/excludematchers"
|
|
browserEngine "github.com/projectdiscovery/nuclei/v3/pkg/protocols/headless/engine"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/reporting"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
|
)
|
|
|
|
type nucleiExecutor struct {
|
|
engine *core.Engine
|
|
store *loader.Store
|
|
options *NucleiExecutorOptions
|
|
executorOpts protocols.ExecutorOptions
|
|
}
|
|
|
|
type NucleiExecutorOptions struct {
|
|
Options *types.Options
|
|
Output output.Writer
|
|
Progress progress.Progress
|
|
Catalog catalog.Catalog
|
|
IssuesClient reporting.Client
|
|
RateLimiter *ratelimit.Limiter
|
|
Interactsh *interactsh.Client
|
|
ProjectFile *projectfile.ProjectFile
|
|
Browser *browserEngine.Browser
|
|
FuzzStatsDB *stats.Tracker
|
|
Colorizer aurora.Aurora
|
|
Parser parser.Parser
|
|
TemporaryDirectory string
|
|
}
|
|
|
|
func newNucleiExecutor(opts *NucleiExecutorOptions) (*nucleiExecutor, error) {
|
|
fuzzFreqCache := frequency.New(frequency.DefaultMaxTrackCount, opts.Options.FuzzParamFrequency)
|
|
resumeCfg := types.NewResumeCfg()
|
|
|
|
// Create the executor options which will be used throughout the execution
|
|
// stage by the nuclei engine modules.
|
|
executorOpts := protocols.ExecutorOptions{
|
|
Output: opts.Output,
|
|
Options: opts.Options,
|
|
Progress: opts.Progress,
|
|
Catalog: opts.Catalog,
|
|
IssuesClient: opts.IssuesClient,
|
|
RateLimiter: opts.RateLimiter,
|
|
Interactsh: opts.Interactsh,
|
|
ProjectFile: opts.ProjectFile,
|
|
Browser: opts.Browser,
|
|
Colorizer: opts.Colorizer,
|
|
ResumeCfg: resumeCfg,
|
|
ExcludeMatchers: excludematchers.New(opts.Options.ExcludeMatchers),
|
|
InputHelper: input.NewHelper(),
|
|
TemporaryDirectory: opts.TemporaryDirectory,
|
|
Parser: opts.Parser,
|
|
FuzzParamsFrequency: fuzzFreqCache,
|
|
GlobalMatchers: globalmatchers.New(),
|
|
FuzzStatsDB: opts.FuzzStatsDB,
|
|
}
|
|
|
|
if opts.Options.ShouldUseHostError() {
|
|
maxHostError := opts.Options.MaxHostError
|
|
if maxHostError == 30 {
|
|
maxHostError = 100 // auto adjust for fuzzings
|
|
}
|
|
if opts.Options.TemplateThreads > maxHostError {
|
|
gologger.Info().Msgf("Adjusting max-host-error to the concurrency value: %d", opts.Options.TemplateThreads)
|
|
|
|
maxHostError = opts.Options.TemplateThreads
|
|
}
|
|
|
|
cache := hosterrorscache.New(maxHostError, hosterrorscache.DefaultMaxHostsCount, opts.Options.TrackError)
|
|
cache.SetVerbose(opts.Options.Verbose)
|
|
|
|
executorOpts.HostErrorsCache = cache
|
|
}
|
|
|
|
executorEngine := core.New(opts.Options)
|
|
executorEngine.SetExecuterOptions(executorOpts)
|
|
|
|
workflowLoader, err := parsers.NewLoader(&executorOpts)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "Could not create loader options.")
|
|
}
|
|
executorOpts.WorkflowLoader = workflowLoader
|
|
|
|
// If using input-file flags, only load http fuzzing based templates.
|
|
loaderConfig := loader.NewConfig(opts.Options, opts.Catalog, executorOpts)
|
|
if !strings.EqualFold(opts.Options.InputFileMode, "list") || opts.Options.DAST || opts.Options.DASTServer {
|
|
// if input type is not list (implicitly enable fuzzing)
|
|
opts.Options.DAST = true
|
|
}
|
|
store, err := loader.New(loaderConfig)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "Could not create loader options.")
|
|
}
|
|
store.Load()
|
|
|
|
return &nucleiExecutor{
|
|
engine: executorEngine,
|
|
store: store,
|
|
options: opts,
|
|
executorOpts: executorOpts,
|
|
}, nil
|
|
}
|
|
|
|
// proxifyRequest is a request for proxify
|
|
type proxifyRequest struct {
|
|
URL string `json:"url"`
|
|
Request struct {
|
|
Header map[string]string `json:"header"`
|
|
Body string `json:"body"`
|
|
Raw string `json:"raw"`
|
|
} `json:"request"`
|
|
}
|
|
|
|
func (n *nucleiExecutor) ExecuteScan(target PostRequestsHandlerRequest) error {
|
|
finalTemplates := []*templates.Template{}
|
|
finalTemplates = append(finalTemplates, n.store.Templates()...)
|
|
finalTemplates = append(finalTemplates, n.store.Workflows()...)
|
|
|
|
if len(finalTemplates) == 0 {
|
|
return errors.New("no templates provided for scan")
|
|
}
|
|
|
|
payload := proxifyRequest{
|
|
URL: target.URL,
|
|
Request: struct {
|
|
Header map[string]string `json:"header"`
|
|
Body string `json:"body"`
|
|
Raw string `json:"raw"`
|
|
}{
|
|
Raw: target.RawHTTP,
|
|
},
|
|
}
|
|
|
|
marshalledYaml, err := yaml.Marshal(payload)
|
|
if err != nil {
|
|
return fmt.Errorf("error marshalling yaml: %s", err)
|
|
}
|
|
|
|
inputProvider, err := http.NewHttpInputProvider(&http.HttpMultiFormatOptions{
|
|
InputContents: string(marshalledYaml),
|
|
InputMode: "yaml",
|
|
Options: formats.InputFormatOptions{
|
|
Variables: make(map[string]interface{}),
|
|
},
|
|
})
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not create input provider")
|
|
}
|
|
|
|
// We don't care about the result as its a boolean
|
|
// stating whether we got matches or not
|
|
_ = n.engine.ExecuteScanWithOpts(context.Background(), finalTemplates, inputProvider, true)
|
|
return nil
|
|
}
|
|
|
|
func (n *nucleiExecutor) Close() {
|
|
if n.executorOpts.FuzzStatsDB != nil {
|
|
n.executorOpts.FuzzStatsDB.Close()
|
|
}
|
|
if n.options.Interactsh != nil {
|
|
_ = n.options.Interactsh.Close()
|
|
}
|
|
if n.executorOpts.InputHelper != nil {
|
|
_ = n.executorOpts.InputHelper.Close()
|
|
}
|
|
|
|
}
|