refactor: remove internal pkg, use engine replace

fix: chunk mod not read
fix: nil bar panic
enhance: add default accept and user-agent
This commit is contained in:
M09Ic 2025-02-22 20:31:32 +08:00
parent c07c2305af
commit f1b9400e19
18 changed files with 127 additions and 107 deletions

View File

@ -5,8 +5,8 @@ import (
"fmt" "fmt"
"github.com/chainreactors/files" "github.com/chainreactors/files"
"github.com/chainreactors/logs" "github.com/chainreactors/logs"
"github.com/chainreactors/spray/internal" "github.com/chainreactors/spray/core"
"github.com/chainreactors/spray/internal/ihttp" "github.com/chainreactors/spray/core/ihttp"
"github.com/chainreactors/spray/pkg" "github.com/chainreactors/spray/pkg"
"github.com/chainreactors/utils/iutils" "github.com/chainreactors/utils/iutils"
"github.com/jessevdk/go-flags" "github.com/jessevdk/go-flags"
@ -28,11 +28,11 @@ func init() {
} }
func Spray() { func Spray() {
var option internal.Option var option core.Option
if files.IsExist(DefaultConfig) { if files.IsExist(DefaultConfig) {
logs.Log.Debug("config.yaml exist, loading") logs.Log.Debug("config.yaml exist, loading")
err := internal.LoadConfig(DefaultConfig, &option) err := core.LoadConfig(DefaultConfig, &option)
if err != nil { if err != nil {
logs.Log.Error(err.Error()) logs.Log.Error(err.Error())
return return
@ -83,7 +83,7 @@ func Spray() {
logs.Log.SetLevel(pkg.LogVerbose) logs.Log.SetLevel(pkg.LogVerbose)
} }
if option.InitConfig { if option.InitConfig {
configStr := internal.InitDefaultConfig(&option, 0) configStr := core.InitDefaultConfig(&option, 0)
err := os.WriteFile(DefaultConfig, []byte(configStr), 0o744) err := os.WriteFile(DefaultConfig, []byte(configStr), 0o744)
if err != nil { if err != nil {
logs.Log.Warn("cannot create config: config.yaml, " + err.Error()) logs.Log.Warn("cannot create config: config.yaml, " + err.Error())
@ -96,7 +96,7 @@ func Spray() {
return return
} }
if option.Config != "" { if option.Config != "" {
err := internal.LoadConfig(option.Config, &option) err := core.LoadConfig(option.Config, &option)
if err != nil { if err != nil {
logs.Log.Error(err.Error()) logs.Log.Error(err.Error())
return return
@ -123,13 +123,13 @@ func Spray() {
if err != nil { if err != nil {
iutils.Fatal(err.Error()) iutils.Fatal(err.Error())
} }
internal.PrintPreset() core.PrintPreset()
return return
} }
if option.Format != "" { if option.Format != "" {
internal.Format(option) core.Format(option)
return return
} }

View File

@ -1,14 +1,16 @@
package pkg package baseline
import ( import (
"bytes" "bytes"
"github.com/chainreactors/fingers/common" "github.com/chainreactors/fingers/common"
"github.com/chainreactors/parsers" "github.com/chainreactors/parsers"
"github.com/chainreactors/spray/internal/ihttp" "github.com/chainreactors/spray/core/ihttp"
"github.com/chainreactors/spray/pkg"
"github.com/chainreactors/utils/encode" "github.com/chainreactors/utils/encode"
"github.com/chainreactors/utils/iutils" "github.com/chainreactors/utils/iutils"
"net/http" "net/http"
"net/url" "net/url"
"strconv"
"strings" "strings"
) )
@ -23,7 +25,7 @@ func NewBaseline(u, host string, resp *ihttp.Response) *Baseline {
}, },
} }
if t, ok := ContentTypeMap[resp.ContentType()]; ok { if t, ok := pkg.ContentTypeMap[resp.ContentType()]; ok {
bl.ContentType = t bl.ContentType = t
bl.Title = t + " data" bl.Title = t + " data"
} else { } else {
@ -34,7 +36,6 @@ func NewBaseline(u, host string, resp *ihttp.Response) *Baseline {
bl.Header = make([]byte, len(header)) bl.Header = make([]byte, len(header))
copy(bl.Header, header) copy(bl.Header, header)
bl.HeaderLength = len(bl.Header) bl.HeaderLength = len(bl.Header)
if i := resp.ContentLength(); ihttp.CheckBodySize(i) { if i := resp.ContentLength(); ihttp.CheckBodySize(i) {
if body := resp.Body(); body != nil { if body := resp.Body(); body != nil {
bl.Body = make([]byte, len(body)) bl.Body = make([]byte, len(body))
@ -50,10 +51,10 @@ func NewBaseline(u, host string, resp *ihttp.Response) *Baseline {
} }
bl.Raw = append(bl.Header, bl.Body...) bl.Raw = append(bl.Header, bl.Body...)
bl.Response, err = ParseRawResponse(bl.Raw) bl.Response, err = pkg.ParseRawResponse(bl.Raw)
if err != nil { if err != nil {
bl.IsValid = false bl.IsValid = false
bl.Reason = ErrResponseError.Error() bl.Reason = pkg.ErrResponseError.Error()
bl.ErrString = err.Error() bl.ErrString = err.Error()
return bl return bl
} }
@ -73,7 +74,7 @@ func NewBaseline(u, host string, resp *ihttp.Response) *Baseline {
} }
} else { } else {
bl.IsValid = false bl.IsValid = false
bl.Reason = ErrUrlError.Error() bl.Reason = pkg.ErrUrlError.Error()
bl.ErrString = err.Error() bl.ErrString = err.Error()
} }
bl.Unique = UniqueHash(bl) bl.Unique = UniqueHash(bl)
@ -116,9 +117,9 @@ type Baseline struct {
Url *url.URL `json:"-"` Url *url.URL `json:"-"`
Dir bool `json:"-"` Dir bool `json:"-"`
Chunked bool `json:"-"` Chunked bool `json:"-"`
Body BS `json:"-"` Body pkg.BS `json:"-"`
Header BS `json:"-"` Header pkg.BS `json:"-"`
Raw BS `json:"-"` Raw pkg.BS `json:"-"`
Response *http.Response `json:"-"` Response *http.Response `json:"-"`
Recu bool `json:"-"` Recu bool `json:"-"`
RecuDepth int `json:"-"` RecuDepth int `json:"-"`
@ -147,10 +148,10 @@ func (bl *Baseline) Collect() {
if bl.ContentType == "html" || bl.ContentType == "json" || bl.ContentType == "txt" { if bl.ContentType == "html" || bl.ContentType == "json" || bl.ContentType == "txt" {
// 指纹库设计的时候没考虑js,css文件的指纹, 跳过非必要的指纹收集减少误报提高性能 // 指纹库设计的时候没考虑js,css文件的指纹, 跳过非必要的指纹收集减少误报提高性能
//fmt.Println(bl.Source, bl.Url.String()+bl.Path, bl.RedirectURL, "call fingersengine") //fmt.Println(bl.Source, bl.Url.String()+bl.Path, bl.RedirectURL, "call fingersengine")
if EnableAllFingerEngine { if pkg.EnableAllFingerEngine {
bl.Frameworks = EngineDetect(bl.Raw) bl.Frameworks = pkg.EngineDetect(bl.Raw)
} else { } else {
bl.Frameworks = FingersDetect(bl.Raw) bl.Frameworks = pkg.FingersDetect(bl.Raw)
} }
} }
@ -158,14 +159,14 @@ func (bl *Baseline) Collect() {
if bl.ContentType == "html" { if bl.ContentType == "html" {
bl.Title = iutils.AsciiEncode(parsers.MatchTitle(bl.Body)) bl.Title = iutils.AsciiEncode(parsers.MatchTitle(bl.Body))
} else if bl.ContentType == "ico" { } else if bl.ContentType == "ico" {
if frame := FingerEngine.Favicon().Match(bl.Body); frame != nil { if frame := pkg.FingerEngine.Favicon().Match(bl.Body); frame != nil {
bl.Frameworks.Merge(frame) bl.Frameworks.Merge(frame)
} }
} }
} }
bl.Hashes = parsers.NewHashes(bl.Raw) bl.Hashes = parsers.NewHashes(bl.Raw)
bl.Extracteds = Extractors.Extract(string(bl.Raw)) bl.Extracteds = pkg.Extractors.Extract(string(bl.Raw))
bl.Unique = UniqueHash(bl) bl.Unique = UniqueHash(bl)
} }
@ -173,21 +174,21 @@ func (bl *Baseline) CollectURL() {
if len(bl.Body) == 0 { if len(bl.Body) == 0 {
return return
} }
for _, reg := range ExtractRegexps["js"][0].CompiledRegexps { for _, reg := range pkg.ExtractRegexps["js"][0].CompiledRegexps {
urls := reg.FindAllStringSubmatch(string(bl.Body), -1) urls := reg.FindAllStringSubmatch(string(bl.Body), -1)
for _, u := range urls { for _, u := range urls {
u[1] = CleanURL(u[1]) u[1] = pkg.CleanURL(u[1])
if u[1] != "" && !FilterJs(u[1]) { if u[1] != "" && !pkg.FilterJs(u[1]) {
bl.URLs = append(bl.URLs, u[1]) bl.URLs = append(bl.URLs, u[1])
} }
} }
} }
for _, reg := range ExtractRegexps["url"][0].CompiledRegexps { for _, reg := range pkg.ExtractRegexps["url"][0].CompiledRegexps {
urls := reg.FindAllStringSubmatch(string(bl.Body), -1) urls := reg.FindAllStringSubmatch(string(bl.Body), -1)
for _, u := range urls { for _, u := range urls {
u[1] = CleanURL(u[1]) u[1] = pkg.CleanURL(u[1])
if u[1] != "" && !FilterUrl(u[1]) { if u[1] != "" && !pkg.FilterUrl(u[1]) {
bl.URLs = append(bl.URLs, u[1]) bl.URLs = append(bl.URLs, u[1])
} }
} }
@ -255,3 +256,9 @@ func (bl *Baseline) FuzzyCompare(other *Baseline) bool {
} }
return false return false
} }
func UniqueHash(bl *Baseline) uint16 {
// 由host+状态码+重定向url+content-type+title+length舍去个位组成的hash
// body length可能会导致一些误报, 目前没有更好的解决办法
return pkg.CRC16Hash([]byte(bl.Host + strconv.Itoa(bl.Status) + bl.RedirectURL + bl.ContentType + bl.Title + strconv.Itoa(bl.BodyLength/10*10)))
}

View File

@ -1,4 +1,4 @@
package internal package core
import ( import (
"fmt" "fmt"

View File

@ -1,4 +1,4 @@
package internal package core
import ( import (
"fmt" "fmt"

View File

@ -1,9 +1,10 @@
package internal package core
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"github.com/chainreactors/logs" "github.com/chainreactors/logs"
"github.com/chainreactors/spray/core/baseline"
"github.com/chainreactors/spray/pkg" "github.com/chainreactors/spray/pkg"
"github.com/chainreactors/words/mask" "github.com/chainreactors/words/mask"
"io" "io"
@ -24,9 +25,9 @@ func Format(opts Option) {
if err != nil { if err != nil {
return return
} }
group := make(map[string][]*pkg.Baseline) group := make(map[string][]*baseline.Baseline)
for _, line := range bytes.Split(bytes.TrimSpace(content), []byte("\n")) { for _, line := range bytes.Split(bytes.TrimSpace(content), []byte("\n")) {
var result pkg.Baseline var result baseline.Baseline
err := json.Unmarshal(line, &result) err := json.Unmarshal(line, &result)
if err != nil { if err != nil {
logs.Log.Error(err.Error()) logs.Log.Error(err.Error())

View File

@ -2,6 +2,7 @@ package ihttp
import ( import (
"context" "context"
"github.com/chainreactors/spray/pkg"
"github.com/valyala/fasthttp" "github.com/valyala/fasthttp"
"net/http" "net/http"
) )
@ -30,7 +31,19 @@ type Request struct {
ClientType int ClientType int
} }
func (r *Request) SetHeaders(header map[string]string) { func (r *Request) SetHeaders(header map[string]string, RandomUA bool) {
if header["User-Agent"] == "" {
if RandomUA {
header["User-Agent"] = pkg.RandomUA()
} else {
header["User-Agent"] = pkg.DefaultUserAgent
}
}
if header["Accept"] == "" {
header["Accept"] = "*/*"
}
if r.StandardRequest != nil { if r.StandardRequest != nil {
for k, v := range header { for k, v := range header {
r.StandardRequest.Header.Set(k, v) r.StandardRequest.Header.Set(k, v)

View File

@ -29,7 +29,7 @@ func (r *Response) Body() []byte {
if r.FastResponse != nil { if r.FastResponse != nil {
return r.FastResponse.Body() return r.FastResponse.Body()
} else if r.StandardResponse != nil { } else if r.StandardResponse != nil {
if DefaultMaxBodySize == -1 { if r.StandardResponse.ContentLength == -1 {
body, err := io.ReadAll(r.StandardResponse.Body) body, err := io.ReadAll(r.StandardResponse.Body)
if err != nil { if err != nil {
return nil return nil

View File

@ -1,4 +1,4 @@
package internal package core
import ( import (
"bufio" "bufio"
@ -7,8 +7,9 @@ import (
"github.com/chainreactors/files" "github.com/chainreactors/files"
"github.com/chainreactors/logs" "github.com/chainreactors/logs"
"github.com/chainreactors/parsers" "github.com/chainreactors/parsers"
"github.com/chainreactors/spray/internal/ihttp" "github.com/chainreactors/spray/core/baseline"
"github.com/chainreactors/spray/internal/pool" "github.com/chainreactors/spray/core/ihttp"
"github.com/chainreactors/spray/core/pool"
"github.com/chainreactors/spray/pkg" "github.com/chainreactors/spray/pkg"
"github.com/chainreactors/utils" "github.com/chainreactors/utils"
"github.com/chainreactors/utils/iutils" "github.com/chainreactors/utils/iutils"
@ -231,7 +232,7 @@ func (opt *Option) Prepare() error {
} }
// 初始化全局变量 // 初始化全局变量
pkg.Distance = uint8(opt.SimhashDistance) baseline.Distance = uint8(opt.SimhashDistance)
if opt.MaxBodyLength == -1 { if opt.MaxBodyLength == -1 {
ihttp.DefaultMaxBodySize = -1 ihttp.DefaultMaxBodySize = -1
} else { } else {
@ -263,10 +264,10 @@ func (opt *Option) NewRunner() (*Runner, error) {
r := &Runner{ r := &Runner{
Option: opt, Option: opt,
taskCh: make(chan *Task), taskCh: make(chan *Task),
outputCh: make(chan *pkg.Baseline, 256), outputCh: make(chan *baseline.Baseline, 256),
poolwg: &sync.WaitGroup{}, poolwg: &sync.WaitGroup{},
outwg: &sync.WaitGroup{}, outwg: &sync.WaitGroup{},
fuzzyCh: make(chan *pkg.Baseline, 256), fuzzyCh: make(chan *baseline.Baseline, 256),
Headers: make(map[string]string), Headers: make(map[string]string),
Total: opt.Limit, Total: opt.Limit,
Color: true, Color: true,

View File

@ -6,7 +6,8 @@ import (
"fmt" "fmt"
"github.com/chainreactors/logs" "github.com/chainreactors/logs"
"github.com/chainreactors/parsers" "github.com/chainreactors/parsers"
"github.com/chainreactors/spray/internal/ihttp" "github.com/chainreactors/spray/core/baseline"
"github.com/chainreactors/spray/core/ihttp"
"github.com/chainreactors/spray/pkg" "github.com/chainreactors/spray/pkg"
"github.com/chainreactors/utils/iutils" "github.com/chainreactors/utils/iutils"
"github.com/chainreactors/words/rule" "github.com/chainreactors/words/rule"
@ -49,7 +50,7 @@ func NewBrutePool(ctx context.Context, config *Config) (*BrutePool, error) {
}), }),
additionCh: make(chan *Unit, config.Thread), additionCh: make(chan *Unit, config.Thread),
closeCh: make(chan struct{}), closeCh: make(chan struct{}),
processCh: make(chan *pkg.Baseline, config.Thread), processCh: make(chan *baseline.Baseline, config.Thread),
wg: &sync.WaitGroup{}, wg: &sync.WaitGroup{},
}, },
base: u.Scheme + "://" + u.Host, base: u.Scheme + "://" + u.Host,
@ -271,10 +272,7 @@ func (pool *BrutePool) Invoke(v interface{}) {
return return
} }
req.SetHeaders(pool.Headers) req.SetHeaders(pool.Headers, pool.RandomUserAgent)
if pool.RandomUserAgent {
req.SetHeader("User-Agent", pkg.RandomUA())
}
start := time.Now() start := time.Now()
resp, reqerr := pool.client.Do(req) resp, reqerr := pool.client.Do(req)
@ -284,11 +282,11 @@ func (pool *BrutePool) Invoke(v interface{}) {
} }
// compare与各种错误处理 // compare与各种错误处理
var bl *pkg.Baseline var bl *baseline.Baseline
if reqerr != nil && !errors.Is(reqerr, fasthttp.ErrBodyTooLarge) { if reqerr != nil && !errors.Is(reqerr, fasthttp.ErrBodyTooLarge) {
atomic.AddInt32(&pool.failedCount, 1) atomic.AddInt32(&pool.failedCount, 1)
atomic.AddInt32(&pool.Statistor.FailedNumber, 1) atomic.AddInt32(&pool.Statistor.FailedNumber, 1)
bl = &pkg.Baseline{ bl = &baseline.Baseline{
SprayResult: &parsers.SprayResult{ SprayResult: &parsers.SprayResult{
UrlString: pool.base + unit.path, UrlString: pool.base + unit.path,
ErrString: reqerr.Error(), ErrString: reqerr.Error(),
@ -301,15 +299,15 @@ func (pool *BrutePool) Invoke(v interface{}) {
} else { // 特定场景优化 } else { // 特定场景优化
if unit.source <= 3 || unit.source == parsers.CrawlSource || unit.source == parsers.CommonFileSource { if unit.source <= 3 || unit.source == parsers.CrawlSource || unit.source == parsers.CommonFileSource {
// 一些高优先级的source, 将跳过PreCompare // 一些高优先级的source, 将跳过PreCompare
bl = pkg.NewBaseline(req.URI(), req.Host(), resp) bl = baseline.NewBaseline(req.URI(), req.Host(), resp)
} else if pool.MatchExpr != nil { } else if pool.MatchExpr != nil {
// 如果自定义了match函数, 则所有数据送入tempch中 // 如果自定义了match函数, 则所有数据送入tempch中
bl = pkg.NewBaseline(req.URI(), req.Host(), resp) bl = baseline.NewBaseline(req.URI(), req.Host(), resp)
} else if err = pool.PreCompare(resp); err == nil { } else if err = pool.PreCompare(resp); err == nil {
// 通过预对比跳过一些无用数据, 减少性能消耗 // 通过预对比跳过一些无用数据, 减少性能消耗
bl = pkg.NewBaseline(req.URI(), req.Host(), resp) bl = baseline.NewBaseline(req.URI(), req.Host(), resp)
} else { } else {
bl = pkg.NewInvalidBaseline(req.URI(), req.Host(), resp, err.Error()) bl = baseline.NewInvalidBaseline(req.URI(), req.Host(), resp, err.Error())
} }
} }
@ -395,7 +393,7 @@ func (pool *BrutePool) NoScopeInvoke(v interface{}) {
logs.Log.Error(err.Error()) logs.Log.Error(err.Error())
return return
} }
req.SetHeaders(pool.Headers) req.SetHeaders(pool.Headers, pool.RandomUserAgent)
req.SetHeader("User-Agent", pkg.RandomUA()) req.SetHeader("User-Agent", pkg.RandomUA())
resp, reqerr := pool.client.Do(req) resp, reqerr := pool.client.Do(req)
if pool.ClientType == ihttp.FAST { if pool.ClientType == ihttp.FAST {
@ -407,7 +405,7 @@ func (pool *BrutePool) NoScopeInvoke(v interface{}) {
return return
} }
if resp.StatusCode() == 200 { if resp.StatusCode() == 200 {
bl := pkg.NewBaseline(req.URI(), req.Host(), resp) bl := baseline.NewBaseline(req.URI(), req.Host(), resp)
bl.Source = unit.source bl.Source = unit.source
bl.ReqDepth = unit.depth bl.ReqDepth = unit.depth
bl.Collect() bl.Collect()
@ -522,7 +520,7 @@ func (pool *BrutePool) checkRedirect(redirectURL string) bool {
} }
} }
func (pool *BrutePool) Upgrade(bl *pkg.Baseline) error { func (pool *BrutePool) Upgrade(bl *baseline.Baseline) error {
rurl, err := url.Parse(bl.RedirectURL) rurl, err := url.Parse(bl.RedirectURL)
if err == nil && rurl.Hostname() == bl.Url.Hostname() && bl.Url.Scheme == "http" && rurl.Scheme == "https" { if err == nil && rurl.Hostname() == bl.Url.Hostname() && bl.Url.Scheme == "http" && rurl.Scheme == "https" {
logs.Log.Infof("baseurl %s upgrade http to https, reinit", pool.BaseURL) logs.Log.Infof("baseurl %s upgrade http to https, reinit", pool.BaseURL)
@ -579,7 +577,7 @@ func (pool *BrutePool) checkHost(u string) bool {
return true return true
} }
func (pool *BrutePool) BaseCompare(bl *pkg.Baseline) bool { func (pool *BrutePool) BaseCompare(bl *baseline.Baseline) bool {
if !bl.IsValid { if !bl.IsValid {
return false return false
} }
@ -640,7 +638,7 @@ func (pool *BrutePool) BaseCompare(bl *pkg.Baseline) bool {
return true return true
} }
func (pool *BrutePool) addFuzzyBaseline(bl *pkg.Baseline) { func (pool *BrutePool) addFuzzyBaseline(bl *baseline.Baseline) {
if _, ok := pool.baselines[bl.Status]; !ok && (EnableAllFuzzy || iutils.IntsContains(pkg.FuzzyStatus, bl.Status)) { if _, ok := pool.baselines[bl.Status]; !ok && (EnableAllFuzzy || iutils.IntsContains(pkg.FuzzyStatus, bl.Status)) {
bl.IsBaseline = true bl.IsBaseline = true
bl.Collect() bl.Collect()
@ -706,7 +704,7 @@ func (pool *BrutePool) doCheck() {
} }
} }
func (pool *BrutePool) doRedirect(bl *pkg.Baseline, depth int) { func (pool *BrutePool) doRedirect(bl *baseline.Baseline, depth int) {
if depth >= pool.MaxRedirect { if depth >= pool.MaxRedirect {
return return
} }
@ -730,7 +728,7 @@ func (pool *BrutePool) doRedirect(bl *pkg.Baseline, depth int) {
}() }()
} }
func (pool *BrutePool) doCrawl(bl *pkg.Baseline) { func (pool *BrutePool) doCrawl(bl *baseline.Baseline) {
if !pool.Crawl || bl.ReqDepth >= pool.MaxCrawlDepth { if !pool.Crawl || bl.ReqDepth >= pool.MaxCrawlDepth {
return return
} }
@ -762,7 +760,7 @@ func (pool *BrutePool) doCrawl(bl *pkg.Baseline) {
} }
func (pool *BrutePool) doScopeCrawl(bl *pkg.Baseline) { func (pool *BrutePool) doScopeCrawl(bl *baseline.Baseline) {
if bl.ReqDepth >= pool.MaxCrawlDepth { if bl.ReqDepth >= pool.MaxCrawlDepth {
pool.wg.Done() pool.wg.Done()
return return
@ -813,13 +811,13 @@ func (pool *BrutePool) doBak() {
} }
} }
func (pool *BrutePool) doAppend(bl *pkg.Baseline) { func (pool *BrutePool) doAppend(bl *baseline.Baseline) {
pool.wg.Add(2) pool.wg.Add(2)
pool.doAppendWords(bl) pool.doAppendWords(bl)
pool.doAppendRule(bl) pool.doAppendRule(bl)
} }
func (pool *BrutePool) doAppendRule(bl *pkg.Baseline) { func (pool *BrutePool) doAppendRule(bl *baseline.Baseline) {
if pool.AppendRule == nil || bl.Source == parsers.AppendRuleSource || bl.ReqDepth >= pool.MaxAppendDepth { if pool.AppendRule == nil || bl.Source == parsers.AppendRuleSource || bl.ReqDepth >= pool.MaxAppendDepth {
pool.wg.Done() pool.wg.Done()
return return
@ -840,7 +838,7 @@ func (pool *BrutePool) doAppendRule(bl *pkg.Baseline) {
}() }()
} }
func (pool *BrutePool) doAppendWords(bl *pkg.Baseline) { func (pool *BrutePool) doAppendWords(bl *baseline.Baseline) {
if pool.AppendWords == nil || bl.Source == parsers.AppendSource || bl.Source == parsers.RuleSource || bl.ReqDepth >= pool.MaxAppendDepth { if pool.AppendWords == nil || bl.Source == parsers.AppendSource || bl.Source == parsers.RuleSource || bl.ReqDepth >= pool.MaxAppendDepth {
// 防止自身递归 // 防止自身递归
pool.wg.Done() pool.wg.Done()

View File

@ -4,7 +4,8 @@ import (
"context" "context"
"github.com/chainreactors/logs" "github.com/chainreactors/logs"
"github.com/chainreactors/parsers" "github.com/chainreactors/parsers"
"github.com/chainreactors/spray/internal/ihttp" "github.com/chainreactors/spray/core/baseline"
"github.com/chainreactors/spray/core/ihttp"
"github.com/chainreactors/spray/pkg" "github.com/chainreactors/spray/pkg"
"github.com/panjf2000/ants/v2" "github.com/panjf2000/ants/v2"
"net/url" "net/url"
@ -32,7 +33,7 @@ func NewCheckPool(ctx context.Context, config *Config) (*CheckPool, error) {
wg: &sync.WaitGroup{}, wg: &sync.WaitGroup{},
additionCh: make(chan *Unit, 1024), additionCh: make(chan *Unit, 1024),
closeCh: make(chan struct{}), closeCh: make(chan struct{}),
processCh: make(chan *pkg.Baseline, config.Thread), processCh: make(chan *baseline.Baseline, config.Thread),
}, },
} }
pool.Headers = map[string]string{"Connection": "close"} pool.Headers = map[string]string{"Connection": "close"}
@ -115,7 +116,7 @@ func (pool *CheckPool) Invoke(v interface{}) {
req, err := ihttp.BuildRequest(pool.ctx, pool.ClientType, unit.path, "", "", "GET") req, err := ihttp.BuildRequest(pool.ctx, pool.ClientType, unit.path, "", "", "GET")
if err != nil { if err != nil {
logs.Log.Debug(err.Error()) logs.Log.Debug(err.Error())
bl := &pkg.Baseline{ bl := &baseline.Baseline{
SprayResult: &parsers.SprayResult{ SprayResult: &parsers.SprayResult{
UrlString: unit.path, UrlString: unit.path,
IsValid: false, IsValid: false,
@ -127,13 +128,13 @@ func (pool *CheckPool) Invoke(v interface{}) {
pool.processCh <- bl pool.processCh <- bl
return return
} }
req.SetHeaders(pool.Headers) req.SetHeaders(pool.Headers, pool.RandomUserAgent)
start := time.Now() start := time.Now()
var bl *pkg.Baseline var bl *baseline.Baseline
resp, reqerr := pool.client.Do(req) resp, reqerr := pool.client.Do(req)
if reqerr != nil { if reqerr != nil {
pool.failedCount++ pool.failedCount++
bl = &pkg.Baseline{ bl = &baseline.Baseline{
SprayResult: &parsers.SprayResult{ SprayResult: &parsers.SprayResult{
UrlString: unit.path, UrlString: unit.path,
IsValid: false, IsValid: false,
@ -145,7 +146,7 @@ func (pool *CheckPool) Invoke(v interface{}) {
logs.Log.Debugf("%s, %s", unit.path, reqerr.Error()) logs.Log.Debugf("%s, %s", unit.path, reqerr.Error())
pool.doUpgrade(bl) pool.doUpgrade(bl)
} else { } else {
bl = pkg.NewBaseline(req.URI(), req.Host(), resp) bl = baseline.NewBaseline(req.URI(), req.Host(), resp)
bl.ReqDepth = unit.depth bl.ReqDepth = unit.depth
bl.Collect() bl.Collect()
if bl.Status == 400 { if bl.Status == 400 {
@ -180,7 +181,7 @@ func (pool *CheckPool) Handler() {
} }
} }
func (pool *CheckPool) doRedirect(bl *pkg.Baseline, depth int) { func (pool *CheckPool) doRedirect(bl *baseline.Baseline, depth int) {
if depth >= pool.MaxRedirect { if depth >= pool.MaxRedirect {
return return
} }
@ -209,7 +210,7 @@ func (pool *CheckPool) doRedirect(bl *pkg.Baseline, depth int) {
} }
// tcp与400进行协议转换 // tcp与400进行协议转换
func (pool *CheckPool) doUpgrade(bl *pkg.Baseline) { func (pool *CheckPool) doUpgrade(bl *baseline.Baseline) {
if bl.ReqDepth >= 1 { if bl.ReqDepth >= 1 {
return return
} }

View File

@ -2,7 +2,7 @@ package pool
import ( import (
"github.com/chainreactors/logs" "github.com/chainreactors/logs"
"github.com/chainreactors/spray/pkg" "github.com/chainreactors/spray/core/baseline"
"github.com/chainreactors/words" "github.com/chainreactors/words"
"github.com/chainreactors/words/rule" "github.com/chainreactors/words/rule"
"github.com/expr-lang/expr/vm" "github.com/expr-lang/expr/vm"
@ -16,9 +16,9 @@ type Config struct {
Thread int Thread int
Wordlist []string Wordlist []string
Timeout time.Duration Timeout time.Duration
ProcessCh chan *pkg.Baseline ProcessCh chan *baseline.Baseline
OutputCh chan *pkg.Baseline OutputCh chan *baseline.Baseline
FuzzyCh chan *pkg.Baseline FuzzyCh chan *baseline.Baseline
Outwg *sync.WaitGroup Outwg *sync.WaitGroup
RateLimit int RateLimit int
CheckPeriod int CheckPeriod int

View File

@ -3,7 +3,8 @@ package pool
import ( import (
"context" "context"
"github.com/chainreactors/parsers" "github.com/chainreactors/parsers"
"github.com/chainreactors/spray/internal/ihttp" "github.com/chainreactors/spray/core/baseline"
"github.com/chainreactors/spray/core/ihttp"
"github.com/chainreactors/spray/pkg" "github.com/chainreactors/spray/pkg"
"github.com/chainreactors/words" "github.com/chainreactors/words"
"sync" "sync"
@ -18,7 +19,7 @@ type BasePool struct {
Cancel context.CancelFunc Cancel context.CancelFunc
client *ihttp.Client client *ihttp.Client
ctx context.Context ctx context.Context
processCh chan *pkg.Baseline // 待处理的baseline processCh chan *baseline.Baseline // 待处理的baseline
dir string dir string
reqCount int reqCount int
failedCount int failedCount int
@ -28,7 +29,7 @@ type BasePool struct {
isFallback atomic.Bool isFallback atomic.Bool
} }
func (pool *BasePool) doRetry(bl *pkg.Baseline) { func (pool *BasePool) doRetry(bl *baseline.Baseline) {
if bl.Retry >= pool.RetryLimit { if bl.Retry >= pool.RetryLimit {
return return
} }
@ -56,7 +57,7 @@ func (pool *BasePool) addAddition(u *Unit) {
pool.additionCh <- u pool.additionCh <- u
} }
func (pool *BasePool) putToOutput(bl *pkg.Baseline) { func (pool *BasePool) putToOutput(bl *baseline.Baseline) {
if bl.IsValid || bl.IsFuzzy { if bl.IsValid || bl.IsFuzzy {
bl.Collect() bl.Collect()
} }
@ -64,7 +65,7 @@ func (pool *BasePool) putToOutput(bl *pkg.Baseline) {
pool.OutputCh <- bl pool.OutputCh <- bl
} }
func (pool *BasePool) putToFuzzy(bl *pkg.Baseline) { func (pool *BasePool) putToFuzzy(bl *baseline.Baseline) {
pool.Outwg.Add(1) pool.Outwg.Add(1)
bl.IsFuzzy = true bl.IsFuzzy = true
pool.FuzzyCh <- bl pool.FuzzyCh <- bl

View File

@ -2,7 +2,7 @@ package pool
import ( import (
"github.com/chainreactors/parsers" "github.com/chainreactors/parsers"
"github.com/chainreactors/spray/pkg" "github.com/chainreactors/spray/core/baseline"
) )
func newUnit(path string, source parsers.SpraySource) *Unit { func newUnit(path string, source parsers.SpraySource) *Unit {
@ -21,7 +21,7 @@ type Unit struct {
depth int depth int
} }
func (u *Unit) Update(bl *pkg.Baseline) { func (u *Unit) Update(bl *baseline.Baseline) {
bl.Number = u.number bl.Number = u.number
bl.Parent = u.parent bl.Parent = u.parent
bl.Host = u.host bl.Host = u.host
@ -31,15 +31,15 @@ func (u *Unit) Update(bl *pkg.Baseline) {
func NewBaselines() *Baselines { func NewBaselines() *Baselines {
return &Baselines{ return &Baselines{
baselines: map[int]*pkg.Baseline{}, baselines: map[int]*baseline.Baseline{},
} }
} }
type Baselines struct { type Baselines struct {
FailedBaselines []*pkg.Baseline FailedBaselines []*baseline.Baseline
random *pkg.Baseline random *baseline.Baseline
index *pkg.Baseline index *baseline.Baseline
baselines map[int]*pkg.Baseline baselines map[int]*baseline.Baseline
} }
type SprayMod int type SprayMod int

View File

@ -1,11 +1,12 @@
package internal package core
import ( import (
"context" "context"
"github.com/chainreactors/files" "github.com/chainreactors/files"
"github.com/chainreactors/logs" "github.com/chainreactors/logs"
"github.com/chainreactors/spray/internal/ihttp" "github.com/chainreactors/spray/core/baseline"
"github.com/chainreactors/spray/internal/pool" "github.com/chainreactors/spray/core/ihttp"
"github.com/chainreactors/spray/core/pool"
"github.com/chainreactors/spray/pkg" "github.com/chainreactors/spray/pkg"
"github.com/chainreactors/words" "github.com/chainreactors/words"
"github.com/chainreactors/words/rule" "github.com/chainreactors/words/rule"
@ -28,8 +29,8 @@ type Runner struct {
taskCh chan *Task taskCh chan *Task
poolwg *sync.WaitGroup poolwg *sync.WaitGroup
outwg *sync.WaitGroup outwg *sync.WaitGroup
outputCh chan *pkg.Baseline outputCh chan *baseline.Baseline
fuzzyCh chan *pkg.Baseline fuzzyCh chan *baseline.Baseline
bar *mpb.Bar bar *mpb.Bar
bruteMod bool bruteMod bool
IsCheck bool IsCheck bool
@ -254,7 +255,9 @@ Loop:
} }
} }
r.bar.Wait() if r.bar != nil {
r.bar.Wait()
}
r.poolwg.Wait() r.poolwg.Wait()
r.outwg.Wait() r.outwg.Wait()
} }
@ -285,7 +288,7 @@ Loop:
r.outwg.Wait() r.outwg.Wait()
} }
func (r *Runner) AddRecursive(bl *pkg.Baseline) { func (r *Runner) AddRecursive(bl *baseline.Baseline) {
// 递归新任务 // 递归新任务
task := &Task{ task := &Task{
baseUrl: bl.UrlString, baseUrl: bl.UrlString,
@ -361,7 +364,7 @@ func (r *Runner) saveStat(content string) {
} }
} }
func (r *Runner) Output(bl *pkg.Baseline) { func (r *Runner) Output(bl *baseline.Baseline) {
var out string var out string
if r.Option.Json { if r.Option.Json {
out = bl.ToJson() out = bl.ToJson()

View File

@ -1,4 +1,4 @@
package internal package core
import ( import (
"fmt" "fmt"

View File

@ -1,4 +1,4 @@
package internal package core
import ( import (
"github.com/chainreactors/spray/pkg" "github.com/chainreactors/spray/pkg"

View File

@ -87,7 +87,8 @@ var (
"Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)", "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)",
"Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)", "Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)",
} }
uacount = len(randomUserAgent) uacount = len(randomUserAgent)
DefaultUserAgent = randomUserAgent[rand.Intn(uacount)]
) )
type BS []byte type BS []byte
@ -327,12 +328,6 @@ func Dir(u string) string {
} }
} }
func UniqueHash(bl *Baseline) uint16 {
// 由host+状态码+重定向url+content-type+title+length舍去个位组成的hash
// body length可能会导致一些误报, 目前没有更好的解决办法
return CRC16Hash([]byte(bl.Host + strconv.Itoa(bl.Status) + bl.RedirectURL + bl.ContentType + bl.Title + strconv.Itoa(bl.BodyLength/10*10)))
}
func FormatURL(base, u string) string { func FormatURL(base, u string) string {
if strings.HasPrefix(u, "http") { if strings.HasPrefix(u, "http") {
parsed, err := url.Parse(u) parsed, err := url.Parse(u)