Merge pull request #74 from chainreactors/dev

merge v1.1.2
This commit is contained in:
M09Ic 2024-09-10 18:03:59 +08:00 committed by GitHub
commit 2f28b0ec3c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 327 additions and 176 deletions

9
go.mod
View File

@ -6,9 +6,8 @@ require (
github.com/chainreactors/files v0.0.0-20240716182835-7884ee1e77f0
github.com/chainreactors/fingers v0.0.0-20240716172449-2fc3147b9c2a
github.com/chainreactors/logs v0.0.0-20240207121836-c946f072f81f
github.com/chainreactors/parsers v0.0.0-20240829055950-923f89a92b84
github.com/chainreactors/parsers v0.0.0-20240910081704-fd57f462fc65
github.com/chainreactors/utils v0.0.0-20240805193040-ff3b97aa3c3f
github.com/chainreactors/words v0.4.1-0.20240510105042-5ba5c2edc508
github.com/expr-lang/expr v1.16.9
github.com/gookit/config/v2 v2.2.5
github.com/jessevdk/go-flags v1.5.0
@ -25,6 +24,10 @@ require (
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/chainreactors/words v0.0.0-20240910083848-19a289e8984b // indirect
github.com/charmbracelet/lipgloss v0.13.0 // indirect
github.com/charmbracelet/x/ansi v0.1.4 // indirect
github.com/facebookincubator/nvdtools v0.1.5 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/go-dedup/megophone v0.0.0-20170830025436-f01be21026f5 // indirect
@ -37,10 +40,12 @@ require (
github.com/gookit/goutil v0.6.15 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect

18
go.sum
View File

@ -70,6 +70,8 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
@ -97,14 +99,26 @@ github.com/chainreactors/parsers v0.0.0-20240825154421-240ad7eba29d h1:/rtYIkD9F
github.com/chainreactors/parsers v0.0.0-20240825154421-240ad7eba29d/go.mod h1:7rXdYz6jrdjF0WUH1ICcAXKIKKjKmJo2PU8u43V7jkA=
github.com/chainreactors/parsers v0.0.0-20240829055950-923f89a92b84 h1:F6umsdHLxKILxrH5wY2zVYwxMmb8XdCEc9apf7GWpOk=
github.com/chainreactors/parsers v0.0.0-20240829055950-923f89a92b84/go.mod h1:7rXdYz6jrdjF0WUH1ICcAXKIKKjKmJo2PU8u43V7jkA=
github.com/chainreactors/parsers v0.0.0-20240910081704-fd57f462fc65 h1:subSvyczsErYMRnCD07s4Ub6zOSaw2xZ1/O9t3tHkuw=
github.com/chainreactors/parsers v0.0.0-20240910081704-fd57f462fc65/go.mod h1:7rXdYz6jrdjF0WUH1ICcAXKIKKjKmJo2PU8u43V7jkA=
github.com/chainreactors/utils v0.0.0-20240528085651-ba1b255482c1/go.mod h1:JA4eiQZm+7AsfjXBcIzIdVKBEhDCb16eNtWFCGTxlvs=
github.com/chainreactors/utils v0.0.0-20240704062557-662d623b74f4/go.mod h1:JA4eiQZm+7AsfjXBcIzIdVKBEhDCb16eNtWFCGTxlvs=
github.com/chainreactors/utils v0.0.0-20240715080349-d2d0484c95ed/go.mod h1:LajXuvESQwP+qCMAvlcoSXppQCjuLlBrnQpu9XQ1HtU=
github.com/chainreactors/utils v0.0.0-20240716182459-e85f2b01ee16/go.mod h1:LajXuvESQwP+qCMAvlcoSXppQCjuLlBrnQpu9XQ1HtU=
github.com/chainreactors/utils v0.0.0-20240805193040-ff3b97aa3c3f h1:2NKmadFYP9vCwC0YrazgttFACleOhxScTPzg0i76YAY=
github.com/chainreactors/utils v0.0.0-20240805193040-ff3b97aa3c3f/go.mod h1:LajXuvESQwP+qCMAvlcoSXppQCjuLlBrnQpu9XQ1HtU=
github.com/chainreactors/words v0.0.0-20240910083135-4b34aa283ee2 h1:7hVdyBKy8Obs4faaVhnQX1cUwZizeIA6wKHHX5OZTjA=
github.com/chainreactors/words v0.0.0-20240910083135-4b34aa283ee2/go.mod h1:zfz367PUmyaX6oAqV9SktVqyRXKlEh0sel9Wsq9dd2c=
github.com/chainreactors/words v0.0.0-20240910083848-19a289e8984b h1:OsZ1fyarW4NwK/Oi+Yf3nm/dTW0uX0UfxFjyky5Mb60=
github.com/chainreactors/words v0.0.0-20240910083848-19a289e8984b/go.mod h1:zfz367PUmyaX6oAqV9SktVqyRXKlEh0sel9Wsq9dd2c=
github.com/chainreactors/words v0.4.1-0.20240510105042-5ba5c2edc508 h1:iT4HWkoZzUAfQYcQMRH8XyrMau9tCVE0zSuFQnkhrqw=
github.com/chainreactors/words v0.4.1-0.20240510105042-5ba5c2edc508/go.mod h1:DUDx7PdsMEm5PvVhzkFyppzpiUhQb8dOJaWjVc1SMVk=
github.com/chainreactors/words v0.4.1-0.20240910075300-24fe4facd5a6 h1:QpUlRyzw5wn9avRbTHBKRhV06X79Yj/ghGKn2Gyb9v4=
github.com/chainreactors/words v0.4.1-0.20240910075300-24fe4facd5a6/go.mod h1:zfz367PUmyaX6oAqV9SktVqyRXKlEh0sel9Wsq9dd2c=
github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw=
github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY=
github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM=
github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@ -324,6 +338,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
@ -361,6 +377,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/panjf2000/ants/v2 v2.9.1 h1:Q5vh5xohbsZXGcD6hhszzGqB7jSSc2/CRr3QKIga8Kw=
github.com/panjf2000/ants/v2 v2.9.1/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=

View File

@ -1,7 +1,6 @@
package ihttp
import (
"context"
"crypto/tls"
"fmt"
"github.com/chainreactors/logs"
@ -56,7 +55,7 @@ func NewClient(config *ClientConfig) *Client {
DisablePathNormalizing: true,
DisableHeaderNamesNormalizing: true,
},
Config: config,
ClientConfig: config,
}
} else {
client = &Client{
@ -76,7 +75,7 @@ func NewClient(config *ClientConfig) *Client {
return http.ErrUseLastResponse
},
},
Config: config,
ClientConfig: config,
}
if config.ProxyAddr != "" {
client.standardClient.Transport.(*http.Transport).Proxy = func(_ *http.Request) (*url.URL, error) {
@ -97,7 +96,7 @@ type ClientConfig struct {
type Client struct {
fastClient *fasthttp.Client
standardClient *http.Client
Config *ClientConfig
*ClientConfig
}
func (c *Client) TransToCheck() {
@ -108,22 +107,22 @@ func (c *Client) TransToCheck() {
}
}
func (c *Client) FastDo(ctx context.Context, req *fasthttp.Request) (*fasthttp.Response, error) {
func (c *Client) FastDo(req *fasthttp.Request) (*fasthttp.Response, error) {
resp := fasthttp.AcquireResponse()
err := c.fastClient.Do(req, resp)
err := c.fastClient.DoTimeout(req, resp, c.Timeout)
return resp, err
}
func (c *Client) StandardDo(ctx context.Context, req *http.Request) (*http.Response, error) {
func (c *Client) StandardDo(req *http.Request) (*http.Response, error) {
return c.standardClient.Do(req)
}
func (c *Client) Do(ctx context.Context, req *Request) (*Response, error) {
func (c *Client) Do(req *Request) (*Response, error) {
if c.fastClient != nil {
resp, err := c.FastDo(ctx, req.FastRequest)
resp, err := c.FastDo(req.FastRequest)
return &Response{FastResponse: resp, ClientType: FAST}, err
} else if c.standardClient != nil {
resp, err := c.StandardDo(ctx, req.StandardRequest)
resp, err := c.StandardDo(req.StandardRequest)
return &Response{StandardResponse: resp, ClientType: STANDARD}, err
} else {
return nil, fmt.Errorf("not found client")

View File

@ -1,11 +1,12 @@
package ihttp
import (
"context"
"github.com/valyala/fasthttp"
"net/http"
)
func BuildRequest(clientType int, base, path, host, method string) (*Request, error) {
func BuildRequest(ctx context.Context, clientType int, base, path, host, method string) (*Request, error) {
if clientType == FAST {
req := fasthttp.AcquireRequest()
req.Header.SetMethod(method)
@ -15,7 +16,7 @@ func BuildRequest(clientType int, base, path, host, method string) (*Request, er
}
return &Request{FastRequest: req, ClientType: FAST}, nil
} else {
req, err := http.NewRequest(method, base+path, nil)
req, err := http.NewRequestWithContext(ctx, method, base+path, nil)
if host != "" {
req.Host = host
}

View File

@ -14,6 +14,7 @@ import (
"github.com/chainreactors/utils/iutils"
"github.com/chainreactors/words/mask"
"github.com/chainreactors/words/rule"
"github.com/charmbracelet/lipgloss"
"github.com/expr-lang/expr"
"github.com/vbauerster/mpb/v8"
"io/ioutil"
@ -30,7 +31,6 @@ import (
var (
DefaultThreads = 20
SkipChar = "%SKIP%"
)
type Option struct {
@ -305,10 +305,6 @@ func (opt *Option) NewRunner() (*Runner, error) {
r.ClientType = ihttp.STANDARD
}
if opt.Threads == DefaultThreads && len(opt.Dictionaries) == 0 {
r.Threads = 1000
}
err = opt.BuildPlugin(r)
if err != nil {
return nil, err
@ -319,6 +315,10 @@ func (opt *Option) NewRunner() (*Runner, error) {
return nil, err
}
if opt.Threads == DefaultThreads && r.bruteMod {
r.Threads = 1000
}
pkg.DefaultStatistor = pkg.Statistor{
Word: opt.Word,
WordCount: len(r.Wordlist),
@ -393,6 +393,8 @@ func (opt *Option) NewRunner() (*Runner, error) {
r.Probes = strings.Split(opt.OutputProbe, ",")
}
fmt.Println(opt.PrintConfig(r))
// init output file
if opt.OutputFile != "" {
r.OutputFile, err = files.NewFile(opt.OutputFile, false, false, true)
@ -436,30 +438,136 @@ func (opt *Option) NewRunner() (*Runner, error) {
return r, nil
}
func (opt *Option) PrintPlugin() {
var s strings.Builder
if opt.CrawlPlugin {
s.WriteString("crawl enable; ")
}
if opt.Finger {
s.WriteString("active fingerprint enable; ")
}
if opt.BakPlugin {
s.WriteString("bak file enable; ")
}
if opt.CommonPlugin {
s.WriteString("common file enable; ")
}
if opt.ReconPlugin {
s.WriteString("recon enable; ")
}
if opt.RetryCount > 0 {
s.WriteString("Retry Count: " + strconv.Itoa(opt.RetryCount))
func (opt *Option) PrintConfig(r *Runner) string {
// 定义颜色样式
keyStyle := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#FFFFFF")).Width(20) // Key 加粗并设定宽度
stringValueStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#FFA07A")) // 字符串样式
arrayValueStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#98FB98")) // 数组样式
numberValueStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#ADD8E6")) // 数字样式
panelWidth := 60 // 调整 panelWidth 使内容稍微靠左
padding := 2 // 减少 padding 以调整布局靠左
// 分割线样式和终端宽度计算
divider := strings.Repeat("─", panelWidth) // 使用"─"符号生成更加连贯的分割线
// 处理不同类型的值
formatValue := func(value interface{}) string {
switch v := value.(type) {
case string:
return stringValueStyle.Render(v)
case []string:
return arrayValueStyle.Render(fmt.Sprintf("%v", v))
case int, int64, float64:
return numberValueStyle.Render(fmt.Sprintf("%v", v))
default:
return stringValueStyle.Render(fmt.Sprintf("%v", v)) // 默认为字符串样式
}
}
if s.Len() > 0 {
logs.Log.Important(s.String())
// 处理互斥参数,选择输出有值的那一个
inputSource := ""
if opt.ResumeFrom != "" {
inputSource = lipgloss.JoinHorizontal(lipgloss.Left, "🌀 ", keyStyle.Render("ResumeFrom: "), formatValue(opt.ResumeFrom))
} else if len(opt.URL) > 0 {
inputSource = lipgloss.JoinHorizontal(lipgloss.Left, "🌐 ", keyStyle.Render("URL: "), formatValue(opt.URL))
} else if opt.URLFile != "" {
inputSource = lipgloss.JoinHorizontal(lipgloss.Left, "📂 ", keyStyle.Render("URLFile: "), formatValue(opt.URLFile))
} else if len(opt.CIDRs) > 0 {
inputSource = lipgloss.JoinHorizontal(lipgloss.Left, "📡 ", keyStyle.Render("CIDRs: "), formatValue(opt.CIDRs))
} else if opt.RawFile != "" {
inputSource = lipgloss.JoinHorizontal(lipgloss.Left, "📄 ", keyStyle.Render("RawFile: "), formatValue(opt.RawFile))
}
// Input Options
inputOptions := lipgloss.JoinVertical(lipgloss.Left,
inputSource, // 互斥量处理
// PortRange 展示
lipgloss.JoinHorizontal(lipgloss.Left, "🔢 ", keyStyle.Render("PortRange: "), formatValue(opt.PortRange)),
// Dictionaries 展示
lipgloss.JoinHorizontal(lipgloss.Left, "📚 ", keyStyle.Render("Dictionaries: "), formatValue(opt.Dictionaries)),
// Word, Rules, FilterRule 展开为单独的行
lipgloss.JoinVertical(lipgloss.Left,
lipgloss.JoinHorizontal(lipgloss.Left, "💡 ", keyStyle.Render("Word: "), formatValue(r.Word)),
lipgloss.JoinHorizontal(lipgloss.Left, "📜 ", keyStyle.Render("Rules: "), formatValue(opt.Rules)),
lipgloss.JoinHorizontal(lipgloss.Left, "🔍 ", keyStyle.Render("FilterRule: "), formatValue(opt.FilterRule)),
),
// AppendRule 和 AppendWords 展开为单独的行
lipgloss.JoinVertical(lipgloss.Left,
lipgloss.JoinHorizontal(lipgloss.Left, "🔧 ", keyStyle.Render("AppendRule: "), formatValue(r.AppendRule)),
lipgloss.JoinHorizontal(lipgloss.Left, "🧩 ", keyStyle.Render("AppendWords: "), formatValue(len(r.AppendWords))),
),
)
// Output Options
outputOptions := lipgloss.JoinVertical(lipgloss.Left,
lipgloss.JoinHorizontal(lipgloss.Left, "📊 ", keyStyle.Render("Match: "), formatValue(opt.Match)),
lipgloss.JoinHorizontal(lipgloss.Left, "⚙️ ", keyStyle.Render("Filter: "), formatValue(opt.Filter)),
)
// Plugin Options
pluginValues := []string{}
if opt.ActivePlugin {
pluginValues = append(pluginValues, "active")
}
if opt.ReconPlugin {
pluginValues = append(pluginValues, "recon")
}
if opt.BakPlugin {
pluginValues = append(pluginValues, "bak")
}
if opt.CommonPlugin {
pluginValues = append(pluginValues, "common")
}
if opt.CrawlPlugin {
pluginValues = append(pluginValues, "crawl")
}
pluginOptions := lipgloss.JoinVertical(lipgloss.Left,
lipgloss.JoinHorizontal(lipgloss.Left, "🔎 ", keyStyle.Render("Extracts: "), formatValue(opt.Extracts)),
lipgloss.JoinHorizontal(lipgloss.Left, "🔌 ", keyStyle.Render("Plugins: "), formatValue(strings.Join(pluginValues, ", "))),
)
// Mode Options
modeOptions := lipgloss.JoinVertical(lipgloss.Left,
lipgloss.JoinHorizontal(lipgloss.Left, "🛑 ", keyStyle.Render("BlackStatus: "), formatValue(pkg.BlackStatus)),
lipgloss.JoinHorizontal(lipgloss.Left, "✅ ", keyStyle.Render("WhiteStatus: "), formatValue(pkg.WhiteStatus)),
lipgloss.JoinHorizontal(lipgloss.Left, "🔄 ", keyStyle.Render("FuzzyStatus: "), formatValue(pkg.FuzzyStatus)),
lipgloss.JoinHorizontal(lipgloss.Left, "🔒 ", keyStyle.Render("UniqueStatus: "), formatValue(pkg.UniqueStatus)),
lipgloss.JoinHorizontal(lipgloss.Left, "🔑 ", keyStyle.Render("Unique: "), formatValue(opt.Unique)),
)
// Misc Options
miscOptions := lipgloss.JoinVertical(lipgloss.Left,
lipgloss.JoinHorizontal(lipgloss.Left, "⏱ ", keyStyle.Render("Timeout: "), formatValue(opt.Timeout)),
lipgloss.JoinHorizontal(lipgloss.Left, "📈 ", keyStyle.Render("PoolSize: "), formatValue(opt.PoolSize)),
lipgloss.JoinHorizontal(lipgloss.Left, "🧵 ", keyStyle.Render("Threads: "), formatValue(opt.Threads)),
lipgloss.JoinHorizontal(lipgloss.Left, "🌍 ", keyStyle.Render("Proxy: "), formatValue(opt.Proxy)),
)
// 将所有内容拼接在一起
content := lipgloss.JoinVertical(lipgloss.Left,
inputOptions,
outputOptions,
pluginOptions,
modeOptions,
miscOptions,
)
// 使用正确的方式添加 padding并居中显示内容
contentWithPadding := lipgloss.NewStyle().PaddingLeft(padding).Render(content)
// 使用 Place 方法来将整个内容居中显示
return lipgloss.Place(panelWidth+padding*2, 0, lipgloss.Center, lipgloss.Center,
lipgloss.JoinVertical(lipgloss.Center,
divider, // 顶部分割线
contentWithPadding,
divider, // 底部分割线
),
)
}
func (opt *Option) BuildPlugin(r *Runner) error {
@ -502,7 +610,6 @@ func (opt *Option) BuildPlugin(r *Runner) error {
r.bruteMod = true
}
opt.PrintPlugin()
if r.bruteMod {
logs.Log.Important("enabling brute mod, because of enabled brute plugin")
}
@ -614,19 +721,10 @@ func (opt *Option) BuildWords(r *Runner) error {
// 类似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
})
r.AppendFunction(pkg.ParseEXTPlaceholderFunc(strings.Split(opt.Extensions, ",")))
} else {
r.AppendFunction(func(s string) []string {
if strings.Contains(s, "%EXT%") {
if strings.Contains(s, pkg.EXTChar) {
return nil
}
return []string{s}
@ -669,13 +767,6 @@ func (opt *Option) BuildWords(r *Runner) error {
})
}
// 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 {
@ -687,7 +778,6 @@ func (opt *Option) BuildWords(r *Runner) error {
})
}
logs.Log.Importantf("%s mod, Loaded %d dictionaries, %d rules and %d decorators", opt.Mod, len(opt.Dictionaries), len(opt.Rules), len(r.Fns))
return nil
}

View File

@ -9,7 +9,6 @@ import (
"github.com/chainreactors/spray/internal/ihttp"
"github.com/chainreactors/spray/pkg"
"github.com/chainreactors/utils/iutils"
"github.com/chainreactors/words"
"github.com/chainreactors/words/rule"
"github.com/panjf2000/ants/v2"
"github.com/valyala/fasthttp"
@ -48,7 +47,7 @@ func NewBrutePool(ctx context.Context, config *Config) (*BrutePool, error) {
client: ihttp.NewClient(&ihttp.ClientConfig{
Thread: config.Thread,
Type: config.ClientType,
Timeout: time.Duration(config.Timeout) * time.Second,
Timeout: config.Timeout,
ProxyAddr: config.ProxyAddr,
}),
additionCh: make(chan *Unit, config.Thread),
@ -167,7 +166,7 @@ func (pool *BrutePool) Init() error {
return nil
}
func (pool *BrutePool) Run(ctx context.Context, offset, limit int) {
func (pool *BrutePool) Run(offset, limit int) {
pool.Worder.Run()
if pool.Active {
pool.wg.Add(1)
@ -200,7 +199,7 @@ func (pool *BrutePool) Run(ctx context.Context, offset, limit int) {
Loop:
for {
select {
case w, ok := <-pool.Worder.C:
case w, ok := <-pool.Worder.Output:
if !ok {
done = true
continue
@ -250,8 +249,6 @@ Loop:
}
case <-pool.closeCh:
break Loop
case <-ctx.Done():
break Loop
case <-pool.ctx.Done():
break Loop
}
@ -271,7 +268,7 @@ func (pool *BrutePool) Invoke(v interface{}) {
var req *ihttp.Request
var err error
req, err = ihttp.BuildRequest(pool.ClientType, pool.BaseURL, unit.path, unit.host, pool.Method)
req, err = ihttp.BuildRequest(pool.ctx, pool.ClientType, pool.BaseURL, unit.path, unit.host, pool.Method)
if err != nil {
logs.Log.Error(err.Error())
return
@ -283,7 +280,7 @@ func (pool *BrutePool) Invoke(v interface{}) {
}
start := time.Now()
resp, reqerr := pool.client.Do(pool.ctx, req)
resp, reqerr := pool.client.Do(req)
if pool.ClientType == ihttp.FAST {
defer fasthttp.ReleaseResponse(resp.FastResponse)
defer fasthttp.ReleaseRequest(req.FastRequest)
@ -334,21 +331,28 @@ func (pool *BrutePool) Invoke(v interface{}) {
bl.Spended = time.Since(start).Milliseconds()
switch unit.source {
case parsers.InitRandomSource:
bl.Collect()
defer pool.initwg.Done()
pool.locker.Lock()
pool.random = bl
pool.addFuzzyBaseline(bl)
if !bl.IsValid {
return
}
pool.locker.Unlock()
pool.initwg.Done()
case parsers.InitIndexSource:
bl.Collect()
pool.addFuzzyBaseline(bl)
case parsers.InitIndexSource:
defer pool.initwg.Done()
pool.locker.Lock()
pool.index = bl
pool.locker.Unlock()
if !bl.IsValid {
return
}
bl.Collect()
pool.doCrawl(bl)
pool.doAppend(bl)
pool.putToOutput(bl)
pool.initwg.Done()
case parsers.CheckSource:
if bl.ErrString != "" {
logs.Log.Warnf("[check.error] %s maybe ip had banned, break (%d/%d), error: %s", pool.BaseURL, pool.failedCount, pool.BreakThreshold, bl.ErrString)
@ -390,14 +394,14 @@ func (pool *BrutePool) Invoke(v interface{}) {
func (pool *BrutePool) NoScopeInvoke(v interface{}) {
defer pool.wg.Done()
unit := v.(*Unit)
req, err := ihttp.BuildRequest(pool.ClientType, unit.path, "", "", "GET")
req, err := ihttp.BuildRequest(pool.ctx, pool.ClientType, unit.path, "", "", "GET")
if err != nil {
logs.Log.Error(err.Error())
return
}
req.SetHeaders(pool.Headers)
req.SetHeader("User-Agent", pkg.RandomUA())
resp, reqerr := pool.client.Do(pool.ctx, req)
resp, reqerr := pool.client.Do(req)
if pool.ClientType == ihttp.FAST {
defer fasthttp.ReleaseResponse(resp.FastResponse)
defer fasthttp.ReleaseRequest(req.FastRequest)
@ -509,7 +513,7 @@ func (pool *BrutePool) Handler() {
}
func (pool *BrutePool) doAppendRule(bl *pkg.Baseline) {
if pool.AppendRule == nil || bl.Source == parsers.RuleSource {
if pool.AppendRule == nil || bl.Source == parsers.AppendRuleSource {
pool.wg.Done()
return
}
@ -520,7 +524,7 @@ func (pool *BrutePool) doAppendRule(bl *pkg.Baseline) {
pool.addAddition(&Unit{
path: pkg.Dir(bl.Url.Path) + u,
host: bl.Host,
source: parsers.RuleSource,
source: parsers.AppendRuleSource,
})
}
}()
@ -535,7 +539,8 @@ func (pool *BrutePool) doAppendWords(bl *pkg.Baseline) {
go func() {
defer pool.wg.Done()
for _, u := range pool.AppendWords {
for u := range NewBruteWords(pool.Config, pool.AppendWords).Output {
pool.addAddition(&Unit{
path: pkg.SafePath(bl.Path, u),
host: bl.Host,
@ -569,13 +574,7 @@ func (pool *BrutePool) doCommonFile() {
if pool.Mod == HostSpray {
return
}
for _, u := range pkg.Dicts["common"] {
pool.addAddition(&Unit{
path: pool.dir + u,
source: parsers.CommonFileSource,
})
}
for _, u := range pkg.Dicts["log"] {
for u := range NewBruteWords(pool.Config, append(pkg.Dicts["common"], pkg.Dicts["log"]...)).Output {
pool.addAddition(&Unit{
path: pool.dir + u,
source: parsers.CommonFileSource,
@ -721,7 +720,8 @@ func (pool *BrutePool) Close() {
close(pool.additionCh) // 关闭addition管道
close(pool.checkCh) // 关闭check管道
pool.Statistor.EndTime = time.Now().Unix()
pool.Bar.Close()
pool.reqPool.Release()
pool.scopePool.Release()
}
func (pool *BrutePool) safePath(u string) string {
@ -814,24 +814,14 @@ func (pool *BrutePool) doBak() {
if pool.Mod == HostSpray {
return
}
worder, err := words.NewWorderWithDsl("{?0}.{?@bak_ext}", [][]string{pkg.BakGenerator(pool.url.Host)}, nil)
if err != nil {
return
}
worder.Run()
for w := range worder.C {
for w := range NewBruteDSL(pool.Config, "{?0}.{?@bak_ext}", [][]string{pkg.BakGenerator(pool.url.Host)}).Output {
pool.addAddition(&Unit{
path: pool.dir + w,
source: parsers.BakSource,
})
}
worder, err = words.NewWorderWithDsl("{?@bak_name}.{?@bak_ext}", nil, nil)
if err != nil {
return
}
worder.Run()
for w := range worder.C {
for w := range NewBruteDSL(pool.Config, "{?@bak_name}.{?@bak_ext}", nil).Output {
pool.addAddition(&Unit{
path: pool.dir + w,
source: parsers.BakSource,

View File

@ -18,7 +18,7 @@ func NewCheckPool(ctx context.Context, config *Config) (*CheckPool, error) {
pctx, cancel := context.WithCancel(ctx)
config.ClientType = ihttp.STANDARD
pool := &CheckPool{
&BasePool{
BasePool: &BasePool{
Config: config,
Statistor: pkg.NewStatistor(""),
ctx: pctx,
@ -38,13 +38,14 @@ func NewCheckPool(ctx context.Context, config *Config) (*CheckPool, error) {
pool.Headers = map[string]string{"Connection": "close"}
p, _ := ants.NewPoolWithFunc(config.Thread, pool.Invoke)
pool.BasePool.Pool = p
pool.Pool = p
go pool.Handler()
return pool, nil
}
type CheckPool struct {
*BasePool
Pool *ants.PoolWithFunc
}
func (pool *CheckPool) Run(ctx context.Context, offset, limit int) {
@ -66,7 +67,7 @@ func (pool *CheckPool) Run(ctx context.Context, offset, limit int) {
Loop:
for {
select {
case u, ok := <-pool.Worder.C:
case u, ok := <-pool.Worder.Output:
if !ok {
done = true
continue
@ -82,12 +83,12 @@ Loop:
}
pool.wg.Add(1)
_ = pool.BasePool.Pool.Invoke(newUnit(u, parsers.CheckSource))
_ = pool.Pool.Invoke(newUnit(u, parsers.CheckSource))
case u, ok := <-pool.additionCh:
if !ok {
continue
}
_ = pool.BasePool.Pool.Invoke(u)
_ = pool.Pool.Invoke(u)
case <-pool.closeCh:
break Loop
case <-ctx.Done():
@ -99,6 +100,10 @@ Loop:
pool.Close()
}
func (pool *CheckPool) Close() {
pool.Bar.Close()
pool.Pool.Release()
}
func (pool *CheckPool) Invoke(v interface{}) {
defer func() {
@ -107,7 +112,7 @@ func (pool *CheckPool) Invoke(v interface{}) {
}()
unit := v.(*Unit)
req, err := ihttp.BuildRequest(pool.ClientType, unit.path, "", "", "GET")
req, err := ihttp.BuildRequest(pool.ctx, pool.ClientType, unit.path, "", "", "GET")
if err != nil {
logs.Log.Debug(err.Error())
bl := &pkg.Baseline{
@ -125,7 +130,7 @@ func (pool *CheckPool) Invoke(v interface{}) {
req.SetHeaders(pool.Headers)
start := time.Now()
var bl *pkg.Baseline
resp, reqerr := pool.client.Do(pool.ctx, req)
resp, reqerr := pool.client.Do(req)
if reqerr != nil {
pool.failedCount++
bl = &pkg.Baseline{

View File

@ -1,58 +0,0 @@
package pool
import (
"github.com/chainreactors/spray/pkg"
"github.com/chainreactors/words/rule"
"github.com/expr-lang/expr/vm"
"sync"
)
type SprayMod int
const (
PathSpray SprayMod = iota + 1
HostSpray
ParamSpray
CustomSpray
)
var ModMap = map[string]SprayMod{
"path": PathSpray,
"host": HostSpray,
}
type Config struct {
BaseURL string
ProxyAddr string
Thread int
Wordlist []string
Timeout int
ProcessCh chan *pkg.Baseline
OutputCh chan *pkg.Baseline
FuzzyCh chan *pkg.Baseline
Outwg *sync.WaitGroup
RateLimit int
CheckPeriod int
ErrPeriod int32
BreakThreshold int32
Method string
Mod SprayMod
Headers map[string]string
ClientType int
MatchExpr *vm.Program
FilterExpr *vm.Program
RecuExpr *vm.Program
AppendRule *rule.Program
AppendWords []string
Fuzzy bool
IgnoreWaf bool
Crawl bool
Scope []string
Active bool
Bak bool
Common bool
RetryLimit int
RandomUserAgent bool
Random string
Index string
}

View File

@ -6,14 +6,12 @@ import (
"github.com/chainreactors/spray/internal/ihttp"
"github.com/chainreactors/spray/pkg"
"github.com/chainreactors/words"
"github.com/panjf2000/ants/v2"
"sync"
)
type BasePool struct {
*Config
Statistor *pkg.Statistor
Pool *ants.PoolWithFunc
Bar *pkg.Bar
Worder *words.Worder
Cancel context.CancelFunc
@ -72,10 +70,6 @@ func (pool *BasePool) addAddition(u *Unit) {
pool.additionCh <- u
}
func (pool *BasePool) Close() {
pool.Bar.Close()
}
func (pool *BasePool) putToOutput(bl *pkg.Baseline) {
if bl.IsValid || bl.IsFuzzy {
bl.Collect()

View File

@ -1,8 +1,14 @@
package pool
import (
"github.com/chainreactors/logs"
"github.com/chainreactors/parsers"
"github.com/chainreactors/spray/pkg"
"github.com/chainreactors/words"
"github.com/chainreactors/words/rule"
"github.com/expr-lang/expr/vm"
"sync"
"time"
)
func newUnit(path string, source parsers.SpraySource) *Unit {
@ -31,3 +37,71 @@ type Baselines struct {
index *pkg.Baseline
baselines map[int]*pkg.Baseline
}
type SprayMod int
const (
PathSpray SprayMod = iota + 1
HostSpray
ParamSpray
CustomSpray
)
var ModMap = map[string]SprayMod{
"path": PathSpray,
"host": HostSpray,
}
type Config struct {
BaseURL string
ProxyAddr string
Thread int
Wordlist []string
Timeout time.Duration
ProcessCh chan *pkg.Baseline
OutputCh chan *pkg.Baseline
FuzzyCh chan *pkg.Baseline
Outwg *sync.WaitGroup
RateLimit int
CheckPeriod int
ErrPeriod int32
BreakThreshold int32
Method string
Mod SprayMod
Headers map[string]string
ClientType int
MatchExpr *vm.Program
FilterExpr *vm.Program
RecuExpr *vm.Program
AppendRule *rule.Program
Fns []words.WordFunc
AppendWords []string
Fuzzy bool
IgnoreWaf bool
Crawl bool
Scope []string
Active bool
Bak bool
Common bool
RetryLimit int
RandomUserAgent bool
Random string
Index string
}
func NewBruteWords(config *Config, list []string) *words.Worder {
word := words.NewWorderWithList(list)
word.Fns = config.Fns
word.Run()
return word
}
func NewBruteDSL(config *Config, dsl string, params [][]string) *words.Worder {
word, err := words.NewWorderWithDsl(dsl, params, nil)
if err != nil {
logs.Log.Error(err.Error())
}
word.Fns = config.Fns
word.Run()
return word
}

View File

@ -13,7 +13,9 @@ import (
"github.com/panjf2000/ants/v2"
"github.com/vbauerster/mpb/v8"
"github.com/vbauerster/mpb/v8/decor"
"strings"
"sync"
"time"
)
var (
@ -45,7 +47,7 @@ type Runner struct {
DumpFile *files.File
StatFile *files.File
Progress *mpb.Progress
Fns []func(string) []string
Fns []words.WordFunc
Count int // tasks total number
Wordlist []string
AppendWords []string
@ -60,7 +62,7 @@ type Runner struct {
func (r *Runner) PrepareConfig() *pool.Config {
config := &pool.Config{
Thread: r.Threads,
Timeout: r.Timeout,
Timeout: time.Duration(r.Timeout) * time.Second,
RateLimit: r.RateLimit,
Headers: r.Headers,
Method: r.Method,
@ -77,6 +79,7 @@ func (r *Runner) PrepareConfig() *pool.Config {
RecuExpr: r.RecursiveExpr,
AppendRule: r.AppendRules, // 对有效目录追加规则, 根据rule生成
AppendWords: r.AppendWords, // 对有效目录追加字典
Fns: r.Fns,
//IgnoreWaf: r.IgnoreWaf,
Crawl: r.CrawlPlugin,
Scope: r.Scope,
@ -180,7 +183,7 @@ func (r *Runner) Prepare(ctx context.Context) error {
brutePool.Statistor.Total = t.origin.sum
} else {
brutePool.Statistor = pkg.NewStatistor(t.baseUrl)
brutePool.Worder = words.NewWorder(r.Wordlist)
brutePool.Worder = words.NewWorderWithList(r.Wordlist)
brutePool.Worder.Fns = r.Fns
brutePool.Worder.Rules = r.Rules.Expressions
}
@ -205,7 +208,7 @@ func (r *Runner) Prepare(ctx context.Context) error {
}
}
brutePool.Run(ctx, brutePool.Statistor.Offset, limit)
brutePool.Run(brutePool.Statistor.Offset, limit)
if brutePool.IsFailed && len(brutePool.FailedBaselines) > 0 {
// 如果因为错误积累退出, end将指向第一个错误发生时, 防止resume时跳过大量目标
@ -228,6 +231,7 @@ Loop:
for {
select {
case <-ctx.Done():
// 如果超过了deadline, 尚未开始的任务都将被记录到stat中
if len(r.taskCh) > 0 {
for t := range r.taskCh {
stat := pkg.NewStatistor(t.baseUrl)
@ -366,7 +370,6 @@ func (r *Runner) Output(bl *pkg.Baseline) {
if bl.IsValid {
logs.Log.Console(out + "\n")
} else if r.Fuzzy && bl.IsFuzzy {
logs.Log.Console("[fuzzy] " + out + "\n")
}
@ -376,6 +379,10 @@ func (r *Runner) Output(bl *pkg.Baseline) {
r.OutputFile.SafeWrite(bl.ToJson() + "\n")
} else if r.FileOutput == "csv" {
r.OutputFile.SafeWrite(bl.ToCSV() + "\n")
} else if r.FileOutput == "full" {
r.OutputFile.SafeWrite(bl.String() + "\n")
} else {
r.OutputFile.SafeWrite(bl.Format(strings.Split(r.FileOutput, ",")) + "\n")
}
r.OutputFile.SafeSync()

View File

@ -14,13 +14,13 @@ type Origin struct {
sum int
}
func (o *Origin) InitWorder(fns []func(string) []string) (*words.Worder, error) {
func (o *Origin) InitWorder(fns []words.WordFunc) (*words.Worder, error) {
var worder *words.Worder
wl, err := pkg.LoadWordlist(o.Word, o.Dictionaries)
if err != nil {
return nil, err
}
worder = words.NewWorder(wl)
worder = words.NewWorderWithList(wl)
worder.Fns = fns
rules, err := pkg.LoadRuleWithFiles(o.RuleFiles, o.RuleFilter)
if err != nil {

View File

@ -14,3 +14,9 @@ package internal
// })
// }
//}
var ConfigTemplate = `
-----------------------------------------------------------
-----------------------------------------------------------
`

20
pkg/parse.go Normal file
View File

@ -0,0 +1,20 @@
package pkg
import "strings"
var (
SkipChar = "%SKIP%"
EXTChar = "%EXT%"
)
func ParseEXTPlaceholderFunc(exts []string) func(string) []string {
return func(s string) []string {
ss := make([]string, len(exts))
for i, e := range exts {
if strings.Contains(s, EXTChar) {
ss[i] = strings.Replace(s, EXTChar, e, -1)
}
}
return ss
}
}