Commit 95acbe45 authored by Sophie Brun's avatar Sophie Brun

Update upstream source from tag 'upstream/2.9'

Update to upstream version '2.9'
with Debian dir 430b3e4a4188f2fc8806a86f3be7759533c84ac3
parents aad52f6d 3129fc7b
......@@ -4,6 +4,5 @@
pcaps
caplets
build
bettercap*.*
bettercap*
bettercap
bettercap.history
......@@ -9,19 +9,20 @@ deps: godep golint gomegacheck
build: resources
@go build -o $(TARGET) .
resources: network/oui.go
resources: network/manuf.go
network/oui.go:
@python ./network/make_oui.py
network/manuf.go:
@python ./network/make_manuf.py
clean:
@rm -rf $(TARGET).*
@rm -rf $(TARGET)*
@rm -rf $(TARGET)
@rm -rf build
install:
@mkdir -p /usr/share/bettercap/caplets
@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 .
......
[Unit]
Description=bettercap api.rest service.
Documentation=https://bettercap.org
Wants=network.target
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"
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target
......@@ -2,7 +2,7 @@ package core
const (
Name = "bettercap"
Version = "2.8"
Version = "2.9"
Author = "Simone 'evilsocket' Margaritelli"
Website = "https://bettercap.org/"
)
package core
import "testing"
import (
"os"
"testing"
)
func TestIsDumbTerminal(t *testing.T) {
term := os.Getenv("TERM")
os.Setenv("TERM", "dumb")
if !isDumbTerminal() {
t.Fatal("Expected false when TERM==dumb")
}
os.Setenv("TERM", "")
if !isDumbTerminal() {
t.Fatal("Expected false when TERM empty")
}
os.Setenv("TERM", term)
}
func TestW(t *testing.T) {
exp := "<3\033[0m"
......@@ -57,3 +73,23 @@ func TestYellow(t *testing.T) {
t.Fatalf("expected path '%s', got '%s'", exp, got)
}
}
func TestInitSwag(t *testing.T) {
// Run after other tests to avoid breaking globals
// Test InitSwag unsets globals when set
BOLD = "\033[1m"
InitSwag(true)
if BOLD != "" {
t.Fatal("expected BOLD to be empty string")
}
term := os.Getenv("TERM")
os.Setenv("TERM", "dumb")
BOLD = "\033[1m"
InitSwag(false)
if BOLD != "" {
t.Fatal("expected BOLD to be empty string")
}
os.Setenv("TERM", term)
// Would be good to test BOLD *isn't* unset when we have a TTY
// but less trivial to stub os.File.Fd() without complicating architecture
}
......@@ -50,13 +50,13 @@ func (f PfFirewall) sysCtlRead(param string) (string, error) {
func (f PfFirewall) sysCtlWrite(param string, value string) (string, error) {
args := []string{"-w", fmt.Sprintf("%s=%s", param, value)}
out, err := core.ExecSilent("sysctl", args)
_, err := core.ExecSilent("sysctl", args)
if err != nil {
return "", err
}
// make sure we actually wrote the value
if out, err = f.sysCtlRead(param); err != nil {
if out, err := f.sysCtlRead(param); err != nil {
return "", err
} else if out != value {
return "", fmt.Errorf("Expected value for '%s' is %s, found %s", param, value, out)
......
......@@ -4,6 +4,7 @@ import (
"fmt"
"io"
"os"
"strings"
"github.com/bettercap/bettercap/core"
"github.com/bettercap/bettercap/log"
......@@ -90,8 +91,19 @@ func main() {
if err != nil {
if err == io.EOF {
continue
} else if err.Error() == "Interrupt" {
var ans string
fmt.Printf("Are you sure you want to quit this session? y/n ")
fmt.Scan(&ans)
if strings.ToLower(ans) == "y" {
sess.Run("exit")
os.Exit(0)
}
continue
} else {
log.Fatal("%s", err)
}
log.Fatal("%s", err)
}
for _, cmd := range session.ParseCommands(line) {
......
......@@ -50,12 +50,12 @@ func NewRestAPI(s *session.Session) *RestAPI {
api.AddParam(session.NewStringParameter("api.rest.username",
"",
".+",
"",
"API authentication username."))
api.AddParam(session.NewStringParameter("api.rest.password",
"",
".+",
"",
"API authentication password."))
api.AddParam(session.NewStringParameter("api.rest.certificate",
......@@ -109,6 +109,10 @@ func (api *RestAPI) Author() string {
return "Simone Margaritelli <evilsocket@protonmail.com>"
}
func (api *RestAPI) isTLS() bool {
return api.certFile != "" && api.keyFile != ""
}
func (api *RestAPI) Configure() error {
var err error
var ip string
......@@ -136,21 +140,23 @@ func (api *RestAPI) Configure() error {
return err
}
if !core.Exists(api.certFile) || !core.Exists(api.keyFile) {
err, cfg := tls.CertConfigFromModule("api.rest", api.SessionModule)
if err != nil {
return err
if api.isTLS() {
if !core.Exists(api.certFile) || !core.Exists(api.keyFile) {
err, cfg := tls.CertConfigFromModule("api.rest", api.SessionModule)
if err != nil {
return err
}
log.Debug("%+v", cfg)
log.Info("generating TLS key to %s", api.keyFile)
log.Info("generating TLS certificate to %s", api.certFile)
if err := tls.Generate(cfg, api.certFile, api.keyFile); err != nil {
return err
}
} else {
log.Info("loading TLS key from %s", api.keyFile)
log.Info("loading TLS certificate from %s", api.certFile)
}
log.Debug("%+v", cfg)
log.Info("Generating TLS key to %s", api.keyFile)
log.Info("Generating TLS certificate to %s", api.certFile)
if err := tls.Generate(cfg, api.certFile, api.keyFile); err != nil {
return err
}
} else {
log.Info("Loading TLS key from %s", api.keyFile)
log.Info("Loading TLS certificate from %s", api.certFile)
}
api.server.Addr = fmt.Sprintf("%s:%d", ip, port)
......@@ -183,8 +189,16 @@ func (api *RestAPI) Start() error {
}
api.SetRunning(true, func() {
log.Info("API server starting on https://%s", api.server.Addr)
err := api.server.ListenAndServeTLS(api.certFile, api.keyFile)
var err error
if api.isTLS() {
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)
err = api.server.ListenAndServe()
}
if err != nil && err != http.ErrServerClosed {
panic(err)
}
......
......@@ -43,12 +43,14 @@ func toJSON(w http.ResponseWriter, o interface{}) {
}
func (api *RestAPI) checkAuth(r *http.Request) bool {
user, pass, _ := r.BasicAuth()
// timing attack my ass
if subtle.ConstantTimeCompare([]byte(user), []byte(api.username)) != 1 {
return false
} else if subtle.ConstantTimeCompare([]byte(pass), []byte(api.password)) != 1 {
return false
if api.username != "" && api.password != "" {
user, pass, _ := r.BasicAuth()
// timing attack my ass
if subtle.ConstantTimeCompare([]byte(user), []byte(api.username)) != 1 {
return false
} else if subtle.ConstantTimeCompare([]byte(pass), []byte(api.password)) != 1 {
return false
}
}
return true
}
......
......@@ -154,7 +154,7 @@ func (p *ArpSpoofer) Start() error {
func (p *ArpSpoofer) unSpoof() error {
nTargets := len(p.addresses) + len(p.macs)
log.Info("Restoring ARP cache of %d targets.", nTargets)
log.Info("restoring ARP cache of %d targets.", nTargets)
p.sendArp(p.Session.Gateway.IP, p.Session.Gateway.HW, false, false)
if p.internal {
......@@ -174,7 +174,7 @@ func (p *ArpSpoofer) unSpoof() error {
func (p *ArpSpoofer) Stop() error {
return p.SetRunning(false, func() {
log.Info("Waiting for ARP spoofer to stop ...")
log.Info("waiting for ARP spoofer to stop ...")
p.unSpoof()
p.ban = false
p.waitGroup.Wait()
......
......@@ -17,10 +17,6 @@ import (
"github.com/bettercap/gatt"
)
const (
macRegexp = "([a-fA-F0-9]{1,2}:[a-fA-F0-9]{1,2}:[a-fA-F0-9]{1,2}:[a-fA-F0-9]{1,2}:[a-fA-F0-9]{1,2}:[a-fA-F0-9]{1,2})"
)
type BLERecon struct {
session.SessionModule
gattDevice gatt.Device
......@@ -62,7 +58,7 @@ func NewBLERecon(s *session.Session) *BLERecon {
return d.Show()
}))
d.AddHandler(session.NewModuleHandler("ble.enum MAC", "ble.enum "+macRegexp,
d.AddHandler(session.NewModuleHandler("ble.enum MAC", "ble.enum "+network.BLEMacValidator,
"Enumerate services and characteristics for the given BLE device.",
func(args []string) error {
if d.isEnumerating() {
......@@ -75,7 +71,7 @@ func NewBLERecon(s *session.Session) *BLERecon {
return d.enumAllTheThings(network.NormalizeMac(args[0]))
}))
d.AddHandler(session.NewModuleHandler("ble.write MAC UUID HEX_DATA", "ble.write "+macRegexp+" ([a-fA-F0-9]+) ([a-fA-F0-9]+)",
d.AddHandler(session.NewModuleHandler("ble.write MAC UUID HEX_DATA", "ble.write "+network.BLEMacValidator+" ([a-fA-F0-9]+) ([a-fA-F0-9]+)",
"Write the HEX_DATA buffer to the BLE device with the specified MAC address, to the characteristics with the given UUID.",
func(args []string) error {
mac := network.NormalizeMac(args[0])
......@@ -134,20 +130,6 @@ func (d *BLERecon) Configure() (err error) {
return nil
}
func (d *BLERecon) pruner() {
log.Debug("Started BLE devices pruner ...")
for d.Running() {
for _, dev := range d.Session.BLE.Devices() {
if time.Since(dev.LastSeen) > blePresentInterval {
d.Session.BLE.Remove(dev.Device.ID())
}
}
time.Sleep(5 * time.Second)
}
}
func (d *BLERecon) Start() error {
if err := d.Configure(); err != nil {
return err
......@@ -166,6 +148,32 @@ func (d *BLERecon) Start() error {
})
}
func (d *BLERecon) Stop() error {
return d.SetRunning(false, func() {
d.quit <- true
<-d.done
})
}
func (d *BLERecon) pruner() {
log.Debug("Started BLE devices pruner ...")
for d.Running() {
for _, dev := range d.Session.BLE.Devices() {
if time.Since(dev.LastSeen) > blePresentInterval {
d.Session.BLE.Remove(dev.Device.ID())
}
}
time.Sleep(5 * time.Second)
}
}
func (d *BLERecon) setCurrentDevice(dev *network.BLEDevice) {
d.connected = false
d.currDevice = dev
}
func (d *BLERecon) writeBuffer(mac string, uuid gatt.UUID, data []byte) error {
d.writeUUID = &uuid
d.writeData = data
......@@ -199,76 +207,3 @@ func (d *BLERecon) enumAllTheThings(mac string) error {
return nil
}
func (d *BLERecon) Stop() error {
return d.SetRunning(false, func() {
d.quit <- true
<-d.done
})
}
func (d *BLERecon) setCurrentDevice(dev *network.BLEDevice) {
d.connected = false
d.currDevice = dev
}
func (d *BLERecon) onStateChanged(dev gatt.Device, s gatt.State) {
switch s {
case gatt.StatePoweredOn:
if d.currDevice == nil {
log.Info("Starting BLE discovery ...")
dev.Scan([]gatt.UUID{}, true)
}
case gatt.StatePoweredOff:
d.gattDevice = nil
default:
log.Warning("Unexpected BLE state: %v", s)
}
}
func (d *BLERecon) onPeriphDiscovered(p gatt.Peripheral, a *gatt.Advertisement, rssi int) {
d.Session.BLE.AddIfNew(p.ID(), p, a, rssi)
}
func (d *BLERecon) onPeriphDisconnected(p gatt.Peripheral, err error) {
if d.Running() {
// restore scanning
log.Info("Device disconnected, restoring BLE discovery.")
d.setCurrentDevice(nil)
d.gattDevice.Scan([]gatt.UUID{}, true)
}
}
func (d *BLERecon) onPeriphConnected(p gatt.Peripheral, err error) {
if err != nil {
log.Warning("Connected to %s but with error: %s", p.ID(), err)
return
} else if d.currDevice == nil {
// timed out
log.Warning("Connected to %s but after the timeout :(", p.ID())
return
}
d.connected = true
defer func(per gatt.Peripheral) {
log.Info("Disconnecting from %s ...", per.ID())
per.Device().CancelConnection(per)
}(p)
d.Session.Events.Add("ble.device.connected", d.currDevice)
if err := p.SetMTU(500); err != nil {
log.Warning("Failed to set MTU: %s", err)
}
log.Info("Connected, enumerating all the things for %s!", p.ID())
services, err := p.DiscoverServices(nil)
if err != nil {
log.Error("Error discovering services: %s", err)
return
}
d.showServices(p, services)
}
// +build !windows
// +build !darwin
package modules
import (
"github.com/bettercap/bettercap/log"
"github.com/bettercap/gatt"
)
func (d *BLERecon) onStateChanged(dev gatt.Device, s gatt.State) {
log.Info("BLE state changed to %v", s)
switch s {
case gatt.StatePoweredOn:
if d.currDevice == nil {
log.Info("Starting BLE discovery ...")
dev.Scan([]gatt.UUID{}, true)
}
case gatt.StatePoweredOff:
d.gattDevice = nil
default:
log.Warning("Unexpected BLE state: %v", s)
}
}
func (d *BLERecon) onPeriphDiscovered(p gatt.Peripheral, a *gatt.Advertisement, rssi int) {
d.Session.BLE.AddIfNew(p.ID(), p, a, rssi)
}
func (d *BLERecon) onPeriphDisconnected(p gatt.Peripheral, err error) {
if d.Running() {
// restore scanning
log.Info("Device disconnected, restoring BLE discovery.")
d.setCurrentDevice(nil)
d.gattDevice.Scan([]gatt.UUID{}, true)
}
}
func (d *BLERecon) onPeriphConnected(p gatt.Peripheral, err error) {
if err != nil {
log.Warning("Connected to %s but with error: %s", p.ID(), err)
return
} else if d.currDevice == nil {
// timed out
log.Warning("Connected to %s but after the timeout :(", p.ID())
return
}
d.connected = true
defer func(per gatt.Peripheral) {
log.Info("Disconnecting from %s ...", per.ID())
per.Device().CancelConnection(per)
}(p)
d.Session.Events.Add("ble.device.connected", d.currDevice)
if err := p.SetMTU(500); err != nil {
log.Warning("Failed to set MTU: %s", err)
}
log.Info("Connected, enumerating all the things for %s!", p.ID())
services, err := p.DiscoverServices(nil)
if err != nil {
log.Error("Error discovering services: %s", err)
return
}
d.showServices(p, services)
}
......@@ -134,14 +134,14 @@ func (s *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp *
who = t.String()
}
log.Info("[%s] Sending spoofed DNS reply for %s %s to %s.", core.Green("dns"), core.Red(domain), core.Dim(redir), core.Bold(who))
log.Info("[%s] sending spoofed DNS reply for %s %s to %s.", core.Green("dns"), core.Red(domain), core.Dim(redir), core.Bold(who))
var err error
var src, dst net.IP
nlayer := pkt.NetworkLayer()
if nlayer == nil {
log.Debug("Missing network layer skipping packet.")
log.Debug("missing network layer skipping packet.")
return
}
......@@ -210,7 +210,7 @@ func (s *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp *
err, raw = packets.Serialize(&eth, &ip6, &udp, &dns)
if err != nil {
log.Error("Error serializing packet: %s.", err)
log.Error("error serializing packet: %s.", err)
return
}
} else {
......@@ -231,14 +231,14 @@ func (s *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp *
err, raw = packets.Serialize(&eth, &ip4, &udp, &dns)
if err != nil {
log.Error("Error serializing packet: %s.", err)
log.Error("error serializing packet: %s.", err)
return
}
}
log.Debug("Sending %d bytes of packet ...", len(raw))
log.Debug("sending %d bytes of packet ...", len(raw))
if err := s.Session.Queue.Send(raw); err != nil {
log.Error("Error sending packet: %s", err)
log.Error("error sending packet: %s", err)
}
}
......@@ -269,7 +269,7 @@ func (s *DNSSpoofer) onPacket(pkt gopacket.Packet) {
s.dnsReply(pkt, eth, udp, qName, dns, eth.SrcMAC)
break
} else {
log.Debug("Skipping domain %s", qName)
log.Debug("skipping domain %s", qName)
}
}
}
......
......@@ -11,7 +11,7 @@ import (
)
var (
ErrEmptyExpression = errors.New("Expression can not be empty.")
ErrEmptyExpression = errors.New("expression can not be empty")
)
type IgnoreFilter string
......@@ -51,7 +51,7 @@ func (l *IgnoreList) Add(expr string) (err error) {
// first check for duplicates
for _, filter := range l.filters {
if filter.Matches(expr) {
return fmt.Errorf("Filter '%s' already matches the expression '%s'.", filter, expr)
return fmt.Errorf("filter '%s' already matches the expression '%s'", filter, expr)
}
}
......@@ -79,7 +79,7 @@ func (l *IgnoreList) Remove(expr string) (err error) {
}
if len(newList) == len(l.filters) {
return fmt.Errorf("Expression '%s' did not match any filter.", expr)
return fmt.Errorf("expression '%s' did not match any filter", expr)
}
// swap
......
......@@ -162,7 +162,7 @@ func (s *EventsStream) Start() error {
if !s.ignoreList.Ignored(e) {
s.View(e, true)
} else {
log.Debug("Skipping ignored event %v", e)
log.Debug("skipping ignored event %v", e)
}
case <-s.quit:
......@@ -197,9 +197,9 @@ func (s *EventsStream) Show(limit int) error {
func (s *EventsStream) startWaitingFor(tag string, timeout int) error {
if timeout == 0 {
log.Info("Waiting for event %s ...", core.Green(tag))
log.Info("waiting for event %s ...", core.Green(tag))
} else {
log.Info("Waiting for event %s for %d seconds ...", core.Green(tag), timeout)
log.Info("waiting for event %s for %d seconds ...", core.Green(tag), timeout)
go func() {
time.Sleep(time.Duration(timeout) * time.Second)
s.waitFor = ""
......@@ -213,7 +213,7 @@ func (s *EventsStream) startWaitingFor(tag string, timeout int) error {
if event == nil {
return fmt.Errorf("'events.waitFor %s %d' timed out.", tag, timeout)
} else {
log.Debug("Got event: %v", event)
log.Debug("got event: %v", event)
}
return nil
......
......@@ -37,7 +37,7 @@ func (s *EventsStream) viewWiFiEvent(e session.Event) {
}
if e.Tag == "wifi.ap.new" {
fmt.Fprintf(s.output, "[%s] [%s] WiFi access point %s%s detected as %s%s.\n",
fmt.Fprintf(s.output, "[%s] [%s] wifi access point %s%s detected as %s%s.\n",
e.Time.Format(eventTimeFormat),
core.Green(e.Tag),
core.Bold(ap.ESSID()),
......@@ -45,7 +45,7 @@ func (s *EventsStream) viewWiFiEvent(e session.Event) {
core.Green(ap.BSSID()),
core.Dim(vend))
} else if e.Tag == "wifi.ap.lost" {
fmt.Fprintf(s.output, "[%s] [%s] WiFi access point %s (%s) lost.\n",
fmt.Fprintf(s.output, "[%s] [%s] wifi access point %s (%s) lost.\n",
e.Time.Format(eventTimeFormat),
core.Green(e.Tag),
core.Red(ap.ESSID()),
......@@ -69,7 +69,7 @@ func (s *EventsStream) viewWiFiEvent(e session.Event) {
rssi = fmt.Sprintf(" (%d dBm)", probe.RSSI)
}
fmt.Fprintf(s.output, "[%s] [%s] Station %s%s is probing for SSID %s%s\n",
fmt.Fprintf(s.output, "[%s] [%s] station %s%s is probing for SSID %s%s\n",
e.Time.Format(eventTimeFormat),
core.Green(e.Tag),
probe.FromAddr.String(),
......@@ -79,7 +79,7 @@ func (s *EventsStream) viewWiFiEvent(e session.Event) {
}
}
func (s *EventsStream) viewEndpointEvent(e session.Event) {
func (s *EventsStream) viewendpointEvent(e session.Event) {
t := e.Data.(*network.Endpoint)
vend := ""
name := ""
......@@ -95,7 +95,7 @@ func (s *EventsStream) viewEndpointEvent(e session.Event) {
}
if e.Tag == "endpoint.new" {
fmt.Fprintf(s.output, "[%s] [%s] Endpoint %s%s detected as %s%s.\n",
fmt.Fprintf(s.output, "[%s] [%s] endpoint %s%s detected as %s%s.\n",
e.Time.Format(eventTimeFormat),
core.Green(e.Tag),
core.Bold(t.IpAddress),
......@@ -103,7 +103,7 @@ func (s *EventsStream) viewEndpointEvent(e session.Event) {
core.Green(t.HwAddress),
core.Dim(vend))
} else if e.Tag == "endpoint.lost" {
fmt.Fprintf(s.output, "[%s] [%s] Endpoint %s%s lost.\n",
fmt.Fprintf(s.output, "[%s] [%s] endpoint %s%s lost.\n",
e.Time.Format(eventTimeFormat),
core.Green(e.Tag),
core.Red(t.IpAddress),
......@@ -140,8 +140,12 @@ func (s *EventsStream) viewSnifferEvent(e session.Event) {
if err := req.ParseForm(); err == nil {
misc += " \n Form:\n\n"
for key, values := range req.Form {
misc += fmt.Sprintf(" %s => %s\n", core.Green(key), core.Bold(strings.Join(values, ", ")))
if len(req.Form) == 0 {
misc += fmt.Sprintf(" %s\n", core.Dim("<empty>"))
} else {
for key, values := range req.Form {
misc += fmt.Sprintf(" %s => %s\n", core.Green(key), core.Bold(strings.Join(values, ", ")))
}
}
} else if req.Body != nil {
b, _ := ioutil.ReadAll(req.Body)
......@@ -159,7 +163,7 @@ func (s *EventsStream) viewSnifferEvent(e session.Event) {
func (s *EventsStream) viewSynScanEvent(e session.Event) {
se := e.Data.(SynScanEvent)
fmt.Fprintf(s.output, "[%s] [%s] Found open port %d for %s\n",
fmt.Fprintf(s.output, "[%s] [%s] found open port %d for %s\n",
e.Time.Format(eventTimeFormat),
core.Green(e.Tag),
se.Port,
......@@ -169,7 +173,7 @@ func (s *EventsStream) viewSynScanEvent(e session.Event) {
func (s *EventsStream) viewUpdateEvent(e session.Event) {
update := e.Data.(*github.RepositoryRelease)
fmt.Fprintf(s.output, "[%s] [%s] An update to version %s is available at %s\n",
fmt.Fprintf(s.output, "[%s] [%s] an update to version %s is available at %s\n",
e.Time.Format(eventTimeFormat),
core.Bold(core.Yellow(e.Tag)),
core.Bold(*update.TagName),
......@@ -180,7 +184,7 @@ func (s *EventsStream) View(e session.Event, refresh bool) {
if e.Tag == "sys.log" {