diff --git a/cmd/cmd.go b/cmd/cmd.go index 3edd2bc..7d44fd2 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -15,7 +15,7 @@ import ( "time" ) -var ver = "v1.0.0" +var ver = "v1.0.1" var DefaultConfig = "config.yaml" func init() { @@ -44,13 +44,19 @@ func Spray() { WIKI: https://chainreactors.github.io/wiki/spray QUICKSTART: - simple example: + basic: + spray -u http://example.com + + basic cidr and port: + spray -i example -p top2,top3 + + simple brute: spray -u http://example.com -d wordlist1.txt -d wordlist2.txt - mask-base wordlist: + mask-base brute with wordlist: spray -u http://example.com -w "/aaa/bbb{?l#4}/ccc" - rule-base wordlist: + rule-base brute with wordlist: spray -u http://example.com -r rule.txt -d 1.txt list input spray: @@ -127,13 +133,6 @@ func Spray() { } ctx, canceler := context.WithTimeout(context.Background(), time.Duration(runner.Deadline)*time.Second) - - err = runner.Prepare(ctx) - if err != nil { - logs.Log.Errorf(err.Error()) - return - } - go func() { exitChan := make(chan os.Signal, 2) signal.Notify(exitChan, os.Interrupt, syscall.SIGTERM) @@ -154,10 +153,11 @@ func Spray() { }() }() - if runner.IsCheck { - runner.RunWithCheck(ctx) - } else { - runner.Run(ctx) + err = runner.Prepare(ctx) + if err != nil { + logs.Log.Errorf(err.Error()) + return } + time.Sleep(1 * time.Second) } diff --git a/go.mod b/go.mod index 120386e..98faacc 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/chainreactors/fingers v0.0.0-20240716172449-2fc3147b9c2a github.com/chainreactors/logs v0.0.0-20240207121836-c946f072f81f github.com/chainreactors/parsers v0.0.0-20240708072709-07deeece7ce2 - github.com/chainreactors/utils v0.0.0-20240716182459-e85f2b01ee16 + github.com/chainreactors/utils v0.0.0-20240805193040-ff3b97aa3c3f github.com/chainreactors/words v0.4.1-0.20240510105042-5ba5c2edc508 github.com/expr-lang/expr v1.16.9 github.com/gookit/config/v2 v2.2.5 diff --git a/go.sum b/go.sum index 7df73e9..aa9c067 100644 --- a/go.sum +++ b/go.sum @@ -98,6 +98,8 @@ github.com/chainreactors/utils v0.0.0-20240704062557-662d623b74f4/go.mod h1:JA4e github.com/chainreactors/utils v0.0.0-20240715080349-d2d0484c95ed/go.mod h1:LajXuvESQwP+qCMAvlcoSXppQCjuLlBrnQpu9XQ1HtU= github.com/chainreactors/utils v0.0.0-20240716182459-e85f2b01ee16 h1:TCOshCp7PrWqhP/HSAM5kT3VxoOe7EoJbRseyoSX3RM= github.com/chainreactors/utils v0.0.0-20240716182459-e85f2b01ee16/go.mod h1:LajXuvESQwP+qCMAvlcoSXppQCjuLlBrnQpu9XQ1HtU= +github.com/chainreactors/utils v0.0.0-20240805193040-ff3b97aa3c3f h1:2NKmadFYP9vCwC0YrazgttFACleOhxScTPzg0i76YAY= +github.com/chainreactors/utils v0.0.0-20240805193040-ff3b97aa3c3f/go.mod h1:LajXuvESQwP+qCMAvlcoSXppQCjuLlBrnQpu9XQ1HtU= github.com/chainreactors/words v0.4.1-0.20240510105042-5ba5c2edc508 h1:iT4HWkoZzUAfQYcQMRH8XyrMau9tCVE0zSuFQnkhrqw= github.com/chainreactors/words v0.4.1-0.20240510105042-5ba5c2edc508/go.mod h1:DUDx7PdsMEm5PvVhzkFyppzpiUhQb8dOJaWjVc1SMVk= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= diff --git a/internal/ihttp/response.go b/internal/ihttp/response.go index 1de39af..3d2089e 100644 --- a/internal/ihttp/response.go +++ b/internal/ihttp/response.go @@ -1,8 +1,8 @@ package ihttp import ( - "bytes" "github.com/chainreactors/logs" + "github.com/chainreactors/utils/httputils" "github.com/valyala/fasthttp" "io" "net/http" @@ -93,15 +93,7 @@ func (r *Response) Header() []byte { if r.FastResponse != nil { return r.FastResponse.Header.Header() } else if r.StandardResponse != nil { - var header bytes.Buffer - header.WriteString(r.StandardResponse.Proto + " " + r.StandardResponse.Status) - for k, v := range r.StandardResponse.Header { - for _, i := range v { - header.WriteString(k + ": " + i + "\r\n") - } - } - header.WriteString("\r\n") - return header.Bytes() + return append(httputils.ReadRawHeader(r.StandardResponse), []byte("\r\n")...) } else { return nil } diff --git a/internal/option.go b/internal/option.go index 5a33e3d..e1d0b32 100644 --- a/internal/option.go +++ b/internal/option.go @@ -111,6 +111,7 @@ type PluginOptions struct { Advance bool `short:"a" long:"advance" description:"Bool, enable all plugin" config:"all" ` Extracts []string `long:"extract" description:"Strings, extract response, e.g.: --extract js --extract ip --extract version:(.*?)" config:"extract"` ExtractConfig string `long:"extract-config" description:"String, extract config filename" config:"extract-config"` + Active bool `long:"active" description:"Bool, enable active finger path"` Recon bool `long:"recon" description:"Bool, enable recon" config:"recon"` Bak bool `long:"bak" description:"Bool, enable bak found" config:"bak"` FileBak bool `long:"file-bak" description:"Bool, enable valid result bak found, equal --append-rule rule/filebak.txt" config:"file-bak"` @@ -312,28 +313,45 @@ func (opt *Option) NewRunner() (*Runner, error) { pkg.Extractors["recon"] = pkg.ExtractRegexps["pentest"] } + if opt.Finger { + pkg.EnableAllFingerEngine = true + } + + // brute only if opt.Advance { r.Crawl = true r.Finger = true r.Bak = true r.Common = true + r.Active = true pkg.EnableAllFingerEngine = true pkg.Extractors["recon"] = pkg.ExtractRegexps["pentest"] + r.IsCheck = false opt.AppendRule = append(opt.AppendRule, "filebak") } if opt.FileBak { + r.IsCheck = false opt.AppendRule = append(opt.AppendRule, "filebak") } if opt.Common { + r.IsCheck = false r.AppendWords = append(r.AppendWords, mask.SpecialWords["common_file"]...) } - if opt.Finger { + + if opt.Active { + r.IsCheck = false r.AppendWords = append(r.AppendWords, pkg.ActivePath...) - pkg.EnableAllFingerEngine = true + } + + if opt.Crawl { + r.IsCheck = false } opt.PrintPlugin() + if r.IsCheck == false { + logs.Log.Important("enabling brute mod, because of enabled brute plugin") + } if opt.NoScope { r.Scope = []string{"*"} @@ -497,6 +515,7 @@ func (opt *Option) PrintPlugin() { if opt.RetryCount > 0 { s.WriteString("Retry Count: " + strconv.Itoa(opt.RetryCount)) } + if s.Len() > 0 { logs.Log.Important(s.String()) } @@ -521,7 +540,8 @@ func (opt *Option) BuildWords(r *Runner) error { logs.Log.Logf(pkg.LogVerbose, "Loaded %d word from %s", len(dicts[i]), f) } - if len(dicts) == 0 { + + if len(dicts) == 0 && opt.Word == "" { r.IsCheck = true } diff --git a/internal/pool/checkpool.go b/internal/pool/checkpool.go index a351aa6..45bc087 100644 --- a/internal/pool/checkpool.go +++ b/internal/pool/checkpool.go @@ -50,35 +50,21 @@ type CheckPool struct { func (pool *CheckPool) Run(ctx context.Context, offset, limit int) { pool.Worder.Run() - var done bool - // 挂起一个监控goroutine, 每100ms判断一次done, 如果已经done, 则关闭closeCh, 然后通过Loop中的select case closeCh去break, 实现退出 - go func() { - for { - if done { - pool.wg.Wait() - close(pool.closeCh) - return - } - time.Sleep(100 * time.Millisecond) - } - }() - Loop: for { select { case u, ok := <-pool.Worder.C: if !ok { - done = true - continue + break Loop } if pool.reqCount < offset { pool.reqCount++ - continue + break Loop } if pool.reqCount > limit { - continue + break Loop } pool.wg.Add(1) @@ -96,7 +82,7 @@ Loop: break Loop } } - + pool.wg.Wait() pool.Close() } diff --git a/internal/runner.go b/internal/runner.go index c412057..aeeae11 100644 --- a/internal/runner.go +++ b/internal/runner.go @@ -111,6 +111,7 @@ func (r *Runner) AppendFunction(fn func(string) []string) { } func (r *Runner) Prepare(ctx context.Context) error { + r.OutputHandler() var err error if r.IsCheck { // 仅check, 类似httpx @@ -138,6 +139,7 @@ func (r *Runner) Prepare(ctx context.Context) error { checkPool.Run(ctx, r.Offset, r.Count) r.poolwg.Done() }) + r.RunWithCheck(ctx) } else { // 完整探测模式 go func() { @@ -214,12 +216,12 @@ func (r *Runner) Prepare(ctx context.Context) error { r.PrintStat(brutePool) r.Done() }) + r.Run(ctx) } - if err != nil { return err } - r.OutputHandler() + return nil } diff --git a/pkg/baseline.go b/pkg/baseline.go index 350e066..0099040 100644 --- a/pkg/baseline.go +++ b/pkg/baseline.go @@ -36,9 +36,10 @@ func NewBaseline(u, host string, resp *ihttp.Response) *Baseline { bl.HeaderLength = len(bl.Header) if i := resp.ContentLength(); ihttp.CheckBodySize(i) { - body := resp.Body() - bl.Body = make([]byte, len(body)) - copy(bl.Body, body) + if body := resp.Body(); body != nil { + bl.Body = make([]byte, len(body)) + copy(bl.Body, body) + } if i == -1 { bl.Chunked = true diff --git a/templates b/templates index 3e85234..f2980b8 160000 --- a/templates +++ b/templates @@ -1 +1 @@ -Subproject commit 3e85234341b95f7e6e45b31468311f01093ac970 +Subproject commit f2980b8d312c8088f3947d914499e96cfc40d975