mirror of
https://github.com/chainreactors/spray.git
synced 2025-05-06 18:51:22 +00:00
support -x/--method custom http method
support --raw parser input from raw http
This commit is contained in:
parent
75680c21f4
commit
f755fc3816
@ -5,13 +5,14 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BuildPathRequest(clientType int, base, path string) (*Request, error) {
|
func BuildPathRequest(clientType int, base, path, method string) (*Request, error) {
|
||||||
if clientType == FAST {
|
if clientType == FAST {
|
||||||
req := fasthttp.AcquireRequest()
|
req := fasthttp.AcquireRequest()
|
||||||
|
req.Header.SetMethod(method)
|
||||||
req.SetRequestURI(base + path)
|
req.SetRequestURI(base + path)
|
||||||
return &Request{FastRequest: req, ClientType: FAST}, nil
|
return &Request{FastRequest: req, ClientType: FAST}, nil
|
||||||
} else {
|
} else {
|
||||||
req, err := http.NewRequest("GET", base+path, nil)
|
req, err := http.NewRequest(method, base+path, nil)
|
||||||
return &Request{StandardRequest: req, ClientType: STANDARD}, err
|
return &Request{StandardRequest: req, ClientType: STANDARD}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -17,6 +18,7 @@ import (
|
|||||||
"github.com/chainreactors/words/rule"
|
"github.com/chainreactors/words/rule"
|
||||||
"github.com/vbauerster/mpb/v8"
|
"github.com/vbauerster/mpb/v8"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -43,13 +45,13 @@ type Option struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type InputOptions struct {
|
type InputOptions struct {
|
||||||
ResumeFrom string `long:"resume" description:"File, resume filename" `
|
ResumeFrom string `long:"resume" description:"File, resume filename" `
|
||||||
Config string `short:"c" long:"config" description:"File, config filename"`
|
Config string `short:"c" long:"config" description:"File, config filename"`
|
||||||
URL []string `short:"u" long:"url" description:"Strings, input baseurl, e.g.: http://google.com"`
|
URL []string `short:"u" long:"url" description:"Strings, input baseurl, e.g.: http://google.com"`
|
||||||
URLFile string `short:"l" long:"list" description:"File, input filename"`
|
URLFile string `short:"l" long:"list" description:"File, input filename"`
|
||||||
PortRange string `short:"p" long:"port" description:"String, input port range, e.g.: 80,8080-8090,db"`
|
PortRange string `short:"p" long:"port" description:"String, input port range, e.g.: 80,8080-8090,db"`
|
||||||
CIDRs string `long:"cidr" description:"String, input cidr, e.g.: 1.1.1.1/24 "`
|
CIDRs string `long:"cidr" description:"String, input cidr, e.g.: 1.1.1.1/24 "`
|
||||||
//Raw string `long:"raw" description:"File, input raw request filename"`
|
RawFile string `long:"raw" description:"File, input raw request filename"`
|
||||||
Dictionaries []string `short:"d" long:"dict" description:"Files, Multi,dict files, e.g.: -d 1.txt -d 2.txt" config:"dictionaries"`
|
Dictionaries []string `short:"d" long:"dict" description:"Files, Multi,dict files, e.g.: -d 1.txt -d 2.txt" config:"dictionaries"`
|
||||||
NoDict bool `long:"no-dict" description:"Bool, no dictionary" config:"no-dict"`
|
NoDict bool `long:"no-dict" description:"Bool, no dictionary" config:"no-dict"`
|
||||||
Word string `short:"w" long:"word" description:"String, word generate dsl, e.g.: -w test{?ld#4}" config:"word"`
|
Word string `short:"w" long:"word" description:"String, word generate dsl, e.g.: -w test{?ld#4}" config:"word"`
|
||||||
@ -92,6 +94,7 @@ type OutputOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RequestOptions struct {
|
type RequestOptions struct {
|
||||||
|
Method string `short:"x" long:"method" default:"GET" description:"String, request method, e.g.: --method POST" config:"method"`
|
||||||
Headers []string `long:"header" description:"Strings, custom headers, e.g.: --headers 'Auth: example_auth'" config:"headers"`
|
Headers []string `long:"header" description:"Strings, custom headers, e.g.: --headers 'Auth: example_auth'" config:"headers"`
|
||||||
UserAgent string `long:"user-agent" description:"String, custom user-agent, e.g.: --user-agent Custom" config:"useragent"`
|
UserAgent string `long:"user-agent" description:"String, custom user-agent, e.g.: --user-agent Custom" config:"useragent"`
|
||||||
RandomUserAgent bool `long:"random-agent" description:"Bool, use random with default user-agent" config:"random-useragent"`
|
RandomUserAgent bool `long:"random-agent" description:"Bool, use random with default user-agent" config:"random-useragent"`
|
||||||
@ -162,6 +165,7 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
|
|||||||
RateLimit: opt.RateLimit,
|
RateLimit: opt.RateLimit,
|
||||||
Deadline: opt.Deadline,
|
Deadline: opt.Deadline,
|
||||||
Headers: make(map[string]string),
|
Headers: make(map[string]string),
|
||||||
|
Method: opt.Method,
|
||||||
Offset: opt.Offset,
|
Offset: opt.Offset,
|
||||||
Total: opt.Limit,
|
Total: opt.Limit,
|
||||||
taskCh: make(chan *Task),
|
taskCh: make(chan *Task),
|
||||||
@ -459,6 +463,24 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
|
|||||||
|
|
||||||
taskfrom = "cmd"
|
taskfrom = "cmd"
|
||||||
r.Count = len(opt.URL)
|
r.Count = len(opt.URL)
|
||||||
|
} else if opt.RawFile != "" {
|
||||||
|
raw, err := os.Open(opt.RawFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.ReadRequest(bufio.NewReader(raw))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
opt.GenerateTasks(tasks, fmt.Sprintf("http://%s%s", req.Host, req.URL.String()), ports)
|
||||||
|
close(tasks)
|
||||||
|
}()
|
||||||
|
r.Method = req.Method
|
||||||
|
for k, _ := range req.Header {
|
||||||
|
r.Headers[k] = req.Header.Get(k)
|
||||||
|
}
|
||||||
} else if opt.CIDRs != "" {
|
} else if opt.CIDRs != "" {
|
||||||
if len(ports) == 0 {
|
if len(ports) == 0 {
|
||||||
ports = []string{"80", "443"}
|
ports = []string{"80", "443"}
|
||||||
@ -739,7 +761,7 @@ func (opt *Option) Validate() error {
|
|||||||
return errors.New("--resume and --depth cannot be used at the same time")
|
return errors.New("--resume and --depth cannot be used at the same time")
|
||||||
}
|
}
|
||||||
|
|
||||||
if opt.ResumeFrom == "" && opt.URL == nil && opt.URLFile == "" && opt.CIDRs == "" {
|
if opt.ResumeFrom == "" && opt.URL == nil && opt.URLFile == "" && opt.CIDRs == "" && opt.RawFile == "" {
|
||||||
return fmt.Errorf("without any target, please use -u/-l/-c/--resume to set targets")
|
return fmt.Errorf("without any target, please use -u/-l/-c/--resume to set targets")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -2,6 +2,7 @@ package pool
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/chainreactors/logs"
|
"github.com/chainreactors/logs"
|
||||||
"github.com/chainreactors/parsers"
|
"github.com/chainreactors/parsers"
|
||||||
@ -37,7 +38,7 @@ func NewBrutePool(ctx context.Context, config *Config) (*BrutePool, error) {
|
|||||||
pctx, cancel := context.WithCancel(ctx)
|
pctx, cancel := context.WithCancel(ctx)
|
||||||
pool := &BrutePool{
|
pool := &BrutePool{
|
||||||
Baselines: NewBaselines(),
|
Baselines: NewBaselines(),
|
||||||
This: &This{
|
BasePool: &BasePool{
|
||||||
Config: config,
|
Config: config,
|
||||||
ctx: pctx,
|
ctx: pctx,
|
||||||
Cancel: cancel,
|
Cancel: cancel,
|
||||||
@ -83,7 +84,7 @@ func NewBrutePool(ctx context.Context, config *Config) (*BrutePool, error) {
|
|||||||
|
|
||||||
type BrutePool struct {
|
type BrutePool struct {
|
||||||
*Baselines
|
*Baselines
|
||||||
*This
|
*BasePool
|
||||||
base string // url的根目录, 在爬虫或者redirect时, 会需要用到根目录进行拼接
|
base string // url的根目录, 在爬虫或者redirect时, 会需要用到根目录进行拼接
|
||||||
isDir bool
|
isDir bool
|
||||||
url *url.URL
|
url *url.URL
|
||||||
@ -125,7 +126,7 @@ func (pool *BrutePool) genReq(mod SprayMod, s string) (*ihttp.Request, error) {
|
|||||||
if mod == HostSpray {
|
if mod == HostSpray {
|
||||||
return ihttp.BuildHostRequest(pool.ClientType, pool.BaseURL, s)
|
return ihttp.BuildHostRequest(pool.ClientType, pool.BaseURL, s)
|
||||||
} else if mod == PathSpray {
|
} else if mod == PathSpray {
|
||||||
return ihttp.BuildPathRequest(pool.ClientType, pool.base, s)
|
return ihttp.BuildPathRequest(pool.ClientType, pool.base, s, pool.Method)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("unknown mod")
|
return nil, fmt.Errorf("unknown mod")
|
||||||
}
|
}
|
||||||
@ -311,7 +312,9 @@ func (pool *BrutePool) Invoke(v interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
req.SetHeaders(pool.Headers)
|
req.SetHeaders(pool.Headers)
|
||||||
req.SetHeader("User-Agent", pkg.RandomUA())
|
if pool.RandomUserAgent {
|
||||||
|
req.SetHeader("User-Agent", pkg.RandomUA())
|
||||||
|
}
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
resp, reqerr := pool.client.Do(pool.ctx, req)
|
resp, reqerr := pool.client.Do(pool.ctx, req)
|
||||||
@ -322,7 +325,7 @@ func (pool *BrutePool) Invoke(v interface{}) {
|
|||||||
|
|
||||||
// compare与各种错误处理
|
// compare与各种错误处理
|
||||||
var bl *pkg.Baseline
|
var bl *pkg.Baseline
|
||||||
if reqerr != nil && 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 = &pkg.Baseline{
|
||||||
@ -422,7 +425,7 @@ func (pool *BrutePool) Invoke(v interface{}) {
|
|||||||
func (pool *BrutePool) NoScopeInvoke(v interface{}) {
|
func (pool *BrutePool) NoScopeInvoke(v interface{}) {
|
||||||
defer pool.wg.Done()
|
defer pool.wg.Done()
|
||||||
unit := v.(*Unit)
|
unit := v.(*Unit)
|
||||||
req, err := ihttp.BuildPathRequest(pool.ClientType, unit.path, "")
|
req, err := ihttp.BuildPathRequest(pool.ClientType, unit.path, "", pool.Method)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Log.Error(err.Error())
|
logs.Log.Error(err.Error())
|
||||||
return
|
return
|
||||||
|
@ -19,7 +19,7 @@ import (
|
|||||||
func NewCheckPool(ctx context.Context, config *Config) (*CheckPool, error) {
|
func NewCheckPool(ctx context.Context, config *Config) (*CheckPool, error) {
|
||||||
pctx, cancel := context.WithCancel(ctx)
|
pctx, cancel := context.WithCancel(ctx)
|
||||||
pool := &CheckPool{
|
pool := &CheckPool{
|
||||||
&This{
|
&BasePool{
|
||||||
Config: config,
|
Config: config,
|
||||||
Statistor: pkg.NewStatistor(""),
|
Statistor: pkg.NewStatistor(""),
|
||||||
ctx: pctx,
|
ctx: pctx,
|
||||||
@ -38,12 +38,12 @@ func NewCheckPool(ctx context.Context, config *Config) (*CheckPool, error) {
|
|||||||
pool.Headers = map[string]string{"Connection": "close"}
|
pool.Headers = map[string]string{"Connection": "close"}
|
||||||
p, _ := ants.NewPoolWithFunc(config.Thread, pool.Invoke)
|
p, _ := ants.NewPoolWithFunc(config.Thread, pool.Invoke)
|
||||||
|
|
||||||
pool.This.Pool = p
|
pool.BasePool.Pool = p
|
||||||
return pool, nil
|
return pool, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckPool struct {
|
type CheckPool struct {
|
||||||
*This
|
*BasePool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *CheckPool) Run(ctx context.Context, offset, limit int) {
|
func (pool *CheckPool) Run(ctx context.Context, offset, limit int) {
|
||||||
@ -81,12 +81,12 @@ Loop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
pool.wg.Add(1)
|
pool.wg.Add(1)
|
||||||
_ = pool.This.Pool.Invoke(newUnit(u, parsers.CheckSource))
|
_ = pool.BasePool.Pool.Invoke(newUnit(u, parsers.CheckSource))
|
||||||
case u, ok := <-pool.additionCh:
|
case u, ok := <-pool.additionCh:
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_ = pool.This.Pool.Invoke(u)
|
_ = pool.BasePool.Pool.Invoke(u)
|
||||||
case <-pool.closeCh:
|
case <-pool.closeCh:
|
||||||
break Loop
|
break Loop
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type This struct {
|
type BasePool struct {
|
||||||
*Config
|
*Config
|
||||||
Statistor *pkg.Statistor
|
Statistor *pkg.Statistor
|
||||||
Pool *ants.PoolWithFunc
|
Pool *ants.PoolWithFunc
|
||||||
@ -31,7 +31,7 @@ type This struct {
|
|||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *This) doRedirect(bl *pkg.Baseline, depth int) {
|
func (pool *BasePool) doRedirect(bl *pkg.Baseline, depth int) {
|
||||||
if depth >= MaxRedirect {
|
if depth >= MaxRedirect {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ func (pool *This) doRedirect(bl *pkg.Baseline, depth int) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *This) doRule(bl *pkg.Baseline) {
|
func (pool *BasePool) doRule(bl *pkg.Baseline) {
|
||||||
if pool.AppendRule == nil {
|
if pool.AppendRule == nil {
|
||||||
pool.wg.Done()
|
pool.wg.Done()
|
||||||
return
|
return
|
||||||
@ -69,7 +69,7 @@ func (pool *This) doRule(bl *pkg.Baseline) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *This) doAppendWords(bl *pkg.Baseline) {
|
func (pool *BasePool) doAppendWords(bl *pkg.Baseline) {
|
||||||
if pool.AppendWords == nil {
|
if pool.AppendWords == nil {
|
||||||
pool.wg.Done()
|
pool.wg.Done()
|
||||||
return
|
return
|
||||||
@ -90,7 +90,7 @@ func (pool *This) doAppendWords(bl *pkg.Baseline) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *This) doRetry(bl *pkg.Baseline) {
|
func (pool *BasePool) doRetry(bl *pkg.Baseline) {
|
||||||
if bl.Retry >= pool.Retry {
|
if bl.Retry >= pool.Retry {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -105,7 +105,7 @@ func (pool *This) doRetry(bl *pkg.Baseline) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *This) doActive() {
|
func (pool *BasePool) doActive() {
|
||||||
defer pool.wg.Done()
|
defer pool.wg.Done()
|
||||||
for _, u := range pkg.ActivePath {
|
for _, u := range pkg.ActivePath {
|
||||||
pool.addAddition(&Unit{
|
pool.addAddition(&Unit{
|
||||||
@ -115,7 +115,7 @@ func (pool *This) doActive() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *This) doCommonFile() {
|
func (pool *BasePool) doCommonFile() {
|
||||||
defer pool.wg.Done()
|
defer pool.wg.Done()
|
||||||
for _, u := range mask.SpecialWords["common_file"] {
|
for _, u := range mask.SpecialWords["common_file"] {
|
||||||
pool.addAddition(&Unit{
|
pool.addAddition(&Unit{
|
||||||
@ -125,7 +125,7 @@ func (pool *This) doCommonFile() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *This) addAddition(u *Unit) {
|
func (pool *BasePool) addAddition(u *Unit) {
|
||||||
// 强行屏蔽报错, 防止goroutine泄露
|
// 强行屏蔽报错, 防止goroutine泄露
|
||||||
pool.wg.Add(1)
|
pool.wg.Add(1)
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -135,25 +135,25 @@ func (pool *This) addAddition(u *Unit) {
|
|||||||
pool.additionCh <- u
|
pool.additionCh <- u
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *This) Close() {
|
func (pool *BasePool) Close() {
|
||||||
pool.Bar.Close()
|
pool.Bar.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *This) genReq(s string) (*ihttp.Request, error) {
|
func (pool *BasePool) genReq(s string) (*ihttp.Request, error) {
|
||||||
if pool.Mod == HostSpray {
|
if pool.Mod == HostSpray {
|
||||||
return ihttp.BuildHostRequest(pool.ClientType, pool.BaseURL, s)
|
return ihttp.BuildHostRequest(pool.ClientType, pool.BaseURL, s)
|
||||||
} else if pool.Mod == PathSpray {
|
} else if pool.Mod == PathSpray {
|
||||||
return ihttp.BuildPathRequest(pool.ClientType, pool.BaseURL, s)
|
return ihttp.BuildPathRequest(pool.ClientType, pool.BaseURL, s, pool.Method)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("unknown mod")
|
return nil, fmt.Errorf("unknown mod")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *This) putToOutput(bl *pkg.Baseline) {
|
func (pool *BasePool) putToOutput(bl *pkg.Baseline) {
|
||||||
pool.OutLocker.Add(1)
|
pool.OutLocker.Add(1)
|
||||||
pool.OutputCh <- bl
|
pool.OutputCh <- bl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *This) putToFuzzy(bl *pkg.Baseline) {
|
func (pool *BasePool) putToFuzzy(bl *pkg.Baseline) {
|
||||||
pool.OutLocker.Add(1)
|
pool.OutLocker.Add(1)
|
||||||
bl.IsFuzzy = true
|
bl.IsFuzzy = true
|
||||||
pool.FuzzyCh <- bl
|
pool.FuzzyCh <- bl
|
||||||
|
@ -43,6 +43,7 @@ type Runner struct {
|
|||||||
AppendRules *rule.Program
|
AppendRules *rule.Program
|
||||||
AppendWords []string
|
AppendWords []string
|
||||||
Headers map[string]string
|
Headers map[string]string
|
||||||
|
Method string
|
||||||
Fns []func(string) []string
|
Fns []func(string) []string
|
||||||
FilterExpr *vm.Program
|
FilterExpr *vm.Program
|
||||||
MatchExpr *vm.Program
|
MatchExpr *vm.Program
|
||||||
@ -92,6 +93,7 @@ func (r *Runner) PrepareConfig() *pool.Config {
|
|||||||
Timeout: r.Timeout,
|
Timeout: r.Timeout,
|
||||||
RateLimit: r.RateLimit,
|
RateLimit: r.RateLimit,
|
||||||
Headers: r.Headers,
|
Headers: r.Headers,
|
||||||
|
Method: r.Method,
|
||||||
Mod: pool.ModMap[r.Mod],
|
Mod: pool.ModMap[r.Mod],
|
||||||
OutputCh: r.outputCh,
|
OutputCh: r.outputCh,
|
||||||
FuzzyCh: r.fuzzyCh,
|
FuzzyCh: r.fuzzyCh,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user