diff --git a/README.md b/README.md index 36e8a64..6768545 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,6 @@ K8S的 ETCD扫描 SQLCMD模块 * [数据库利用工具](http://ryze-t.com/posts/2022/02/16/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E5%88%A9%E7%94%A8%E5%B7%A5%E5%85%B7-Sylas.html] -Crack模块输出: -通用信息和Extra信息 -IP_ADDRESS: -LOGIN_USER: -LOGIN_PASS: - diff --git a/cli/cmd/crack.go b/cli/cmd/crack.go index 99cb640..687cdbe 100644 --- a/cli/cmd/crack.go +++ b/cli/cmd/crack.go @@ -2,10 +2,12 @@ package cmd import ( "cube/core" - "cube/core/crackmodule/plugins" + "cube/core/crackmodule" "cube/gologger" "fmt" + "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" + "os" ) var crackCli *cobra.Command @@ -19,16 +21,16 @@ func runCrack(cmd *cobra.Command, args []string) { } } - plugins.StartCrack(opt, globalopts) + crackmodule.StartCrack(opt, globalopts) } -func parseCrackOptions() (*core.GlobalOption, *plugins.CrackOption, error) { +func parseCrackOptions() (*core.GlobalOption, *crackmodule.CrackOption, error) { globalOpts, err := parseGlobalOptions() if err != nil { return nil, nil, err } - crackOption := plugins.NewCrackOptions() + crackOption := crackmodule.NewCrackOptions() crackOption.Ip, err = crackCli.Flags().GetString("service") if err != nil { @@ -69,14 +71,13 @@ func parseCrackOptions() (*core.GlobalOption, *plugins.CrackOption, error) { if err != nil { return nil, nil, fmt.Errorf("invalid value for scan plugin: %w", err) } - gologger.Debugf("ip: %s, ipfile: %s, user: %s, uf:%s, pass:%s, pf:%s", crackOption.Ip, crackOption.IpFile, crackOption.User, crackOption.UserFile, crackOption.Pass, crackOption.PassFile) return globalOpts, crackOption, nil } func init() { crackCli = &cobra.Command{ Use: "crack", - Long: "Hello", + Long: "Hello", //TODO Short: "crack service password", Run: runCrack, Example: `cube crack -u root -p root -i 192.168.1.1 -x ssh @@ -104,3 +105,17 @@ cube crack -u root -pass-file pass.txt -i http://127.0.0.1:8080 -x phpmyadmin rootCmd.AddCommand(crackCli) } + +func CrackHelpTable() { + flag := "N" + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Func", "Port", "Load By ALL"}) + for _, k := range crackmodule.CrackKeys { + if crackmodule.GetLoadStatus(k) { + flag = "Y" + } + table.Append([]string{k, crackmodule.GetPort(k), flag}) + table.SetRowLine(true) + } + table.Render() +} diff --git a/cli/cmd/probe.go b/cli/cmd/probe.go new file mode 100644 index 0000000..fbbe5b6 --- /dev/null +++ b/cli/cmd/probe.go @@ -0,0 +1,69 @@ +package cmd + +import ( + "cube/core" + "cube/core/probemodule" + "cube/gologger" + "fmt" + "github.com/spf13/cobra" +) + +var probeCli *cobra.Command + +func runProbe(cmd *cobra.Command, args []string) { + globalopts, opt, _ := parseProbeOptions() + + probemodule.StartProbe(opt, globalopts) +} + +func parseProbeOptions() (*core.GlobalOption, *probemodule.ProbeOption, error) { + globalOpts, err := parseGlobalOptions() + if err != nil { + return nil, nil, err + } + probeOption := probemodule.NewProbeOption() + + probeOption.PluginName, err = probeCli.Flags().GetString("plugin") + if err != nil { + return nil, nil, fmt.Errorf("invalid value for plugin: %v", err) + } + + probeOption.Port, err = probeCli.Flags().GetString("port") + if err != nil { + return nil, nil, fmt.Errorf("invalid value for scan port: %v", err) + } + + probeOption.Ip, err = probeCli.Flags().GetString("service") + if err != nil { + return nil, nil, fmt.Errorf("invalid value for target-ip: %w", err) + } + probeOption.IpFile, err = probeCli.Flags().GetString("service-file") + if err != nil { + return nil, nil, fmt.Errorf("invalid value for target-file: %w", err) + } + return globalOpts, probeOption, nil +} + +func init() { + probeCli = &cobra.Command{ + Use: "probe", + Long: "long Desc", //TODO + Short: "probe pentest env", + Run: runProbe, + Example: `cube probe -s 192.168.1.1 -x oxid +cube probe -s 192.168.1.1 -x oxid,zookeeper,ms17010 +cube probe -s 192.168.1.1/24 -x X + `, + } + + probeCli.Flags().StringP("port", "", "", "target port") + probeCli.Flags().StringP("plugin", "x", "", "plugin to scan(e.g. oxid,ms17010)") + probeCli.Flags().StringP("service", "s", "", "service ip(in the nmap format: 10.0.0.1, 10.0.0.5-10, 192.168.1.*, 192.168.10.0/24)") + probeCli.Flags().StringP("service-file", "S", "", "File to probe for(e.g. ip.txt)") + + if err := crackCli.MarkFlagRequired("plugin"); err != nil { + gologger.Errorf("error on marking flag as required: %v", err) + } + + rootCmd.AddCommand(probeCli) +} diff --git a/cli/cmd/root.go b/cli/cmd/root.go index df918e6..ac6fbff 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -51,7 +51,7 @@ func parseGlobalOptions() (*core.GlobalOption, error) { func init() { rootCmd.PersistentFlags().IntP("threads", "n", 30, "Number of concurrent requests") rootCmd.PersistentFlags().IntP("timeout", "", 5, "Seconds to wait before timeout connection") - rootCmd.PersistentFlags().Float64P("delay", "", 0, "Delay in random seconds between each TCP/UDP request") + rootCmd.PersistentFlags().Float64P("delay", "d", 0, "Delay in random seconds between each TCP/UDP request") //rootCmd.PersistentFlags().StringP("output", "o", "", "Output file to write results to (defaults to stdout)") rootCmd.PersistentFlags().BoolP("verbose", "v", false, "Verbose (Default error)") } diff --git a/conf/config.go b/config/config.go similarity index 96% rename from conf/config.go rename to config/config.go index 3ffb628..42e1f1a 100644 --- a/conf/config.go +++ b/config/config.go @@ -1,4 +1,4 @@ -package conf +package config import ( "sync" @@ -7,13 +7,12 @@ import ( const ( TcpConnTimeout = 5 * time.Second - ThreadTimeout = 7 * time.Second + ThreadTimeout = 14 * time.Second ) var ( CommonPortMap map[string]int - //SuccessHash map[string]bool - Mutex sync.Mutex + Mutex sync.Mutex ) var SuccessHash = struct { diff --git a/core/crackmodule/check_port.go b/core/crackmodule/check_port.go new file mode 100644 index 0000000..2a7b7c9 --- /dev/null +++ b/core/crackmodule/check_port.go @@ -0,0 +1,112 @@ +package crackmodule + +import ( + "context" + "cube/config" + "cube/core" + "cube/gologger" + "fmt" + "net" + "sync" + "time" +) + +type IpAddr struct { + Ip string + Port string + PluginName string +} + +var ( + mutex sync.Mutex + AliveAddr []IpAddr + ipList []IpAddr +) + +func CheckPort(ctx context.Context, threadNum int, delay float64, ips []string, pluginNames []string, port string) []IpAddr { + //指定插件端口的时候,只允许加载一个插件 + if len(port) > 0 { + for _, ip := range ips { + ipList = append(ipList, IpAddr{ + Ip: ip, + Port: port, + PluginName: pluginNames[0], + }) + } + } else { + for _, plugin := range pluginNames { + for _, ip := range ips { + ipList = append(ipList, IpAddr{ + Ip: ip, + Port: GetPort(plugin), + PluginName: plugin, + }) + } + } + + } + + var addrChan = make(chan IpAddr, threadNum*2) + var wg sync.WaitGroup + wg.Add(len(ipList)) + + for i := 0; i < threadNum; i++ { + go func() { + for { + select { + case <-ctx.Done(): + return + case addr, ok := <-addrChan: + if !ok { + return + } + if GetTCP(addr.PluginName) { + //TCP的时候检查端口,UDP跳过 + SaveAddr(check(addr)) + } + wg.Done() + select { + case <-ctx.Done(): + case <-time.After(time.Duration(core.RandomDelay(delay)) * time.Second): + } + } + } + }() + } + + for _, addr := range ipList { + addrChan <- addr + } + close(addrChan) + wg.Wait() + + return AliveAddr +} + +func check(addr IpAddr) (bool, IpAddr) { + alive := false + gologger.Debugf("tcp port conn check: %s:%s", addr.Ip, addr.Port) + _, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", addr.Ip, addr.Port), config.TcpConnTimeout) + if err == nil { + gologger.Infof("Open %s:%s", addr.Ip, addr.Port) + alive = true + } + return alive, addr +} + +//func checkUDP(addr IpAddr) (bool, IpAddr) { +// //https://github.com/bronzdoc/gops +// //alive := true +// gologger.Debugf("skip udp port conn check: %s:%s", addr.Ip, addr.Port) +// time.Sleep(time.Millisecond * 10) +// +// return true, addr +//} + +func SaveAddr(alive bool, addr IpAddr) { + if alive { + mutex.Lock() + AliveAddr = append(AliveAddr, addr) + mutex.Unlock() + } +} diff --git a/core/crackmodule/plugins/crack_interface.go b/core/crackmodule/crack_interface.go similarity index 80% rename from core/crackmodule/plugins/crack_interface.go rename to core/crackmodule/crack_interface.go index fdbab75..ab51c05 100644 --- a/core/crackmodule/plugins/crack_interface.go +++ b/core/crackmodule/crack_interface.go @@ -1,4 +1,4 @@ -package plugins +package crackmodule import ( "cube/gologger" @@ -39,17 +39,6 @@ func AddCrackKeys(s string) { CrackKeys = append(CrackKeys, s) } -//type Result interface { -// ResultToString() (string, error) //probe、crack、sqlcmd都实现获取结果的接口 -//} -// - -//var ICrackMap map[string]ICrack -// -//func init() { -// ICrackMap = make(map[string]ICrack) -//} - func (c *Crack) NewICrack() ICrack { switch c.Name { case "ssh": @@ -73,14 +62,10 @@ func GetPort(s string) string { return ic.SetPort() } -func GetLoadStatus(s string) string { +func GetLoadStatus(s string) bool { c := NewCrack(s) ic := c.NewICrack() - if ic.IsLoad() == true { - return "Y" - } else { - return "N" - } + return ic.IsLoad() } func GetMutexStatus(s string) bool { @@ -107,13 +92,25 @@ func getPluginAuthPass(s string) []string { return ic.SetAuthPass() } +func getPluginAuthCred(s string) bool { + //检查插件是否设置了默认的用户和密码 + if len(getPluginAuthPass(s)) == 0 || len(getPluginAuthPass(s)) == 0 { + return false + } + return true +} + func GetPluginAuthMap(s string) map[string][]Auth { auths := make([]Auth, 0) authMaps := make(map[string][]Auth, 0) + credStatus := getPluginAuthCred(s) + if !credStatus { + gologger.Errorf("SetAuthUser() or SetAuthPass() is Empty for %s", s) + } for _, user := range getPluginAuthUser(s) { for _, pass := range getPluginAuthPass(s) { - gologger.Debugf("%s is preparing credentials: %s <=> %s", s, user, pass) pass = strings.Replace(pass, "{user}", user, -1) + gologger.Debugf("%s is preparing default credentials: %s <=> %s", s, user, pass) auths = append(auths, Auth{ User: user, Password: pass, diff --git a/core/crackmodule/crack_option.go b/core/crackmodule/crack_option.go new file mode 100644 index 0000000..73a4761 --- /dev/null +++ b/core/crackmodule/crack_option.go @@ -0,0 +1,159 @@ +package crackmodule + +import ( + "bufio" + "cube/gologger" + "cube/pkg/util" + "github.com/malfunkt/iprange" + "os" + "strconv" + "strings" +) + +type CrackOption struct { + Ip string + IpFile string + User string + UserFile string + Pass string + PassFile string + Port string + PluginName string +} + +func NewCrackOptions() *CrackOption { + return &CrackOption{} +} + +func (cp *CrackOption) ParsePluginName() []string { + var pluginNameList []string + + pns := strings.Split(cp.PluginName, ",") + if len(pns) > 2 && util.Contains("X", pns) { + //指定-X只能单独加载 + pluginNameList = nil + } + if len(pns) > 2 && util.Contains("Y", pns) { + pluginNameList = nil + } + switch { + case len(pns) == 1: + if pns[0] == "X" { + for _, k := range CrackKeys { + if !GetMutexStatus(k) && GetLoadStatus(k) { + pluginNameList = append(pluginNameList, k) + } + } + } + if pns[0] == "Y" { + for _, k := range CrackKeys { + if !GetMutexStatus(k) { + pluginNameList = append(pluginNameList, k) + } + } + } + if util.Contains(pns[0], CrackKeys) { + pluginNameList = pns + } + default: + for _, k := range pns { + if util.Contains(k, CrackKeys) { + pluginNameList = append(pluginNameList, k) + } + } + } + return pluginNameList +} + +func (cp *CrackOption) ParseAuth() []Auth { + var auths []Auth + user := cp.User + userFile := cp.UserFile + pass := cp.Pass + passFile := cp.PassFile + us := opt2slice(user, userFile) + ps := opt2slice(pass, passFile) + + for _, u := range us { + for _, p := range ps { + auths = append(auths, Auth{ + User: u, + Password: p, + }) + } + } + return auths +} + +func (cp *CrackOption) ParseIP() []string { + var hosts []string + ip := cp.Ip + fp := cp.IpFile + + if ip != "" { + hosts = ExpandIp(ip) + } + + if fp != "" { + var ips []string + ips, _ = ReadIPFile(fp) + hosts = append(hosts, ips...) + } + hosts = util.RemoveDuplicate(hosts) + return hosts +} + +func (cp *CrackOption) ParsePort() bool { + b, err := strconv.ParseBool(cp.Port) + if err != nil { + gologger.Errorf("error while parse port option: %v", cp.Port) + } + return b +} + +func opt2slice(str, file string) []string { + if len(str+file) == 0 { + gologger.Errorf("Provide login name(-l/-L) and login password(-p/-P)") + } + if len(str) > 0 { + r := strings.Split(str, ",") + + return r + } + r := util.FileReader(file) + return r +} + +func ExpandIp(ip string) (hosts []string) { + + list, err := iprange.ParseList(ip) + if err != nil { + gologger.Errorf("IP parsing error\nformat: 10.0.0.1, 10.0.0.5-10, 192.168.1.*, 192.168.10.0/24") + } + rng := list.Expand() + for _, v := range rng { + hosts = append(hosts, v.String()) + + } + return hosts + +} + +func ReadIPFile(filename string) ([]string, error) { + file, err := os.Open(filename) + if err != nil { + gologger.Debugf("Open %s error, %s\n", filename, err) + } + defer file.Close() + var content []string + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + text := strings.TrimSpace(scanner.Text()) + if text != "" { + host := ExpandIp(text) + content = append(content, host...) + } + } + return content, nil +} diff --git a/core/crackmodule/crack_report.go b/core/crackmodule/crack_report.go new file mode 100644 index 0000000..514acac --- /dev/null +++ b/core/crackmodule/crack_report.go @@ -0,0 +1,5 @@ +package crackmodule + +type CrackReport struct { + *CrackResult +} diff --git a/core/crackmodule/plugins/crack_task.go b/core/crackmodule/crack_task.go similarity index 60% rename from core/crackmodule/plugins/crack_task.go rename to core/crackmodule/crack_task.go index 9e67242..7ec30f7 100644 --- a/core/crackmodule/plugins/crack_task.go +++ b/core/crackmodule/crack_task.go @@ -1,9 +1,9 @@ -package plugins +package crackmodule import ( "context" "crypto/md5" - "cube/conf" + "cube/config" "cube/core" "cube/gologger" "fmt" @@ -25,17 +25,17 @@ func MakeTaskHash(k string) string { } func CheckTaskHash(hash string) bool { - conf.SuccessHash.Lock() - _, ok := conf.SuccessHash.S[hash] - conf.SuccessHash.Unlock() + config.SuccessHash.Lock() + _, ok := config.SuccessHash.S[hash] + config.SuccessHash.Unlock() //log.Debugf("Success: %#v\n", model.SuccessHash) return ok } func SetTaskHash(hash string) { - conf.SuccessHash.Lock() - conf.SuccessHash.S[hash] = true - conf.SuccessHash.Unlock() + config.SuccessHash.Lock() + config.SuccessHash.S[hash] = true + config.SuccessHash.Unlock() } // ResultMap 当Mysql或者redis空密码的时候,任何密码都正确,会导致密码刷屏 @@ -46,7 +46,7 @@ var ResultMap = struct { func SetResultMap(r CrackResult) { ResultMap.Lock() - ResultMap.m[fmt.Sprintf("%s==>%s:%s", r.Crack.Name, r.Crack.Ip, r.Crack.Port)] = r.Result + ResultMap.m[fmt.Sprintf("\nCRACK_PLUG: %s\nCRACK_PORT: %s\nCRACK_ADDR: %s\nCRACK_USER: %s\nCRACK_PASS: %s", r.Crack.Name, r.Crack.Port, r.Crack.Ip, r.Crack.Auth.User, r.Crack.Auth.Password)] = r.Result ResultMap.Unlock() } @@ -54,8 +54,9 @@ func ReadResultMap() { ResultMap.RLock() n := ResultMap.m ResultMap.RUnlock() - for k, v := range n { - gologger.Infof("[*]: %s %v", k, v) + for k, _ := range n { + //gologger.Infof("%s %v", k, v) + gologger.Infof("%s", k) } } @@ -63,7 +64,7 @@ func GetFinishTime(t1 time.Time) { fmt.Println(strings.Repeat(">", 50)) End := time.Now().Format("2006-01-02 15:04:05") - fmt.Printf("Finished:%s Cost:%s", End, time.Since(t1)) + fmt.Printf("Finished: %s Cost: %s", End, fmt.Sprintf("%.4ss", time.Since(t1))) } @@ -88,6 +89,7 @@ func buildDefaultTasks(AliveIPS []IpAddr) (cracks []Crack) { auths := authMaps[addr.PluginName] for _, auth := range auths { s := Crack{Ip: addr.Ip, Port: addr.Port, Auth: auth, Name: addr.PluginName} + gologger.Debugf("build task: IP:%s Port:%s Login:%s Pass:%s", s.Ip, s.Port, s.Auth.User, s.Auth.Password) cracks = append(cracks, s) } } @@ -99,26 +101,23 @@ func buildTasks(AliveIPS []IpAddr, auths []Auth) (cracks []Crack) { for _, addr := range AliveIPS { for _, auth := range auths { s := Crack{Ip: addr.Ip, Port: addr.Port, Auth: auth, Name: addr.PluginName} + gologger.Debugf("build task: IP:%s Port:%s Login:%s Pass:%s", s.Ip, s.Port, s.Auth.User, s.Auth.Password) cracks = append(cracks, s) } } return cracks } -func saveResult() { +func saveCrackResult(crackResult CrackResult) { -} - -func saveCrackReport(crackResult CrackResult) { - - if len(taskResult.Result) > 0 { - gologger.Debugf("Put Result to Map: %v\n", taskResult) - k := fmt.Sprintf("%v-%v-%v", taskResult.CrackTask.Ip, taskResult.CrackTask.Port, taskResult.CrackTask.CrackPlugin) - h := util.MakeTaskHash(k) - util.SetTaskHash(h) + if len(crackResult.Result) > 0 { + gologger.Debugf("Successful: IP:%s Port:%s Login:%s Pass:%s", crackResult.Crack.Ip, crackResult.Crack.Port, crackResult.Crack.Auth.User, crackResult.Crack.Auth.Password) + k := fmt.Sprintf("%v-%v-%v", crackResult.Crack.Ip, crackResult.Crack.Port, crackResult.Crack.Name) + h := MakeTaskHash(k) + SetTaskHash(h) //s1 := fmt.Sprintf("[+]: %s://%s:%s %s", taskResult.CrackTask.CrackPlugin, taskResult.CrackTask.Ip, taskResult.CrackTask.Port, taskResult.Result) //fmt.Println(s1) - util.SetResultMap(taskResult) + SetResultMap(crackResult) } } @@ -131,51 +130,26 @@ func runSingleTask(ctx context.Context, crackTasksChan chan Crack, wg *sync.Wait if !ok { return } - //if task.Port == "" { - // task.Port = strconv.Itoa(model.CommonPortMap[task.CrackPlugin]) - //} - //alive := CheckAlive(task) - //if !alive { - // wg.Done() - // continue - //} - - //gologger.Debugf("Cracking %s password ", crackTask.Ip, crackTask.CrackPlugin, task.Auth.User, task.Auth.Password, task.Ip, task.Port) k := fmt.Sprintf("%v-%v-%v", crackTask.Ip, crackTask.Port, crackTask.Name) h := MakeTaskHash(k) if CheckTaskHash(h) { wg.Done() continue } - c := crackTask.NewICrack() - r := c.Exec() - //fn := CrackFuncMap[task.CrackPlugin] - //r := fn(task) - saveCrackReport(r) + ic := crackTask.NewICrack() + gologger.Debugf("cracking: IP:%s Port:%s Login:%s Pass:%s", crackTask.Ip, crackTask.Port, crackTask.Auth.User, crackTask.Auth.Password) + r := ic.Exec() + saveCrackResult(r) wg.Done() select { case <-ctx.Done(): - case <-time.After(time.Duration(delay) * time.Second): + case <-time.After(time.Duration(core.RandomDelay(delay)) * time.Second): } - } - } } -func parseAuthOption() { - -} - -func parsePluginOption() { - -} - -func getAuthList() { - -} - func StartCrack(opt *CrackOption, globalopt *core.GlobalOption) { var ( crackPlugins []string @@ -190,12 +164,12 @@ func StartCrack(opt *CrackOption, globalopt *core.GlobalOption) { ctx := context.Background() t1 := time.Now() delay = globalopt.Delay + threadNum = globalopt.Threads if delay > 0 { //添加使用--delay选项的时候,强制单线程。现在还停留在想象中的攻击 threadNum = 1 - } else { - threadNum = globalopt.Threads + gologger.Infof("Running in single thread mode when --delay is set") } crackPlugins = opt.ParsePluginName() @@ -205,21 +179,18 @@ func StartCrack(opt *CrackOption, globalopt *core.GlobalOption) { validPort := opt.ParsePort() if len(crackPlugins) > 1 && validPort { //指定端口的时候仅限定一个插件使用 - gologger.Errorf("plugins are limited to single one when --port is set\n") + gologger.Errorf("plugin is limited to single one when --port is set\n") } } aliveIPS = CheckPort(ctx, threadNum, delay, crackIPS, crackPlugins, opt.Port) - gologger.Infof("crackPlugins: %s\n", crackPlugins) - gologger.Infof("crackIPS: %s\n", crackIPS) - if len(opt.User+opt.UserFile+opt.Pass+opt.PassFile) > 0 { crackAuths = opt.ParseAuth() crackTasks = buildTasks(aliveIPS, crackAuths) } else { crackTasks = buildDefaultTasks(aliveIPS) } - gologger.Debugf("build tasks: %v", crackTasks) + var wg sync.WaitGroup taskChan := make(chan Crack, threadNum*2) @@ -232,25 +203,7 @@ func StartCrack(opt *CrackOption, globalopt *core.GlobalOption) { taskChan <- task } //wg.Wait() - WaitThreadTimeout(&wg, conf.ThreadTimeout*2) + WaitThreadTimeout(&wg, config.ThreadTimeout) ReadResultMap() GetFinishTime(t1) - //if util.Contains(opt.CrackPluginName, Plugins.CrackFuncExclude) { - // //当-x是单独使用的插件,比如phpmyadmin、basicAuth类型的时候 - // AliveIPS = append(AliveIPS, util.IpAddr{ - // Ip: opt.Ip, - // Port: "", - // Plugin: opt.CrackPlugin, - // }) - //} else { - // optPlugins = genPlugins(opt.CrackPlugin) - // gologger.Infof("Loading plugin: %s", strings.Join(optPlugins, ",")) - // ips, _ = util.ParseIP(opt.Ip, opt.IpFile) - // - // AliveIPS = util.CheckAlive(ctx, num, delay, ips, optPlugins, opt.Port) - // //AliveIPS = RemoveRepByMap(AliveIPS) // 去重IP - // gologger.Debugf("Receive alive IP: %s", AliveIPS) - //} - - gologger.Debugf(string(rune(threadNum)), aliveIPS, ctx, t1, crackPlugins, crackIPS, crackAuths) } diff --git a/core/crackmodule/plugins/ftp.go b/core/crackmodule/ftp.go similarity index 90% rename from core/crackmodule/plugins/ftp.go rename to core/crackmodule/ftp.go index f8740eb..963afba 100644 --- a/core/crackmodule/plugins/ftp.go +++ b/core/crackmodule/ftp.go @@ -1,6 +1,8 @@ -package plugins +package crackmodule -import "cube/conf" +import ( + "cube/config" +) type FtpCrack struct { *Crack @@ -19,7 +21,7 @@ func (ftpCrack FtpCrack) SetAuthUser() []string { } func (ftpCrack FtpCrack) SetAuthPass() []string { - return conf.PASSWORDS + return config.PASSWORDS } func (ftpCrack FtpCrack) IsLoad() bool { diff --git a/core/crackmodule/func.go b/core/crackmodule/func.go deleted file mode 100644 index a88d2f9..0000000 --- a/core/crackmodule/func.go +++ /dev/null @@ -1,17 +0,0 @@ -package crackmodule - -import ( - "cube/core/crackmodule/plugins" - "github.com/olekukonko/tablewriter" - "os" -) - -func CrackHelpTable() { - table := tablewriter.NewWriter(os.Stdout) - table.SetHeader([]string{"Func", "Port", "Load By ALL"}) - for _, k := range plugins.CrackKeys { - table.Append([]string{k, plugins.GetPort(k), plugins.GetLoadStatus(k)}) - table.SetRowLine(true) - } - table.Render() -} diff --git a/core/crackmodule/func_test.go b/core/crackmodule/func_test.go deleted file mode 100644 index f6bca29..0000000 --- a/core/crackmodule/func_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package crackmodule - -import ( - "testing" -) - -func TestCrackHelp(t *testing.T) { - CrackHelpTable() -} diff --git a/core/crackmodule/plugins/interfaces_test.go b/core/crackmodule/interfaces_test.go similarity index 96% rename from core/crackmodule/plugins/interfaces_test.go rename to core/crackmodule/interfaces_test.go index 1ef9dea..9b8a319 100644 --- a/core/crackmodule/plugins/interfaces_test.go +++ b/core/crackmodule/interfaces_test.go @@ -1,4 +1,4 @@ -package plugins +package crackmodule import ( "cube/gologger" diff --git a/core/crackmodule/plugins/crack_struct.go b/core/crackmodule/plugins/crack_struct.go deleted file mode 100644 index d5c343e..0000000 --- a/core/crackmodule/plugins/crack_struct.go +++ /dev/null @@ -1 +0,0 @@ -package plugins diff --git a/core/crackmodule/plugins/ssh.go b/core/crackmodule/ssh.go similarity index 95% rename from core/crackmodule/plugins/ssh.go rename to core/crackmodule/ssh.go index f1461cd..1084ca5 100644 --- a/core/crackmodule/plugins/ssh.go +++ b/core/crackmodule/ssh.go @@ -1,7 +1,7 @@ -package plugins +package crackmodule import ( - "cube/conf" + "cube/config" "fmt" "golang.org/x/crypto/ssh" ) @@ -23,7 +23,7 @@ func (sshCrack SshCrack) SetAuthUser() []string { } func (sshCrack SshCrack) SetAuthPass() []string { - return conf.PASSWORDS + return config.PASSWORDS } func (sshCrack SshCrack) IsLoad() bool { diff --git a/core/crackmodule/tasks.go.bak b/core/crackmodule/tasks.go.bak deleted file mode 100644 index ede349f..0000000 --- a/core/crackmodule/tasks.go.bak +++ /dev/null @@ -1,219 +0,0 @@ -package crackmodule - -import ( - "context" - "cube/conf" - "cube/gologger" - "cube/lib/crackmodule/plugins" - "cube/pkg/model" - "cube/pkg/util" - "fmt" - "strings" - "sync" - "time" -) - -func loadDefaultDict(p string) map[string][]plugins.Auth { - authSlice := make([]plugins.Auth, 0) - r := make(map[string][]plugins.Auth, 0) - users, ok := conf.UserDict[p] - if !ok { - users = []string{""} - } - { - gologger.Debugf("User: %s", user) - for _, pass := range conf.PassDict { - pass = strings.Replace(pass, "{user}", user, -1) - authSlice = append(authSlice, plugins.Auth{ - User: user, - Password: pass, - }) - } - } - r[p] = authSlice - return r -} - -func genDefaultTasks(AliveIPS []util.IpAddr) (tasks []plugins.CrackTask) { - tasks = make([]plugins.CrackTask, 0) - for _, addr := range AliveIPS { - mapAuthSlice := loadDefaultDict(addr.Plugin) - authSlice := mapAuthSlice[addr.Plugin] - for _, auth := range authSlice { - s := plugins.CrackTask{Ip: addr.Ip, Port: addr.Port, Auth: auth, CrackPlugin: addr.Plugin} - tasks = append(tasks, s) - } - - } - return tasks -} - -func genCrackTasks(AliveIPS []util.IpAddr, auths []plugins.Auth) (tasks []plugins.CrackTask) { - tasks = make([]plugins.CrackTask, 0) - for _, addr := range AliveIPS { - for _, auth := range auths { - s := plugins.CrackTask{Ip: addr.Ip, Port: addr.Port, Auth: auth, CrackPlugin: addr.Plugin} - tasks = append(tasks, s) - } - } - return tasks -} - -func saveCrackReport(taskResult plugins.CrackTaskResult) { - - if len(taskResult.Result) > 0 { - gologger.Debugf("Put Result to Map: %v\n", taskResult) - k := fmt.Sprintf("%v-%v-%v", taskResult.CrackTask.Ip, taskResult.CrackTask.Port, taskResult.CrackTask.CrackPlugin) - h := util.MakeTaskHash(k) - util.SetTaskHash(h) - //s1 := fmt.Sprintf("[+]: %s://%s:%s %s", taskResult.CrackTask.CrackPlugin, taskResult.CrackTask.Ip, taskResult.CrackTask.Port, taskResult.Result) - //fmt.Println(s1) - util.SetResultMap(taskResult) - } -} - -func runUnitTask(ctx context.Context, tasks chan plugins.CrackTask, wg *sync.WaitGroup, delay int) { - for { - select { - case <-ctx.Done(): - return - case task, ok := <-tasks: - if !ok { - return - } - //if task.Port == "" { - // task.Port = strconv.Itoa(model.CommonPortMap[task.CrackPlugin]) - //} - //alive := CheckAlive(task) - //if !alive { - // wg.Done() - // continue - //} - - gologger.Debugf("Checking %s Password: %s://%s:%s@%s:%s", task.CrackPlugin, task.CrackPlugin, task.Auth.User, task.Auth.Password, task.Ip, task.Port) - k := fmt.Sprintf("%v-%v-%v", task.Ip, task.Port, task.CrackPlugin) - h := util.MakeTaskHash(k) - if util.CheckTaskHash(h) { - wg.Done() - continue - } - fn := Plugins.CrackFuncMap[task.CrackPlugin] - r := fn(task) - saveCrackReport(r) - wg.Done() - - select { - case <-ctx.Done(): - case <-time.After(time.Duration(delay) * time.Second): - } - - } - - } -} - -func opt2slice(str string, file string) []string { - if len(str+file) == 0 { - gologger.Errorf("-h for Help, Please set User and Password flag") - } - if len(str) > 0 { - r := strings.Split(str, ",") - - return r - } - r, _ := util.FileReader(file) - return r -} - -func genPlugins(plugin string) []string { - pluginList := strings.Split(plugin, ",") - if len(pluginList) > 1 && util.SliceContain("ALL", pluginList) { - gologger.Errorf("invalid plugin: %s", plugin) - } - - if plugin == "ALL" { - pluginList = Plugins.CrackKeys - } - return pluginList -} - -func genAuths(opt *model.CrackOptions) (auths []model.Auth) { - user := opt.User - userFile := opt.UserFile - pass := opt.Pass - passFile := opt.PassFile - us := opt2slice(user, userFile) - ps := opt2slice(pass, passFile) - - for _, u := range us { - for _, p := range ps { - auths = append(auths, model.Auth{ - User: u, - Password: p, - }) - } - } - return auths -} - -func StartCrackTask(opt *model.CrackOptions, globalopts *model.GlobalOptions) { - var ( - optPlugins []string - ips []string - auths []model.Auth - tasks []model.CrackTask - num int - delay int - AliveIPS []util.IpAddr - ) - ctx := context.Background() - t1 := time.Now() - delay = globalopts.Delay - - if delay > 0 { - num = 1 - } else { - num = globalopts.Threads - } - - if util.SliceContain(opt.CrackPlugin, Plugins.CrackFuncExclude) { - AliveIPS = append(AliveIPS, util.IpAddr{ - Ip: opt.Ip, - Port: "", - Plugin: opt.CrackPlugin, - }) - } else { - optPlugins = genPlugins(opt.CrackPlugin) - gologger.Infof("Loading plugin: %s", strings.Join(optPlugins, ",")) - ips, _ = util.ParseIP(opt.Ip, opt.IpFile) - - AliveIPS = util.CheckAlive(ctx, num, delay, ips, optPlugins, opt.Port) - //AliveIPS = RemoveRepByMap(AliveIPS) // 去重IP - gologger.Debugf("Receive alive IP: %s", AliveIPS) - } - - if len(opt.User+opt.UserFile+opt.Pass+opt.PassFile) > 0 { - auths = genAuths(opt) - tasks = genCrackTasks(AliveIPS, auths) - } else { - tasks = genDefaultTasks(AliveIPS) - } - - gologger.Debugf("Receive %d tasks", len(tasks)) - - var wg sync.WaitGroup - taskChan := make(chan model.CrackTask, num*2) - - for i := 0; i < num; i++ { - go runUnitTask(ctx, taskChan, &wg, delay) - } - - for _, task := range tasks { - wg.Add(1) - taskChan <- task - } - //wg.Wait() - cubelib.waitTimeout(&wg, conf.ThreadTimeout*2) - util.ReadResultMap() - util.GetFinishTime(t1) -} diff --git a/core/options.go b/core/options.go new file mode 100644 index 0000000..a364f4f --- /dev/null +++ b/core/options.go @@ -0,0 +1,31 @@ +package core + +import ( + "cube/gologger" + "fmt" + "math/rand" + "strconv" + "time" +) + +type GlobalOption struct { + Threads int + Timeout int + Verbose bool + Output string + Delay float64 +} + +func NewGlobalOptions() *GlobalOption { + return &GlobalOption{} +} + +func RandomDelay(float float64) float64 { + r := rand.New(rand.NewSource(time.Now().Unix())) + r1 := r.Float64() * float + r1, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", r1), 64) //保留两位小数点 + if r1 > 0 { + gologger.Debugf("thread is going to sleep %v second", r1) + } + return r1 +} diff --git a/core/probemodule/check_port.go b/core/probemodule/check_port.go new file mode 100644 index 0000000..4fb026f --- /dev/null +++ b/core/probemodule/check_port.go @@ -0,0 +1,112 @@ +package probemodule + +import ( + "context" + "cube/config" + "cube/core" + "cube/gologger" + "fmt" + "net" + "sync" + "time" +) + +type IpAddr struct { + Ip string + Port string + PluginName string +} + +var ( + mutex sync.Mutex + AliveAddr []IpAddr + ipList []IpAddr +) + +func CheckPort(ctx context.Context, threadNum int, delay float64, ips []string, pluginNames []string, port string) []IpAddr { + //指定插件端口的时候,只允许加载一个插件 + if len(port) > 0 { + for _, ip := range ips { + ipList = append(ipList, IpAddr{ + Ip: ip, + Port: port, + PluginName: pluginNames[0], + }) + } + } else { + for _, plugin := range pluginNames { + for _, ip := range ips { + ipList = append(ipList, IpAddr{ + Ip: ip, + Port: GetPort(plugin), + PluginName: plugin, + }) + } + } + + } + + var addrChan = make(chan IpAddr, threadNum*2) + var wg sync.WaitGroup + wg.Add(len(ipList)) + + for i := 0; i < threadNum; i++ { + go func() { + for { + select { + case <-ctx.Done(): + return + case addr, ok := <-addrChan: + if !ok { + return + } + if GetTCP(addr.PluginName) { + //TCP的时候检查端口,UDP跳过 + SaveAddr(check(addr)) + } + wg.Done() + select { + case <-ctx.Done(): + case <-time.After(time.Duration(core.RandomDelay(delay)) * time.Second): + } + } + } + }() + } + + for _, addr := range ipList { + addrChan <- addr + } + close(addrChan) + wg.Wait() + + return AliveAddr +} + +func check(addr IpAddr) (bool, IpAddr) { + alive := false + gologger.Debugf("tcp port conn check: %s:%s", addr.Ip, addr.Port) + _, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", addr.Ip, addr.Port), config.TcpConnTimeout) + if err == nil { + gologger.Infof("Open %s:%s", addr.Ip, addr.Port) + alive = true + } + return alive, addr +} + +//func checkUDP(addr IpAddr) (bool, IpAddr) { +// //https://github.com/bronzdoc/gops +// //alive := true +// gologger.Debugf("skip udp port conn check: %s:%s", addr.Ip, addr.Port) +// time.Sleep(time.Millisecond * 10) +// +// return true, addr +//} + +func SaveAddr(alive bool, addr IpAddr) { + if alive { + mutex.Lock() + AliveAddr = append(AliveAddr, addr) + mutex.Unlock() + } +} diff --git a/core/probemodule/oxid.go b/core/probemodule/oxid.go new file mode 100644 index 0000000..264ec39 --- /dev/null +++ b/core/probemodule/oxid.go @@ -0,0 +1,132 @@ +package probemodule + +import ( + "bytes" + "cube/config" + "cube/pkg/util" + "fmt" + "net" + "strings" +) + +type Oxid struct { + *Probe +} + +func (o Oxid) ProbeName() string { + return "oxid" +} + +func (o Oxid) ProbePort() string { + return "135" +} + +func (o Oxid) ProbeLoad() bool { + return true +} + +func (o Oxid) ProbeTcp() bool { + return true +} + +func (o Oxid) ProbeExec() ProbeResult { + result := ProbeResult{Probe: *o.Probe, Result: "", Err: nil} + dl := net.Dialer{Timeout: config.TcpConnTimeout} + t := fmt.Sprintf("%s:%s", o.Ip, o.Port) + conn, err := dl.Dial("tcp", t) + + // defer conn.Close() + if err != nil { + result.Err = err + //log.Printf("Oxid Running Error: %s:%s", task.Ip, err) + return result + } + + conn.Write([]byte("\x05\x00\x0b\x03\x10\x00\x00\x00\x48\x00\x00\x00\x01\x00\x00\x00\xb8\x10\xb8\x10\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\xc4\xfe\xfc\x99\x60\x52\x1b\x10\xbb\xcb\x00\xaa\x00\x21\x34\x7a\x00\x00\x00\x00\x04\x5d\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00\x2b\x10\x48\x60\x02\x00\x00\x00")) + + tmpByte := make([]byte, 1024) + conn.Read(tmpByte) + + // dcerpc finish + + // IOXIDResolve start + conn.Write([]byte("\x05\x00\x00\x03\x10\x00\x00\x00\x18\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00")) + + r := make([]byte, 4096) + _, err = conn.Read(r) + //if err != nil { + // return err + //} + /* + Two parts: + 1. Distributed Computing Enviroment / Remote Prodedure Call Response + 2. DCOM OXID Resolver <- what we need + */ + + r = r[24+12+2+2:] + index := bytes.Index(r, []byte("\x09\x00\xff\xff\x00\x00")) + //if index == -1 { + // return errors.New("Not Found") + //} + r = r[:index] + var results []string + + for { + if len(r) == 0 { + break + } + index = bytes.Index(r, []byte("\x00\x00\x00")) + hosts := util.Bytes2StringUTF16(r[:index+3]) + results = append(results, hosts) + r = r[index+3:] + } + //var hostname string + var netAddr []string + if len(results) > 0 { + hostname := results[0] + for _, v := range results[1:] { + netAddr = append(netAddr, v) + } + result.Result = fmt.Sprintf("Host: %s\nNets: %s\n", hostname, strings.Join(netAddr, "\t")) + } + arch := getArch(o.Ip, o.Port) + if len(arch) > 0 { + result.Result += fmt.Sprintf("Arch: %s\n", arch) + } + return result +} + +func init() { + AddProbeKeys("oxid") +} + +func getArch(ip, port string) (s string) { + dl := net.Dialer{Timeout: config.TcpConnTimeout} + t := fmt.Sprintf("%s:%s", ip, port) + conn, err := dl.Dial("tcp", t) + if err != nil { + return + } + + archPayload := []byte{ /* Packet 186 */ + 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xb8, 0x10, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x08, 0x83, 0xaf, 0xe1, 0x1f, 0x5d, 0xc9, 0x11, + 0x91, 0xa4, 0x08, 0x00, 0x2b, 0x14, 0xa0, 0xfa, + 0x03, 0x00, 0x00, 0x00, 0x33, 0x05, 0x71, 0x71, + 0xba, 0xbe, 0x37, 0x49, 0x83, 0x19, 0xb5, 0xdb, + 0xef, 0x9c, 0xcc, 0x36, 0x01, 0x00, 0x00, 0x00, + } + conn.Write(archPayload) + tmpByte := make([]byte, 60) + conn.Read(tmpByte) + if bytes.Index(tmpByte, []byte("\x33\x05\x71\x71\xba\xbe\x37\x49\x83\x19\xb5\xdb\xef\x9c\xcc\x36")) > 0 { + s = "64-bit" + } + if strings.Contains(string(tmpByte), "syntaxes_not_supported") { + s = "32-bit" + } + return s +} diff --git a/core/probemodule/probe_interface.go b/core/probemodule/probe_interface.go new file mode 100644 index 0000000..2ff0254 --- /dev/null +++ b/core/probemodule/probe_interface.go @@ -0,0 +1,68 @@ +package probemodule + +type Probe struct { + Ip string + Port string + Name string +} + +type ProbeResult struct { + Probe Probe + Result string + Err error +} + +var ProbeKeys []string + +type IProbe interface { + ProbeName() string //插件名称 + ProbePort() string //默认端口 + ProbeLoad() bool //是否默认加载 + ProbeTcp() bool //是否TCP协议 + ProbeExec() ProbeResult //执行插件 +} + +func AddProbeKeys(s string) { + ProbeKeys = append(ProbeKeys, s) +} + +func (p *Probe) NewIProbe() IProbe { + switch p.Name { + case "oxid": + return &Oxid{p} + case "smb": + return &Smb{p} + default: + return nil + } +} + +func NewProbe(s string) Probe { + return Probe{ + Name: s, + } +} + +func GetName(s string) string { + c := NewProbe(s) + ic := c.NewIProbe() + return ic.ProbeName() +} + +func GetPort(s string) string { + c := NewProbe(s) + ic := c.NewIProbe() + return ic.ProbePort() +} + +func GetLoadStatus(s string) bool { + c := NewProbe(s) + ic := c.NewIProbe() + return ic.ProbeLoad() +} + +func GetTCP(s string) bool { + c := NewProbe(s) + ic := c.NewIProbe() + return ic.ProbeTcp() +} diff --git a/core/probemodule/probe_option.go b/core/probemodule/probe_option.go new file mode 100644 index 0000000..f96b8b9 --- /dev/null +++ b/core/probemodule/probe_option.go @@ -0,0 +1,76 @@ +package probemodule + +import ( + "cube/core/crackmodule" + "cube/gologger" + "cube/pkg/util" + "strconv" + "strings" +) + +type ProbeOption struct { + Ip string + IpFile string + Port string + PluginName string +} + +func NewProbeOption() *ProbeOption { + return &ProbeOption{} +} + +func (po *ProbeOption) ParsePluginName() []string { + var pluginNameList []string + + pns := strings.Split(po.PluginName, ",") + if len(pns) > 2 && util.Contains("X", pns) { + //指定-X只能单独加载 + pluginNameList = nil + } + + if len(pns) == 1 { + if pns[0] == "X" { + for _, k := range ProbeKeys { + if GetLoadStatus(k) { + pluginNameList = append(pluginNameList, k) + } + } + } + if util.Contains(pns[0], ProbeKeys) { + pluginNameList = pns + } + } else { + for _, k := range pns { + if util.Contains(k, ProbeKeys) { + pluginNameList = append(pluginNameList, k) + } + } + } + return pluginNameList +} + +func (po *ProbeOption) ParseIP() []string { + var hosts []string + ip := po.Ip + fp := po.IpFile + + if ip != "" { + hosts = crackmodule.ExpandIp(ip) + } + + if fp != "" { + var ips []string + ips, _ = crackmodule.ReadIPFile(fp) + hosts = append(hosts, ips...) + } + hosts = util.RemoveDuplicate(hosts) + return hosts +} + +func (po *ProbeOption) ParsePort() bool { + b, err := strconv.ParseBool(po.Port) + if err != nil { + gologger.Errorf("error while parse port option: %v", po.Port) + } + return b +} diff --git a/core/probemodule/probe_task.go b/core/probemodule/probe_task.go new file mode 100644 index 0000000..3629d8e --- /dev/null +++ b/core/probemodule/probe_task.go @@ -0,0 +1,102 @@ +package probemodule + +import ( + "context" + "cube/config" + "cube/core" + "cube/core/crackmodule" + "cube/gologger" + "fmt" + "sync" + "time" +) + +func buildTasks(aliveIPS []IpAddr, scanPlugins []string) (probes []Probe) { + probes = make([]Probe, 0) + for _, addr := range aliveIPS { + service := Probe{Ip: addr.Ip, Port: addr.Port, Name: addr.PluginName} + probes = append(probes, service) + } + return probes +} + +func saveReport(probeResult ProbeResult) { + if len(probeResult.Result) > 0 { + s := fmt.Sprintf("[*]: %s\n[*]: %s:%s\n", probeResult.Probe.Name, probeResult.Probe.Ip, probeResult.Probe.Port) + s1 := fmt.Sprintf("%s\n", probeResult.Result) + gologger.Infof(s + s1) + } +} + +func runSingleTask(ctx context.Context, taskChan chan Probe, wg *sync.WaitGroup, delay float64) { + for { + select { + case <-ctx.Done(): + return + case probeTask, ok := <-taskChan: + if !ok { + return + } + ic := probeTask.NewIProbe() + gologger.Debugf("probing: IP:%s Port:%s", probeTask.Ip, probeTask.Port) + r := ic.ProbeExec() + saveReport(r) + + select { + case <-ctx.Done(): + case <-time.After(time.Duration(core.RandomDelay(delay)) * time.Second): + } + wg.Done() + } + } +} + +func StartProbe(opt *ProbeOption, globalopt *core.GlobalOption) { + var ( + probePlugins []string + probeIPS []string + probeTasks []Probe + threadNum int + delay float64 + aliveIPS []IpAddr + ) + ctx := context.Background() + t1 := time.Now() + delay = globalopt.Delay + threadNum = globalopt.Threads + + if delay > 0 { + //添加使用--delay选项的时候,强制单线程。现在还停留在想象中的攻击 + threadNum = 1 + gologger.Infof("Running in single thread mode when --delay is set") + } + + probePlugins = opt.ParsePluginName() + probeIPS = opt.ParseIP() + if opt.Port != "" { + validPort := opt.ParsePort() + if len(probePlugins) > 1 && validPort { + //指定端口的时候仅限定一个插件使用 + gologger.Errorf("plugin is limited to single one when --port is set\n") + } + } + + aliveIPS = CheckPort(ctx, threadNum, delay, probeIPS, probePlugins, opt.Port) + probeTasks = buildTasks(aliveIPS, probePlugins) + + var wg sync.WaitGroup + taskChan := make(chan Probe, threadNum*2) + + //消费者 + for i := 0; i < threadNum; i++ { + go runSingleTask(ctx, taskChan, &wg, delay) + } + + for _, task := range probeTasks { + wg.Add(1) + taskChan <- task + } + //wg.Wait() + crackmodule.WaitThreadTimeout(&wg, config.ThreadTimeout) + crackmodule.GetFinishTime(t1) +} diff --git a/core/probemodule/smb.go b/core/probemodule/smb.go new file mode 100644 index 0000000..1124a56 --- /dev/null +++ b/core/probemodule/smb.go @@ -0,0 +1,216 @@ +package probemodule + +import ( + "bytes" + "cube/config" + "cube/gologger" + "cube/pkg/util" + "encoding/hex" + "fmt" + "github.com/JKme/go-ntlmssp" + "net" + "strings" +) + +type Smb struct { + *Probe +} + +func (s Smb) ProbeName() string { + return "smb" +} + +func (s Smb) ProbePort() string { + return "445" +} + +func (s Smb) ProbeLoad() bool { + return true +} + +func (s Smb) ProbeTcp() bool { + return true +} + +func (s Smb) ProbeExec() ProbeResult { + result := ProbeResult{Probe: *s.Probe, Result: "", Err: nil} + + host := fmt.Sprintf("%s:%v", s.Ip, s.Port) + conn, err := net.DialTimeout("tcp", host, config.TcpConnTimeout) + if err != nil { + gologger.Debug(err) + result.Err = err + return result + } + _, err = conn.Write(NegotiateSMBv1Data1) + if err != nil { + result.Err = err + return result + } + r1, _ := util.ReadBytes(conn) + + //ff534d42 SMBv1的标示 + //fe534d42 SMBv2的标示 + //先发送探测SMBv1的payload,不支持的SMBv1的时候返回为空,然后尝试发送SMBv2的探测数据包 + //if hex.EncodeToString(r1[4:8]) == "ff534d42" { + if len(r1) > 0 { + _, err = conn.Write(NegotiateSMBv1Data2) + if err != nil { + result.Err = err + return result + } + + ret, err := util.ReadBytes(conn) + + if err != nil || len(ret) < 45 { + result.Err = err + return result + } + + blobLength := uint16(util.Bytes2Uint(ret[43:45], '<')) + blobCount := uint16(util.Bytes2Uint(ret[45:47], '<')) + + gssNative := ret[47:] + offNtlm := bytes.Index(gssNative, []byte("NTLMSSP")) + //fmt.Println(off_ntlm) + //fmt.Printf("GSS-NATIVE: %x\n", gss[off_ntlm:]) + // + //fmt.Printf("NTLM: %x\n", gss[off_ntlm:blob_length]) + //fmt.Printf("native: %x\n", gss[int(blob_length):blob_count]) + native := gssNative[int(blobLength):blobCount] + ss := strings.Split(string(native), "\x00\x00") + //fmt.Println(ss) + + bs := gssNative[offNtlm:blobLength] + type2 := ntlmssp.ChallengeMsg{} + //fmt.Printf("%x\n", bs) + tinfo := type2.String(bs) + //fmt.Println(tinfo) + + NativeOS := util.TrimName(ss[0]) + NativeLM := util.TrimName(ss[1]) + //fmt.Println(NativeOS, NativeLM) + tinfo += fmt.Sprintf("NativeOS: %s\nNativeLM: %s\n", NativeOS, NativeLM) + result.Result = tinfo + } else { + conn2, err := net.DialTimeout("tcp", host, config.TcpConnTimeout) + if err != nil { + result.Err = err + return result + } + _, err = conn2.Write(NegotiateSMBv2Data1) + + if err != nil { + result.Err = err + return result + } + r2, _ := util.ReadBytes(conn2) + + var NtlmsspNegotiateV2Data []byte + if hex.EncodeToString(r2[70:71]) == "03" { + flags := []byte{0x15, 0x82, 0x08, 0xa0} + NtlmsspNegotiateV2Data = getNTLMSSPNegotiateData(flags) + } else { + flags := []byte{0x05, 0x80, 0x08, 0xa0} + NtlmsspNegotiateV2Data = getNTLMSSPNegotiateData(flags) + } + + _, err = conn2.Write(NegotiateSMBv2Data2) + if err != nil { + result.Err = err + return result + } + util.ReadBytes(conn2) + + _, err = conn2.Write(NtlmsspNegotiateV2Data) + ret, _ := util.ReadBytes(conn2) + ntlmOff := bytes.Index(ret, []byte("NTLMSSP")) + type2 := ntlmssp.ChallengeMsg{} + tInfo := type2.String(ret[ntlmOff:]) + result.Result = tInfo + } + + return result +} + +func init() { + AddProbeKeys("smb") +} + +var NegotiateSMBv1Data1 = []byte{ + 0x00, 0x00, 0x00, 0x85, 0xFF, 0x53, 0x4D, 0x42, 0x72, 0x00, 0x00, 0x00, 0x00, 0x18, 0x53, 0xC8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x02, 0x50, 0x43, 0x20, 0x4E, 0x45, 0x54, 0x57, 0x4F, + 0x52, 0x4B, 0x20, 0x50, 0x52, 0x4F, 0x47, 0x52, 0x41, 0x4D, 0x20, 0x31, 0x2E, 0x30, 0x00, 0x02, + 0x4C, 0x41, 0x4E, 0x4D, 0x41, 0x4E, 0x31, 0x2E, 0x30, 0x00, 0x02, 0x57, 0x69, 0x6E, 0x64, 0x6F, + 0x77, 0x73, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x57, 0x6F, 0x72, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x70, + 0x73, 0x20, 0x33, 0x2E, 0x31, 0x61, 0x00, 0x02, 0x4C, 0x4D, 0x31, 0x2E, 0x32, 0x58, 0x30, 0x30, + 0x32, 0x00, 0x02, 0x4C, 0x41, 0x4E, 0x4D, 0x41, 0x4E, 0x32, 0x2E, 0x31, 0x00, 0x02, 0x4E, 0x54, + 0x20, 0x4C, 0x4D, 0x20, 0x30, 0x2E, 0x31, 0x32, 0x00, +} +var NegotiateSMBv1Data2 = []byte{ + 0x00, 0x00, 0x01, 0x0A, 0xFF, 0x53, 0x4D, 0x42, 0x73, 0x00, 0x00, 0x00, 0x00, 0x18, 0x07, 0xC8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, + 0x00, 0x00, 0x40, 0x00, 0x0C, 0xFF, 0x00, 0x0A, 0x01, 0x04, 0x41, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0xA0, 0xCF, 0x00, 0x60, + 0x48, 0x06, 0x06, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x02, 0xA0, 0x3E, 0x30, 0x3C, 0xA0, 0x0E, 0x30, + 0x0C, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0A, 0xA2, 0x2A, 0x04, + 0x28, 0x4E, 0x54, 0x4C, 0x4D, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x82, 0x08, + 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x02, 0xCE, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6E, 0x00, + 0x64, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, + 0x20, 0x00, 0x33, 0x00, 0x37, 0x00, 0x39, 0x00, 0x30, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x76, 0x00, 0x69, 0x00, 0x63, 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x61, 0x00, + 0x63, 0x00, 0x6B, 0x00, 0x20, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x69, 0x00, + 0x6E, 0x00, 0x64, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x33, 0x00, 0x20, 0x00, 0x35, 0x00, 0x2E, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, +} + +var NegotiateSMBv2Data1 = []byte{ + 0x00, 0x00, 0x00, 0x45, 0xFF, 0x53, 0x4D, 0x42, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xAC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x02, + 0x4E, 0x54, 0x20, 0x4C, 0x4D, 0x20, 0x30, 0x2E, 0x31, 0x32, + 0x00, 0x02, 0x53, 0x4D, 0x42, 0x20, 0x32, 0x2E, 0x30, 0x30, + 0x32, 0x00, 0x02, 0x53, 0x4D, 0x42, 0x20, 0x32, 0x2E, 0x3F, + 0x3F, 0x3F, 0x00, +} +var NegotiateSMBv2Data2 = []byte{ + 0x00, 0x00, 0x00, 0x68, 0xFE, 0x53, 0x4D, 0x42, 0x40, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x02, +} + +func getNTLMSSPNegotiateData(Flags []byte) []byte { + return []byte{ + 0x00, 0x00, 0x00, 0x9A, 0xFE, 0x53, 0x4D, 0x42, 0x40, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x40, 0x06, 0x06, 0x2B, 0x06, 0x01, 0x05, + 0x05, 0x02, 0xA0, 0x36, 0x30, 0x34, 0xA0, 0x0E, 0x30, 0x0C, + 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, + 0x02, 0x0A, 0xA2, 0x22, 0x04, 0x20, 0x4E, 0x54, 0x4C, 0x4D, + 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, + Flags[0], Flags[1], + Flags[2], Flags[3], + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } +} diff --git a/core/report.go b/core/report.go new file mode 100644 index 0000000..0075698 --- /dev/null +++ b/core/report.go @@ -0,0 +1,17 @@ +package core + +type IReport interface { + Module() string //三个模块,每个模块一个名称 + Save() //每个模块实现保存结果到csv的方法 +} + +//type Result interface { +// ResultToString() (string, error) //probe、crack、sqlcmd都实现获取结果的接口 +//} +// + +//var ICrackMap map[string]ICrack +// +//func init() { +// ICrackMap = make(map[string]ICrack) +//} diff --git a/gologger/gologger.go b/gologger/gologger.go index 2574bfc..8504b28 100644 --- a/gologger/gologger.go +++ b/gologger/gologger.go @@ -79,8 +79,8 @@ func (l *Logger) doLog(level Level, v ...interface{}) bool { if level == 1 { l.l.Output(3, fmt.Sprintln(fmt.Sprintln(v...))) } else { - //l.l.Output(3, "\t"+level.String()+"\t"+fmt.Sprintln(fmt.Sprintln(v...))) - l.l.Output(3, fmt.Sprintln(fmt.Sprintln(v...))) + l.l.Output(3, "\t"+level.String()+"\t"+fmt.Sprintln(fmt.Sprintln(v...))) + } return true } @@ -92,8 +92,7 @@ func (l *Logger) doLogf(level Level, format string, v ...interface{}) bool { if level == 1 { l.l.Output(3, fmt.Sprintln(fmt.Sprintf(format, v...))) } else { - //l.l.Output(3, "\t"+level.String()+"\t"+fmt.Sprintln(fmt.Sprintf(format, v...))) - l.l.Output(3, fmt.Sprintln(fmt.Sprintf(format, v...))) + l.l.Output(3, "\t"+level.String()+"\t"+fmt.Sprintln(fmt.Sprintf(format, v...))) } return true @@ -113,12 +112,12 @@ func Warn(v ...interface{}) { func Error(v ...interface{}) { formatLogger.doLog(LevelError, v...) - os.Exit(2) + os.Exit(1) } func Errorf(format string, v ...interface{}) { formatLogger.doLogf(LevelError, format, v...) - os.Exit(2) + os.Exit(1) } func Warnf(format string, v ...interface{}) { diff --git a/pkg/model/model.go b/pkg/model/model.go index 6b12e7f..800f063 100644 --- a/pkg/model/model.go +++ b/pkg/model/model.go @@ -1,19 +1,5 @@ package model -type ProbeTask struct { - Ip string - Port string - ScanPlugin string -} - -type ProbeTaskResult struct { - ProbeTask ProbeTask - Result string - Err error -} - - - type SqlcmdTask struct { Ip string Port int diff --git a/pkg/model/options.go b/pkg/model/options.go index 74f4954..4a1d12e 100644 --- a/pkg/model/options.go +++ b/pkg/model/options.go @@ -8,13 +8,6 @@ type GlobalOptions struct { Delay int } -type ProbeOptions struct { - Target string - TargetFile string - Port string - ScanPlugin string -} - type Service struct { Schema string Ip string @@ -28,10 +21,6 @@ type SqlcmdOptions struct { Query string } -func NewProbeOptions() *ProbeOptions { - return &ProbeOptions{} -} - func NewSqlcmdOptions() *SqlcmdOptions { return &SqlcmdOptions{} } diff --git a/pkg/util/ParseIP_test.go b/pkg/util/ParseIP_test.go deleted file mode 100644 index e202bc9..0000000 --- a/pkg/util/ParseIP_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package util - -import ( - "cube/core/crackmodule/plugins" - "cube/pkg" - "fmt" - "testing" -) - -func TestName(t *testing.T) { - ips, _ := ParseIP("172.20.42.5/24,10.0.0.5-a", "") - fmt.Println(ips) - for _, ip := range ips { - fmt.Println(ip) - } -} - -func TestIsUpper(t *testing.T) { - r := IsUpper("ALL") - fmt.Println(r) -} - -//func TestCheckAlive(t *testing.T) { -// ips, _ := ParseIP("172.20.40.22/24", "") -// plugins := []string{"ssh"} -// //r := CheckAlive(ips, plugins, "") -// fmt.Println(r) -//} - -func TestStrXor(t *testing.T) { - r := StrXor("", "1") - fmt.Println(r) - print(r) - -} - -func TestReadipfile(t *testing.T) { - r, _ := Readipfile("/tmp/ip.txt") - fmt.Println(r) -} - -func TestReadipfile2(t *testing.T) { - ip := plugins.IpAddr{ - Ip: "172.20.40.1", - Port: "137", - Plugin: "netbios", - } - pkg.checkUDP(ip) - -} - -func TestSubset(t *testing.T) { - a := []string{"ntlm-winrm"} - b := []string{"ntlm-smb", "ntlm-wmi", "zookeeper", "oxid", "netbios", "ntlm-winrm"} - fmt.Println(Subset(a, b)) -} - -func TestBytes2Uint(t *testing.T) { - //a := []byte("\xcb\xef\xd2\xc0\xc1\xd5\x2d\x34\x35\x36\x20\x20\x20\x20\x20\x20") - //s := bytes2StringUTF16(a) - //fmt.Println(s) -} diff --git a/pkg/util/common.go b/pkg/util/common.go deleted file mode 100644 index 4781b7d..0000000 --- a/pkg/util/common.go +++ /dev/null @@ -1,212 +0,0 @@ -package util - -import ( - "bufio" - "bytes" - "cube/conf" - "cube/gologger" - "cube/pkg/model" - "fmt" - "golang.org/x/text/encoding/simplifiedchinese" - "golang.org/x/text/transform" - "io/ioutil" - "net" - "os" - "reflect" - "regexp" - "strconv" - "strings" - "sync" - "unicode/utf16" - "unicode/utf8" - "unsafe" -) - -func ValidIp(ip string) bool { - addr := strings.Trim(ip, " ") - regStr := `^(([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.)(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){2}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$` - if match, _ := regexp.MatchString(regStr, addr); match { - return true - } - return false -} - -func Split(r rune) bool { - return strings.ContainsRune("://:", r) -} - -func ParseService(str string) (service model.Service, err error) { - a := strings.FieldsFunc(str, Split) - l := len(a) - if l < 2 || l > 3 { - return service, fmt.Errorf("invalid target: %s (eg: cube sqlcmd -x mssql1://172.16.157.163:1434 -usa -p123456aa -e \"whoami\")", str) - } - - service.Schema = a[0] - service.Ip = a[1] - if !ValidIp(service.Ip) { - return service, fmt.Errorf("invalid ip: %s", service.Ip) - } - - if len(a) == 2 { - service.Port = conf.CommonPortMap[service.Schema] - } else { - service.Port, _ = strconv.Atoi(a[2]) - } - - return service, nil -} - -func FileReader(filename string) []string { - file, err := os.Open(filename) - if err != nil { - gologger.Errorf("Open file %s error, %v\n", filename, err) - } - defer file.Close() - var content []string - scanner := bufio.NewScanner(file) - scanner.Split(bufio.ScanLines) - for scanner.Scan() { - text := strings.TrimSpace(scanner.Text()) - if text != "" { - content = append(content, scanner.Text()) - } - } - return content -} - -func SameStringSlice(x, y []string) bool { - if len(x) != len(y) { - return false - } - // create a map of string -> int - diff := make(map[string]int, len(x)) - for _, _x := range x { - // 0 value for int is 0, so just increment a counter for the string - diff[_x]++ - } - for _, _y := range y { - // If the string _y is not in diff bail out early - if _, ok := diff[_y]; !ok { - return false - } - diff[_y] -= 1 - if diff[_y] == 0 { - delete(diff, _y) - } - } - return len(diff) == 0 -} - -func RemoveRepByMap(slc []string) []string { - var result []string - tempMap := map[string]byte{} // 存放不重复主键 - for _, e := range slc { - l := len(tempMap) - tempMap[e] = 0 - if len(tempMap) != l { // 加入map后,map长度变化,则元素不重复 - result = append(result, e) - } - } - return result -} - -func Subset(first, second []string) bool { - set := make(map[string]int) - for _, value := range second { - set[value] += 1 - } - - for _, value := range first { - if count, found := set[value]; !found { - return false - } else if count < 1 { - return false - } else { - set[value] = count - 1 - } - } - return true -} - -var mu *sync.RWMutex - -func WriteToFile(f *os.File, output string) error { - mu.Lock() - _, err := f.WriteString(fmt.Sprintf("%s\n", output)) - mu.Unlock() - if err != nil { - return fmt.Errorf("[!] Unable to write to file %w", err) - } - return nil -} - -func ReadBytes(conn net.Conn) (result []byte, err error) { - buf := make([]byte, 4096) - for { - count, err := conn.Read(buf) - if err != nil { - break - } - result = append(result, buf[0:count]...) - if count < 4096 { - break - } - } - return result, err -} - -func TrimName(name string) string { - return strings.TrimSpace(strings.Replace(name, "\x00", "", -1)) -} -func Bytes2Uint(bs []byte, endian byte) uint64 { - var u uint64 - if endian == '>' { - for i := 0; i < len(bs); i++ { - u += uint64(bs[i]) << (8 * (len(bs) - i - 1)) - } - } else { - for i := 0; i < len(bs); i++ { - u += uint64(bs[len(bs)-i-1]) << (8 * (len(bs) - i - 1)) - } - } - return u -} - -func RemoveDuplicate(old []string) []string { - result := make([]string, 0, len(old)) - temp := map[string]struct{}{} - for _, item := range old { - if _, ok := temp[item]; !ok { - temp[item] = struct{}{} - result = append(result, item) - } - } - return result -} - -func Bytes2StringUTF16(bs []byte) string { - ptr := (*reflect.SliceHeader)(unsafe.Pointer(&bs)) - (*ptr).Len = ptr.Len / 2 - - s := (*[]uint16)(unsafe.Pointer(&bs)) - return string(utf16.Decode(*s)) -} - -func GbkToUtf8(s []byte) ([]byte, error) { - reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewDecoder()) - d, e := ioutil.ReadAll(reader) - if e != nil { - return nil, e - } - return d, nil -} - -func ByteToString(buf []byte) (string, error) { - if utf8.Valid(buf) { - return TrimName(string(buf)), nil - } else { - s1, _ := GbkToUtf8(buf) - return TrimName(string(string(s1))), nil - } -} diff --git a/pkg/util/report.go b/pkg/util/report.go deleted file mode 100644 index efb242e..0000000 --- a/pkg/util/report.go +++ /dev/null @@ -1,26 +0,0 @@ -package util - -import ( - "unicode" -) - -func IsUpper(s string) bool { - for _, r := range s { - if !unicode.IsUpper(r) && unicode.IsLetter(r) { - return false - } - } - return true -} - -func StrXor(message string, keywords string) string { - messageLen := len(message) - keywordsLen := len(keywords) - - result := "" - - for i := 0; i < messageLen; i++ { - result += string(message[i] ^ keywords[i%keywordsLen]) - } - return result -} diff --git a/pkg/util/str_util.go b/pkg/util/str_util.go index dafdd70..2aa79d6 100644 --- a/pkg/util/str_util.go +++ b/pkg/util/str_util.go @@ -1,5 +1,28 @@ package util +import ( + "bufio" + "bytes" + "cube/config" + "cube/gologger" + "cube/pkg/model" + "fmt" + "golang.org/x/text/encoding/simplifiedchinese" + "golang.org/x/text/transform" + "io/ioutil" + "net" + "os" + "reflect" + "regexp" + "strconv" + "strings" + "sync" + "unicode" + "unicode/utf16" + "unicode/utf8" + "unsafe" +) + func Contains(str string, slice []string) bool { //str是否在slice列表里面 for _, value := range slice { @@ -9,3 +32,213 @@ func Contains(str string, slice []string) bool { } return false } + +func IsUpper(s string) bool { + for _, r := range s { + if !unicode.IsUpper(r) && unicode.IsLetter(r) { + return false + } + } + return true +} + +func StrXor(message string, keywords string) string { + messageLen := len(message) + keywordsLen := len(keywords) + + result := "" + + for i := 0; i < messageLen; i++ { + result += string(message[i] ^ keywords[i%keywordsLen]) + } + return result +} + +func ValidIp(ip string) bool { + addr := strings.Trim(ip, " ") + regStr := `^(([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.)(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){2}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$` + if match, _ := regexp.MatchString(regStr, addr); match { + return true + } + return false +} + +func Split(r rune) bool { + return strings.ContainsRune("://:", r) +} + +func ParseService(str string) (service model.Service, err error) { + a := strings.FieldsFunc(str, Split) + l := len(a) + if l < 2 || l > 3 { + return service, fmt.Errorf("invalid target: %s (eg: cube sqlcmd -x mssql1://172.16.157.163:1434 -usa -p123456aa -e \"whoami\")", str) + } + + service.Schema = a[0] + service.Ip = a[1] + if !ValidIp(service.Ip) { + return service, fmt.Errorf("invalid ip: %s", service.Ip) + } + + if len(a) == 2 { + service.Port = config.CommonPortMap[service.Schema] + } else { + service.Port, _ = strconv.Atoi(a[2]) + } + + return service, nil +} + +func FileReader(filename string) []string { + file, err := os.Open(filename) + if err != nil { + gologger.Errorf("Open file %s error, %v\n", filename, err) + } + defer file.Close() + var content []string + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + text := strings.TrimSpace(scanner.Text()) + if text != "" { + content = append(content, scanner.Text()) + } + } + return content +} + +func SameStringSlice(x, y []string) bool { + if len(x) != len(y) { + return false + } + // create a map of string -> int + diff := make(map[string]int, len(x)) + for _, _x := range x { + // 0 value for int is 0, so just increment a counter for the string + diff[_x]++ + } + for _, _y := range y { + // If the string _y is not in diff bail out early + if _, ok := diff[_y]; !ok { + return false + } + diff[_y] -= 1 + if diff[_y] == 0 { + delete(diff, _y) + } + } + return len(diff) == 0 +} + +func RemoveRepByMap(slc []string) []string { + var result []string + tempMap := map[string]byte{} // 存放不重复主键 + for _, e := range slc { + l := len(tempMap) + tempMap[e] = 0 + if len(tempMap) != l { // 加入map后,map长度变化,则元素不重复 + result = append(result, e) + } + } + return result +} + +func Subset(first, second []string) bool { + set := make(map[string]int) + for _, value := range second { + set[value] += 1 + } + + for _, value := range first { + if count, found := set[value]; !found { + return false + } else if count < 1 { + return false + } else { + set[value] = count - 1 + } + } + return true +} + +var mu *sync.RWMutex + +func WriteToFile(f *os.File, output string) error { + mu.Lock() + _, err := f.WriteString(fmt.Sprintf("%s\n", output)) + mu.Unlock() + if err != nil { + return fmt.Errorf("[!] Unable to write to file %w", err) + } + return nil +} + +func ReadBytes(conn net.Conn) (result []byte, err error) { + buf := make([]byte, 4096) + for { + count, err := conn.Read(buf) + if err != nil { + break + } + result = append(result, buf[0:count]...) + if count < 4096 { + break + } + } + return result, err +} + +func TrimName(name string) string { + return strings.TrimSpace(strings.Replace(name, "\x00", "", -1)) +} +func Bytes2Uint(bs []byte, endian byte) uint64 { + var u uint64 + if endian == '>' { + for i := 0; i < len(bs); i++ { + u += uint64(bs[i]) << (8 * (len(bs) - i - 1)) + } + } else { + for i := 0; i < len(bs); i++ { + u += uint64(bs[len(bs)-i-1]) << (8 * (len(bs) - i - 1)) + } + } + return u +} + +func RemoveDuplicate(old []string) []string { + result := make([]string, 0, len(old)) + temp := map[string]struct{}{} + for _, item := range old { + if _, ok := temp[item]; !ok { + temp[item] = struct{}{} + result = append(result, item) + } + } + return result +} + +func Bytes2StringUTF16(bs []byte) string { + ptr := (*reflect.SliceHeader)(unsafe.Pointer(&bs)) + (*ptr).Len = ptr.Len / 2 + + s := (*[]uint16)(unsafe.Pointer(&bs)) + return string(utf16.Decode(*s)) +} + +func GbkToUtf8(s []byte) ([]byte, error) { + reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewDecoder()) + d, e := ioutil.ReadAll(reader) + if e != nil { + return nil, e + } + return d, nil +} + +func ByteToString(buf []byte) (string, error) { + if utf8.Valid(buf) { + return TrimName(string(buf)), nil + } else { + s1, _ := GbkToUtf8(buf) + return TrimName(string(string(s1))), nil + } +}