mirror of
https://github.com/chainreactors/spray.git
synced 2025-06-22 02:40:41 +00:00
support append-file
This commit is contained in:
parent
09c2a86a18
commit
e37201eb75
@ -83,6 +83,8 @@ func Spray() {
|
|||||||
logs.AddLevel(internal.LogVerbose, "verbose", "[=] %s {{suffix}}")
|
logs.AddLevel(internal.LogVerbose, "verbose", "[=] %s {{suffix}}")
|
||||||
if option.Debug {
|
if option.Debug {
|
||||||
logs.Log.SetLevel(logs.Debug)
|
logs.Log.SetLevel(logs.Debug)
|
||||||
|
} else if len(option.Verbose) > 0 {
|
||||||
|
logs.Log.SetLevel(internal.LogVerbose)
|
||||||
}
|
}
|
||||||
|
|
||||||
logs.Log.SetColorMap(map[logs.Level]func(string) string{
|
logs.Log.SetColorMap(map[logs.Level]func(string) string{
|
||||||
|
@ -37,6 +37,7 @@ type Config struct {
|
|||||||
FilterExpr *vm.Program
|
FilterExpr *vm.Program
|
||||||
RecuExpr *vm.Program
|
RecuExpr *vm.Program
|
||||||
AppendRule *rule.Program
|
AppendRule *rule.Program
|
||||||
|
AppendWords []string
|
||||||
OutputCh chan *Baseline
|
OutputCh chan *Baseline
|
||||||
FuzzyCh chan *Baseline
|
FuzzyCh chan *Baseline
|
||||||
Fuzzy bool
|
Fuzzy bool
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/antonmedv/expr"
|
"github.com/antonmedv/expr"
|
||||||
@ -49,6 +50,7 @@ type InputOptions struct {
|
|||||||
Rules []string `short:"r" long:"rules" description:"Files, rule files, e.g.: -r rule1.txt -r rule2.txt"`
|
Rules []string `short:"r" long:"rules" description:"Files, rule files, e.g.: -r rule1.txt -r rule2.txt"`
|
||||||
AppendRule []string `long:"append-rule" description:"Files, when found valid path , use append rule generator new word with current path"`
|
AppendRule []string `long:"append-rule" description:"Files, when found valid path , use append rule generator new word with current path"`
|
||||||
FilterRule string `long:"filter-rule" description:"String, filter rule, e.g.: --rule-filter '>8 <4'"`
|
FilterRule string `long:"filter-rule" description:"String, filter rule, e.g.: --rule-filter '>8 <4'"`
|
||||||
|
AppendFile []string `long:"append-file" description:"Files, when found valid path , use append file new word with current path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FunctionOptions struct {
|
type FunctionOptions struct {
|
||||||
@ -108,7 +110,7 @@ type ModeOptions struct {
|
|||||||
Scope []string `long:"scope" description:"String, custom scope, e.g.: --scope *.example.com"`
|
Scope []string `long:"scope" description:"String, custom scope, e.g.: --scope *.example.com"`
|
||||||
Recursive string `long:"recursive" default:"current.IsDir()" description:"String,custom recursive rule, e.g.: --recursive current.IsDir()"`
|
Recursive string `long:"recursive" default:"current.IsDir()" description:"String,custom recursive rule, e.g.: --recursive current.IsDir()"`
|
||||||
Depth int `long:"depth" default:"0" description:"Int, recursive depth"`
|
Depth int `long:"depth" default:"0" description:"Int, recursive depth"`
|
||||||
Index string `long:"index" default:"" description:"String, custom index path"`
|
Index string `long:"index" default:"/" description:"String, custom index path"`
|
||||||
Random string `long:"random" default:"" description:"String, custom random path"`
|
Random string `long:"random" default:"" description:"String, custom random path"`
|
||||||
CheckPeriod int `long:"check-period" default:"200" description:"Int, check period when request"`
|
CheckPeriod int `long:"check-period" default:"200" description:"Int, check period when request"`
|
||||||
ErrPeriod int `long:"error-period" default:"10" description:"Int, check period when error"`
|
ErrPeriod int `long:"error-period" default:"10" description:"Int, check period when error"`
|
||||||
@ -188,9 +190,6 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
|
|||||||
r.Progress.Start()
|
r.Progress.Start()
|
||||||
logs.Log.SetOutput(r.Progress.Bypass())
|
logs.Log.SetOutput(r.Progress.Bypass())
|
||||||
}
|
}
|
||||||
if len(opt.Verbose) == 1 {
|
|
||||||
logs.Log.SetLevel(LogVerbose)
|
|
||||||
}
|
|
||||||
|
|
||||||
// configuration
|
// configuration
|
||||||
if opt.Force {
|
if opt.Force {
|
||||||
@ -328,7 +327,7 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if opt.Rules != nil {
|
if opt.Rules != nil {
|
||||||
rules, err := loadFileAndCombine(opt.Rules)
|
rules, err := loadRuleAndCombine(opt.Rules)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -357,13 +356,30 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if opt.AppendRule != nil {
|
if opt.AppendRule != nil {
|
||||||
content, err := loadFileAndCombine(opt.AppendRule)
|
content, err := loadRuleAndCombine(opt.AppendRule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
r.AppendRules = rule.Compile(string(content), "")
|
r.AppendRules = rule.Compile(string(content), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opt.AppendFile != nil {
|
||||||
|
var bs bytes.Buffer
|
||||||
|
for _, f := range opt.AppendFile {
|
||||||
|
content, err := ioutil.ReadFile(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bs.Write(bytes.TrimSpace(content))
|
||||||
|
bs.WriteString("\n")
|
||||||
|
}
|
||||||
|
lines := strings.Split(bs.String(), "\n")
|
||||||
|
for i, line := range lines {
|
||||||
|
lines[i] = strings.TrimSpace(line)
|
||||||
|
}
|
||||||
|
r.AppendWords = lines
|
||||||
|
}
|
||||||
|
|
||||||
ports := utils.ParsePort(opt.PortRange)
|
ports := utils.ParsePort(opt.PortRange)
|
||||||
|
|
||||||
// prepare task
|
// prepare task
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/chainreactors/logs"
|
"github.com/chainreactors/logs"
|
||||||
"github.com/chainreactors/parsers"
|
"github.com/chainreactors/parsers"
|
||||||
ihttp2 "github.com/chainreactors/spray/internal/ihttp"
|
"github.com/chainreactors/spray/internal/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"
|
"github.com/chainreactors/words"
|
||||||
@ -48,7 +48,7 @@ func NewPool(ctx context.Context, config *Config) (*Pool, error) {
|
|||||||
url: u,
|
url: u,
|
||||||
ctx: pctx,
|
ctx: pctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
client: ihttp2.NewClient(&ihttp2.ClientConfig{
|
client: ihttp.NewClient(&ihttp.ClientConfig{
|
||||||
Thread: config.Thread,
|
Thread: config.Thread,
|
||||||
Type: config.ClientType,
|
Type: config.ClientType,
|
||||||
Timeout: time.Duration(config.Timeout) * time.Second,
|
Timeout: time.Duration(config.Timeout) * time.Second,
|
||||||
@ -92,7 +92,7 @@ type Pool struct {
|
|||||||
isDir bool
|
isDir bool
|
||||||
url *url.URL
|
url *url.URL
|
||||||
Statistor *pkg.Statistor
|
Statistor *pkg.Statistor
|
||||||
client *ihttp2.Client
|
client *ihttp.Client
|
||||||
reqPool *ants.PoolWithFunc
|
reqPool *ants.PoolWithFunc
|
||||||
scopePool *ants.PoolWithFunc
|
scopePool *ants.PoolWithFunc
|
||||||
bar *pkg.Bar
|
bar *pkg.Bar
|
||||||
@ -133,22 +133,24 @@ func (pool *Pool) checkRedirect(redirectURL string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *Pool) genReq(mod SprayMod, s string) (*ihttp2.Request, error) {
|
func (pool *Pool) genReq(mod SprayMod, s string) (*ihttp.Request, error) {
|
||||||
if mod == HostSpray {
|
if mod == HostSpray {
|
||||||
return ihttp2.BuildHostRequest(pool.ClientType, pool.BaseURL, s)
|
return ihttp.BuildHostRequest(pool.ClientType, pool.BaseURL, s)
|
||||||
} else if mod == PathSpray {
|
} else if mod == PathSpray {
|
||||||
return ihttp2.BuildPathRequest(pool.ClientType, pool.base, s)
|
return ihttp.BuildPathRequest(pool.ClientType, pool.base, s)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("unknown mod")
|
return nil, fmt.Errorf("unknown mod")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *Pool) Init() error {
|
func (pool *Pool) Init() error {
|
||||||
pool.initwg.Add(2)
|
pool.initwg.Add(2)
|
||||||
if pool.Index != "" {
|
if pool.Index != "/" {
|
||||||
logs.Log.Logf(LogVerbose, "custom index url: %s", BaseURL(pool.url)+FormatURL(BaseURL(pool.url), pool.Index))
|
logs.Log.Logf(LogVerbose, "custom index url: %s", BaseURL(pool.url)+FormatURL(BaseURL(pool.url), pool.Index))
|
||||||
pool.reqPool.Invoke(newUnit(pool.Index, InitIndexSource))
|
pool.reqPool.Invoke(newUnit(pool.Index, InitIndexSource))
|
||||||
|
//pool.urls[Dir(pool.Index)] = struct{}{}
|
||||||
} else {
|
} else {
|
||||||
pool.reqPool.Invoke(newUnit(pool.url.Path, InitIndexSource))
|
pool.reqPool.Invoke(newUnit(pool.url.Path, InitIndexSource))
|
||||||
|
//pool.urls[Dir(pool.url.Path)] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if pool.Random != "" {
|
if pool.Random != "" {
|
||||||
@ -163,7 +165,7 @@ func (pool *Pool) Init() error {
|
|||||||
logs.Log.Error(pool.index.String())
|
logs.Log.Error(pool.index.String())
|
||||||
return fmt.Errorf(pool.index.ErrString)
|
return fmt.Errorf(pool.index.ErrString)
|
||||||
}
|
}
|
||||||
if pool.index.Chunked && pool.ClientType == ihttp2.FAST {
|
if pool.index.Chunked && pool.ClientType == ihttp.FAST {
|
||||||
logs.Log.Warn("chunk encoding! buf current client FASTHTTP not support chunk decode")
|
logs.Log.Warn("chunk encoding! buf current client FASTHTTP not support chunk decode")
|
||||||
}
|
}
|
||||||
logs.Log.Logf(LogVerbose, "[baseline.index] "+pool.index.Format([]string{"status", "length", "spend", "title", "frame", "redirect"}))
|
logs.Log.Logf(LogVerbose, "[baseline.index] "+pool.index.Format([]string{"status", "length", "spend", "title", "frame", "redirect"}))
|
||||||
@ -286,7 +288,7 @@ func (pool *Pool) Invoke(v interface{}) {
|
|||||||
atomic.AddInt32(&pool.Statistor.ReqTotal, 1)
|
atomic.AddInt32(&pool.Statistor.ReqTotal, 1)
|
||||||
unit := v.(*Unit)
|
unit := v.(*Unit)
|
||||||
|
|
||||||
var req *ihttp2.Request
|
var req *ihttp.Request
|
||||||
var err error
|
var err error
|
||||||
if unit.source == WordSource {
|
if unit.source == WordSource {
|
||||||
req, err = pool.genReq(pool.Mod, unit.path)
|
req, err = pool.genReq(pool.Mod, unit.path)
|
||||||
@ -304,7 +306,7 @@ func (pool *Pool) Invoke(v interface{}) {
|
|||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
resp, reqerr := pool.client.Do(pool.ctx, req)
|
resp, reqerr := pool.client.Do(pool.ctx, req)
|
||||||
if pool.ClientType == ihttp2.FAST {
|
if pool.ClientType == ihttp.FAST {
|
||||||
defer fasthttp.ReleaseResponse(resp.FastResponse)
|
defer fasthttp.ReleaseResponse(resp.FastResponse)
|
||||||
defer fasthttp.ReleaseRequest(req.FastRequest)
|
defer fasthttp.ReleaseRequest(req.FastRequest)
|
||||||
}
|
}
|
||||||
@ -317,7 +319,6 @@ func (pool *Pool) Invoke(v interface{}) {
|
|||||||
bl = &Baseline{
|
bl = &Baseline{
|
||||||
SprayResult: &parsers.SprayResult{
|
SprayResult: &parsers.SprayResult{
|
||||||
UrlString: pool.base + unit.path,
|
UrlString: pool.base + unit.path,
|
||||||
IsValid: false,
|
|
||||||
ErrString: reqerr.Error(),
|
ErrString: reqerr.Error(),
|
||||||
Reason: ErrRequestFailed.Error(),
|
Reason: ErrRequestFailed.Error(),
|
||||||
},
|
},
|
||||||
@ -346,7 +347,7 @@ func (pool *Pool) Invoke(v interface{}) {
|
|||||||
pool.doRedirect(bl, unit.depth)
|
pool.doRedirect(bl, unit.depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ihttp2.DefaultMaxBodySize != 0 && bl.BodyLength > ihttp2.DefaultMaxBodySize {
|
if ihttp.DefaultMaxBodySize != 0 && bl.BodyLength > ihttp.DefaultMaxBodySize {
|
||||||
bl.ExceedLength = true
|
bl.ExceedLength = true
|
||||||
}
|
}
|
||||||
bl.Source = unit.source
|
bl.Source = unit.source
|
||||||
@ -412,7 +413,7 @@ func (pool *Pool) Invoke(v interface{}) {
|
|||||||
func (pool *Pool) NoScopeInvoke(v interface{}) {
|
func (pool *Pool) NoScopeInvoke(v interface{}) {
|
||||||
defer pool.waiter.Done()
|
defer pool.waiter.Done()
|
||||||
unit := v.(*Unit)
|
unit := v.(*Unit)
|
||||||
req, err := ihttp2.BuildPathRequest(pool.ClientType, unit.path, "")
|
req, err := ihttp.BuildPathRequest(pool.ClientType, unit.path, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Log.Error(err.Error())
|
logs.Log.Error(err.Error())
|
||||||
return
|
return
|
||||||
@ -420,7 +421,7 @@ func (pool *Pool) NoScopeInvoke(v interface{}) {
|
|||||||
req.SetHeaders(pool.Headers)
|
req.SetHeaders(pool.Headers)
|
||||||
req.SetHeader("User-Agent", RandomUA())
|
req.SetHeader("User-Agent", RandomUA())
|
||||||
resp, reqerr := pool.client.Do(pool.ctx, req)
|
resp, reqerr := pool.client.Do(pool.ctx, req)
|
||||||
if pool.ClientType == ihttp2.FAST {
|
if pool.ClientType == ihttp.FAST {
|
||||||
defer fasthttp.ReleaseResponse(resp.FastResponse)
|
defer fasthttp.ReleaseResponse(resp.FastResponse)
|
||||||
defer fasthttp.ReleaseRequest(req.FastRequest)
|
defer fasthttp.ReleaseRequest(req.FastRequest)
|
||||||
}
|
}
|
||||||
@ -507,9 +508,12 @@ func (pool *Pool) Handler() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if bl.IsValid || bl.IsFuzzy {
|
if bl.IsValid || bl.IsFuzzy {
|
||||||
pool.waiter.Add(2)
|
pool.waiter.Add(3)
|
||||||
pool.doCrawl(bl)
|
pool.doCrawl(bl)
|
||||||
pool.doRule(bl)
|
pool.doRule(bl)
|
||||||
|
if _, ok := pool.urls[Dir(bl.Url.Path)]; !ok {
|
||||||
|
pool.doAppendWords(bl)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 如果要进行递归判断, 要满足 bl有效, mod为path-spray, 当前深度小于最大递归深度
|
// 如果要进行递归判断, 要满足 bl有效, mod为path-spray, 当前深度小于最大递归深度
|
||||||
if bl.IsValid {
|
if bl.IsValid {
|
||||||
@ -530,7 +534,7 @@ func (pool *Pool) Handler() {
|
|||||||
pool.analyzeDone = true
|
pool.analyzeDone = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *Pool) PreCompare(resp *ihttp2.Response) error {
|
func (pool *Pool) PreCompare(resp *ihttp.Response) error {
|
||||||
status := resp.StatusCode()
|
status := resp.StatusCode()
|
||||||
if iutils.IntsContains(WhiteStatus, status) {
|
if iutils.IntsContains(WhiteStatus, status) {
|
||||||
// 如果为白名单状态码则直接返回
|
// 如果为白名单状态码则直接返回
|
||||||
@ -720,6 +724,27 @@ func (pool *Pool) doRule(bl *Baseline) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pool *Pool) doAppendWords(bl *Baseline) {
|
||||||
|
if pool.AppendWords == nil {
|
||||||
|
pool.waiter.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bl.Source == AppendSource {
|
||||||
|
pool.waiter.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer pool.waiter.Done()
|
||||||
|
for _, u := range pool.AppendWords {
|
||||||
|
pool.addAddition(&Unit{
|
||||||
|
path: Dir(bl.Url.Path) + u,
|
||||||
|
source: AppendSource,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
func (pool *Pool) doRetry(bl *Baseline) {
|
func (pool *Pool) doRetry(bl *Baseline) {
|
||||||
if bl.Retry >= pool.Retry {
|
if bl.Retry >= pool.Retry {
|
||||||
return
|
return
|
||||||
|
@ -41,6 +41,7 @@ type Runner struct {
|
|||||||
Wordlist []string
|
Wordlist []string
|
||||||
Rules *rule.Program
|
Rules *rule.Program
|
||||||
AppendRules *rule.Program
|
AppendRules *rule.Program
|
||||||
|
AppendWords []string
|
||||||
Headers map[string]string
|
Headers map[string]string
|
||||||
Fns []func(string) []string
|
Fns []func(string) []string
|
||||||
FilterExpr *vm.Program
|
FilterExpr *vm.Program
|
||||||
@ -104,6 +105,7 @@ func (r *Runner) PrepareConfig() *Config {
|
|||||||
FilterExpr: r.FilterExpr,
|
FilterExpr: r.FilterExpr,
|
||||||
RecuExpr: r.RecursiveExpr,
|
RecuExpr: r.RecursiveExpr,
|
||||||
AppendRule: r.AppendRules,
|
AppendRule: r.AppendRules,
|
||||||
|
AppendWords: r.AppendWords,
|
||||||
IgnoreWaf: r.IgnoreWaf,
|
IgnoreWaf: r.IgnoreWaf,
|
||||||
Crawl: r.Crawl,
|
Crawl: r.Crawl,
|
||||||
Scope: r.Scope,
|
Scope: r.Scope,
|
||||||
|
@ -20,6 +20,7 @@ const (
|
|||||||
CommonFileSource
|
CommonFileSource
|
||||||
UpgradeSource
|
UpgradeSource
|
||||||
RetrySource
|
RetrySource
|
||||||
|
AppendSource
|
||||||
)
|
)
|
||||||
|
|
||||||
func newUnit(path string, source int) *Unit {
|
func newUnit(path string, source int) *Unit {
|
||||||
|
@ -97,7 +97,7 @@ func loadFileToSlice(filename string) ([]string, error) {
|
|||||||
return ss, nil
|
return ss, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadFileAndCombine(filename []string) (string, error) {
|
func loadRuleAndCombine(filename []string) (string, error) {
|
||||||
var bs bytes.Buffer
|
var bs bytes.Buffer
|
||||||
for _, f := range filename {
|
for _, f := range filename {
|
||||||
if data, ok := pkg.Rules[f]; ok {
|
if data, ok := pkg.Rules[f]; ok {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user