support -x/--method custom http method

support --raw parser input from raw http
This commit is contained in:
M09ic 2024-05-17 17:47:11 +08:00
parent 75680c21f4
commit f755fc3816
6 changed files with 62 additions and 34 deletions

View File

@ -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
} }
} }

View File

@ -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

View File

@ -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

View File

@ -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():

View File

@ -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

View File

@ -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,