mirror of
https://github.com/bettercap/bettercap
synced 2025-07-29 19:20:00 -07:00
fix: working directory is temporarily switched when running a caplet in order to properly load/include files relative to the caplet itself
This commit is contained in:
parent
9a3a514f6a
commit
f213d0d0ca
4 changed files with 265 additions and 254 deletions
|
@ -3,7 +3,6 @@ package session
|
|||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
@ -22,7 +21,6 @@ type Caplet struct {
|
|||
}
|
||||
|
||||
var (
|
||||
CapletsTree = make(map[string][]string)
|
||||
CapletLoadPaths = []string{
|
||||
"./caplets/",
|
||||
"/usr/share/bettercap/caplets/",
|
||||
|
@ -32,20 +30,6 @@ var (
|
|||
cacheLock = sync.Mutex{}
|
||||
)
|
||||
|
||||
func buildCapletsTree(path string, prefix string) {
|
||||
files, _ := ioutil.ReadDir(path)
|
||||
for _, file := range files {
|
||||
filename := file.Name()
|
||||
if strings.HasSuffix(filename, CapletSuffix) {
|
||||
base := strings.TrimPrefix(path, prefix)
|
||||
name := strings.Replace(filename, CapletSuffix, "", -1)
|
||||
CapletsTree[base+name] = []string{}
|
||||
} else if file.IsDir() {
|
||||
buildCapletsTree(filepath.Join(path, filename)+"/", prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
for _, path := range core.SepSplit(core.Trim(os.Getenv("CAPSPATH")), ":") {
|
||||
if path = core.Trim(path); len(path) > 0 {
|
||||
|
@ -56,10 +40,6 @@ func init() {
|
|||
for i, path := range CapletLoadPaths {
|
||||
CapletLoadPaths[i], _ = filepath.Abs(path)
|
||||
}
|
||||
|
||||
for _, path := range CapletLoadPaths {
|
||||
buildCapletsTree(path, path)
|
||||
}
|
||||
}
|
||||
|
||||
func LoadCaplet(name string) (error, *Caplet) {
|
||||
|
@ -133,3 +113,41 @@ func parseCapletCommand(line string) (is bool, caplet *Caplet, argv []string) {
|
|||
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
func (cap *Caplet) Eval(s *Session, argv []string) error {
|
||||
// the caplet might include other files (include directive, proxy modules, etc),
|
||||
// temporarily change the working directory
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while getting current working directory: %v", err)
|
||||
}
|
||||
|
||||
capPath := filepath.Dir(cap.Path)
|
||||
if err := os.Chdir(capPath); err != nil {
|
||||
return fmt.Errorf("error while changing current working directory: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := os.Chdir(cwd); err != nil {
|
||||
s.Events.Log(core.ERROR, "error while restoring working directory: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if argv == nil {
|
||||
argv = []string{}
|
||||
}
|
||||
|
||||
for _, line := range cap.Code {
|
||||
// replace $0 with argv[0], $1 with argv[1] and so on
|
||||
for i, arg := range argv {
|
||||
what := fmt.Sprintf("$%d", i)
|
||||
line = strings.Replace(line, what, arg, -1)
|
||||
}
|
||||
|
||||
if err = s.Run(line + "\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -5,13 +5,10 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/bettercap/readline"
|
||||
|
@ -65,70 +62,6 @@ type Session struct {
|
|||
UnkCmdCallback UnknownCommandCallback `json:"-"`
|
||||
}
|
||||
|
||||
func ParseCommands(line string) []string {
|
||||
args := []string{}
|
||||
buf := ""
|
||||
|
||||
singleQuoted := false
|
||||
doubleQuoted := false
|
||||
finish := false
|
||||
|
||||
for _, c := range line {
|
||||
switch c {
|
||||
case ';':
|
||||
if !singleQuoted && !doubleQuoted {
|
||||
finish = true
|
||||
} else {
|
||||
buf += string(c)
|
||||
}
|
||||
|
||||
case '"':
|
||||
if doubleQuoted {
|
||||
// finish of quote
|
||||
doubleQuoted = false
|
||||
} else if singleQuoted {
|
||||
// quote initiated with ', so we ignore it
|
||||
buf += string(c)
|
||||
} else {
|
||||
// quote init here
|
||||
doubleQuoted = true
|
||||
}
|
||||
|
||||
case '\'':
|
||||
if singleQuoted {
|
||||
singleQuoted = false
|
||||
} else if doubleQuoted {
|
||||
buf += string(c)
|
||||
} else {
|
||||
singleQuoted = true
|
||||
}
|
||||
|
||||
default:
|
||||
buf += string(c)
|
||||
}
|
||||
|
||||
if finish {
|
||||
args = append(args, buf)
|
||||
finish = false
|
||||
buf = ""
|
||||
}
|
||||
}
|
||||
|
||||
if len(buf) > 0 {
|
||||
args = append(args, buf)
|
||||
}
|
||||
|
||||
cmds := make([]string, 0)
|
||||
for _, cmd := range args {
|
||||
cmd = core.Trim(cmd)
|
||||
if cmd != "" || (len(cmd) > 0 && cmd[0] != '#') {
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
return cmds
|
||||
}
|
||||
|
||||
func New() (*Session, error) {
|
||||
var err error
|
||||
|
||||
|
@ -182,70 +115,6 @@ func (s *Session) Module(name string) (err error, mod Module) {
|
|||
return fmt.Errorf("Module %s not found", name), mod
|
||||
}
|
||||
|
||||
func (s *Session) setupReadline() error {
|
||||
var err error
|
||||
|
||||
pcompleters := make([]readline.PrefixCompleterInterface, 0)
|
||||
for _, h := range s.CoreHandlers {
|
||||
if h.Completer == nil {
|
||||
pcompleters = append(pcompleters, readline.PcItem(h.Name))
|
||||
} else {
|
||||
pcompleters = append(pcompleters, h.Completer)
|
||||
}
|
||||
}
|
||||
|
||||
tree := make(map[string][]string)
|
||||
for _, m := range s.Modules {
|
||||
for _, h := range m.Handlers() {
|
||||
parts := strings.Split(h.Name, " ")
|
||||
name := parts[0]
|
||||
|
||||
if _, found := tree[name]; !found {
|
||||
tree[name] = []string{}
|
||||
}
|
||||
|
||||
var appendedOption = strings.Join(parts[1:], " ")
|
||||
|
||||
if len(appendedOption) > 0 && !containsCapitals(appendedOption) {
|
||||
tree[name] = append(tree[name], appendedOption)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for root, subElems := range CapletsTree {
|
||||
item := readline.PcItem(root)
|
||||
item.Children = []readline.PrefixCompleterInterface{}
|
||||
for _, child := range subElems {
|
||||
item.Children = append(item.Children, readline.PcItem(child))
|
||||
}
|
||||
pcompleters = append(pcompleters, item)
|
||||
}
|
||||
|
||||
history := ""
|
||||
if !*s.Options.NoHistory {
|
||||
history, _ = core.ExpandPath(HistoryFile)
|
||||
}
|
||||
|
||||
cfg := readline.Config{
|
||||
HistoryFile: history,
|
||||
InterruptPrompt: "^C",
|
||||
EOFPrompt: "exit",
|
||||
AutoComplete: readline.NewPrefixCompleter(pcompleters...),
|
||||
}
|
||||
|
||||
s.Input, err = readline.NewEx(&cfg)
|
||||
return err
|
||||
}
|
||||
|
||||
func containsCapitals(s string) bool {
|
||||
for _, ch := range s {
|
||||
if ch < 133 && ch > 101 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Session) Close() {
|
||||
if *s.Options.Debug {
|
||||
fmt.Printf("\nStopping modules and cleaning session state ...\n")
|
||||
|
@ -290,77 +159,6 @@ func (s *Session) Register(mod Module) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) startNetMon() {
|
||||
// keep reading network events in order to add / update endpoints
|
||||
go func() {
|
||||
for event := range s.Queue.Activities {
|
||||
if !s.Active {
|
||||
return
|
||||
}
|
||||
|
||||
if s.IsOn("net.recon") && event.Source {
|
||||
addr := event.IP.String()
|
||||
mac := event.MAC.String()
|
||||
|
||||
existing := s.Lan.AddIfNew(addr, mac)
|
||||
if existing != nil {
|
||||
existing.LastSeen = time.Now()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *Session) setupSignals() {
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-c
|
||||
fmt.Println()
|
||||
s.Events.Log(core.WARNING, "Got SIGTERM")
|
||||
s.Close()
|
||||
os.Exit(0)
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *Session) setupEnv() {
|
||||
s.Env.Set("iface.index", fmt.Sprintf("%d", s.Interface.Index))
|
||||
s.Env.Set("iface.name", s.Interface.Name())
|
||||
s.Env.Set("iface.ipv4", s.Interface.IpAddress)
|
||||
s.Env.Set("iface.ipv6", s.Interface.Ip6Address)
|
||||
s.Env.Set("iface.mac", s.Interface.HwAddress)
|
||||
s.Env.Set("gateway.address", s.Gateway.IpAddress)
|
||||
s.Env.Set("gateway.mac", s.Gateway.HwAddress)
|
||||
|
||||
if found, v := s.Env.Get(PromptVariable); !found || v == "" {
|
||||
s.Env.Set(PromptVariable, DefaultPrompt)
|
||||
}
|
||||
|
||||
dbg := "false"
|
||||
if *s.Options.Debug {
|
||||
dbg = "true"
|
||||
}
|
||||
s.Env.WithCallback("log.debug", dbg, func(newValue string) {
|
||||
newDbg := false
|
||||
if newValue == "true" {
|
||||
newDbg = true
|
||||
}
|
||||
s.Events.SetDebug(newDbg)
|
||||
})
|
||||
|
||||
silent := "false"
|
||||
if *s.Options.Silent {
|
||||
silent = "true"
|
||||
}
|
||||
s.Env.WithCallback("log.silent", silent, func(newValue string) {
|
||||
newSilent := false
|
||||
if newValue == "true" {
|
||||
newSilent = true
|
||||
}
|
||||
s.Events.SetSilent(newSilent)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Session) Start() error {
|
||||
var err error
|
||||
|
||||
|
@ -445,19 +243,6 @@ func (s *Session) IsOn(moduleName string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (s *Session) parseEnvTokens(str string) (string, error) {
|
||||
// replace all {env.something} with their values
|
||||
for _, m := range reEnvVarCapture.FindAllString(str, -1) {
|
||||
varName := strings.Trim(strings.Replace(m, "env.", "", -1), "{}")
|
||||
if found, value := s.Env.Get(varName); found {
|
||||
str = strings.Replace(str, m, value, -1)
|
||||
} else {
|
||||
return "", fmt.Errorf("variable '%s' is not defined", varName)
|
||||
}
|
||||
}
|
||||
return str, nil
|
||||
}
|
||||
|
||||
func (s *Session) Refresh() {
|
||||
p, _ := s.parseEnvTokens(s.Prompt.Render(s))
|
||||
s.Input.SetPrompt(p)
|
||||
|
@ -475,13 +260,7 @@ func (s *Session) RunCaplet(filename string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
for _, line := range caplet.Code {
|
||||
if err = s.Run(line + "\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return caplet.Eval(s, nil)
|
||||
}
|
||||
|
||||
func (s *Session) Run(line string) error {
|
||||
|
@ -491,6 +270,7 @@ func (s *Session) Run(line string) error {
|
|||
// to 'arp.spoof on' (fixes #178)
|
||||
line = reCmdSpaceCleaner.ReplaceAllString(line, "$1 $2")
|
||||
|
||||
// replace all {env.something} with their values
|
||||
line, err := s.parseEnvTokens(line)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -514,18 +294,7 @@ func (s *Session) Run(line string) error {
|
|||
|
||||
// is it a caplet command?
|
||||
if parsed, caplet, argv := parseCapletCommand(line); parsed {
|
||||
for _, line := range caplet.Code {
|
||||
// replace $0 with argv[0], $1 with argv[1] and so on
|
||||
for i, arg := range argv {
|
||||
line = strings.Replace(line, fmt.Sprintf("$%d", i), arg, -1)
|
||||
}
|
||||
|
||||
if err = s.Run(line + "\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return caplet.Eval(s, argv)
|
||||
}
|
||||
|
||||
// is it a proxy module custom command?
|
||||
|
|
84
session/session_parse.go
Normal file
84
session/session_parse.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
package session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/bettercap/bettercap/core"
|
||||
)
|
||||
|
||||
func ParseCommands(line string) []string {
|
||||
args := []string{}
|
||||
buf := ""
|
||||
|
||||
singleQuoted := false
|
||||
doubleQuoted := false
|
||||
finish := false
|
||||
|
||||
for _, c := range line {
|
||||
switch c {
|
||||
case ';':
|
||||
if !singleQuoted && !doubleQuoted {
|
||||
finish = true
|
||||
} else {
|
||||
buf += string(c)
|
||||
}
|
||||
|
||||
case '"':
|
||||
if doubleQuoted {
|
||||
// finish of quote
|
||||
doubleQuoted = false
|
||||
} else if singleQuoted {
|
||||
// quote initiated with ', so we ignore it
|
||||
buf += string(c)
|
||||
} else {
|
||||
// quote init here
|
||||
doubleQuoted = true
|
||||
}
|
||||
|
||||
case '\'':
|
||||
if singleQuoted {
|
||||
singleQuoted = false
|
||||
} else if doubleQuoted {
|
||||
buf += string(c)
|
||||
} else {
|
||||
singleQuoted = true
|
||||
}
|
||||
|
||||
default:
|
||||
buf += string(c)
|
||||
}
|
||||
|
||||
if finish {
|
||||
args = append(args, buf)
|
||||
finish = false
|
||||
buf = ""
|
||||
}
|
||||
}
|
||||
|
||||
if len(buf) > 0 {
|
||||
args = append(args, buf)
|
||||
}
|
||||
|
||||
cmds := make([]string, 0)
|
||||
for _, cmd := range args {
|
||||
cmd = core.Trim(cmd)
|
||||
if cmd != "" || (len(cmd) > 0 && cmd[0] != '#') {
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
return cmds
|
||||
}
|
||||
|
||||
func (s *Session) parseEnvTokens(str string) (string, error) {
|
||||
for _, m := range reEnvVarCapture.FindAllString(str, -1) {
|
||||
varName := strings.Trim(strings.Replace(m, "env.", "", -1), "{}")
|
||||
if found, value := s.Env.Get(varName); found {
|
||||
str = strings.Replace(str, m, value, -1)
|
||||
} else {
|
||||
return "", fmt.Errorf("variable '%s' is not defined", varName)
|
||||
}
|
||||
}
|
||||
return str, nil
|
||||
}
|
140
session/session_setup.go
Normal file
140
session/session_setup.go
Normal file
|
@ -0,0 +1,140 @@
|
|||
package session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/bettercap/readline"
|
||||
|
||||
"github.com/bettercap/bettercap/core"
|
||||
)
|
||||
|
||||
func containsCapitals(s string) bool {
|
||||
for _, ch := range s {
|
||||
if ch < 133 && ch > 101 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Session) setupReadline() error {
|
||||
var err error
|
||||
|
||||
pcompleters := make([]readline.PrefixCompleterInterface, 0)
|
||||
for _, h := range s.CoreHandlers {
|
||||
if h.Completer == nil {
|
||||
pcompleters = append(pcompleters, readline.PcItem(h.Name))
|
||||
} else {
|
||||
pcompleters = append(pcompleters, h.Completer)
|
||||
}
|
||||
}
|
||||
|
||||
tree := make(map[string][]string)
|
||||
for _, m := range s.Modules {
|
||||
for _, h := range m.Handlers() {
|
||||
parts := strings.Split(h.Name, " ")
|
||||
name := parts[0]
|
||||
|
||||
if _, found := tree[name]; !found {
|
||||
tree[name] = []string{}
|
||||
}
|
||||
|
||||
var appendedOption = strings.Join(parts[1:], " ")
|
||||
|
||||
if len(appendedOption) > 0 && !containsCapitals(appendedOption) {
|
||||
tree[name] = append(tree[name], appendedOption)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
history := ""
|
||||
if !*s.Options.NoHistory {
|
||||
history, _ = core.ExpandPath(HistoryFile)
|
||||
}
|
||||
|
||||
cfg := readline.Config{
|
||||
HistoryFile: history,
|
||||
InterruptPrompt: "^C",
|
||||
EOFPrompt: "exit",
|
||||
AutoComplete: readline.NewPrefixCompleter(pcompleters...),
|
||||
}
|
||||
|
||||
s.Input, err = readline.NewEx(&cfg)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Session) startNetMon() {
|
||||
// keep reading network events in order to add / update endpoints
|
||||
go func() {
|
||||
for event := range s.Queue.Activities {
|
||||
if !s.Active {
|
||||
return
|
||||
}
|
||||
|
||||
if s.IsOn("net.recon") && event.Source {
|
||||
addr := event.IP.String()
|
||||
mac := event.MAC.String()
|
||||
|
||||
existing := s.Lan.AddIfNew(addr, mac)
|
||||
if existing != nil {
|
||||
existing.LastSeen = time.Now()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *Session) setupSignals() {
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-c
|
||||
fmt.Println()
|
||||
s.Events.Log(core.WARNING, "Got SIGTERM")
|
||||
s.Close()
|
||||
os.Exit(0)
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *Session) setupEnv() {
|
||||
s.Env.Set("iface.index", fmt.Sprintf("%d", s.Interface.Index))
|
||||
s.Env.Set("iface.name", s.Interface.Name())
|
||||
s.Env.Set("iface.ipv4", s.Interface.IpAddress)
|
||||
s.Env.Set("iface.ipv6", s.Interface.Ip6Address)
|
||||
s.Env.Set("iface.mac", s.Interface.HwAddress)
|
||||
s.Env.Set("gateway.address", s.Gateway.IpAddress)
|
||||
s.Env.Set("gateway.mac", s.Gateway.HwAddress)
|
||||
|
||||
if found, v := s.Env.Get(PromptVariable); !found || v == "" {
|
||||
s.Env.Set(PromptVariable, DefaultPrompt)
|
||||
}
|
||||
|
||||
dbg := "false"
|
||||
if *s.Options.Debug {
|
||||
dbg = "true"
|
||||
}
|
||||
s.Env.WithCallback("log.debug", dbg, func(newValue string) {
|
||||
newDbg := false
|
||||
if newValue == "true" {
|
||||
newDbg = true
|
||||
}
|
||||
s.Events.SetDebug(newDbg)
|
||||
})
|
||||
|
||||
silent := "false"
|
||||
if *s.Options.Silent {
|
||||
silent = "true"
|
||||
}
|
||||
s.Env.WithCallback("log.silent", silent, func(newValue string) {
|
||||
newSilent := false
|
||||
if newValue == "true" {
|
||||
newSilent = true
|
||||
}
|
||||
s.Events.SetSilent(newSilent)
|
||||
})
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue