Commit 9157ca98 authored by Sophie Brun's avatar Sophie Brun

Update upstream source from tag 'upstream/2.10'

Update to upstream version '2.10'
with Debian dir 14ce47a21cb592b391a2bab590900ff1c0d36577
parents 9ef7cf05 171ceb97
......@@ -2,7 +2,6 @@
*.tar.gz
*.prof*
pcaps
caplets
build
bettercap
bettercap.history
......@@ -48,19 +48,19 @@
name = "github.com/dustin/go-humanize"
packages = ["."]
pruneopts = "UT"
revision = "02af3965c54e8cacf948b97fef38925c4120652c"
revision = "9f541cc9db5d55bce703bd99987c9d5cb8eea45e"
[[projects]]
branch = "master"
digest = "1:744c8fceaf0e0384d75e2371debd4eda76b1404768328d03a184f1d08be5a60f"
digest = "1:2b7b174ae68705866555b73fd848de0749b93b1f99e3295e27f89bebe8702203"
name = "github.com/elazarl/goproxy"
packages = ["."]
pruneopts = "UT"
revision = "a96fa3a318260eab29abaf32f7128c9eb07fb073"
revision = "947c36da3153ff334e74d9d980de341d25f358ba"
[[projects]]
branch = "master"
digest = "1:911bcb1598df4c960dd9e0a62443d2ac3fa33111ef691937af3bd1ed7ecdebdd"
digest = "1:f46dd7c5783c6caa5ea1072eb4165446c58651ff0849c9d366ac318f140661fa"
name = "github.com/gobwas/glob"
packages = [
".",
......@@ -73,7 +73,7 @@
"util/strings",
]
pruneopts = "UT"
revision = "f00a7392b43971b2fdb562418faab1f18da2067a"
revision = "f756513aec94125582ee6c0dc94179251ef87370"
[[projects]]
digest = "1:51bee9f1987dcdb9f9a1b4c20745d78f6bf6f5f14ad4e64ca883eb64df4c0045"
......@@ -105,28 +105,28 @@
version = "v1.1.14"
[[projects]]
digest = "1:160eabf7a69910fd74f29c692718bc2437c1c1c7d4c9dea9712357752a70e5df"
digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1"
name = "github.com/gorilla/context"
packages = ["."]
pruneopts = "UT"
revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a"
version = "v1.1"
revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42"
version = "v1.1.1"
[[projects]]
digest = "1:88aa9e326e2bd6045a46e00a922954b3e1a9ac5787109f49ac85366df370e1e5"
digest = "1:e73f5b0152105f18bc131fba127d9949305c8693f8a762588a82a48f61756f5f"
name = "github.com/gorilla/mux"
packages = ["."]
pruneopts = "UT"
revision = "53c1911da2b537f792e7cafcb446b05ffe33b996"
version = "v1.6.1"
revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf"
version = "v1.6.2"
[[projects]]
digest = "1:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e"
digest = "1:7b5c6e2eeaa9ae5907c391a91c132abfd5c9e8a784a341b5625e750c67e6825d"
name = "github.com/gorilla/websocket"
packages = ["."]
pruneopts = "UT"
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
version = "v1.2.0"
revision = "66b9c49e59c6c48f0ffce28c2d8b8a5678502c6d"
version = "v1.4.0"
[[projects]]
branch = "master"
......@@ -138,11 +138,11 @@
[[projects]]
branch = "master"
digest = "1:031558139b6ee2fb7c1bb9f14bc9f394c9ab94c1722897292622d20d5039b246"
digest = "1:afaa86f5ab9275b1c5cccb3343fa3b7ea77045572a59e9f97d36fd67d14fbe62"
name = "github.com/jpillora/go-tld"
packages = ["."]
pruneopts = "UT"
revision = "a31ae10e978ab5f352c5dad2cfbd60546dcea75f"
revision = "4bfc8d9a90b591e101a56265afc2239359fb0810"
[[projects]]
digest = "1:4701b2acabe16722ecb1e387d39741a29269386bfc4ba6283ecda362d289eff1"
......@@ -161,12 +161,12 @@
version = "v0.0.9"
[[projects]]
digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb"
digest = "1:0981502f9816113c9c8c4ac301583841855c8cf4da8c72f696b3ebedf6d0e4e5"
name = "github.com/mattn/go-isatty"
packages = ["."]
pruneopts = "UT"
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
revision = "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c"
version = "v0.0.4"
[[projects]]
branch = "master"
......@@ -218,23 +218,23 @@
"token",
]
pruneopts = "UT"
revision = "03d472dc43abece8691e609a23d295ab732abba6"
revision = "15f95af6e78dcd2030d8195a138bd88d4f403546"
[[projects]]
branch = "master"
digest = "1:b9b666a56e920eaa59ffea0d25a9b848d7073be13689c4a29d04e4a0f548a031"
digest = "1:52b21e6be25049834aea5ecdde35d723c00fbdad3ea0357f2072dfb105836e02"
name = "github.com/tarm/serial"
packages = ["."]
pruneopts = "UT"
revision = "eaafced92e9619f03c72527efeab21e326f3bc36"
revision = "98f6abe2eb07edd42f6dfa2a934aea469acc29b7"
[[projects]]
branch = "master"
digest = "1:32f55762862902f08c5d28ed59fbe86cd0c38b088473ed072807467ebadc3f15"
digest = "1:6eb2645d74b43d9c87b51947df39f7c668a4f422cd512053f7f6f75bfaad0197"
name = "golang.org/x/sys"
packages = ["unix"]
pruneopts = "UT"
revision = "d0faeb539838e250bd0a9db4182d48d4a1915181"
revision = "d0be0721c37eeb5299f245a996a483160fc36940"
[[projects]]
digest = "1:9935525a8c49b8434a0b0a54e1980e94a6fae73aaff45c5d33ba8dff69de123e"
......
......@@ -21,8 +21,6 @@ clean:
install:
@mkdir -p /usr/local/share/bettercap/caplets
@cp bettercap /usr/local/bin/
@cp bettercap.service /etc/systemd/system/
@systemctl daemon-reload
docker:
@docker build -t bettercap:latest .
......
......@@ -18,17 +18,25 @@ A [precompiled version is available](https://github.com/bettercap/bettercap/rele
Make sure you have a correctly configured **Go >= 1.8** environment, that `$GOPATH/bin` is in `$PATH`, that the `libpcap-dev` and `libnetfilter-queue-dev` (this one is only required on Linux) package installed for your system and then:
$ go get github.com/bettercap/bettercap
$ cd $GOPATH/src/github.com/bettercap/bettercap
$ make build && sudo make install
This command will download bettercap, install its dependencies, compile it and move the `bettercap` executable to `$GOPATH/bin`.
This command will download bettercap, install its dependencies, compile it and move the `bettercap` executable to `/usr/local/bin`.
Now you can use `sudo bettercap -h` to show the basic command line options and just `sudo bettercap` to start an
[interactive session](https://github.com/bettercap/bettercap/wiki/Interactive-Mode) on your default network interface, otherwise you can [load a caplet](https://github.com/bettercap/bettercap/wiki/Caplets) from [the dedicated repository](https://github.com/bettercap/caplets).
[interactive session](https://github.com/bettercap/bettercap/wiki/Interactive-Mode) on your default network interface, otherwise you can [load a caplet](https://github.com/bettercap/bettercap/wiki/Caplets).
Once bettercap is installed, you can download/update system caplet with the command:
sudo bettercap -eval "caplets.update; q"
## Update
In order to update to an unstable but bleeding edge release from this repository, run the command below:
In order to update to an unstable but bleeding edge release from this repository, run the commands below:
$ go get -u github.com/bettercap/bettercap
$ cd $GOPATH/src/github.com/bettercap/bettercap
$ make build && sudo make install
## Documentation and Examples
......
......@@ -7,7 +7,7 @@ After=network.target
[Service]
Type=simple
PermissionsStartOnly=true
ExecStart=/usr/local/bin/bettercap -autostart "events.stream, net.recon, api.rest" -no-colors -eval "set events.stream.output /var/log/bettercap.log"
ExecStart=/usr/local/bin/bettercap -no-colors -eval "set events.stream.output /var/log/bettercap.log; api.rest on"
Restart=always
RestartSec=30
......
package caplets
import (
"fmt"
"os"
"path/filepath"
"strings"
)
type Caplet struct {
Name string
Path string
Size int64
Code []string
}
func (cap *Caplet) Eval(argv []string, lineCb func(line string) error) 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 {
fmt.Printf("error while restoring working directory: %v\n", 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 = lineCb(line); err != nil {
return err
}
}
return nil
}
// Package caplets contains functions to enumerate, load and execute caplets.
package caplets
package caplets
import (
"os"
"path/filepath"
"github.com/bettercap/bettercap/core"
)
const (
EnvVarName = "CAPSPATH"
Suffix = ".cap"
InstallArchive = "https://github.com/bettercap/caplets/archive/master.zip"
InstallBase = "/usr/local/share/bettercap/"
)
var (
InstallPathArchive = filepath.Join(InstallBase, "caplets-master")
InstallPath = filepath.Join(InstallBase, "caplets")
LoadPaths = []string{
"./",
"./caplets/",
InstallPath,
}
)
func init() {
for _, path := range core.SepSplit(core.Trim(os.Getenv(EnvVarName)), ":") {
if path = core.Trim(path); len(path) > 0 {
LoadPaths = append(LoadPaths, path)
}
}
for i, path := range LoadPaths {
LoadPaths[i], _ = filepath.Abs(path)
}
}
package session
package caplets
import (
"bufio"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"github.com/bettercap/bettercap/core"
)
const (
CapletSuffix = ".cap"
)
type Caplet struct {
Path string
Code []string
}
var (
CapletLoadPaths = []string{
"./caplets/",
"/usr/local/share/bettercap/caplets/",
}
cache = make(map[string]*Caplet)
cacheLock = sync.Mutex{}
)
func init() {
for _, path := range core.SepSplit(core.Trim(os.Getenv("CAPSPATH")), ":") {
if path = core.Trim(path); len(path) > 0 {
CapletLoadPaths = append(CapletLoadPaths, path)
func List() []Caplet {
caplets := make([]Caplet, 0)
for _, searchPath := range LoadPaths {
files, _ := filepath.Glob(searchPath + "/*" + Suffix)
files2, _ := filepath.Glob(searchPath + "/*/*" + Suffix)
for _, fileName := range append(files, files2...) {
if stats, err := os.Stat(fileName); err == nil {
base := strings.Replace(fileName, searchPath+"/", "", -1)
base = strings.Replace(base, Suffix, "", -1)
caplets = append(caplets, Caplet{
Name: base,
Path: fileName,
Size: stats.Size(),
})
}
}
}
for i, path := range CapletLoadPaths {
CapletLoadPaths[i], _ = filepath.Abs(path)
}
sort.Slice(caplets, func(i, j int) bool {
return strings.Compare(caplets[i].Name, caplets[j].Name) == -1
})
return caplets
}
func LoadCaplet(name string) (error, *Caplet) {
func Load(name string) (error, *Caplet) {
cacheLock.Lock()
defer cacheLock.Unlock()
......@@ -50,15 +52,12 @@ func LoadCaplet(name string) (error, *Caplet) {
return nil, caplet
}
names := []string{name}
if !strings.HasSuffix(name, CapletSuffix) {
names = append(names, name+CapletSuffix)
names := []string{}
if !strings.HasSuffix(name, Suffix) {
name += Suffix
}
for _, path := range CapletLoadPaths {
if !strings.HasSuffix(name, CapletSuffix) {
name += CapletSuffix
}
for _, path := range LoadPaths {
names = append(names, filepath.Join(path, name))
}
......@@ -69,7 +68,6 @@ func LoadCaplet(name string) (error, *Caplet) {
Code: make([]string, 0),
}
I.Events.Log(core.INFO, "reading from caplet %s ...", filename)
input, err := os.Open(filename)
if err != nil {
return fmt.Errorf("error reading caplet %s: %v", filename, err), nil
......@@ -93,61 +91,3 @@ func LoadCaplet(name string) (error, *Caplet) {
return fmt.Errorf("caplet %s not found", name), nil
}
func parseCapletCommand(line string) (is bool, caplet *Caplet, argv []string) {
file := core.Trim(line)
parts := strings.Split(file, " ")
argc := len(parts)
argv = make([]string, 0)
// check for any arguments
if argc > 1 {
file = core.Trim(parts[0])
if argc >= 2 {
argv = parts[1:]
}
}
if err, cap := LoadCaplet(file); err == nil {
return true, cap, argv
}
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
}
......@@ -2,7 +2,7 @@ package core
const (
Name = "bettercap"
Version = "2.9"
Version = "2.10"
Author = "Simone 'evilsocket' Margaritelli"
Website = "https://bettercap.org/"
)
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
}
......@@ -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))
......
......@@ -22,6 +22,7 @@ type RestAPI struct {
password string
certFile string
keyFile string
allowOrigin string
useWebsocket bool
upgrader websocket.Upgrader
quit chan bool
......@@ -33,6 +34,7 @@ func NewRestAPI(s *session.Session) *RestAPI {
server: &http.Server{},
quit: make(chan bool),
useWebsocket: false,
allowOrigin: "*",
upgrader: websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
......@@ -45,9 +47,14 @@ func NewRestAPI(s *session.Session) *RestAPI {
"Address to bind the API REST server to."))
api.AddParam(session.NewIntParameter("api.rest.port",
"8083",
"8081",
"Port to bind the API REST server to."))
api.AddParam(session.NewStringParameter("api.rest.alloworigin",
api.allowOrigin,
"",
"Value of the Access-Control-Allow-Origin header of the API server."))
api.AddParam(session.NewStringParameter("api.rest.username",
"",
"",
......@@ -59,14 +66,14 @@ func NewRestAPI(s *session.Session) *RestAPI {
"API authentication password."))
api.AddParam(session.NewStringParameter("api.rest.certificate",
"~/.bcap-api.rest.certificate.pem",
"",
"",
"API TLS certificate."))
tls.CertConfigToModule("api.rest", &api.SessionModule, tls.DefaultLegitConfig)
api.AddParam(session.NewStringParameter("api.rest.key",
"~/.bcap-api.rest.key.pem",
"",
"",
"API TLS key"))
......@@ -124,6 +131,8 @@ func (api *RestAPI) Configure() error {
return err
} else if err, port = api.IntParam("api.rest.port"); err != nil {
return err
} else if err, api.allowOrigin = api.StringParam("api.rest.alloworigin"); err != nil {
return err
} else if err, api.certFile = api.StringParam("api.rest.certificate"); err != nil {
return err
} else if api.certFile, err = core.ExpandPath(api.certFile); err != nil {
......@@ -170,6 +179,7 @@ func (api *RestAPI) Configure() error {
router.HandleFunc("/api/session/env", api.sessionRoute)
router.HandleFunc("/api/session/gateway", api.sessionRoute)
router.HandleFunc("/api/session/interface", api.sessionRoute)
router.HandleFunc("/api/session/modules", api.sessionRoute)
router.HandleFunc("/api/session/lan", api.sessionRoute)
router.HandleFunc("/api/session/lan/{mac}", api.sessionRoute)
router.HandleFunc("/api/session/options", api.sessionRoute)
......@@ -180,6 +190,10 @@ func (api *RestAPI) Configure() error {
api.server.Handler = router
if api.username == "" || api.password == "" {
log.Warning("api.rest.username and/or api.rest.password parameters are empty, authentication is disabled.")
}
return nil
}
......@@ -192,10 +206,10 @@ func (api *RestAPI) Start() error {
var err error
if api.isTLS() {
log.Info("API server starting on https://%s", api.server.Addr)
log.Info("api server starting on https://%s", api.server.Addr)
err = api.server.ListenAndServeTLS(api.certFile, api.keyFile)
} else {
log.Info("API server starting on http://%s", api.server.Addr)
log.Info("api server starting on http://%s", api.server.Addr)
err = api.server.ListenAndServe()
}
......
......@@ -30,16 +30,19 @@ func setAuthFailed(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Unauthorized"))
}
func setSecurityHeaders(w http.ResponseWriter) {
func toJSON(w http.ResponseWriter, o interface{}) {
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(o); err != nil {
log.Error("error while encoding object to JSON: %v", err)
}
}
func (api *RestAPI) setSecurityHeaders(w http.ResponseWriter) {
w.Header().Add("X-Frame-Options", "DENY")