package session import ( "bufio" "fmt" "os" "os/exec" "path/filepath" "runtime" "sort" "strconv" "strings" "time" "github.com/bettercap/bettercap/v2/core" "github.com/bettercap/bettercap/v2/log" "github.com/bettercap/bettercap/v2/network" "github.com/bettercap/readline" "github.com/evilsocket/islazy/str" "github.com/evilsocket/islazy/tui" "github.com/robertkrimen/otto" ) func (s *Session) generalHelp() { fmt.Println() maxLen := 0 for _, h := range s.CoreHandlers { len := len(h.Name) if len > maxLen { maxLen = len } } pad := "%" + strconv.Itoa(maxLen) + "s" for _, h := range s.CoreHandlers { s.Events.Printf(" "+tui.Yellow(pad)+" : %s\n", h.Name, h.Description) } s.Events.Printf("%s\n", tui.Bold("\nModules\n")) maxLen = 0 for _, m := range s.Modules { len := len(m.Name()) if len > maxLen { maxLen = len } } pad = "%" + strconv.Itoa(maxLen) + "s" for _, m := range s.Modules { status := "" if m.Running() { status = tui.Green("running") } else { status = tui.Red("not running") } s.Events.Printf(" "+tui.Yellow(pad)+" > %s\n", m.Name(), status) } fmt.Println() } func (s *Session) moduleHelp(filter string) error { err, m := s.Module(filter) if err != nil { return err } fmt.Println() status := "" if m.Running() { status = tui.Green("running") } else { status = tui.Red("not running") } s.Events.Printf("%s (%s): %s\n\n", tui.Yellow(m.Name()), status, tui.Dim(m.Description())) maxLen := 0 handlers := m.Handlers() for _, h := range handlers { len := len(h.Name) if len > maxLen { maxLen = len } } for _, h := range handlers { s.Events.Printf("%s", h.Help(maxLen)) } fmt.Println() params := m.Parameters() if len(params) > 0 { v := make([]*ModuleParam, 0) maxLen := 0 for _, h := range params { len := len(h.Name) if len > maxLen { maxLen = len } v = append(v, h) } sort.Slice(v, func(i, j int) bool { return v[i].Name < v[j].Name }) s.Events.Printf(" Parameters\n\n") for _, p := range v { s.Events.Printf("%s", p.Help(maxLen)) } fmt.Println() } return nil } func (s *Session) helpHandler(args []string, sess *Session) error { filter := "" if len(args) == 2 { filter = str.Trim(args[1]) } if filter == "" { s.generalHelp() } else { if err := s.moduleHelp(filter); err != nil { return err } } return nil } func (s *Session) activeHandler(args []string, sess *Session) error { for _, m := range s.Modules { if !m.Running() { continue } s.Events.Printf("%s (%s)\n", tui.Bold(m.Name()), tui.Dim(m.Description())) params := m.Parameters() if len(params) > 0 { fmt.Println() for _, p := range params { _, val := s.Env.Get(p.Name) s.Events.Printf(" %s : %s\n", tui.Yellow(p.Name), val) } } fmt.Println() } return nil } func (s *Session) exitHandler(args []string, sess *Session) error { if s.script != nil { if s.script.Plugin.HasFunc("onExit") { if _, err := s.script.Plugin.Call("onExit"); err != nil { log.Error("Error while executing onExit callback: %s", "\nTraceback:\n "+err.(*otto.Error).String()) } } } // notify any listener that the session is about to end s.Events.Add("session.stopped", nil) for _, mod := range s.Modules { if mod.Running() { mod.Stop() } } s.Active = false s.Input.Close() return nil } func (s *Session) sleepHandler(args []string, sess *Session) error { if secs, err := strconv.Atoi(args[0]); err == nil { time.Sleep(time.Duration(secs) * time.Second) return nil } else { return err } } func (s *Session) getHandler(args []string, sess *Session) error { key := args[0] if strings.Contains(key, "*") { prev_ns := "" fmt.Println() last := len(key) - 1 prefix := key[:last] sortedKeys := s.Env.Sorted() padding := 0 for _, k := range sortedKeys { l := len(k) if l > padding { padding = l } } for _, k := range sortedKeys { if strings.HasPrefix(k, prefix) { ns := "" toks := strings.Split(k, ".") if len(toks) > 0 { ns = toks[0] } if ns != prev_ns { fmt.Println() prev_ns = ns } s.Events.Printf(" %"+strconv.Itoa(padding)+"s: '%s'\n", k, s.Env.Data[k]) } } fmt.Println() } else if found, value := s.Env.Get(key); found { fmt.Println() s.Events.Printf(" %s: '%s'\n", key, value) fmt.Println() } else { return fmt.Errorf("%s not found", key) } return nil } func (s *Session) setHandler(args []string, sess *Session) error { key := args[0] value := args[1] if value == "\"\"" || value == "''" { value = "" } s.Env.Set(key, value) return nil } func (s *Session) readHandler(args []string, sess *Session) error { key := args[0] prompt := args[1] reader := bufio.NewReader(os.Stdin) fmt.Printf("%s ", prompt) value, _ := reader.ReadString('\n') value = str.Trim(value) if value == "\"\"" || value == "''" { value = "" } s.Env.Set(key, value) return nil } func (s *Session) clsHandler(args []string, sess *Session) error { cmd := "clear" if runtime.GOOS == "windows" { cmd = "cls" } c := exec.Command(cmd) c.Stdout = os.Stdout c.Run() return nil } func (s *Session) includeHandler(args []string, sess *Session) error { return s.RunCaplet(args[0]) } func (s *Session) shHandler(args []string, sess *Session) error { out, err := core.Shell(args[0]) if err == nil { s.Events.Printf("%s\n", out) } return err } func normalizeMac(mac string) string { var parts []string if strings.ContainsRune(mac, '-') { parts = strings.Split(mac, "-") } else { parts = strings.Split(mac, ":") } for i, p := range parts { if len(p) < 2 { parts[i] = "0" + p } } return strings.ToLower(strings.Join(parts, ":")) } func (s *Session) propagateAlias(mac, alias string) { mac = normalizeMac(mac) s.Aliases.Set(mac, alias) if dev, found := s.BLE.Get(mac); found { dev.Alias = alias } if dev, found := s.HID.Get(mac); found { dev.Alias = alias } if ap, found := s.WiFi.Get(mac); found { ap.Alias = alias } if sta, found := s.WiFi.GetClient(mac); found { sta.Alias = alias } if host, found := s.Lan.Get(mac); found { host.Alias = alias } } func (s *Session) aliasHandler(args []string, sess *Session) error { mac := args[0] alias := str.Trim(args[1]) if alias == "\"\"" || alias == "''" { alias = "" } s.propagateAlias(mac, alias) return nil } func (s *Session) addHandler(h CommandHandler, c *readline.PrefixCompleter) { h.Completer = c s.CoreHandlers = append(s.CoreHandlers, h) } func (s *Session) registerCoreHandlers() { s.addHandler(NewCommandHandler("help MODULE", "^(help|\\?)(.*)$", "List available commands or show module specific help if no module name is provided.", s.helpHandler), readline.PcItem("help", readline.PcItemDynamic(func(prefix string) []string { prefix = str.Trim(prefix[4:]) modNames := []string{""} for _, m := range s.Modules { if prefix == "" || strings.HasPrefix(m.Name(), prefix) { modNames = append(modNames, m.Name()) } } return modNames }))) s.addHandler(NewCommandHandler("active", "^active$", "Show information about active modules.", s.activeHandler), readline.PcItem("active")) s.addHandler(NewCommandHandler("quit", "^(q|quit|e|exit)$", "Close the session and exit.", s.exitHandler), readline.PcItem("quit")) s.addHandler(NewCommandHandler("sleep SECONDS", "^sleep\\s+(\\d+)$", "Sleep for the given amount of seconds.", s.sleepHandler), readline.PcItem("sleep")) s.addHandler(NewCommandHandler("get NAME", "^get\\s+(.+)", "Get the value of variable NAME, use * alone for all, or NAME* as a wildcard.", s.getHandler), readline.PcItem("get", readline.PcItemDynamic(func(prefix string) []string { prefix = str.Trim(prefix[3:]) varNames := []string{""} for key := range s.Env.Data { if prefix == "" || strings.HasPrefix(key, prefix) { varNames = append(varNames, key) } } return varNames }))) s.addHandler(NewCommandHandler("set NAME VALUE", "^set\\s+([^\\s]+)\\s+(.+)", "Set the VALUE of variable NAME.", s.setHandler), readline.PcItem("set", readline.PcItemDynamic(func(prefix string) []string { prefix = str.Trim(prefix[3:]) varNames := []string{""} for key := range s.Env.Data { if prefix == "" || strings.HasPrefix(key, prefix) { varNames = append(varNames, key) } } return varNames }))) s.addHandler(NewCommandHandler("read VARIABLE PROMPT", `^read\s+([^\s]+)\s+(.+)$`, "Show a PROMPT to ask the user for input that will be saved inside VARIABLE.", s.readHandler), readline.PcItem("read")) s.addHandler(NewCommandHandler("clear", "^(clear|cls)$", "Clear the screen.", s.clsHandler), readline.PcItem("clear")) s.addHandler(NewCommandHandler("include CAPLET", "^include\\s+(.+)", "Load and run this caplet in the current session.", s.includeHandler), readline.PcItem("include", readline.PcItemDynamic(func(prefix string) []string { prefix = str.Trim(prefix[8:]) if prefix == "" { prefix = "." } files, _ := filepath.Glob(prefix + "*") return files }))) s.addHandler(NewCommandHandler("! COMMAND", "^!\\s*(.+)$", "Execute a shell command and print its output.", s.shHandler), readline.PcItem("!")) s.addHandler(NewCommandHandler("alias MAC NAME", "^alias\\s+([a-fA-F0-9:]{14,17})\\s*(.*)", "Assign an alias to a given endpoint given its MAC address.", s.aliasHandler), readline.PcItem("alias", readline.PcItemDynamic(func(prefix string) []string { prefix = str.Trim(prefix[5:]) macs := []string{""} s.Lan.EachHost(func(mac string, e *network.Endpoint) { if prefix == "" || strings.HasPrefix(mac, prefix) { macs = append(macs, mac) } }) return macs }))) }