diff --git a/caplets/env.go b/caplets/env.go index 257ff7e6..7d5350c9 100644 --- a/caplets/env.go +++ b/caplets/env.go @@ -8,8 +8,12 @@ import ( ) const ( - Suffix = ".cap" - InstallPath = "/usr/local/share/bettercap/caplets/" + EnvVarName = "CAPSPATH" + Suffix = ".cap" + InstallArchive = "https://github.com/bettercap/caplets/archive/master.zip" + InstallBase = "/usr/local/share/bettercap/" + InstallPathArchive = "/usr/local/share/bettercap/caplets-master/" + InstallPath = "/usr/local/share/bettercap/caplets/" ) var ( @@ -20,7 +24,7 @@ var ( ) func init() { - for _, path := range core.SepSplit(core.Trim(os.Getenv("CAPSPATH")), ":") { + for _, path := range core.SepSplit(core.Trim(os.Getenv(EnvVarName)), ":") { if path = core.Trim(path); len(path) > 0 { LoadPaths = append(LoadPaths, path) } diff --git a/caplets/manager.go b/caplets/manager.go index c094dc8d..e93ff733 100644 --- a/caplets/manager.go +++ b/caplets/manager.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "sort" "strings" "sync" @@ -38,6 +39,10 @@ func List() []Caplet { } } + sort.Slice(caplets, func(i, j int) bool { + return strings.Compare(caplets[i].Name, caplets[j].Name) == -1 + }) + return caplets } diff --git a/core/core.go b/core/core.go index 2fe2d19c..891e2099 100644 --- a/core/core.go +++ b/core/core.go @@ -1,7 +1,9 @@ package core import ( + "archive/zip" "fmt" + "io" "os" "os/exec" "os/user" @@ -102,3 +104,48 @@ func ExpandPath(path string) (string, error) { } return "", nil } + +// Unzip will decompress a zip archive, moving all files and folders +// within the zip file (parameter 1) to an output directory (parameter 2). +// Credits to https://golangcode.com/unzip-files-in-go/ +func Unzip(src string, dest string) ([]string, error) { + var filenames []string + + r, err := zip.OpenReader(src) + if err != nil { + return filenames, err + } + defer r.Close() + + for _, f := range r.File { + rc, err := f.Open() + if err != nil { + return filenames, err + } + defer rc.Close() + + // Store filename/path for returning and using later on + fpath := filepath.Join(dest, f.Name) + + // Check for ZipSlip. More Info: https://snyk.io/research/zip-slip-vulnerability#go + if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) { + return filenames, fmt.Errorf("%s: illegal file path", fpath) + } + + filenames = append(filenames, fpath) + if f.FileInfo().IsDir() { + os.MkdirAll(fpath, os.ModePerm) + } else if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { + return filenames, err + } else if outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()); err != nil { + return filenames, err + } else { + defer outFile.Close() + if _, err = io.Copy(outFile, rc); err != nil { + return filenames, err + } + } + } + + return filenames, nil +} diff --git a/main.go b/main.go index 61eae0bf..622d3dc6 100644 --- a/main.go +++ b/main.go @@ -35,6 +35,7 @@ func main() { sess.Register(modules.NewEventsStream(sess)) sess.Register(modules.NewTicker(sess)) sess.Register(modules.NewUpdateModule(sess)) + sess.Register(modules.NewCapletsModule(sess)) sess.Register(modules.NewMacChanger(sess)) sess.Register(modules.NewProber(sess)) sess.Register(modules.NewDiscovery(sess)) diff --git a/modules/caplets.go b/modules/caplets.go new file mode 100644 index 00000000..890e7009 --- /dev/null +++ b/modules/caplets.go @@ -0,0 +1,148 @@ +package modules + +import ( + "fmt" + "io" + "net/http" + "os" + + "github.com/bettercap/bettercap/caplets" + "github.com/bettercap/bettercap/core" + "github.com/bettercap/bettercap/log" + "github.com/bettercap/bettercap/session" + + "github.com/dustin/go-humanize" +) + +type CapletsModule struct { + session.SessionModule +} + +func NewCapletsModule(s *session.Session) *CapletsModule { + c := &CapletsModule{ + SessionModule: session.NewSessionModule("caplets", s), + } + + c.AddHandler(session.NewModuleHandler("caplets.show", "", + "Show a list of installed caplets.", + func(args []string) error { + return c.Show() + })) + + c.AddHandler(session.NewModuleHandler("caplets.paths", "", + "Show a list caplet search paths.", + func(args []string) error { + return c.Paths() + })) + + c.AddHandler(session.NewModuleHandler("caplets.update", "", + "Install/updates the caplets.", + func(args []string) error { + return c.Update() + })) + + return c +} + +func (c *CapletsModule) Name() string { + return "caplets" +} + +func (c *CapletsModule) Description() string { + return "A module to list and update caplets." +} + +func (c *CapletsModule) Author() string { + return "Simone Margaritelli " +} + +func (c *CapletsModule) Configure() error { + return nil +} + +func (c *CapletsModule) Stop() error { + return nil +} + +func (c *CapletsModule) Start() error { + return nil +} + +func (c *CapletsModule) Show() error { + caplets := caplets.List() + if len(caplets) == 0 { + return fmt.Errorf("no installed caplets on this system, use the caplets.update command to download them") + } + + colNames := []string{ + "Name", + "Path", + "Size", + } + rows := [][]string{} + + for _, caplet := range caplets { + rows = append(rows, []string{ + core.Bold(caplet.Name), + caplet.Path, + core.Dim(humanize.Bytes(uint64(caplet.Size))), + }) + } + + core.AsTable(os.Stdout, colNames, rows) + + return nil +} + +func (c *CapletsModule) Paths() error { + colNames := []string{ + "Path", + } + rows := [][]string{[]string{"."}} + + for _, path := range caplets.LoadPaths { + rows = append(rows, []string{path}) + } + + core.AsTable(os.Stdout, colNames, rows) + fmt.Printf("(paths can be customized by defining the %s environment variable)\n", core.Bold(caplets.EnvVarName)) + + return nil +} + +func (c *CapletsModule) Update() error { + if !core.Exists(caplets.InstallBase) { + log.Info("creating caplets install path %s ...", caplets.InstallBase) + if err := os.MkdirAll(caplets.InstallBase, os.ModePerm); err != nil { + return err + } + } + + out, err := os.Create("/tmp/caplets.zip") + if err != nil { + return err + } + defer out.Close() + + log.Info("downloading caplets from %s ...", caplets.InstallArchive) + + resp, err := http.Get(caplets.InstallArchive) + if err != nil { + return err + } + defer resp.Body.Close() + + if _, err := io.Copy(out, resp.Body); err != nil { + return err + } + + log.Info("installing caplets to %s ...", caplets.InstallPath) + + if _, err = core.Unzip("/tmp/caplets.zip", caplets.InstallBase); err != nil { + return err + } + + os.RemoveAll(caplets.InstallPath) + + return os.Rename(caplets.InstallPathArchive, caplets.InstallPath) +} diff --git a/session/events.go b/session/events.go index 5b424344..5c986073 100644 --- a/session/events.go +++ b/session/events.go @@ -63,6 +63,11 @@ func (p *EventPool) Listen() <-chan Event { // will receive all the queued events go func() { for i := len(p.events) - 1; i >= 0; i-- { + defer func() { + if recover() != nil { + + } + }() l <- p.events[i] } }()