mirror of
https://github.com/chainreactors/spray.git
synced 2025-09-15 11:40:13 +00:00
添加option文件, 从runner中分离不必要的功能
This commit is contained in:
parent
9582a32586
commit
5c48bce5d4
10
cmd/spray.go
10
cmd/spray.go
@ -8,8 +8,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var runner internal.Runner
|
var option internal.Option
|
||||||
parser := flags.NewParser(&runner, flags.Default)
|
parser := flags.NewParser(&option, flags.Default)
|
||||||
_, err := parser.Parse()
|
_, err := parser.Parse()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.(*flags.Error).Type != flags.ErrHelp {
|
if err.(*flags.Error).Type != flags.ErrHelp {
|
||||||
@ -18,6 +18,12 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runner, err := option.PrepareRunner()
|
||||||
|
if err != nil {
|
||||||
|
logs.Log.Errorf(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err = runner.Prepare()
|
err = runner.Prepare()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Log.Errorf(err.Error())
|
logs.Log.Errorf(err.Error())
|
||||||
|
@ -11,11 +11,10 @@ import (
|
|||||||
|
|
||||||
func NewBaseline(u, host string, resp *ihttp.Response) *baseline {
|
func NewBaseline(u, host string, resp *ihttp.Response) *baseline {
|
||||||
bl := &baseline{
|
bl := &baseline{
|
||||||
//Url: u,
|
Url: u,
|
||||||
UrlString: u,
|
Host: host,
|
||||||
Host: host,
|
Status: resp.StatusCode(),
|
||||||
Status: resp.StatusCode(),
|
IsValid: true,
|
||||||
IsValid: true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bl.Body = resp.Body()
|
bl.Body = resp.Body()
|
||||||
@ -29,11 +28,10 @@ func NewBaseline(u, host string, resp *ihttp.Response) *baseline {
|
|||||||
|
|
||||||
func NewInvalidBaseline(u, host string, resp *ihttp.Response) *baseline {
|
func NewInvalidBaseline(u, host string, resp *ihttp.Response) *baseline {
|
||||||
bl := &baseline{
|
bl := &baseline{
|
||||||
//Url: u,
|
Url: u,
|
||||||
UrlString: u,
|
Host: host,
|
||||||
Host: host,
|
Status: resp.StatusCode(),
|
||||||
Status: resp.StatusCode(),
|
IsValid: false,
|
||||||
IsValid: false,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bl.RedirectURL = string(resp.GetHeader("Location"))
|
bl.RedirectURL = string(resp.GetHeader("Location"))
|
||||||
@ -42,7 +40,7 @@ func NewInvalidBaseline(u, host string, resp *ihttp.Response) *baseline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type baseline struct {
|
type baseline struct {
|
||||||
UrlString string `json:"url"`
|
Url string `json:"url"`
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
Body []byte `json:"-"`
|
Body []byte `json:"-"`
|
||||||
BodyLength int `json:"body_length"`
|
BodyLength int `json:"body_length"`
|
||||||
@ -98,7 +96,7 @@ func (bl *baseline) FuzzyEqual(other *baseline) bool {
|
|||||||
func (bl *baseline) String() string {
|
func (bl *baseline) String() string {
|
||||||
var line strings.Builder
|
var line strings.Builder
|
||||||
//line.WriteString("[+] ")
|
//line.WriteString("[+] ")
|
||||||
line.WriteString(bl.UrlString)
|
line.WriteString(bl.Url)
|
||||||
line.WriteString(" (" + bl.Host + ")")
|
line.WriteString(" (" + bl.Host + ")")
|
||||||
line.WriteString(" - ")
|
line.WriteString(" - ")
|
||||||
line.WriteString(strconv.Itoa(bl.Status))
|
line.WriteString(strconv.Itoa(bl.Status))
|
||||||
|
127
internal/option.go
Normal file
127
internal/option.go
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/chainreactors/logs"
|
||||||
|
"github.com/chainreactors/spray/pkg"
|
||||||
|
"github.com/gosuri/uiprogress"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Option struct {
|
||||||
|
URL string `short:"u" long:"url"`
|
||||||
|
URLFile string `short:"l" long:"list"`
|
||||||
|
WordLists []string `short:"w" long:"word"`
|
||||||
|
Extension string `short:"e" long:"extensions"`
|
||||||
|
ExcludeExtensions bool `long:"exclude-extensions"`
|
||||||
|
RemoveExtensions bool `long:"remove-extensions"`
|
||||||
|
Uppercase bool `short:"U" long:"uppercase"`
|
||||||
|
Lowercase bool `short:"L" long:"lowercase"`
|
||||||
|
|
||||||
|
Deadline int `long:"deadline" default:"600"` // todo 总的超时时间,适配云函数的deadline
|
||||||
|
Timeout int `long:"timeout" default:"2"`
|
||||||
|
Headers []string `long:"header"`
|
||||||
|
OutputFile string `short:"f"`
|
||||||
|
OutputProbe string `long:"probe"`
|
||||||
|
Offset int `long:"offset"`
|
||||||
|
Limit int `long:"limit"`
|
||||||
|
Threads int `short:"t" long:"thread" default:"20"`
|
||||||
|
PoolSize int `short:"p" long:"pool" default:"5"`
|
||||||
|
Debug bool `long:"debug"`
|
||||||
|
Quiet bool `short:"q" long:"quiet"`
|
||||||
|
Mod string `short:"m" long:"mod" default:"path"`
|
||||||
|
Client string `short:"c" long:"client" default:"auto"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opt *Option) PrepareRunner() (*Runner, error) {
|
||||||
|
r := &Runner{
|
||||||
|
Progress: uiprogress.New(),
|
||||||
|
Threads: opt.Threads,
|
||||||
|
PoolSize: opt.PoolSize,
|
||||||
|
Mod: opt.Mod,
|
||||||
|
Timeout: opt.Timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opt.Debug {
|
||||||
|
logs.Log.Level = logs.Debug
|
||||||
|
}
|
||||||
|
if !opt.Quiet {
|
||||||
|
r.Progress.Start()
|
||||||
|
logs.Log.Writer = r.Progress.Bypass()
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare url
|
||||||
|
var file *os.File
|
||||||
|
var err error
|
||||||
|
urlfrom := opt.URLFile
|
||||||
|
if opt.URL != "" {
|
||||||
|
r.URLList = append(r.URLList, opt.URL)
|
||||||
|
urlfrom = "cmd"
|
||||||
|
} else if opt.URLFile != "" {
|
||||||
|
file, err = os.Open(opt.URLFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else if pkg.HasStdin() {
|
||||||
|
file = os.Stdin
|
||||||
|
urlfrom = "stdin"
|
||||||
|
}
|
||||||
|
|
||||||
|
if file != nil {
|
||||||
|
content, err := ioutil.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r.URLList = strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, u := range r.URLList {
|
||||||
|
r.URLList[i] = strings.TrimSpace(u)
|
||||||
|
}
|
||||||
|
logs.Log.Importantf("load %d urls from %s", len(r.URLList), urlfrom)
|
||||||
|
|
||||||
|
// prepare word
|
||||||
|
words := make([][]string, len(opt.WordLists))
|
||||||
|
for i, f := range opt.WordLists {
|
||||||
|
words[i], err = loadFileToSlice(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
logs.Log.Importantf("load %d word from %s", len(r.Wordlist), f)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, w := range words {
|
||||||
|
r.Wordlist = append(r.Wordlist, w...)
|
||||||
|
}
|
||||||
|
// todo mask
|
||||||
|
|
||||||
|
// prepare header
|
||||||
|
for _, h := range opt.Headers {
|
||||||
|
i := strings.Index(h, ":")
|
||||||
|
if i == -1 {
|
||||||
|
logs.Log.Warn("invalid header")
|
||||||
|
} else {
|
||||||
|
r.Headers.Add(h[:i], h[i+2:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadFileToSlice(filename string) ([]string, error) {
|
||||||
|
var ss []string
|
||||||
|
content, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ss = strings.Split(string(content), "\n")
|
||||||
|
|
||||||
|
// 统一windows与linux的回车换行差异
|
||||||
|
for i, word := range ss {
|
||||||
|
ss[i] = strings.TrimSpace(word)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss, nil
|
||||||
|
}
|
@ -84,7 +84,7 @@ func NewPool(ctx context.Context, config *pkg.Config, outputCh chan *baseline) (
|
|||||||
|
|
||||||
if reqerr != nil && reqerr != fasthttp.ErrBodyTooLarge {
|
if reqerr != nil && reqerr != fasthttp.ErrBodyTooLarge {
|
||||||
pool.failedCount++
|
pool.failedCount++
|
||||||
bl = &baseline{UrlString: pool.BaseURL + unit.path, Err: reqerr}
|
bl = &baseline{Url: pool.BaseURL + unit.path, Err: reqerr}
|
||||||
} else {
|
} else {
|
||||||
pool.failedCount = 0
|
pool.failedCount = 0
|
||||||
if err = pool.PreCompare(resp); err == nil || unit.source == CheckSource {
|
if err = pool.PreCompare(resp); err == nil || unit.source == CheckSource {
|
||||||
@ -192,8 +192,6 @@ Loop:
|
|||||||
}
|
}
|
||||||
p.wg.Add(1)
|
p.wg.Add(1)
|
||||||
_ = p.pool.Invoke(newUnit(u, WordSource))
|
_ = p.pool.Invoke(newUnit(u, WordSource))
|
||||||
case <-time.NewTimer(time.Duration(p.DeadlineTime) * time.Second).C:
|
|
||||||
break Loop
|
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
break Loop
|
break Loop
|
||||||
case <-p.ctx.Done():
|
case <-p.ctx.Done():
|
||||||
|
@ -2,16 +2,12 @@ package internal
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"github.com/chainreactors/logs"
|
"github.com/chainreactors/logs"
|
||||||
"github.com/chainreactors/spray/pkg"
|
"github.com/chainreactors/spray/pkg"
|
||||||
"github.com/chainreactors/spray/pkg/ihttp"
|
"github.com/chainreactors/spray/pkg/ihttp"
|
||||||
"github.com/gosuri/uiprogress"
|
"github.com/gosuri/uiprogress"
|
||||||
"github.com/panjf2000/ants/v2"
|
"github.com/panjf2000/ants/v2"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,88 +15,21 @@ var BlackStatus = []int{400, 404, 410}
|
|||||||
var FuzzyStatus = []int{403, 500, 501, 502, 503}
|
var FuzzyStatus = []int{403, 500, 501, 502, 503}
|
||||||
|
|
||||||
type Runner struct {
|
type Runner struct {
|
||||||
URL string `short:"u" long:"url"`
|
URLList []string
|
||||||
URLFile string `short:"l" long:"list"`
|
Wordlist []string
|
||||||
URLList []string
|
Headers http.Header
|
||||||
WordFile string `short:"w" long:"work"`
|
Threads int
|
||||||
Wordlist []string
|
PoolSize int
|
||||||
Headers http.Header `long:"header"`
|
Pools *ants.PoolWithFunc
|
||||||
OutputFile string `short:"f"`
|
poolwg sync.WaitGroup
|
||||||
Offset int `long:"offset"`
|
Timeout int
|
||||||
Limit int `long:"limit"`
|
Mod string
|
||||||
Threads int `short:"t" long:"thread" default:"20"`
|
OutputCh chan *baseline
|
||||||
PoolSize int `short:"p" long:"pool" default:"5"`
|
Progress *uiprogress.Progress
|
||||||
Pools *ants.PoolWithFunc
|
|
||||||
poolwg sync.WaitGroup
|
|
||||||
Deadline int `long:"deadline" default:"600"` // todo 总的超时时间,适配云函数的deadline
|
|
||||||
Debug bool `long:"debug"`
|
|
||||||
Quiet bool `short:"q" long:"quiet"`
|
|
||||||
Mod string `short:"m" long:"mod" default:"path"`
|
|
||||||
OutputCh chan *baseline
|
|
||||||
Progress *uiprogress.Progress
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runner) Prepare() error {
|
func (r *Runner) Prepare() error {
|
||||||
r.Progress = uiprogress.New()
|
|
||||||
|
|
||||||
if r.Debug {
|
|
||||||
logs.Log.Level = logs.Debug
|
|
||||||
}
|
|
||||||
if !r.Quiet {
|
|
||||||
r.Progress.Start()
|
|
||||||
logs.Log.Writer = r.Progress.Bypass()
|
|
||||||
}
|
|
||||||
|
|
||||||
var file *os.File
|
|
||||||
var err error
|
var err error
|
||||||
urlfrom := r.URLFile
|
|
||||||
if r.URL != "" {
|
|
||||||
r.URLList = append(r.URLList, r.URL)
|
|
||||||
urlfrom = "cmd"
|
|
||||||
} else if r.URLFile != "" {
|
|
||||||
file, err = os.Open(r.URLFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else if pkg.HasStdin() {
|
|
||||||
file = os.Stdin
|
|
||||||
urlfrom = "stdin"
|
|
||||||
}
|
|
||||||
|
|
||||||
if file != nil {
|
|
||||||
content, err := ioutil.ReadAll(file)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
r.URLList = strings.Split(string(content), "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo url formatter
|
|
||||||
for i, u := range r.URLList {
|
|
||||||
r.URLList[i] = strings.TrimSpace(u)
|
|
||||||
}
|
|
||||||
logs.Log.Importantf("load %d urls from %s", len(r.URLList), urlfrom)
|
|
||||||
|
|
||||||
if r.WordFile != "" {
|
|
||||||
content, err := ioutil.ReadFile(r.WordFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
r.Wordlist = strings.Split(string(content), "\n")
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("not special wordlist")
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Wordlist != nil && len(r.Wordlist) > 0 {
|
|
||||||
// todo suffix/prefix/trim/generator
|
|
||||||
for i, word := range r.Wordlist {
|
|
||||||
r.Wordlist[i] = strings.TrimSpace(word)
|
|
||||||
}
|
|
||||||
logs.Log.Importantf("load %d word from %s", len(r.Wordlist), r.WordFile)
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("no wordlist")
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckStatusCode = func(status int) bool {
|
CheckStatusCode = func(status int) bool {
|
||||||
for _, black := range BlackStatus {
|
for _, black := range BlackStatus {
|
||||||
if black == status {
|
if black == status {
|
||||||
@ -116,13 +45,12 @@ func (r *Runner) Prepare() error {
|
|||||||
r.Pools, err = ants.NewPoolWithFunc(r.PoolSize, func(i interface{}) {
|
r.Pools, err = ants.NewPoolWithFunc(r.PoolSize, func(i interface{}) {
|
||||||
u := i.(string)
|
u := i.(string)
|
||||||
config := &pkg.Config{
|
config := &pkg.Config{
|
||||||
BaseURL: u,
|
BaseURL: u,
|
||||||
Wordlist: r.Wordlist,
|
Wordlist: r.Wordlist,
|
||||||
Thread: r.Threads,
|
Thread: r.Threads,
|
||||||
Timeout: 2,
|
Timeout: r.Timeout,
|
||||||
Headers: r.Headers,
|
Headers: r.Headers,
|
||||||
Mod: pkg.ModMap[r.Mod],
|
Mod: pkg.ModMap[r.Mod],
|
||||||
DeadlineTime: r.Deadline,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Mod == pkg.PathSpray {
|
if config.Mod == pkg.PathSpray {
|
||||||
@ -146,6 +74,10 @@ func (r *Runner) Prepare() error {
|
|||||||
pool.Run(ctx)
|
pool.Run(ctx)
|
||||||
r.poolwg.Done()
|
r.poolwg.Done()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
go r.Outputting()
|
go r.Outputting()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,14 @@ var ModMap = map[string]SprayMod{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
BaseURL string
|
BaseURL string
|
||||||
Wordlist []string
|
Wordlist []string
|
||||||
Thread int
|
Thread int
|
||||||
Timeout int
|
Timeout int
|
||||||
BaseReq *http.Request
|
BaseReq *http.Request
|
||||||
Method string
|
Method string
|
||||||
Mod SprayMod
|
Mod SprayMod
|
||||||
Headers http.Header
|
Headers http.Header
|
||||||
DeadlineTime int
|
EnableFuzzy bool
|
||||||
EnableFuzzy bool
|
ClientType int
|
||||||
ClientType int
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user