2022-10-27 18:53:26 +08:00
package internal
import (
2024-02-08 16:28:27 +08:00
"bytes"
2023-12-28 14:34:19 +08:00
"errors"
2022-11-10 04:22:42 +08:00
"fmt"
2022-11-21 20:44:02 +08:00
"github.com/antonmedv/expr"
2022-11-29 15:08:10 +08:00
"github.com/chainreactors/files"
2022-10-27 18:53:26 +08:00
"github.com/chainreactors/logs"
2024-02-20 19:12:05 +08:00
"github.com/chainreactors/parsers"
2024-02-08 15:26:01 +08:00
"github.com/chainreactors/spray/internal/ihttp"
2024-02-10 18:23:50 +08:00
"github.com/chainreactors/spray/internal/pool"
2022-10-27 18:53:26 +08:00
"github.com/chainreactors/spray/pkg"
2023-03-27 14:58:40 +08:00
"github.com/chainreactors/utils"
2023-12-28 14:34:19 +08:00
"github.com/chainreactors/utils/iutils"
2022-10-27 23:40:00 +08:00
"github.com/chainreactors/words/mask"
2022-12-06 21:45:14 +08:00
"github.com/chainreactors/words/rule"
2024-03-07 00:24:30 +08:00
"github.com/vbauerster/mpb/v8"
2022-10-27 18:53:26 +08:00
"io/ioutil"
2022-12-12 17:05:44 +08:00
"net/url"
2022-10-27 18:53:26 +08:00
"os"
2024-02-20 19:12:05 +08:00
"regexp"
2022-11-10 04:48:07 +08:00
"strconv"
2022-10-27 18:53:26 +08:00
"strings"
2024-02-10 18:23:50 +08:00
"sync"
2024-03-07 00:24:30 +08:00
"time"
2022-10-27 18:53:26 +08:00
)
2023-03-27 14:58:40 +08:00
var (
DefaultThreads = 20
2024-02-20 18:52:43 +08:00
SkipChar = "%SKIP%"
2023-03-27 14:58:40 +08:00
)
2022-10-27 18:53:26 +08:00
type Option struct {
2024-02-12 16:49:44 +08:00
InputOptions ` group:"Input Options" config:"input" default:"" `
FunctionOptions ` group:"Function Options" config:"functions" default:"" `
OutputOptions ` group:"Output Options" config:"output" `
PluginOptions ` group:"Plugin Options" config:"plugins" `
RequestOptions ` group:"Request Options" config:"request" `
ModeOptions ` group:"Modify Options" config:"mode" `
MiscOptions ` group:"Miscellaneous Options" config:"misc" `
2022-11-10 15:43:25 +08:00
}
type InputOptions struct {
2024-02-12 16:49:44 +08:00
ResumeFrom string ` long:"resume" description:"File, resume filename" `
Config string ` short:"c" long:"config" description:"File, config filename" `
2023-06-12 11:18:23 +08:00
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" `
PortRange string ` short:"p" long:"port" description:"String, input port range, e.g.: 80,8080-8090,db" `
2024-02-12 16:49:44 +08:00
CIDRs string ` long:"cidr" description:"String, input cidr, e.g.: 1.1.1.1/24 " `
2023-06-12 11:18:23 +08:00
//Raw string `long:"raw" description:"File, input raw request filename"`
2024-03-06 18:14:07 +08:00
NoDict bool ` long:"no-dict" description:"Bool, no dictionary" `
2024-02-12 16:49:44 +08:00
Dictionaries [ ] string ` short:"d" long:"dict" description:"Files, Multi,dict files, e.g.: -d 1.txt -d 2.txt" config:"dictionaries" `
Word string ` short:"w" long:"word" description:"String, word generate dsl, e.g.: -w test { ?ld#4}" config:"word" `
Rules [ ] string ` short:"r" long:"rules" description:"Files, rule files, e.g.: -r rule1.txt -r rule2.txt" config:"rules" `
AppendRule [ ] string ` long:"append-rule" description:"Files, when found valid path , use append rule generator new word with current path" config:"append-rules" `
FilterRule string ` long:"filter-rule" description:"String, filter rule, e.g.: --rule-filter '>8 <4'" config:"filter-rule" `
AppendFile [ ] string ` long:"append-file" description:"Files, when found valid path , use append file new word with current path" config:"append-files" `
2024-03-06 18:14:07 +08:00
Offset int ` long:"offset" description:"Int, wordlist offset" `
Limit int ` long:"limit" description:"Int, wordlist limit, start with offset. e.g.: --offset 1000 --limit 100" `
2022-12-12 17:05:44 +08:00
}
type FunctionOptions struct {
2024-02-12 16:49:44 +08:00
Extensions string ` short:"e" long:"extension" description:"String, add extensions (separated by commas), e.g.: -e jsp,jspx" config:"extension" `
ForceExtension bool ` long:"force-extension" description:"Bool, force add extensions" config:"force-extension" `
ExcludeExtensions string ` long:"exclude-extension" description:"String, exclude extensions (separated by commas), e.g.: --exclude-extension jsp,jspx" config:"exclude-extension" `
RemoveExtensions string ` long:"remove-extension" description:"String, remove extensions (separated by commas), e.g.: --remove-extension jsp,jspx" config:"remove-extension" `
Uppercase bool ` short:"U" long:"uppercase" description:"Bool, upper wordlist, e.g.: --uppercase" config:"upper" `
Lowercase bool ` short:"L" long:"lowercase" description:"Bool, lower wordlist, e.g.: --lowercase" config:"lower" `
Prefixes [ ] string ` long:"prefix" description:"Strings, add prefix, e.g.: --prefix aaa --prefix bbb" config:"prefix" `
Suffixes [ ] string ` long:"suffix" description:"Strings, add suffix, e.g.: --suffix aaa --suffix bbb" config:"suffix" `
Replaces map [ string ] string ` long:"replace" description:"Strings, replace string, e.g.: --replace aaa:bbb --replace ccc:ddd" config:"replace" `
Skips [ ] string ` long:"skip" description:"String, skip word when generate. rule, e.g.: --skip aaa" config:"skip" `
2024-02-07 02:34:18 +08:00
//SkipEval string `long:"skip-eval" description:"String, skip word when generate. rule, e.g.: --skip-eval 'current.Length < 4'"`
2022-11-10 15:43:25 +08:00
}
type OutputOptions struct {
2024-02-12 16:49:44 +08:00
Match string ` long:"match" description:"String, custom match function, e.g.: --match 'current.Status != 200''" config:"match" `
Filter string ` long:"filter" description:"String, custom filter function, e.g.: --filter 'current.Body contains \"hello\"'" config:"filter" `
Fuzzy bool ` long:"fuzzy" description:"String, open fuzzy output" config:"fuzzy" `
OutputFile string ` short:"f" long:"file" description:"String, output filename" json:"output_file,omitempty" config:"output-file" `
FuzzyFile string ` long:"fuzzy-file" description:"String, fuzzy output filename" json:"fuzzy_file,omitempty" config:"fuzzy-file" `
DumpFile string ` long:"dump-file" description:"String, dump all request, and write to filename" config:"dump-file" `
Dump bool ` long:"dump" description:"Bool, dump all request" config:"dump" `
AutoFile bool ` long:"auto-file" description:"Bool, auto generator output and fuzzy filename" config:"auto-file" `
Format string ` short:"F" long:"format" description:"String, output format, e.g.: --format 1.json" config:"format" `
OutputProbe string ` short:"o" long:"probe" description:"String, output format" config:"output_probe" `
2022-11-10 15:43:25 +08:00
}
type RequestOptions struct {
2024-02-12 16:49:44 +08:00
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" `
RandomUserAgent bool ` long:"random-agent" description:"Bool, use random with default user-agent" config:"random-useragent" `
Cookie [ ] string ` long:"cookie" description:"Strings, custom cookie" config:"cookies" `
ReadAll bool ` long:"read-all" description:"Bool, read all response body" config:"read-all" `
2024-03-06 17:55:18 +08:00
MaxBodyLength int64 ` long:"max-length" default:"100" description:"Int, max response body length (kb), -1 read-all, 0 not read body, default 100k, e.g. --max-length 1000" config:"max-length" `
2022-11-17 05:40:02 +08:00
}
2023-01-12 17:41:44 +08:00
type PluginOptions struct {
2024-02-20 19:12:05 +08:00
Advance bool ` short:"a" long:"advance" description:"Bool, enable all plugin" config:"all" `
Extracts [ ] string ` long:"extract" description:"Strings, extract response, e.g.: --extract js --extract ip --extract version:(.*?)" config:"extract" `
ExtractConfig string ` long:"extract-config" description:"String, extract config filename" config:"extract-config" `
Recon bool ` long:"recon" description:"Bool, enable recon" config:"recon" `
Finger bool ` long:"finger" description:"Bool, enable active finger detect" config:"finger" `
Bak bool ` long:"bak" description:"Bool, enable bak found" config:"bak" `
FileBak bool ` long:"file-bak" description:"Bool, enable valid result bak found, equal --append-rule rule/filebak.txt" config:"file-bak" `
Common bool ` long:"common" description:"Bool, enable common file found" config:"common" `
Crawl bool ` long:"crawl" description:"Bool, enable crawl" config:"crawl" `
CrawlDepth int ` long:"crawl-depth" default:"3" description:"Int, crawl depth" config:"crawl-depth" `
2023-01-12 17:41:44 +08:00
}
2022-11-17 05:40:02 +08:00
type ModeOptions struct {
2024-02-12 16:49:44 +08:00
RateLimit int ` long:"rate-limit" default:"0" description:"Int, request rate limit (rate/s), e.g.: --rate-limit 100" config:"rate-limit" `
Force bool ` long:"force" description:"Bool, skip error break" config:"force" `
CheckOnly bool ` long:"check-only" description:"Bool, check only" config:"check-only" `
NoScope bool ` long:"no-scope" description:"Bool, no scope" config:"no-scope" `
Scope [ ] string ` long:"scope" description:"String, custom scope, e.g.: --scope *.example.com" config:"scope" `
Recursive string ` long:"recursive" default:"current.IsDir()" description:"String,custom recursive rule, e.g.: --recursive current.IsDir()" config:"recursive" `
Depth int ` long:"depth" default:"0" description:"Int, recursive depth" config:"depth" `
Index string ` long:"index" default:"/" description:"String, custom index path" config:"index" `
Random string ` long:"random" default:"" description:"String, custom random path" config:"random" `
CheckPeriod int ` long:"check-period" default:"200" description:"Int, check period when request" config:"check-period" `
ErrPeriod int ` long:"error-period" default:"10" description:"Int, check period when error" config:"error-period" `
BreakThreshold int ` long:"error-threshold" default:"20" description:"Int, break when the error exceeds the threshold" config:"error-threshold" `
BlackStatus string ` long:"black-status" default:"400,410" description:"Strings (comma split),custom black status" config:"black-status" `
WhiteStatus string ` long:"white-status" default:"200" description:"Strings (comma split), custom white status" config:"white-status" `
FuzzyStatus string ` long:"fuzzy-status" default:"500,501,502,503" description:"Strings (comma split), custom fuzzy status" config:"fuzzy-status" `
UniqueStatus string ` long:"unique-status" default:"403,200,404" description:"Strings (comma split), custom unique status" config:"unique-status" `
Unique bool ` long:"unique" description:"Bool, unique response" config:"unique" `
RetryCount int ` long:"retry" default:"0" description:"Int, retry count" config:"retry" `
SimhashDistance int ` long:"distance" default:"5" config:"distance" `
2022-11-10 15:43:25 +08:00
}
type MiscOptions struct {
2024-02-12 16:49:44 +08:00
Mod string ` short:"m" long:"mod" default:"path" choice:"path" choice:"host" description:"String, path/host spray" config:"mod" `
Client string ` short:"C" long:"client" default:"auto" choice:"fast" choice:"standard" choice:"auto" description:"String, Client type" config:"client" `
Deadline int ` long:"deadline" default:"999999" description:"Int, deadline (seconds)" config:"deadline" ` // todo 总的超时时间,适配云函数的deadline
Timeout int ` long:"timeout" default:"5" description:"Int, timeout with request (seconds)" config:"timeout" `
PoolSize int ` short:"P" long:"pool" default:"5" description:"Int, Pool size" config:"pool" `
Threads int ` short:"t" long:"thread" default:"20" description:"Int, number of threads per pool" config:"thread" `
Debug bool ` long:"debug" description:"Bool, output debug info" config:"debug" `
2024-02-08 14:57:33 +08:00
Version bool ` long:"version" description:"Bool, show version" `
2024-02-12 16:49:44 +08:00
Verbose [ ] bool ` short:"v" description:"Bool, log verbose level ,default 0, level1: -v level2 -vv " config:"verbose" `
Quiet bool ` short:"q" long:"quiet" description:"Bool, Quiet" config:"quiet" `
NoColor bool ` long:"no-color" description:"Bool, no color" config:"no-color" `
NoBar bool ` long:"no-bar" description:"Bool, No progress bar" config:"no-bar" `
Proxy string ` long:"proxy" default:"" description:"String, proxy address, e.g.: --proxy socks5://127.0.0.1:1080" config:"proxy" `
2022-10-27 18:53:26 +08:00
}
func ( opt * Option ) PrepareRunner ( ) ( * Runner , error ) {
2023-12-28 14:34:19 +08:00
err := opt . Validate ( )
if err != nil {
return nil , err
2022-11-10 04:22:42 +08:00
}
2022-10-27 18:53:26 +08:00
r := & Runner {
2024-03-07 00:24:30 +08:00
Progress : mpb . New ( mpb . WithRefreshRate ( 100 * time . Millisecond ) ) ,
2023-03-24 14:20:31 +08:00
Threads : opt . Threads ,
PoolSize : opt . PoolSize ,
Mod : opt . Mod ,
Timeout : opt . Timeout ,
RateLimit : opt . RateLimit ,
Deadline : opt . Deadline ,
Headers : make ( map [ string ] string ) ,
Offset : opt . Offset ,
Total : opt . Limit ,
taskCh : make ( chan * Task ) ,
2024-02-10 18:23:50 +08:00
outputCh : make ( chan * pkg . Baseline , 100 ) ,
outwg : & sync . WaitGroup { } ,
fuzzyCh : make ( chan * pkg . Baseline , 100 ) ,
2023-03-24 14:20:31 +08:00
Fuzzy : opt . Fuzzy ,
Force : opt . Force ,
CheckOnly : opt . CheckOnly ,
CheckPeriod : opt . CheckPeriod ,
ErrPeriod : opt . ErrPeriod ,
BreakThreshold : opt . BreakThreshold ,
Crawl : opt . Crawl ,
2023-04-14 20:05:21 +08:00
Scope : opt . Scope ,
2024-02-08 16:46:34 +08:00
Finger : opt . Finger ,
2023-03-24 14:20:31 +08:00
Bak : opt . Bak ,
Common : opt . Common ,
2023-05-04 12:04:59 +08:00
RetryCount : opt . RetryCount ,
2023-03-24 14:20:31 +08:00
RandomUserAgent : opt . RandomUserAgent ,
2023-06-03 22:24:52 +08:00
Random : opt . Random ,
Index : opt . Index ,
2024-02-07 01:29:05 +08:00
Proxy : opt . Proxy ,
2022-10-27 18:53:26 +08:00
}
2022-10-28 00:46:54 +08:00
2023-01-09 21:47:06 +08:00
// log and bar
2022-12-15 00:19:06 +08:00
if ! opt . NoColor {
2023-12-28 14:34:19 +08:00
logs . Log . SetColor ( true )
2022-12-15 00:19:06 +08:00
r . Color = true
}
2022-12-02 19:59:15 +08:00
if opt . Quiet {
2023-12-28 14:34:19 +08:00
logs . Log . SetQuiet ( true )
logs . Log . SetColor ( false )
2022-12-15 00:19:06 +08:00
r . Color = false
2022-12-02 19:59:15 +08:00
}
2022-12-06 21:45:14 +08:00
if ! ( opt . Quiet || opt . NoBar ) {
2024-03-07 00:24:30 +08:00
logs . Log . SetOutput ( r . Progress )
2022-10-27 18:53:26 +08:00
}
2022-11-10 21:26:07 +08:00
2023-01-05 23:26:14 +08:00
// configuration
2022-11-10 17:19:05 +08:00
if opt . Force {
2022-11-17 05:40:02 +08:00
// 如果开启了force模式, 将关闭check机制, err积累到一定数量自动退出机制
2024-02-20 18:25:43 +08:00
r . BreakThreshold = MAX
r . CheckPeriod = MAX
r . ErrPeriod = MAX
2022-11-10 17:19:05 +08:00
}
2023-04-14 20:05:21 +08:00
// 选择client
2023-01-10 00:57:55 +08:00
if opt . Client == "auto" {
r . ClientType = ihttp . Auto
} else if opt . Client == "fast" {
r . ClientType = ihttp . FAST
2023-04-14 20:05:21 +08:00
} else if opt . Client == "standard" || opt . Client == "base" || opt . Client == "http" {
2023-01-10 00:57:55 +08:00
r . ClientType = ihttp . STANDARD
}
2023-03-27 14:58:40 +08:00
if opt . Threads == DefaultThreads && opt . CheckOnly {
r . Threads = 1000
}
2024-02-20 19:12:05 +08:00
if opt . Extracts != nil {
for _ , e := range opt . Extracts {
if reg , ok := pkg . ExtractRegexps [ e ] ; ok {
pkg . Extractors [ e ] = reg
} else {
pkg . Extractors [ e ] = [ ] * parsers . Extractor {
& parsers . Extractor {
Name : e ,
CompiledRegexps : [ ] * regexp . Regexp { regexp . MustCompile ( e ) } ,
} ,
}
}
}
}
if opt . ExtractConfig != "" {
extracts , err := pkg . LoadExtractorConfig ( opt . ExtractConfig )
if err != nil {
return nil , err
}
pkg . Extractors [ opt . ExtractConfig ] = extracts
}
2023-01-28 15:43:43 +08:00
if opt . Recon {
pkg . Extractors [ "recon" ] = pkg . ExtractRegexps [ "pentest" ]
}
2023-01-05 23:26:14 +08:00
if opt . Advance {
r . Crawl = true
2024-02-08 16:46:34 +08:00
r . Finger = true
2023-01-06 01:28:09 +08:00
r . Bak = true
2023-01-06 13:07:59 +08:00
r . Common = true
2024-03-04 20:03:06 +08:00
pkg . EnableAllFingerEngine = true
2023-01-28 15:43:43 +08:00
pkg . Extractors [ "recon" ] = pkg . ExtractRegexps [ "pentest" ]
2023-01-05 23:26:14 +08:00
opt . AppendRule = append ( opt . AppendRule , "filebak" )
} else if opt . FileBak {
opt . AppendRule = append ( opt . AppendRule , "filebak" )
}
2023-01-28 15:43:43 +08:00
2023-01-12 16:35:34 +08:00
var s strings . Builder
if r . Crawl {
s . WriteString ( "crawl enable; " )
}
2024-02-08 16:46:34 +08:00
if r . Finger {
r . AppendWords = append ( r . AppendWords , pkg . ActivePath ... )
2024-03-04 20:03:06 +08:00
pkg . EnableAllFingerEngine = true
2023-01-12 16:35:34 +08:00
s . WriteString ( "active fingerprint enable; " )
}
if r . Bak {
s . WriteString ( "bak file enable; " )
}
if r . Common {
2024-02-08 16:46:34 +08:00
r . AppendWords = append ( r . AppendWords , mask . SpecialWords [ "common_file" ] ... )
2023-01-12 16:35:34 +08:00
s . WriteString ( "common file enable; " )
}
2023-01-28 15:43:43 +08:00
if opt . Recon {
s . WriteString ( "recon enable; " )
}
2023-01-12 16:35:34 +08:00
if len ( opt . AppendRule ) > 0 {
s . WriteString ( "file bak enable; " )
}
2024-02-08 14:57:33 +08:00
2023-06-12 11:08:21 +08:00
if r . RetryCount > 0 {
2024-02-07 01:54:31 +08:00
s . WriteString ( "Retry Count: " + strconv . Itoa ( r . RetryCount ) )
2023-06-12 11:08:21 +08:00
}
2024-02-08 14:57:33 +08:00
if s . Len ( ) > 0 {
logs . Log . Important ( s . String ( ) )
}
2023-06-12 11:08:21 +08:00
2023-04-14 20:05:21 +08:00
if opt . NoScope {
r . Scope = [ ] string { "*" }
}
2024-02-10 18:23:50 +08:00
pkg . BlackStatus = parseStatus ( pkg . BlackStatus , opt . BlackStatus )
pkg . WhiteStatus = parseStatus ( pkg . WhiteStatus , opt . WhiteStatus )
2023-02-07 18:42:20 +08:00
if opt . FuzzyStatus == "all" {
2024-02-10 18:23:50 +08:00
pool . EnableAllFuzzy = true
2023-02-07 18:42:20 +08:00
} else {
2024-02-10 18:23:50 +08:00
pkg . FuzzyStatus = parseStatus ( pkg . FuzzyStatus , opt . FuzzyStatus )
2023-02-07 18:42:20 +08:00
}
2022-11-29 15:16:33 +08:00
2023-02-08 12:58:56 +08:00
if opt . Unique {
2024-02-10 18:23:50 +08:00
pool . EnableAllUnique = true
2023-02-08 12:58:56 +08:00
} else {
2024-02-10 18:23:50 +08:00
pkg . UniqueStatus = parseStatus ( pkg . UniqueStatus , opt . UniqueStatus )
2023-02-08 12:58:56 +08:00
}
2022-10-27 18:53:26 +08:00
// prepare word
2022-10-27 19:07:17 +08:00
dicts := make ( [ ] [ ] string , len ( opt . Dictionaries ) )
2024-03-06 18:14:07 +08:00
if len ( opt . Dictionaries ) == 0 && ! opt . NoDict {
2024-02-07 02:59:55 +08:00
dicts = append ( dicts , pkg . LoadDefaultDict ( ) )
logs . Log . Warn ( "not set any dictionary, use default dictionary: https://github.com/maurosoria/dirsearch/blob/master/db/dicc.txt" )
} else {
for i , f := range opt . Dictionaries {
dicts [ i ] , err = loadFileToSlice ( f )
if opt . ResumeFrom != "" {
dictCache [ f ] = dicts [ i ]
}
if err != nil {
return nil , err
}
2024-02-08 14:57:33 +08:00
2024-02-10 18:23:50 +08:00
logs . Log . Logf ( pkg . LogVerbose , "Loaded %d word from %s" , len ( dicts [ i ] ) , f )
2022-10-27 18:53:26 +08:00
}
}
2023-01-10 23:44:03 +08:00
if opt . Word == "" {
2024-02-07 02:59:55 +08:00
opt . Word = "{?"
for i , _ := range dicts {
opt . Word += strconv . Itoa ( i )
2022-10-27 23:40:00 +08:00
}
2024-02-07 02:59:55 +08:00
opt . Word += "}"
2022-11-10 04:48:07 +08:00
}
2022-11-10 16:52:00 +08:00
if opt . Suffixes != nil {
2022-11-30 01:31:15 +08:00
mask . SpecialWords [ "suffix" ] = opt . Suffixes
opt . Word += "{@suffix}"
2022-11-10 04:48:07 +08:00
}
if opt . Prefixes != nil {
2022-11-30 01:31:15 +08:00
mask . SpecialWords [ "prefix" ] = opt . Prefixes
opt . Word = "{@prefix}" + opt . Word
2022-11-10 04:48:07 +08:00
}
2024-02-07 02:34:18 +08:00
if opt . ForceExtension && opt . Extensions != "" {
2022-11-30 01:31:15 +08:00
exts := strings . Split ( opt . Extensions , "," )
for i , e := range exts {
if ! strings . HasPrefix ( e , "." ) {
exts [ i ] = "." + e
}
}
mask . SpecialWords [ "ext" ] = exts
opt . Word += "{@ext}"
2022-11-10 04:48:07 +08:00
}
2023-01-06 00:48:13 +08:00
r . Wordlist , err = mask . Run ( opt . Word , dicts , nil )
2022-11-10 04:48:07 +08:00
if err != nil {
2023-03-13 13:03:10 +08:00
return nil , fmt . Errorf ( "%s %w" , opt . Word , err )
2022-10-27 18:53:26 +08:00
}
2022-12-12 17:05:44 +08:00
if len ( r . Wordlist ) > 0 {
2024-02-10 18:23:50 +08:00
logs . Log . Logf ( pkg . LogVerbose , "Parsed %d words by %s" , len ( r . Wordlist ) , opt . Word )
2022-12-02 19:59:15 +08:00
}
2022-11-21 11:45:48 +08:00
2022-12-06 21:45:14 +08:00
if opt . Rules != nil {
2024-02-08 16:28:27 +08:00
rules , err := loadRuleAndCombine ( opt . Rules )
2023-01-05 23:26:14 +08:00
if err != nil {
return nil , err
2022-12-06 21:45:14 +08:00
}
2023-01-05 23:26:14 +08:00
r . Rules = rule . Compile ( rules , opt . FilterRule )
2022-12-09 19:30:12 +08:00
} else if opt . FilterRule != "" {
// if filter rule is not empty, set rules to ":", force to open filter mode
r . Rules = rule . Compile ( ":" , opt . FilterRule )
2023-01-05 22:42:07 +08:00
} else {
r . Rules = new ( rule . Program )
2022-12-06 21:45:14 +08:00
}
2023-01-05 22:42:07 +08:00
if len ( r . Rules . Expressions ) > 0 {
r . Total = len ( r . Wordlist ) * len ( r . Rules . Expressions )
2022-12-06 21:45:14 +08:00
} else {
r . Total = len ( r . Wordlist )
}
2023-01-05 22:42:07 +08:00
2022-12-12 17:05:44 +08:00
pkg . DefaultStatistor = pkg . Statistor {
Word : opt . Word ,
WordCount : len ( r . Wordlist ) ,
Dictionaries : opt . Dictionaries ,
Offset : opt . Offset ,
RuleFiles : opt . Rules ,
RuleFilter : opt . FilterRule ,
Total : r . Total ,
2022-12-02 19:59:15 +08:00
}
2023-01-05 23:26:14 +08:00
if opt . AppendRule != nil {
2024-02-08 16:28:27 +08:00
content , err := loadRuleAndCombine ( opt . AppendRule )
2023-01-05 22:42:07 +08:00
if err != nil {
return nil , err
}
r . AppendRules = rule . Compile ( string ( content ) , "" )
}
2023-03-27 14:58:40 +08:00
2024-02-08 16:28:27 +08:00
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 )
}
2024-02-08 16:46:34 +08:00
r . AppendWords = append ( r . AppendWords , lines ... )
2024-02-08 16:28:27 +08:00
}
2023-03-27 14:58:40 +08:00
ports := utils . ParsePort ( opt . PortRange )
2022-12-02 19:59:15 +08:00
// prepare task
2023-03-27 14:58:40 +08:00
tasks := make ( chan * Task , opt . PoolSize )
2022-12-02 19:59:15 +08:00
var taskfrom string
if opt . ResumeFrom != "" {
stats , err := pkg . ReadStatistors ( opt . ResumeFrom )
if err != nil {
2023-03-27 14:58:40 +08:00
logs . Log . Error ( err . Error ( ) )
2022-12-02 19:59:15 +08:00
}
2023-03-27 14:58:40 +08:00
r . Count = len ( stats )
2022-12-02 19:59:15 +08:00
taskfrom = "resume " + opt . ResumeFrom
2023-03-27 14:58:40 +08:00
go func ( ) {
for _ , stat := range stats {
2023-04-14 20:05:21 +08:00
tasks <- & Task { baseUrl : stat . BaseUrl , origin : NewOrigin ( stat ) }
2023-03-27 14:58:40 +08:00
}
close ( tasks )
} ( )
2022-11-11 11:40:53 +08:00
} else {
2022-12-02 19:59:15 +08:00
var file * os . File
2023-03-27 14:58:40 +08:00
// 根据不同的输入类型生成任务
2023-01-05 14:40:20 +08:00
if len ( opt . URL ) == 1 {
u , err := url . Parse ( opt . URL [ 0 ] )
if err != nil {
u , _ = url . Parse ( "http://" + opt . URL [ 0 ] )
}
2023-04-06 20:41:35 +08:00
go func ( ) {
2023-04-06 21:20:24 +08:00
opt . GenerateTasks ( tasks , u . String ( ) , ports )
2023-04-06 20:41:35 +08:00
close ( tasks )
} ( )
2023-01-05 14:40:20 +08:00
taskfrom = u . Host
2023-03-27 14:58:40 +08:00
r . Count = 1
2023-01-05 14:40:20 +08:00
} else if len ( opt . URL ) > 1 {
2023-03-27 14:58:40 +08:00
go func ( ) {
for _ , u := range opt . URL {
opt . GenerateTasks ( tasks , u , ports )
}
close ( tasks )
} ( )
2022-12-02 19:59:15 +08:00
taskfrom = "cmd"
2023-03-27 14:58:40 +08:00
r . Count = len ( opt . URL )
} else if opt . CIDRs != "" {
if len ( ports ) == 0 {
ports = [ ] string { "80" , "443" }
}
for _ , cidr := range strings . Split ( opt . CIDRs , "," ) {
ips := utils . ParseCIDR ( cidr )
if ips != nil {
r . Count += ips . Count ( )
}
}
go func ( ) {
for _ , cidr := range strings . Split ( opt . CIDRs , "," ) {
ips := utils . ParseCIDR ( cidr )
if ips == nil {
logs . Log . Error ( "cidr format error: " + cidr )
}
for ip := range ips . Range ( ) {
opt . GenerateTasks ( tasks , ip . String ( ) , ports )
}
}
close ( tasks )
} ( )
taskfrom = "cidr"
2022-12-02 19:59:15 +08:00
} else if opt . URLFile != "" {
file , err = os . Open ( opt . URLFile )
if err != nil {
2023-03-27 14:58:40 +08:00
logs . Log . Error ( err . Error ( ) )
2022-12-02 19:59:15 +08:00
}
taskfrom = opt . URLFile
2023-12-28 14:34:19 +08:00
} else if files . HasStdin ( ) {
2022-12-02 19:59:15 +08:00
file = os . Stdin
taskfrom = "stdin"
}
if file != nil {
content , err := ioutil . ReadAll ( file )
if err != nil {
2023-06-03 16:39:35 +08:00
return nil , err
2022-12-02 19:59:15 +08:00
}
2023-03-27 14:58:40 +08:00
urls := strings . Split ( strings . TrimSpace ( string ( content ) ) , "\n" )
for _ , u := range urls {
2023-06-03 16:39:35 +08:00
u = strings . TrimSpace ( u )
if _ , err := url . Parse ( u ) ; err == nil {
2023-03-27 14:58:40 +08:00
r . Count ++
} else if ip := utils . ParseIP ( u ) ; ip != nil {
r . Count ++
} else if cidr := utils . ParseCIDR ( u ) ; cidr != nil {
r . Count += cidr . Count ( )
}
2022-12-02 19:59:15 +08:00
}
2023-02-19 16:14:51 +08:00
2023-03-27 14:58:40 +08:00
go func ( ) {
for _ , u := range urls {
2023-06-03 16:39:35 +08:00
u = strings . TrimSpace ( u )
if _ , err := url . Parse ( u ) ; err == nil {
2023-03-27 14:58:40 +08:00
opt . GenerateTasks ( tasks , u , ports )
} else if ip := utils . ParseIP ( u ) ; ip != nil {
opt . GenerateTasks ( tasks , u , ports )
} else if cidr := utils . ParseCIDR ( u ) ; cidr != nil {
for ip := range cidr . Range ( ) {
opt . GenerateTasks ( tasks , ip . String ( ) , ports )
}
}
}
close ( tasks )
} ( )
2022-12-02 19:59:15 +08:00
}
2022-11-10 16:52:00 +08:00
}
2022-10-27 19:07:17 +08:00
2023-04-06 20:41:35 +08:00
if len ( ports ) > 0 {
r . Count = r . Count * len ( ports )
}
2022-12-02 19:59:15 +08:00
r . Tasks = tasks
2024-02-10 18:23:50 +08:00
logs . Log . Logf ( pkg . LogVerbose , "Loaded %d urls from %s" , len ( tasks ) , taskfrom )
2022-12-02 19:59:15 +08:00
2024-02-07 02:34:18 +08:00
// 类似dirsearch中的
if opt . Extensions != "" {
r . AppendFunction ( func ( s string ) [ ] string {
exts := strings . Split ( opt . Extensions , "," )
ss := make ( [ ] string , len ( exts ) )
for i , e := range exts {
if strings . Contains ( s , "%EXT%" ) {
ss [ i ] = strings . Replace ( s , "%EXT%" , e , - 1 )
}
}
return ss
} )
} else {
r . AppendFunction ( func ( s string ) [ ] string {
if strings . Contains ( s , "%EXT%" ) {
return nil
}
return [ ] string { s }
} )
}
2022-11-10 04:22:42 +08:00
if opt . Uppercase {
2024-02-07 02:34:18 +08:00
r . AppendFunction ( wrapWordsFunc ( strings . ToUpper ) )
2022-11-10 04:22:42 +08:00
}
if opt . Lowercase {
2024-02-07 02:34:18 +08:00
r . AppendFunction ( wrapWordsFunc ( strings . ToLower ) )
2022-11-10 04:22:42 +08:00
}
2022-11-10 04:48:07 +08:00
if opt . RemoveExtensions != "" {
rexts := strings . Split ( opt . ExcludeExtensions , "," )
2024-02-07 02:34:18 +08:00
r . AppendFunction ( func ( s string ) [ ] string {
2023-01-28 13:15:49 +08:00
if ext := parseExtension ( s ) ; iutils . StringsContains ( rexts , ext ) {
2024-02-07 02:34:18 +08:00
return [ ] string { strings . TrimSuffix ( s , "." + ext ) }
2022-11-10 04:48:07 +08:00
}
2024-02-07 02:34:18 +08:00
return [ ] string { s }
2022-11-10 04:48:07 +08:00
} )
}
2022-11-10 04:22:42 +08:00
if opt . ExcludeExtensions != "" {
exexts := strings . Split ( opt . ExcludeExtensions , "," )
2024-02-07 02:34:18 +08:00
r . AppendFunction ( func ( s string ) [ ] string {
2023-01-28 13:15:49 +08:00
if ext := parseExtension ( s ) ; iutils . StringsContains ( exexts , ext ) {
2024-02-07 02:34:18 +08:00
return nil
2022-11-10 04:22:42 +08:00
}
2024-02-07 02:34:18 +08:00
return [ ] string { s }
2022-11-10 04:22:42 +08:00
} )
}
2022-11-10 04:48:07 +08:00
2022-11-10 16:52:00 +08:00
if len ( opt . Replaces ) > 0 {
2024-02-07 02:34:18 +08:00
r . AppendFunction ( func ( s string ) [ ] string {
2022-11-10 04:48:07 +08:00
for k , v := range opt . Replaces {
s = strings . Replace ( s , k , v , - 1 )
2022-11-10 04:22:42 +08:00
}
2024-02-07 02:34:18 +08:00
return [ ] string { s }
2022-11-10 04:22:42 +08:00
} )
}
2024-02-07 02:34:18 +08:00
// default skip function, skip %EXT%
r . AppendFunction ( func ( s string ) [ ] string {
if strings . Contains ( s , "%EXT%" ) {
return nil
}
return [ ] string { s }
} )
if len ( opt . Skips ) > 0 {
r . AppendFunction ( func ( s string ) [ ] string {
for _ , skip := range opt . Skips {
if strings . Contains ( s , skip ) {
return nil
}
}
return [ ] string { s }
} )
}
2024-02-10 18:23:50 +08:00
logs . Log . Logf ( pkg . LogVerbose , "Loaded %d dictionaries and %d decorators" , len ( opt . Dictionaries ) , len ( r . Fns ) )
2022-11-21 20:44:02 +08:00
if opt . Match != "" {
2023-06-03 21:09:01 +08:00
exp , err := expr . Compile ( opt . Match , expr . Patch ( & bytesPatcher { } ) )
2022-11-21 20:44:02 +08:00
if err != nil {
return nil , err
}
r . MatchExpr = exp
}
if opt . Filter != "" {
2023-06-03 21:09:01 +08:00
exp , err := expr . Compile ( opt . Filter , expr . Patch ( & bytesPatcher { } ) )
2022-11-21 20:44:02 +08:00
if err != nil {
return nil , err
}
r . FilterExpr = exp
}
2023-04-14 20:05:21 +08:00
// 初始化递归
var express string
if opt . Recursive != "current.IsDir()" && opt . Depth != 0 {
// 默认不打开递归, 除非指定了非默认的递归表达式
2024-02-10 18:23:50 +08:00
pool . MaxRecursion = 1
2023-04-14 20:05:21 +08:00
express = opt . Recursive
}
if opt . Depth != 0 {
// 手动设置的depth优先级高于默认
2024-02-10 18:23:50 +08:00
pool . MaxRecursion = opt . Depth
2023-04-14 20:05:21 +08:00
express = opt . Recursive
}
if express != "" {
2023-06-03 21:09:01 +08:00
exp , err := expr . Compile ( express , expr . Patch ( & bytesPatcher { } ) )
2022-12-14 20:24:25 +08:00
if err != nil {
return nil , err
}
r . RecursiveExpr = exp
2022-12-10 15:18:12 +08:00
}
2023-04-14 20:05:21 +08:00
2022-10-27 18:53:26 +08:00
// prepare header
for _ , h := range opt . Headers {
i := strings . Index ( h , ":" )
if i == - 1 {
logs . Log . Warn ( "invalid header" )
} else {
2023-01-03 18:43:12 +08:00
r . Headers [ h [ : i ] ] = h [ i + 2 : ]
2022-10-27 18:53:26 +08:00
}
}
2023-01-03 18:43:12 +08:00
if opt . UserAgent != "" {
r . Headers [ "User-Agent" ] = opt . UserAgent
}
if opt . Cookie != nil {
r . Headers [ "Cookie" ] = strings . Join ( opt . Cookie , "; " )
}
2022-11-10 17:19:05 +08:00
if opt . OutputProbe != "" {
r . Probes = strings . Split ( opt . OutputProbe , "," )
}
2022-11-29 15:08:10 +08:00
2023-04-04 00:46:00 +08:00
// init output file
2022-11-29 15:08:10 +08:00
if opt . OutputFile != "" {
r . OutputFile , err = files . NewFile ( opt . OutputFile , false , false , true )
if err != nil {
return nil , err
}
2022-12-12 00:13:47 +08:00
} else if opt . AutoFile {
2023-01-12 19:21:35 +08:00
r . OutputFile , err = files . NewFile ( "result.json" , false , false , true )
2022-12-12 00:13:47 +08:00
if err != nil {
return nil , err
}
2022-11-29 20:24:03 +08:00
}
2022-11-29 20:50:00 +08:00
if opt . FuzzyFile != "" {
r . FuzzyFile , err = files . NewFile ( opt . FuzzyFile , false , false , true )
if err != nil {
return nil , err
}
2022-12-12 00:13:47 +08:00
} else if opt . AutoFile {
2023-01-12 19:21:35 +08:00
r . FuzzyFile , err = files . NewFile ( "fuzzy.json" , false , false , true )
2022-12-12 00:13:47 +08:00
if err != nil {
return nil , err
}
2022-11-29 20:24:03 +08:00
}
2022-12-12 00:13:47 +08:00
if opt . DumpFile != "" {
r . DumpFile , err = files . NewFile ( opt . DumpFile , false , false , true )
if err != nil {
return nil , err
}
} else if opt . Dump {
2023-01-12 19:21:35 +08:00
r . DumpFile , err = files . NewFile ( "dump.json" , false , false , true )
2022-12-12 00:13:47 +08:00
if err != nil {
return nil , err
}
}
2022-12-12 17:05:44 +08:00
if opt . ResumeFrom != "" {
r . StatFile , err = files . NewFile ( opt . ResumeFrom , false , true , true )
2023-01-05 14:40:20 +08:00
} else {
2023-01-12 18:17:53 +08:00
r . StatFile , err = files . NewFile ( strings . ReplaceAll ( taskfrom , ":" , "_" ) + ".stat" , false , true , true )
2022-12-12 17:05:44 +08:00
}
if err != nil {
return nil , err
}
r . StatFile . Mod = os . O_WRONLY | os . O_CREATE
err = r . StatFile . Init ( )
2022-11-29 20:24:03 +08:00
if err != nil {
return nil , err
2022-11-29 15:08:10 +08:00
}
2022-10-27 18:53:26 +08:00
return r , nil
}
2023-12-28 14:34:19 +08:00
func ( opt * Option ) Validate ( ) error {
2022-11-10 04:22:42 +08:00
if opt . Uppercase && opt . Lowercase {
2023-12-28 14:34:19 +08:00
return errors . New ( "cannot set -U and -L at the same time" )
2022-11-10 04:22:42 +08:00
}
2022-12-12 17:05:44 +08:00
if ( opt . Offset != 0 || opt . Limit != 0 ) && opt . Depth > 0 {
// 偏移和上限与递归同时使用时也会造成混淆.
2023-12-28 14:34:19 +08:00
return errors . New ( "--offset and --limit cannot be used with --depth at the same time" )
2022-11-10 21:03:07 +08:00
}
2022-12-12 17:05:44 +08:00
if opt . Depth > 0 && opt . ResumeFrom != "" {
// 递归与断点续传会造成混淆, 断点续传的word与rule不是通过命令行获取的
2023-12-28 14:34:19 +08:00
return errors . New ( "--resume and --depth cannot be used at the same time" )
2022-11-10 04:22:42 +08:00
}
2023-06-12 11:18:23 +08:00
if opt . ResumeFrom == "" && opt . URL == nil && opt . URLFile == "" && opt . CIDRs == "" {
2023-12-28 14:34:19 +08:00
return fmt . Errorf ( "without any target, please use -u/-l/-c/--resume to set targets" )
2023-06-12 11:18:23 +08:00
}
2023-12-28 14:34:19 +08:00
return nil
2022-12-02 19:59:15 +08:00
}
2023-03-27 14:58:40 +08:00
// Generate Tasks
func ( opt * Option ) GenerateTasks ( ch chan * Task , u string , ports [ ] string ) {
parsed , err := url . Parse ( u )
if err != nil {
logs . Log . Warn ( err . Error ( ) )
return
}
if parsed . Scheme == "" {
if parsed . Port ( ) == "443" {
parsed . Scheme = "https"
} else {
parsed . Scheme = "http"
}
}
if len ( ports ) == 0 {
ch <- & Task { baseUrl : u }
return
}
for _ , p := range ports {
if parsed . Host == "" {
ch <- & Task { baseUrl : fmt . Sprintf ( "%s://%s:%s" , parsed . Scheme , parsed . Path , p ) }
} else {
ch <- & Task { baseUrl : fmt . Sprintf ( "%s://%s:%s/%s" , parsed . Scheme , parsed . Host , p , parsed . Path ) }
}
}
}