spray/internal/pool.go

253 lines
5.4 KiB
Go
Raw Normal View History

2022-09-08 15:57:17 +08:00
package internal
import (
"context"
"github.com/chainreactors/logs"
"github.com/chainreactors/spray/pkg"
2022-09-15 19:27:07 +08:00
"github.com/chainreactors/words"
2022-09-08 15:57:17 +08:00
"github.com/panjf2000/ants/v2"
"github.com/valyala/fasthttp"
2022-09-08 15:57:17 +08:00
"sync"
2022-09-15 19:27:07 +08:00
"time"
2022-09-08 15:57:17 +08:00
)
var (
CheckStatusCode func(int) bool
CheckRedirect func(string) bool
2022-09-26 17:19:08 +08:00
CheckWaf func([]byte) bool
2022-09-08 15:57:17 +08:00
)
2022-09-23 01:47:24 +08:00
var breakThreshold int = 10
2022-09-15 19:27:07 +08:00
func NewPool(ctx context.Context, config *pkg.Config, outputCh chan *baseline) (*Pool, error) {
2022-09-19 14:42:29 +08:00
pctx, cancel := context.WithCancel(ctx)
2022-09-08 15:57:17 +08:00
pool := &Pool{
Config: config,
ctx: pctx,
2022-09-23 01:47:24 +08:00
cancel: cancel,
client: pkg.NewClient(config.Thread, 2),
worder: words.NewWorder(config.Wordlist),
outputCh: outputCh,
tempCh: make(chan *baseline, config.Thread),
2022-09-23 11:20:41 +08:00
wg: sync.WaitGroup{},
initwg: sync.WaitGroup{},
checkPeriod: 100,
errPeriod: 10,
2022-09-08 15:57:17 +08:00
}
switch config.Mod {
case pkg.PathSpray:
pool.genReq = func(s string) (*fasthttp.Request, error) {
2022-09-23 11:20:41 +08:00
return pool.buildPathRequest(s)
2022-09-08 15:57:17 +08:00
}
case pkg.HostSpray:
pool.genReq = func(s string) (*fasthttp.Request, error) {
2022-09-23 11:20:41 +08:00
return pool.buildHostRequest(s)
2022-09-08 15:57:17 +08:00
}
}
p, _ := ants.NewPoolWithFunc(config.Thread, func(i interface{}) {
unit := i.(*Unit)
req, err := pool.genReq(unit.path)
if err != nil {
logs.Log.Error(err.Error())
return
}
var bl *baseline
2022-09-23 11:20:41 +08:00
resp, reqerr := pool.client.Do(pctx, req)
2022-09-25 21:04:14 +08:00
defer fasthttp.ReleaseResponse(resp)
defer fasthttp.ReleaseRequest(req)
2022-09-23 11:20:41 +08:00
if reqerr != nil && reqerr != fasthttp.ErrBodyTooLarge {
2022-10-19 16:38:23 +08:00
pool.failedCount++
2022-09-23 11:20:41 +08:00
bl = &baseline{UrlString: pool.BaseURL + unit.path, Err: reqerr}
2022-09-08 15:57:17 +08:00
} else {
2022-10-19 16:38:23 +08:00
pool.failedCount = 0
2022-09-20 18:09:06 +08:00
if err = pool.PreCompare(resp); err == nil || unit.source == CheckSource {
2022-09-08 15:57:17 +08:00
// 通过预对比跳过一些无用数据, 减少性能消耗
2022-09-23 01:47:24 +08:00
bl = NewBaseline(req.URI(), resp)
2022-09-08 15:57:17 +08:00
} else {
bl = NewInvalidBaseline(req.URI(), resp)
2022-09-08 15:57:17 +08:00
}
2022-09-23 11:20:41 +08:00
bl.Err = reqerr
2022-09-08 15:57:17 +08:00
}
switch unit.source {
2022-09-20 18:09:06 +08:00
case CheckSource:
2022-09-23 11:20:41 +08:00
logs.Log.Debugf("check: " + bl.String())
if pool.base == nil {
//初次check覆盖baseline
2022-09-23 11:20:41 +08:00
pool.base = bl
pool.initwg.Done()
} else if bl.Err != nil {
logs.Log.Warn("maybe ip banned by waf")
2022-09-23 11:20:41 +08:00
} else if !pool.base.Equal(bl) {
logs.Log.Warn("maybe trigger risk control")
}
2022-09-08 15:57:17 +08:00
case WordSource:
// 异步进行性能消耗较大的深度对比
2022-09-23 11:20:41 +08:00
pool.reqCount++
pool.tempCh <- bl
2022-09-23 11:20:41 +08:00
if pool.reqCount%pool.checkPeriod == 0 {
go pool.check()
} else if pool.reqCount%pool.errPeriod == 0 {
go pool.check()
}
2022-09-08 15:57:17 +08:00
}
2022-10-19 16:38:23 +08:00
pool.bar.Done()
2022-09-08 15:57:17 +08:00
pool.wg.Done()
})
pool.pool = p
2022-09-26 17:19:08 +08:00
go pool.comparing()
2022-09-08 15:57:17 +08:00
return pool, nil
}
type Pool struct {
*pkg.Config
client *pkg.Client
pool *ants.PoolWithFunc
bar *pkg.Bar
2022-09-19 14:42:29 +08:00
ctx context.Context
2022-09-23 01:47:24 +08:00
cancel context.CancelFunc
2022-09-08 15:57:17 +08:00
//baseReq *http.Request
2022-09-23 11:20:41 +08:00
base *baseline
2022-09-26 17:19:08 +08:00
outputCh chan *baseline // 输出的chan, 全局统一
tempCh chan *baseline // 待处理的baseline
reqCount int
2022-09-23 01:47:24 +08:00
failedCount int
checkPeriod int
errPeriod int
2022-09-23 11:20:41 +08:00
analyzeDone bool
genReq func(s string) (*fasthttp.Request, error)
2022-09-23 11:20:41 +08:00
worder *words.Worder
wg sync.WaitGroup
2022-09-26 17:19:08 +08:00
initwg sync.WaitGroup // 初始化用, 之后改成锁
2022-09-08 15:57:17 +08:00
}
func (p *Pool) check() {
2022-09-23 11:20:41 +08:00
p.wg.Add(1)
2022-09-20 18:09:06 +08:00
_ = p.pool.Invoke(newUnit(pkg.RandPath(), CheckSource))
2022-09-23 01:47:24 +08:00
if p.failedCount > breakThreshold {
2022-09-26 17:19:08 +08:00
// 当报错次数超过上限是, 结束任务
2022-09-23 01:47:24 +08:00
p.cancel()
}
}
func (p *Pool) Init() error {
2022-09-23 11:20:41 +08:00
p.initwg.Add(1)
p.check()
2022-09-23 11:20:41 +08:00
p.initwg.Wait()
2022-09-08 15:57:17 +08:00
// todo 分析baseline
// 检测基本访问能力
2022-09-23 11:20:41 +08:00
if p.base != nil && p.base.Err != nil {
2022-09-23 01:47:24 +08:00
p.cancel()
2022-09-23 11:20:41 +08:00
return p.base.Err
2022-09-08 15:57:17 +08:00
}
2022-09-23 11:20:41 +08:00
p.base.Collect()
if p.base.RedirectURL != "" {
CheckRedirect = func(redirectURL string) bool {
2022-09-23 11:20:41 +08:00
if redirectURL == p.base.RedirectURL {
2022-09-08 15:57:17 +08:00
// 相同的RedirectURL将被认为是无效数据
return false
} else {
// path为3xx, 且与baseline中的RedirectURL不同时, 为有效数据
return true
2022-09-08 15:57:17 +08:00
}
}
}
return nil
}
2022-09-15 19:27:07 +08:00
func (p *Pool) Run(ctx context.Context) {
Loop:
for {
select {
case u, ok := <-p.worder.C:
if !ok {
break Loop
}
p.wg.Add(1)
_ = p.pool.Invoke(newUnit(u, WordSource))
case <-time.NewTimer(time.Duration(p.DeadlineTime) * time.Second).C:
2022-09-15 19:27:07 +08:00
break Loop
case <-ctx.Done():
break Loop
2022-09-19 14:42:29 +08:00
case <-p.ctx.Done():
break Loop
2022-09-15 19:27:07 +08:00
}
2022-09-08 15:57:17 +08:00
}
2022-09-23 11:20:41 +08:00
p.Close()
2022-09-08 15:57:17 +08:00
}
func (p *Pool) PreCompare(resp *fasthttp.Response) error {
if !CheckStatusCode(resp.StatusCode()) {
2022-09-15 19:27:07 +08:00
return ErrBadStatus
2022-09-08 15:57:17 +08:00
}
if CheckRedirect != nil && !CheckRedirect(string(resp.Header.Peek("Location"))) {
2022-09-15 19:27:07 +08:00
return ErrRedirect
2022-09-08 15:57:17 +08:00
}
2022-09-26 17:19:08 +08:00
if CheckWaf != nil && !CheckWaf(nil) {
// todo check waf
return ErrWaf
}
2022-09-08 15:57:17 +08:00
2022-09-15 19:27:07 +08:00
return nil
2022-09-08 15:57:17 +08:00
}
2022-09-26 17:19:08 +08:00
func (p *Pool) comparing() {
for bl := range p.tempCh {
2022-09-23 11:20:41 +08:00
if p.base.Equal(bl) {
// 如果是同一个包则设置为无效包
bl.IsValid = false
p.outputCh <- bl
continue
}
bl.Collect()
2022-09-23 11:20:41 +08:00
if p.EnableFuzzy && p.base.FuzzyEqual(bl) {
bl.IsValid = false
p.outputCh <- bl
continue
}
p.outputCh <- bl
}
2022-09-23 11:20:41 +08:00
p.analyzeDone = true
}
2022-09-26 17:19:08 +08:00
2022-09-23 11:20:41 +08:00
func (p *Pool) Close() {
p.wg.Wait()
p.bar.Close()
2022-09-23 11:20:41 +08:00
close(p.tempCh)
for !p.analyzeDone {
time.Sleep(time.Duration(100) * time.Millisecond)
}
}
2022-09-26 17:19:08 +08:00
2022-09-23 11:20:41 +08:00
func (p *Pool) buildPathRequest(path string) (*fasthttp.Request, error) {
req := fasthttp.AcquireRequest()
req.SetRequestURI(p.BaseURL + path)
return req, nil
}
2022-09-23 11:20:41 +08:00
func (p *Pool) buildHostRequest(host string) (*fasthttp.Request, error) {
req := fasthttp.AcquireRequest()
req.SetRequestURI(p.BaseURL)
req.SetHost(host)
return req, nil
}