mirror of
https://github.com/bettercap/bettercap
synced 2025-07-10 15:23:30 -07:00
Merge branch 'master' into wifi.flood
This commit is contained in:
commit
d6b9aa7b7f
24 changed files with 646 additions and 335 deletions
|
@ -3,13 +3,8 @@ FROM golang:1.10-alpine AS build-env
|
||||||
ENV GOPATH=/gocode
|
ENV GOPATH=/gocode
|
||||||
ENV SRC_DIR=/gocode/src/github.com/bettercap/bettercap
|
ENV SRC_DIR=/gocode/src/github.com/bettercap/bettercap
|
||||||
|
|
||||||
# As Alpine Linux uses a different folder, we need this
|
RUN apk add --update ca-certificates
|
||||||
# ugly hack in order to compile gopacket statically
|
RUN apk add --no-cache --update bash iptables wireless-tools build-base libpcap-dev git python py-six
|
||||||
# https://github.com/bettercap/bettercap/issues/106
|
|
||||||
RUN apk add --update ca-certificates && \
|
|
||||||
apk add --no-cache --update bash iptables wireless-tools build-base libpcap-dev git python py-six && \
|
|
||||||
mkdir -p /usr/lib/x86_64-linux-gnu/ && \
|
|
||||||
cp /usr/lib/libpcap.a /usr/lib/x86_64-linux-gnu/libpcap.a
|
|
||||||
|
|
||||||
WORKDIR $SRC_DIR
|
WORKDIR $SRC_DIR
|
||||||
ADD . $SRC_DIR
|
ADD . $SRC_DIR
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
### Prerequisites
|
# Prerequisites
|
||||||
|
|
||||||
* [ ] I read the [README](https://github.com/bettercap/bettercap/blob/master/README.md).
|
Please, before creating this issue make sure that you read the [README](https://github.com/bettercap/bettercap/blob/master/README.md), that you are running the [latest stable version](https://github.com/bettercap/bettercap/releases) and that you already searched [other issues](https://github.com/bettercap/bettercap/issues?q=is%3Aopen+is%3Aissue+label%3Abug) to see if your problem or request was already reported.
|
||||||
* [ ] I am running the [latest stable version](https://github.com/bettercap/bettercap/releases).
|
|
||||||
* [ ] I already searched [other issues](https://github.com/bettercap/bettercap/issues?q=is%3Aopen+is%3Aissue+label%3Abug) to see if this problem was already reported.
|
|
||||||
* [ ] I understand I don't necessarily have to paste this `Prerequisites` section in the issue.
|
|
||||||
|
|
||||||
### Description
|
! PLEASE REMOVE THIS PART AND LEAVE ONLY THE FOLLOWING SECTIONS IN YOUR REPORT !
|
||||||
|
---
|
||||||
|
|
||||||
*Description of the bug or feature request*
|
*Description of the bug or feature request*
|
||||||
|
|
||||||
|
@ -17,8 +15,7 @@ Please provide:
|
||||||
* OS version and architecture you are using.
|
* OS version and architecture you are using.
|
||||||
* Go version if building from sources.
|
* Go version if building from sources.
|
||||||
* Command line arguments you are using.
|
* Command line arguments you are using.
|
||||||
* Caplet code you are using (if any).
|
* Caplet code you are using or the interactive session commands.
|
||||||
* Interactive session commands you are using (if any).
|
|
||||||
* **Full debug output** while reproducing the issue ( `bettercap -debug ...` ).
|
* **Full debug output** while reproducing the issue ( `bettercap -debug ...` ).
|
||||||
|
|
||||||
### Steps to Reproduce
|
### Steps to Reproduce
|
||||||
|
|
34
build.sh
34
build.sh
|
@ -13,6 +13,16 @@ host_dep() {
|
||||||
ping -c 1 $HOST > /dev/null || { echo "@ Virtual machine host $HOST not visible !"; exit 1; }
|
ping -c 1 $HOST > /dev/null || { echo "@ Virtual machine host $HOST not visible !"; exit 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
create_exe_archive() {
|
||||||
|
bin_dep 'zip'
|
||||||
|
|
||||||
|
OUTPUT=$1
|
||||||
|
|
||||||
|
echo "@ Creating archive $OUTPUT ..."
|
||||||
|
zip -j "$OUTPUT" bettercap.exe ../README.md ../LICENSE.md > /dev/null
|
||||||
|
rm -rf bettercap bettercap.exe
|
||||||
|
}
|
||||||
|
|
||||||
create_archive() {
|
create_archive() {
|
||||||
bin_dep 'zip'
|
bin_dep 'zip'
|
||||||
|
|
||||||
|
@ -172,19 +182,17 @@ mkdir $BUILD_FOLDER
|
||||||
cd $BUILD_FOLDER
|
cd $BUILD_FOLDER
|
||||||
|
|
||||||
|
|
||||||
build_linux_amd64 # && create_archive bettercap_linux_amd64_$VERSION.zip
|
build_linux_amd64 && create_archive bettercap_linux_amd64_$VERSION.zip
|
||||||
# build_linux_arm7_static && create_archive bettercap_linux_arm7_$VERSION.zip
|
build_macos_amd64 && create_archive bettercap_macos_amd64_$VERSION.zip
|
||||||
# build_linux_arm7hf_static && create_archive bettercap_linux_arm7hf_$VERSION.zip
|
build_android_arm && create_archive bettercap_android_arm_$VERSION.zip
|
||||||
# build_linux_mips_static && create_archive bettercap_linux_mips_$VERSION.zip
|
build_windows_amd64 && create_exe_archive bettercap_windows_amd64_$VERSION.zip
|
||||||
# build_linux_mipsle_static && create_archive bettercap_linux_mipsle_$VERSION.zip
|
build_linux_arm7_static && create_archive bettercap_linux_arm7_$VERSION.zip
|
||||||
# build_linux_mips64_static && create_archive bettercap_linux_mips64_$VERSION.zip
|
build_linux_arm7hf_static && create_archive bettercap_linux_arm7hf_$VERSION.zip
|
||||||
# build_linux_mips64le_static && create_archive bettercap_linux_mips64le_$VERSION.zip
|
build_linux_mips_static && create_archive bettercap_linux_mips_$VERSION.zip
|
||||||
#
|
build_linux_mipsle_static && create_archive bettercap_linux_mipsle_$VERSION.zip
|
||||||
# # these are still not static :(
|
build_linux_mips64_static && create_archive bettercap_linux_mips64_$VERSION.zip
|
||||||
# build_macos_amd64 && create_archive bettercap_macos_amd64_$VERSION.zip
|
build_linux_mips64le_static && create_archive bettercap_linux_mips64le_$VERSION.zip
|
||||||
# build_android_arm && create_archive bettercap_android_arm_$VERSION.zip
|
sha256sum * > checksums.txt
|
||||||
# build_windows_amd64 && create_archive bettercap_windows_amd64_$VERSION.zip
|
|
||||||
# sha256sum * > checksums.txt
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo
|
echo
|
||||||
|
|
11
core/core.go
11
core/core.go
|
@ -41,6 +41,17 @@ func UniqueInts(a []int, sorted bool) []int {
|
||||||
return uniq
|
return uniq
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CommaSplit(csv string) []string {
|
||||||
|
filtered := make([]string, 0)
|
||||||
|
for _, part := range strings.Split(csv, ",") {
|
||||||
|
part = Trim(part)
|
||||||
|
if part != "" {
|
||||||
|
filtered = append(filtered, part)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
func ExecSilent(executable string, args []string) (string, error) {
|
func ExecSilent(executable string, args []string) (string, error) {
|
||||||
path, err := exec.LookPath(executable)
|
path, err := exec.LookPath(executable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -5,8 +5,10 @@ import "flag"
|
||||||
type Options struct {
|
type Options struct {
|
||||||
InterfaceName *string
|
InterfaceName *string
|
||||||
Caplet *string
|
Caplet *string
|
||||||
|
AutoStart *string
|
||||||
Debug *bool
|
Debug *bool
|
||||||
Silent *bool
|
Silent *bool
|
||||||
|
NoColors *bool
|
||||||
NoHistory *bool
|
NoHistory *bool
|
||||||
EnvFile *string
|
EnvFile *string
|
||||||
Commands *string
|
Commands *string
|
||||||
|
@ -17,9 +19,11 @@ type Options struct {
|
||||||
func ParseOptions() (Options, error) {
|
func ParseOptions() (Options, error) {
|
||||||
o := Options{
|
o := Options{
|
||||||
InterfaceName: flag.String("iface", "", "Network interface to bind to, if empty the default interface will be auto selected."),
|
InterfaceName: flag.String("iface", "", "Network interface to bind to, if empty the default interface will be auto selected."),
|
||||||
|
AutoStart: flag.String("autostart", "events.stream, net.recon", "Comma separated list of modules to auto start."),
|
||||||
Caplet: flag.String("caplet", "", "Read commands from this file and execute them in the interactive session."),
|
Caplet: flag.String("caplet", "", "Read commands from this file and execute them in the interactive session."),
|
||||||
Debug: flag.Bool("debug", false, "Print debug messages."),
|
Debug: flag.Bool("debug", false, "Print debug messages."),
|
||||||
Silent: flag.Bool("silent", false, "Suppress all logs which are not errors."),
|
Silent: flag.Bool("silent", false, "Suppress all logs which are not errors."),
|
||||||
|
NoColors: flag.Bool("no-colors", false, "Disable output color effects."),
|
||||||
NoHistory: flag.Bool("no-history", false, "Disable interactive session history file."),
|
NoHistory: flag.Bool("no-history", false, "Disable interactive session history file."),
|
||||||
EnvFile: flag.String("env-file", "", "Load environment variables from this file if found, set to empty to disable environment persistance."),
|
EnvFile: flag.String("env-file", "", "Load environment variables from this file if found, set to empty to disable environment persistance."),
|
||||||
Commands: flag.String("eval", "", "Run one or more commands separated by ; in the interactive session, used to set variables via command line."),
|
Commands: flag.String("eval", "", "Run one or more commands separated by ; in the interactive session, used to set variables via command line."),
|
||||||
|
|
12
core/swag.go
12
core/swag.go
|
@ -26,14 +26,17 @@ var (
|
||||||
|
|
||||||
RESET = "\033[0m"
|
RESET = "\033[0m"
|
||||||
|
|
||||||
NoColors = false
|
HasColors = true
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func isDumbTerminal() bool {
|
||||||
NoColors = os.Getenv("TERM") == "dumb" ||
|
return os.Getenv("TERM") == "dumb" ||
|
||||||
os.Getenv("TERM") == "" ||
|
os.Getenv("TERM") == "" ||
|
||||||
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
|
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
|
||||||
if NoColors {
|
}
|
||||||
|
|
||||||
|
func InitSwag(disableColors bool) {
|
||||||
|
if disableColors || isDumbTerminal() {
|
||||||
BOLD = ""
|
BOLD = ""
|
||||||
DIM = ""
|
DIM = ""
|
||||||
RED = ""
|
RED = ""
|
||||||
|
@ -48,6 +51,7 @@ func init() {
|
||||||
BG_YELLOW = ""
|
BG_YELLOW = ""
|
||||||
BG_LBLUE = ""
|
BG_LBLUE = ""
|
||||||
RESET = ""
|
RESET = ""
|
||||||
|
HasColors = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
28
main.go
28
main.go
|
@ -14,8 +14,6 @@ import (
|
||||||
var sess *session.Session
|
var sess *session.Session
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Some modules are enabled by default in order
|
|
||||||
// to make the interactive session useful.
|
|
||||||
var autoEnableList = []string{
|
var autoEnableList = []string{
|
||||||
"events.stream",
|
"events.stream",
|
||||||
"net.recon",
|
"net.recon",
|
||||||
|
@ -26,10 +24,15 @@ func main() {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
defer sess.Close()
|
||||||
|
|
||||||
if core.NoColors == true {
|
if core.HasColors == false {
|
||||||
|
if *sess.Options.NoColors == true {
|
||||||
|
fmt.Printf("\n\nWARNING: Terminal colors have been disabled, view will be very limited.\n\n")
|
||||||
|
} else {
|
||||||
fmt.Printf("\n\nWARNING: This terminal does not support colors, view will be very limited.\n\n")
|
fmt.Printf("\n\nWARNING: This terminal does not support colors, view will be very limited.\n\n")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
appName := fmt.Sprintf("%s v%s", core.Name, core.Version)
|
appName := fmt.Sprintf("%s v%s", core.Name, core.Version)
|
||||||
|
|
||||||
|
@ -59,12 +62,6 @@ func main() {
|
||||||
log.Fatal("%s", err)
|
log.Fatal("%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, modName := range autoEnableList {
|
|
||||||
if err = sess.Run(modName + " on"); err != nil {
|
|
||||||
log.Fatal("Error while starting module %s: %", modName, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Commands sent with -eval are used to set specific
|
* Commands sent with -eval are used to set specific
|
||||||
* caplet parameters (i.e. arp.spoof.targets) via command
|
* caplet parameters (i.e. arp.spoof.targets) via command
|
||||||
|
@ -77,6 +74,14 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some modules are enabled by default in order
|
||||||
|
// to make the interactive session useful.
|
||||||
|
for _, modName := range core.CommaSplit(*sess.Options.AutoStart) {
|
||||||
|
if err = sess.Run(modName + " on"); err != nil {
|
||||||
|
log.Fatal("Error while starting module %s: %s", modName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Then run the caplet if specified.
|
// Then run the caplet if specified.
|
||||||
if *sess.Options.Caplet != "" {
|
if *sess.Options.Caplet != "" {
|
||||||
if err = sess.RunCaplet(*sess.Options.Caplet); err != nil {
|
if err = sess.RunCaplet(*sess.Options.Caplet); err != nil {
|
||||||
|
@ -100,9 +105,4 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sess.Close()
|
|
||||||
|
|
||||||
// Windows requires this otherwise the app never exits ...
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/bettercap/bettercap/session"
|
"github.com/bettercap/bettercap/session"
|
||||||
"github.com/bettercap/bettercap/tls"
|
"github.com/bettercap/bettercap/tls"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -146,10 +147,22 @@ func (api *RestAPI) Configure() error {
|
||||||
|
|
||||||
api.server.Addr = fmt.Sprintf("%s:%d", ip, port)
|
api.server.Addr = fmt.Sprintf("%s:%d", ip, port)
|
||||||
|
|
||||||
router := http.NewServeMux()
|
router := mux.NewRouter()
|
||||||
|
|
||||||
router.HandleFunc("/api/session", api.sessionRoute)
|
|
||||||
router.HandleFunc("/api/events", api.eventsRoute)
|
router.HandleFunc("/api/events", api.eventsRoute)
|
||||||
|
router.HandleFunc("/api/session", api.sessionRoute)
|
||||||
|
router.HandleFunc("/api/session/ble", api.sessionRoute)
|
||||||
|
router.HandleFunc("/api/session/ble/{mac}", api.sessionRoute)
|
||||||
|
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/lan", api.sessionRoute)
|
||||||
|
router.HandleFunc("/api/session/lan/{mac}", api.sessionRoute)
|
||||||
|
router.HandleFunc("/api/session/options", api.sessionRoute)
|
||||||
|
router.HandleFunc("/api/session/packets", api.sessionRoute)
|
||||||
|
router.HandleFunc("/api/session/started-at", api.sessionRoute)
|
||||||
|
router.HandleFunc("/api/session/wifi", api.sessionRoute)
|
||||||
|
router.HandleFunc("/api/session/wifi/{mac}", api.sessionRoute)
|
||||||
|
|
||||||
api.server.Handler = router
|
api.server.Handler = router
|
||||||
|
|
||||||
|
|
|
@ -6,21 +6,11 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bettercap/bettercap/log"
|
"github.com/bettercap/bettercap/log"
|
||||||
"github.com/bettercap/bettercap/session"
|
"github.com/bettercap/bettercap/session"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/mux"
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Time allowed to write an event to the client.
|
|
||||||
writeWait = 10 * time.Second
|
|
||||||
// Time allowed to read the next pong message from the client.
|
|
||||||
pongWait = 60 * time.Second
|
|
||||||
// Send pings to client with this period. Must be less than pongWait.
|
|
||||||
pingPeriod = (pongWait * 9) / 10
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommandRequest struct {
|
type CommandRequest struct {
|
||||||
|
@ -67,6 +57,71 @@ func (api *RestAPI) showSession(w http.ResponseWriter, r *http.Request) {
|
||||||
toJSON(w, session.I)
|
toJSON(w, session.I)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *RestAPI) showBle(w http.ResponseWriter, r *http.Request) {
|
||||||
|
params := mux.Vars(r)
|
||||||
|
mac := strings.ToLower(params["mac"])
|
||||||
|
|
||||||
|
if mac == "" {
|
||||||
|
toJSON(w, session.I.BLE)
|
||||||
|
} else if dev, found := session.I.BLE.Get(mac); found == true {
|
||||||
|
toJSON(w, dev)
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Not Found", 404)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *RestAPI) showEnv(w http.ResponseWriter, r *http.Request) {
|
||||||
|
toJSON(w, session.I.Env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *RestAPI) showGateway(w http.ResponseWriter, r *http.Request) {
|
||||||
|
toJSON(w, session.I.Gateway)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *RestAPI) showInterface(w http.ResponseWriter, r *http.Request) {
|
||||||
|
toJSON(w, session.I.Interface)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *RestAPI) showLan(w http.ResponseWriter, r *http.Request) {
|
||||||
|
params := mux.Vars(r)
|
||||||
|
mac := strings.ToLower(params["mac"])
|
||||||
|
|
||||||
|
if mac == "" {
|
||||||
|
toJSON(w, session.I.Lan)
|
||||||
|
} else if host, found := session.I.Lan.Get(mac); found == true {
|
||||||
|
toJSON(w, host)
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Not Found", 404)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *RestAPI) showOptions(w http.ResponseWriter, r *http.Request) {
|
||||||
|
toJSON(w, session.I.Options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *RestAPI) showPackets(w http.ResponseWriter, r *http.Request) {
|
||||||
|
toJSON(w, session.I.Queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *RestAPI) showStartedAt(w http.ResponseWriter, r *http.Request) {
|
||||||
|
toJSON(w, session.I.StartedAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *RestAPI) showWiFi(w http.ResponseWriter, r *http.Request) {
|
||||||
|
params := mux.Vars(r)
|
||||||
|
mac := strings.ToLower(params["mac"])
|
||||||
|
|
||||||
|
if mac == "" {
|
||||||
|
toJSON(w, session.I.WiFi)
|
||||||
|
} else if station, found := session.I.WiFi.Get(mac); found == true {
|
||||||
|
toJSON(w, station)
|
||||||
|
} else if client, found := session.I.WiFi.GetClient(mac); found == true {
|
||||||
|
toJSON(w, client)
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Not Found", 404)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (api *RestAPI) runSessionCommand(w http.ResponseWriter, r *http.Request) {
|
func (api *RestAPI) runSessionCommand(w http.ResponseWriter, r *http.Request) {
|
||||||
var err error
|
var err error
|
||||||
var cmd CommandRequest
|
var cmd CommandRequest
|
||||||
|
@ -82,103 +137,12 @@ func (api *RestAPI) runSessionCommand(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *RestAPI) streamEvent(ws *websocket.Conn, event session.Event) error {
|
|
||||||
msg, err := json.Marshal(event)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Error while creating websocket message: %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.SetWriteDeadline(time.Now().Add(writeWait))
|
|
||||||
if err := ws.WriteMessage(websocket.TextMessage, msg); err != nil {
|
|
||||||
if !strings.Contains(err.Error(), "closed connection") {
|
|
||||||
log.Error("Error while writing websocket message: %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *RestAPI) sendPing(ws *websocket.Conn) error {
|
|
||||||
ws.SetWriteDeadline(time.Now().Add(writeWait))
|
|
||||||
if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
|
|
||||||
log.Error("Error while writing websocket ping message: %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *RestAPI) streamWriter(ws *websocket.Conn, w http.ResponseWriter, r *http.Request) {
|
|
||||||
defer ws.Close()
|
|
||||||
|
|
||||||
// first we stream what we already have
|
|
||||||
events := session.I.Events.Sorted()
|
|
||||||
n := len(events)
|
|
||||||
if n > 0 {
|
|
||||||
log.Debug("Sending %d events.", n)
|
|
||||||
for _, event := range events {
|
|
||||||
if err := api.streamEvent(ws, event); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
session.I.Events.Clear()
|
|
||||||
|
|
||||||
log.Debug("Listening for events and streaming to ws endpoint ...")
|
|
||||||
|
|
||||||
pingTicker := time.NewTicker(pingPeriod)
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-pingTicker.C:
|
|
||||||
if err := api.sendPing(ws); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case event := <-api.eventListener:
|
|
||||||
if err := api.streamEvent(ws, event); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case <-api.quit:
|
|
||||||
log.Info("Stopping websocket events streamer ...")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *RestAPI) streamReader(ws *websocket.Conn) {
|
|
||||||
defer ws.Close()
|
|
||||||
ws.SetReadLimit(512)
|
|
||||||
ws.SetReadDeadline(time.Now().Add(pongWait))
|
|
||||||
ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
|
|
||||||
for {
|
|
||||||
_, _, err := ws.ReadMessage()
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("Closing websocket reader.")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *RestAPI) showEvents(w http.ResponseWriter, r *http.Request) {
|
func (api *RestAPI) showEvents(w http.ResponseWriter, r *http.Request) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if api.useWebsocket {
|
if api.useWebsocket {
|
||||||
ws, err := api.upgrader.Upgrade(w, r, nil)
|
api.startStreamingEvents(w, r)
|
||||||
if err != nil {
|
|
||||||
if _, ok := err.(websocket.HandshakeError); !ok {
|
|
||||||
log.Error("Error while updating api.rest connection to websocket: %s", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("Websocket streaming started for %s", r.RemoteAddr)
|
|
||||||
|
|
||||||
go api.streamWriter(ws, w, r)
|
|
||||||
api.streamReader(ws)
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
events := session.I.Events.Sorted()
|
events := session.I.Events.Sorted()
|
||||||
nevents := len(events)
|
nevents := len(events)
|
||||||
nmax := nevents
|
nmax := nevents
|
||||||
|
@ -210,12 +174,49 @@ func (api *RestAPI) sessionRoute(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
if api.checkAuth(r) == false {
|
if api.checkAuth(r) == false {
|
||||||
setAuthFailed(w, r)
|
setAuthFailed(w, r)
|
||||||
} else if r.Method == "GET" {
|
return
|
||||||
api.showSession(w, r)
|
|
||||||
} else if r.Method == "POST" {
|
} else if r.Method == "POST" {
|
||||||
api.runSessionCommand(w, r)
|
api.runSessionCommand(w, r)
|
||||||
} else {
|
return
|
||||||
|
} else if r.Method != "GET" {
|
||||||
http.Error(w, "Bad Request", 400)
|
http.Error(w, "Bad Request", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
path := r.URL.String()
|
||||||
|
switch {
|
||||||
|
case path == "/api/session":
|
||||||
|
api.showSession(w, r)
|
||||||
|
|
||||||
|
case path == "/api/session/env":
|
||||||
|
api.showEnv(w, r)
|
||||||
|
|
||||||
|
case path == "/api/session/gateway":
|
||||||
|
api.showGateway(w, r)
|
||||||
|
|
||||||
|
case path == "/api/session/interface":
|
||||||
|
api.showInterface(w, r)
|
||||||
|
|
||||||
|
case strings.HasPrefix(path, "/api/session/lan"):
|
||||||
|
api.showLan(w, r)
|
||||||
|
|
||||||
|
case path == "/api/session/options":
|
||||||
|
api.showOptions(w, r)
|
||||||
|
|
||||||
|
case path == "/api/session/packets":
|
||||||
|
api.showPackets(w, r)
|
||||||
|
|
||||||
|
case path == "/api/session/started-at":
|
||||||
|
api.showStartedAt(w, r)
|
||||||
|
|
||||||
|
case strings.HasPrefix(path, "/api/session/ble"):
|
||||||
|
api.showBle(w, r)
|
||||||
|
|
||||||
|
case strings.HasPrefix(path, "/api/session/wifi"):
|
||||||
|
api.showWiFi(w, r)
|
||||||
|
|
||||||
|
default:
|
||||||
|
http.Error(w, "Not Found", 404)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +225,10 @@ func (api *RestAPI) eventsRoute(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
if api.checkAuth(r) == false {
|
if api.checkAuth(r) == false {
|
||||||
setAuthFailed(w, r)
|
setAuthFailed(w, r)
|
||||||
} else if r.Method == "GET" {
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method == "GET" {
|
||||||
api.showEvents(w, r)
|
api.showEvents(w, r)
|
||||||
} else if r.Method == "DELETE" {
|
} else if r.Method == "DELETE" {
|
||||||
api.clearEvents(w, r)
|
api.clearEvents(w, r)
|
||||||
|
|
116
modules/api_rest_ws.go
Normal file
116
modules/api_rest_ws.go
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
package modules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bettercap/bettercap/log"
|
||||||
|
"github.com/bettercap/bettercap/session"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Time allowed to write an event to the client.
|
||||||
|
writeWait = 10 * time.Second
|
||||||
|
// Time allowed to read the next pong message from the client.
|
||||||
|
pongWait = 60 * time.Second
|
||||||
|
// Send pings to client with this period. Must be less than pongWait.
|
||||||
|
pingPeriod = (pongWait * 9) / 10
|
||||||
|
)
|
||||||
|
|
||||||
|
func (api *RestAPI) streamEvent(ws *websocket.Conn, event session.Event) error {
|
||||||
|
msg, err := json.Marshal(event)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error while creating websocket message: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
|
if err := ws.WriteMessage(websocket.TextMessage, msg); err != nil {
|
||||||
|
if !strings.Contains(err.Error(), "closed connection") {
|
||||||
|
log.Error("Error while writing websocket message: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *RestAPI) sendPing(ws *websocket.Conn) error {
|
||||||
|
ws.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
|
if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
|
||||||
|
log.Error("Error while writing websocket ping message: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *RestAPI) streamWriter(ws *websocket.Conn, w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer ws.Close()
|
||||||
|
|
||||||
|
// first we stream what we already have
|
||||||
|
events := session.I.Events.Sorted()
|
||||||
|
n := len(events)
|
||||||
|
if n > 0 {
|
||||||
|
log.Debug("Sending %d events.", n)
|
||||||
|
for _, event := range events {
|
||||||
|
if err := api.streamEvent(ws, event); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session.I.Events.Clear()
|
||||||
|
|
||||||
|
log.Debug("Listening for events and streaming to ws endpoint ...")
|
||||||
|
|
||||||
|
pingTicker := time.NewTicker(pingPeriod)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-pingTicker.C:
|
||||||
|
if err := api.sendPing(ws); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case event := <-api.eventListener:
|
||||||
|
if err := api.streamEvent(ws, event); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-api.quit:
|
||||||
|
log.Info("Stopping websocket events streamer ...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *RestAPI) streamReader(ws *websocket.Conn) {
|
||||||
|
defer ws.Close()
|
||||||
|
ws.SetReadLimit(512)
|
||||||
|
ws.SetReadDeadline(time.Now().Add(pongWait))
|
||||||
|
ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
|
||||||
|
for {
|
||||||
|
_, _, err := ws.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Closing websocket reader.")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *RestAPI) startStreamingEvents(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ws, err := api.upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(websocket.HandshakeError); !ok {
|
||||||
|
log.Error("Error while updating api.rest connection to websocket: %s", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("Websocket streaming started for %s", r.RemoteAddr)
|
||||||
|
|
||||||
|
go api.streamWriter(ws, w, r)
|
||||||
|
api.streamReader(ws)
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package modules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -12,6 +13,7 @@ import (
|
||||||
|
|
||||||
type EventsStream struct {
|
type EventsStream struct {
|
||||||
session.SessionModule
|
session.SessionModule
|
||||||
|
output *os.File
|
||||||
ignoreList *IgnoreList
|
ignoreList *IgnoreList
|
||||||
waitFor string
|
waitFor string
|
||||||
waitChan chan *session.Event
|
waitChan chan *session.Event
|
||||||
|
@ -22,6 +24,7 @@ type EventsStream struct {
|
||||||
func NewEventsStream(s *session.Session) *EventsStream {
|
func NewEventsStream(s *session.Session) *EventsStream {
|
||||||
stream := &EventsStream{
|
stream := &EventsStream{
|
||||||
SessionModule: session.NewSessionModule("events.stream", s),
|
SessionModule: session.NewSessionModule("events.stream", s),
|
||||||
|
output: os.Stdout,
|
||||||
quit: make(chan bool),
|
quit: make(chan bool),
|
||||||
waitChan: make(chan *session.Event),
|
waitChan: make(chan *session.Event),
|
||||||
waitFor: "",
|
waitFor: "",
|
||||||
|
@ -104,6 +107,11 @@ func NewEventsStream(s *session.Session) *EventsStream {
|
||||||
return nil
|
return nil
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
stream.AddParam(session.NewStringParameter("events.stream.output",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"If not empty, events will be written to this file instead of the standard output."))
|
||||||
|
|
||||||
return stream
|
return stream
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,11 +127,25 @@ func (s EventsStream) Author() string {
|
||||||
return "Simone Margaritelli <evilsocket@protonmail.com>"
|
return "Simone Margaritelli <evilsocket@protonmail.com>"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *EventsStream) Configure() error {
|
func (s *EventsStream) Configure() (err error) {
|
||||||
return nil
|
var output string
|
||||||
|
|
||||||
|
if err, output = s.StringParam("events.stream.output"); err == nil {
|
||||||
|
if output == "" {
|
||||||
|
s.output = os.Stdout
|
||||||
|
} else if output, err = core.ExpandPath(output); err == nil {
|
||||||
|
s.output, err = os.OpenFile(output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *EventsStream) Start() error {
|
func (s *EventsStream) Start() error {
|
||||||
|
if err := s.Configure(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return s.SetRunning(true, func() {
|
return s.SetRunning(true, func() {
|
||||||
s.eventListener = s.Session.Events.Listen()
|
s.eventListener = s.Session.Events.Listen()
|
||||||
for {
|
for {
|
||||||
|
@ -196,5 +218,8 @@ func (s *EventsStream) startWaitingFor(tag string, timeout int) error {
|
||||||
func (s *EventsStream) Stop() error {
|
func (s *EventsStream) Stop() error {
|
||||||
return s.SetRunning(false, func() {
|
return s.SetRunning(false, func() {
|
||||||
s.quit <- true
|
s.quit <- true
|
||||||
|
if s.output != os.Stdout {
|
||||||
|
s.output.Close()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/bettercap/bettercap/core"
|
"github.com/bettercap/bettercap/core"
|
||||||
|
@ -13,15 +14,15 @@ import (
|
||||||
|
|
||||||
const eventTimeFormat = "15:04:05"
|
const eventTimeFormat = "15:04:05"
|
||||||
|
|
||||||
func (s EventsStream) viewLogEvent(e session.Event) {
|
func (s *EventsStream) viewLogEvent(e session.Event) {
|
||||||
fmt.Printf("[%s] [%s] [%s] %s\n",
|
fmt.Fprintf(s.output, "[%s] [%s] [%s] %s\n",
|
||||||
e.Time.Format(eventTimeFormat),
|
e.Time.Format(eventTimeFormat),
|
||||||
core.Green(e.Tag),
|
core.Green(e.Tag),
|
||||||
e.Label(),
|
e.Label(),
|
||||||
e.Data.(session.LogMessage).Message)
|
e.Data.(session.LogMessage).Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s EventsStream) viewWiFiEvent(e session.Event) {
|
func (s *EventsStream) viewWiFiEvent(e session.Event) {
|
||||||
|
|
||||||
if strings.HasPrefix(e.Tag, "wifi.ap.") {
|
if strings.HasPrefix(e.Tag, "wifi.ap.") {
|
||||||
ap := e.Data.(*network.AccessPoint)
|
ap := e.Data.(*network.AccessPoint)
|
||||||
|
@ -35,7 +36,7 @@ func (s EventsStream) viewWiFiEvent(e session.Event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.Tag == "wifi.ap.new" {
|
if e.Tag == "wifi.ap.new" {
|
||||||
fmt.Printf("[%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),
|
e.Time.Format(eventTimeFormat),
|
||||||
core.Green(e.Tag),
|
core.Green(e.Tag),
|
||||||
core.Bold(ap.ESSID()),
|
core.Bold(ap.ESSID()),
|
||||||
|
@ -43,13 +44,13 @@ func (s EventsStream) viewWiFiEvent(e session.Event) {
|
||||||
core.Green(ap.BSSID()),
|
core.Green(ap.BSSID()),
|
||||||
core.Dim(vend))
|
core.Dim(vend))
|
||||||
} else if e.Tag == "wifi.ap.lost" {
|
} else if e.Tag == "wifi.ap.lost" {
|
||||||
fmt.Printf("[%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),
|
e.Time.Format(eventTimeFormat),
|
||||||
core.Green(e.Tag),
|
core.Green(e.Tag),
|
||||||
core.Red(ap.ESSID()),
|
core.Red(ap.ESSID()),
|
||||||
ap.BSSID())
|
ap.BSSID())
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("[%s] [%s] %s\n",
|
fmt.Fprintf(s.output, "[%s] [%s] %s\n",
|
||||||
e.Time.Format(eventTimeFormat),
|
e.Time.Format(eventTimeFormat),
|
||||||
core.Green(e.Tag),
|
core.Green(e.Tag),
|
||||||
ap.String())
|
ap.String())
|
||||||
|
@ -67,7 +68,7 @@ func (s EventsStream) viewWiFiEvent(e session.Event) {
|
||||||
rssi = fmt.Sprintf(" (%d dBm)", probe.RSSI)
|
rssi = fmt.Sprintf(" (%d dBm)", probe.RSSI)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("[%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),
|
e.Time.Format(eventTimeFormat),
|
||||||
core.Green(e.Tag),
|
core.Green(e.Tag),
|
||||||
probe.FromAddr.String(),
|
probe.FromAddr.String(),
|
||||||
|
@ -77,7 +78,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)
|
t := e.Data.(*network.Endpoint)
|
||||||
vend := ""
|
vend := ""
|
||||||
name := ""
|
name := ""
|
||||||
|
@ -93,7 +94,7 @@ func (s EventsStream) viewEndpointEvent(e session.Event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.Tag == "endpoint.new" {
|
if e.Tag == "endpoint.new" {
|
||||||
fmt.Printf("[%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),
|
e.Time.Format(eventTimeFormat),
|
||||||
core.Green(e.Tag),
|
core.Green(e.Tag),
|
||||||
core.Bold(t.IpAddress),
|
core.Bold(t.IpAddress),
|
||||||
|
@ -101,27 +102,27 @@ func (s EventsStream) viewEndpointEvent(e session.Event) {
|
||||||
core.Green(t.HwAddress),
|
core.Green(t.HwAddress),
|
||||||
core.Dim(vend))
|
core.Dim(vend))
|
||||||
} else if e.Tag == "endpoint.lost" {
|
} else if e.Tag == "endpoint.lost" {
|
||||||
fmt.Printf("[%s] [%s] Endpoint %s%s lost.\n",
|
fmt.Fprintf(s.output, "[%s] [%s] Endpoint %s%s lost.\n",
|
||||||
e.Time.Format(eventTimeFormat),
|
e.Time.Format(eventTimeFormat),
|
||||||
core.Green(e.Tag),
|
core.Green(e.Tag),
|
||||||
core.Red(t.IpAddress),
|
core.Red(t.IpAddress),
|
||||||
core.Dim(vend))
|
core.Dim(vend))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("[%s] [%s] %s\n",
|
fmt.Fprintf(s.output, "[%s] [%s] %s\n",
|
||||||
e.Time.Format(eventTimeFormat),
|
e.Time.Format(eventTimeFormat),
|
||||||
core.Green(e.Tag),
|
core.Green(e.Tag),
|
||||||
t.String())
|
t.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s EventsStream) viewModuleEvent(e session.Event) {
|
func (s *EventsStream) viewModuleEvent(e session.Event) {
|
||||||
fmt.Printf("[%s] [%s] %s\n",
|
fmt.Fprintf(s.output, "[%s] [%s] %s\n",
|
||||||
e.Time.Format(eventTimeFormat),
|
e.Time.Format(eventTimeFormat),
|
||||||
core.Green(e.Tag),
|
core.Green(e.Tag),
|
||||||
e.Data)
|
e.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s EventsStream) viewSnifferEvent(e session.Event) {
|
func (s *EventsStream) viewSnifferEvent(e session.Event) {
|
||||||
se := e.Data.(SnifferEvent)
|
se := e.Data.(SnifferEvent)
|
||||||
misc := ""
|
misc := ""
|
||||||
|
|
||||||
|
@ -150,16 +151,16 @@ func (s EventsStream) viewSnifferEvent(e session.Event) {
|
||||||
misc = fmt.Sprintf("%s", se.Data)
|
misc = fmt.Sprintf("%s", se.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("[%s] [%s] %s %s\n",
|
fmt.Fprintf(s.output, "[%s] [%s] %s %s\n",
|
||||||
e.Time.Format(eventTimeFormat),
|
e.Time.Format(eventTimeFormat),
|
||||||
core.Green(e.Tag),
|
core.Green(e.Tag),
|
||||||
se.Message,
|
se.Message,
|
||||||
misc)
|
misc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s EventsStream) viewSynScanEvent(e session.Event) {
|
func (s *EventsStream) viewSynScanEvent(e session.Event) {
|
||||||
se := e.Data.(SynScanEvent)
|
se := e.Data.(SynScanEvent)
|
||||||
fmt.Printf("[%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),
|
e.Time.Format(eventTimeFormat),
|
||||||
core.Green(e.Tag),
|
core.Green(e.Tag),
|
||||||
se.Port,
|
se.Port,
|
||||||
|
@ -182,10 +183,10 @@ func (s *EventsStream) View(e session.Event, refresh bool) {
|
||||||
} else if strings.HasPrefix(e.Tag, "syn.scan.") {
|
} else if strings.HasPrefix(e.Tag, "syn.scan.") {
|
||||||
s.viewSynScanEvent(e)
|
s.viewSynScanEvent(e)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("[%s] [%s] %v\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), e)
|
fmt.Fprintf(s.output, "[%s] [%s] %v\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), e)
|
||||||
}
|
}
|
||||||
|
|
||||||
if refresh {
|
if refresh && s.output == os.Stdout {
|
||||||
s.Session.Refresh()
|
s.Session.Refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"github.com/bettercap/bettercap/session"
|
"github.com/bettercap/bettercap/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s EventsStream) viewBLEEvent(e session.Event) {
|
func (s *EventsStream) viewBLEEvent(e session.Event) {
|
||||||
if e.Tag == "ble.device.new" {
|
if e.Tag == "ble.device.new" {
|
||||||
dev := e.Data.(*network.BLEDevice)
|
dev := e.Data.(*network.BLEDevice)
|
||||||
name := dev.Device.Name()
|
name := dev.Device.Name()
|
||||||
|
@ -23,7 +23,7 @@ func (s EventsStream) viewBLEEvent(e session.Event) {
|
||||||
vend = fmt.Sprintf(" (%s)", core.Yellow(vend))
|
vend = fmt.Sprintf(" (%s)", core.Yellow(vend))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("[%s] [%s] New BLE device%s detected as %s%s %s.\n",
|
fmt.Fprintf(s.output, "[%s] [%s] New BLE device%s detected as %s%s %s.\n",
|
||||||
e.Time.Format(eventTimeFormat),
|
e.Time.Format(eventTimeFormat),
|
||||||
core.Green(e.Tag),
|
core.Green(e.Tag),
|
||||||
name,
|
name,
|
||||||
|
@ -41,14 +41,14 @@ func (s EventsStream) viewBLEEvent(e session.Event) {
|
||||||
vend = fmt.Sprintf(" (%s)", core.Yellow(vend))
|
vend = fmt.Sprintf(" (%s)", core.Yellow(vend))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("[%s] [%s] BLE device%s %s%s lost.\n",
|
fmt.Fprintf(s.output, "[%s] [%s] BLE device%s %s%s lost.\n",
|
||||||
e.Time.Format(eventTimeFormat),
|
e.Time.Format(eventTimeFormat),
|
||||||
core.Green(e.Tag),
|
core.Green(e.Tag),
|
||||||
name,
|
name,
|
||||||
dev.Device.ID(),
|
dev.Device.ID(),
|
||||||
vend)
|
vend)
|
||||||
} /* else {
|
} /* else {
|
||||||
fmt.Printf("[%s] [%s]\n",
|
fmt.Fprintf(s.output,"[%s] [%s]\n",
|
||||||
e.Time.Format(eventTimeFormat),
|
e.Time.Format(eventTimeFormat),
|
||||||
core.Green(e.Tag))
|
core.Green(e.Tag))
|
||||||
} */
|
} */
|
||||||
|
|
|
@ -6,6 +6,6 @@ import (
|
||||||
"github.com/bettercap/bettercap/session"
|
"github.com/bettercap/bettercap/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s EventsStream) viewBLEEvent(e session.Event) {
|
func (s *EventsStream) viewBLEEvent(e session.Event) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bettercap/bettercap/log"
|
"github.com/bettercap/bettercap/log"
|
||||||
|
"github.com/bettercap/bettercap/network"
|
||||||
"github.com/bettercap/bettercap/packets"
|
"github.com/bettercap/bettercap/packets"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -62,7 +63,7 @@ func (w *WiFiModule) startDeauth(to net.HardwareAddr) error {
|
||||||
if ap, found := w.Session.WiFi.Get(bssid); found == true {
|
if ap, found := w.Session.WiFi.Get(bssid); found == true {
|
||||||
clients := ap.Clients()
|
clients := ap.Clients()
|
||||||
log.Info("Deauthing %d clients from AP %s ...", len(clients), ap.ESSID())
|
log.Info("Deauthing %d clients from AP %s ...", len(clients), ap.ESSID())
|
||||||
w.onChannel(mhz2chan(ap.Frequency), func() {
|
w.onChannel(network.Dot11Freq2Chan(ap.Frequency), func() {
|
||||||
for _, c := range clients {
|
for _, c := range clients {
|
||||||
if w.Running() == false {
|
if w.Running() == false {
|
||||||
break
|
break
|
||||||
|
@ -81,7 +82,7 @@ func (w *WiFiModule) startDeauth(to net.HardwareAddr) error {
|
||||||
break
|
break
|
||||||
} else if c, found := ap.Get(bssid); found == true {
|
} else if c, found := ap.Get(bssid); found == true {
|
||||||
log.Info("Deauthing client %s from AP %s ...", c.HwAddress, ap.ESSID())
|
log.Info("Deauthing client %s from AP %s ...", c.HwAddress, ap.ESSID())
|
||||||
w.onChannel(mhz2chan(ap.Frequency), func() {
|
w.onChannel(network.Dot11Freq2Chan(ap.Frequency), func() {
|
||||||
w.sendDeauthPacket(ap.HW, c.HW)
|
w.sendDeauthPacket(ap.HW, c.HW)
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
|
|
58
modules/wifi_hopping.go
Normal file
58
modules/wifi_hopping.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package modules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bettercap/bettercap/log"
|
||||||
|
"github.com/bettercap/bettercap/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (w *WiFiModule) onChannel(channel int, cb func()) {
|
||||||
|
prev := w.stickChan
|
||||||
|
w.stickChan = channel
|
||||||
|
|
||||||
|
if err := network.SetInterfaceChannel(w.Session.Interface.Name(), channel); err != nil {
|
||||||
|
log.Warning("Error while hopping to channel %d: %s", channel, err)
|
||||||
|
} else {
|
||||||
|
log.Debug("Hopped on channel %d", channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
cb()
|
||||||
|
|
||||||
|
w.stickChan = prev
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WiFiModule) channelHopper() {
|
||||||
|
w.reads.Add(1)
|
||||||
|
defer w.reads.Done()
|
||||||
|
|
||||||
|
log.Info("Channel hopper started.")
|
||||||
|
for w.Running() == true {
|
||||||
|
delay := w.hopPeriod
|
||||||
|
// if we have both 2.4 and 5ghz capabilities, we have
|
||||||
|
// more channels, therefore we need to increase the time
|
||||||
|
// we hop on each one otherwise me lose information
|
||||||
|
if len(w.frequencies) > 14 {
|
||||||
|
delay = delay * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
frequencies := w.frequencies
|
||||||
|
for _, frequency := range frequencies {
|
||||||
|
channel := network.Dot11Freq2Chan(frequency)
|
||||||
|
// stick to the access point channel as long as it's selected
|
||||||
|
// or as long as we're deauthing on it
|
||||||
|
if w.stickChan != 0 {
|
||||||
|
channel = w.stickChan
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := network.SetInterfaceChannel(w.Session.Interface.Name(), channel); err != nil {
|
||||||
|
log.Warning("Error while hopping to channel %d: %s", channel, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(delay)
|
||||||
|
if w.Running() == false {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,8 @@ package modules
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -31,6 +33,7 @@ type WiFiModule struct {
|
||||||
session.SessionModule
|
session.SessionModule
|
||||||
|
|
||||||
handle *pcap.Handle
|
handle *pcap.Handle
|
||||||
|
source string
|
||||||
channel int
|
channel int
|
||||||
hopPeriod time.Duration
|
hopPeriod time.Duration
|
||||||
frequencies []int
|
frequencies []int
|
||||||
|
@ -38,6 +41,7 @@ type WiFiModule struct {
|
||||||
stickChan int
|
stickChan int
|
||||||
skipBroken bool
|
skipBroken bool
|
||||||
pktSourceChan chan gopacket.Packet
|
pktSourceChan chan gopacket.Packet
|
||||||
|
pktSourceChanClosed bool
|
||||||
writes *sync.WaitGroup
|
writes *sync.WaitGroup
|
||||||
reads *sync.WaitGroup
|
reads *sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
@ -74,7 +78,7 @@ func NewWiFiModule(s *session.Session) *WiFiModule {
|
||||||
return err
|
return err
|
||||||
} else if ap, found := w.Session.WiFi.Get(bssid.String()); found == true {
|
} else if ap, found := w.Session.WiFi.Get(bssid.String()); found == true {
|
||||||
w.ap = ap
|
w.ap = ap
|
||||||
w.stickChan = mhz2chan(ap.Frequency)
|
w.stickChan = network.Dot11Freq2Chan(ap.Frequency)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("Could not find station with BSSID %s", args[0])
|
return fmt.Errorf("Could not find station with BSSID %s", args[0])
|
||||||
|
@ -85,6 +89,10 @@ func NewWiFiModule(s *session.Session) *WiFiModule {
|
||||||
func(args []string) error {
|
func(args []string) error {
|
||||||
w.ap = nil
|
w.ap = nil
|
||||||
w.stickChan = 0
|
w.stickChan = 0
|
||||||
|
var err error
|
||||||
|
if w.frequencies, err = network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
@ -110,13 +118,43 @@ func NewWiFiModule(s *session.Session) *WiFiModule {
|
||||||
return w.Show("rssi")
|
return w.Show("rssi")
|
||||||
}))
|
}))
|
||||||
|
|
||||||
w.AddParam(session.NewIntParameter("wifi.recon.channel",
|
w.AddHandler(session.NewModuleHandler("wifi.recon.channel", `wifi\.recon\.channel[\s]+([0-9]+(?:[, ]+[0-9]+)*|clear)`,
|
||||||
|
"WiFi channels (comma separated) or 'clear' for channel hopping.",
|
||||||
|
func(args []string) error {
|
||||||
|
newfrequencies := w.frequencies[:0]
|
||||||
|
|
||||||
|
if len(args) > 0 && args[0] != "clear" {
|
||||||
|
channels := strings.Split(args[0], ",")
|
||||||
|
for _, c := range channels {
|
||||||
|
trimmed := strings.Trim(c, " ")
|
||||||
|
channel, err := strconv.Atoi(trimmed)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newfrequencies = append(newfrequencies, network.Dot11Chan2Freq(channel))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No channels setted, retrieve frequencies supported by the card
|
||||||
|
if frequencies, err := network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
newfrequencies = frequencies
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.frequencies = newfrequencies
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
|
||||||
|
w.AddParam(session.NewStringParameter("wifi.source.file",
|
||||||
"",
|
"",
|
||||||
"WiFi channel or empty for channel hopping."))
|
"",
|
||||||
|
"If set, the wifi module will read from this pcap file instead of the hardware interface."))
|
||||||
|
|
||||||
w.AddParam(session.NewIntParameter("wifi.hop.period",
|
w.AddParam(session.NewIntParameter("wifi.hop.period",
|
||||||
"250",
|
"250",
|
||||||
"If channel hopping is enabled (empty wifi.recon.channel), this is the time in millseconds the algorithm will hop on every channel (it'll be doubled if both 2.4 and 5.0 bands are available)."))
|
"If channel hopping is enabled (empty wifi.recon.channel), this is the time in milliseconds the algorithm will hop on every channel (it'll be doubled if both 2.4 and 5.0 bands are available)."))
|
||||||
|
|
||||||
w.AddParam(session.NewBoolParameter("wifi.skip-broken",
|
w.AddParam(session.NewBoolParameter("wifi.skip-broken",
|
||||||
"true",
|
"true",
|
||||||
|
@ -137,21 +175,19 @@ func (w WiFiModule) Author() string {
|
||||||
return "Gianluca Braga <matrix86@protonmail.com> && Simone Margaritelli <evilsocket@protonmail.com>>"
|
return "Gianluca Braga <matrix86@protonmail.com> && Simone Margaritelli <evilsocket@protonmail.com>>"
|
||||||
}
|
}
|
||||||
|
|
||||||
func mhz2chan(freq int) int {
|
|
||||||
// ambo!
|
|
||||||
if freq <= 2472 {
|
|
||||||
return ((freq - 2412) / 5) + 1
|
|
||||||
} else if freq == 2484 {
|
|
||||||
return 14
|
|
||||||
} else if freq >= 5035 && freq <= 5865 {
|
|
||||||
return ((freq - 5035) / 5) + 7
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WiFiModule) Configure() error {
|
func (w *WiFiModule) Configure() error {
|
||||||
var hopPeriod int
|
var hopPeriod int
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if err, w.source = w.StringParam("wifi.source.file"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.source != "" {
|
||||||
|
if w.handle, err = pcap.OpenOffline(w.source); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
ihandle, err := pcap.NewInactiveHandle(w.Session.Interface.Name())
|
ihandle, err := pcap.NewInactiveHandle(w.Session.Interface.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -167,6 +203,7 @@ func (w *WiFiModule) Configure() error {
|
||||||
} else if w.handle, err = ihandle.Activate(); err != nil {
|
} else if w.handle, err = ihandle.Activate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err, w.skipBroken = w.BoolParam("wifi.skip-broken"); err != nil {
|
if err, w.skipBroken = w.BoolParam("wifi.skip-broken"); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -176,13 +213,13 @@ func (w *WiFiModule) Configure() error {
|
||||||
|
|
||||||
w.hopPeriod = time.Duration(hopPeriod) * time.Millisecond
|
w.hopPeriod = time.Duration(hopPeriod) * time.Millisecond
|
||||||
|
|
||||||
if err, w.channel = w.IntParam("wifi.recon.channel"); err == nil {
|
if w.source == "" {
|
||||||
if err = network.SetInterfaceChannel(w.Session.Interface.Name(), w.channel); err != nil {
|
// No channels setted, retrieve frequencies supported by the card
|
||||||
|
if len(w.frequencies) == 0 {
|
||||||
|
if w.frequencies, err = network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Info("WiFi recon active on channel %d.", w.channel)
|
|
||||||
} else {
|
|
||||||
w.channel = 0
|
|
||||||
// we need to start somewhere, this is just to check if
|
// we need to start somewhere, this is just to check if
|
||||||
// this OS supports switching channel programmatically.
|
// this OS supports switching channel programmatically.
|
||||||
if err = network.SetInterfaceChannel(w.Session.Interface.Name(), 1); err != nil {
|
if err = network.SetInterfaceChannel(w.Session.Interface.Name(), 1); err != nil {
|
||||||
|
@ -190,55 +227,25 @@ func (w *WiFiModule) Configure() error {
|
||||||
}
|
}
|
||||||
log.Info("WiFi recon active with channel hopping.")
|
log.Info("WiFi recon active with channel hopping.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if frequencies, err := network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
w.frequencies = frequencies
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WiFiModule) onChannel(channel int, cb func()) {
|
|
||||||
prev := w.stickChan
|
|
||||||
w.stickChan = channel
|
|
||||||
|
|
||||||
if err := network.SetInterfaceChannel(w.Session.Interface.Name(), channel); err != nil {
|
|
||||||
log.Warning("Error while hopping to channel %d: %s", channel, err)
|
|
||||||
} else {
|
|
||||||
log.Debug("Hopped on channel %d", channel)
|
|
||||||
}
|
|
||||||
|
|
||||||
cb()
|
|
||||||
|
|
||||||
w.stickChan = prev
|
|
||||||
}
|
|
||||||
|
|
||||||
func isZeroBSSID(bssid net.HardwareAddr) bool {
|
|
||||||
for _, b := range bssid {
|
|
||||||
if b != 0x00 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBroadcastBSSID(bssid net.HardwareAddr) bool {
|
|
||||||
for _, b := range bssid {
|
|
||||||
if b != 0xff {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WiFiModule) discoverAccessPoints(radiotap *layers.RadioTap, dot11 *layers.Dot11, packet gopacket.Packet) {
|
func (w *WiFiModule) discoverAccessPoints(radiotap *layers.RadioTap, dot11 *layers.Dot11, packet gopacket.Packet) {
|
||||||
// search for Dot11InformationElementIDSSID
|
// search for Dot11InformationElementIDSSID
|
||||||
if ok, ssid := packets.Dot11ParseIDSSID(packet); ok == true {
|
if ok, ssid := packets.Dot11ParseIDSSID(packet); ok == true {
|
||||||
if isZeroBSSID(dot11.Address3) == false && isBroadcastBSSID(dot11.Address3) == false {
|
from := dot11.Address3
|
||||||
bssid := dot11.Address3.String()
|
if network.IsZeroMac(from) == false && network.IsBroadcastMac(from) == false {
|
||||||
frequency := int(radiotap.ChannelFrequency)
|
var frequency int
|
||||||
|
bssid := from.String()
|
||||||
|
|
||||||
|
if found, channel := packets.Dot11ParseDSSet(packet); found {
|
||||||
|
frequency = network.Dot11Chan2Freq(channel)
|
||||||
|
} else {
|
||||||
|
frequency = int(radiotap.ChannelFrequency)
|
||||||
|
}
|
||||||
|
|
||||||
w.Session.WiFi.AddIfNew(ssid, bssid, frequency, radiotap.DBMAntennaSignal)
|
w.Session.WiFi.AddIfNew(ssid, bssid, frequency, radiotap.DBMAntennaSignal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,40 +324,6 @@ func (w *WiFiModule) updateStats(dot11 *layers.Dot11, packet gopacket.Packet) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WiFiModule) channelHopper() {
|
|
||||||
w.reads.Add(1)
|
|
||||||
defer w.reads.Done()
|
|
||||||
|
|
||||||
log.Info("Channel hopper started.")
|
|
||||||
for w.Running() == true {
|
|
||||||
delay := w.hopPeriod
|
|
||||||
// if we have both 2.4 and 5ghz capabilities, we have
|
|
||||||
// more channels, therefore we need to increase the time
|
|
||||||
// we hop on each one otherwise me lose information
|
|
||||||
if len(w.frequencies) > 14 {
|
|
||||||
delay = 500 * time.Millisecond
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, frequency := range w.frequencies {
|
|
||||||
channel := mhz2chan(frequency)
|
|
||||||
// stick to the access point channel as long as it's selected
|
|
||||||
// or as long as we're deauthing on it
|
|
||||||
if w.stickChan != 0 {
|
|
||||||
channel = w.stickChan
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := network.SetInterfaceChannel(w.Session.Interface.Name(), channel); err != nil {
|
|
||||||
log.Warning("Error while hopping to channel %d: %s", channel, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(delay)
|
|
||||||
if w.Running() == false {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WiFiModule) stationPruner() {
|
func (w *WiFiModule) stationPruner() {
|
||||||
w.reads.Add(1)
|
w.reads.Add(1)
|
||||||
defer w.reads.Done()
|
defer w.reads.Done()
|
||||||
|
@ -386,7 +359,7 @@ func (w *WiFiModule) Start() error {
|
||||||
|
|
||||||
w.SetRunning(true, func() {
|
w.SetRunning(true, func() {
|
||||||
// start channel hopper if needed
|
// start channel hopper if needed
|
||||||
if w.channel == 0 {
|
if w.channel == 0 && w.source == "" {
|
||||||
go w.channelHopper()
|
go w.channelHopper()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,6 +396,7 @@ func (w *WiFiModule) Start() error {
|
||||||
w.updateStats(dot11, packet)
|
w.updateStats(dot11, packet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
w.pktSourceChanClosed = true
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -433,7 +407,9 @@ func (w *WiFiModule) Stop() error {
|
||||||
// wait any pending write operation
|
// wait any pending write operation
|
||||||
w.writes.Wait()
|
w.writes.Wait()
|
||||||
// signal the main for loop we want to exit
|
// signal the main for loop we want to exit
|
||||||
|
if w.pktSourceChanClosed == false {
|
||||||
w.pktSourceChan <- nil
|
w.pktSourceChan <- nil
|
||||||
|
}
|
||||||
// close the pcap handle to make the main for exit
|
// close the pcap handle to make the main for exit
|
||||||
w.handle.Close()
|
w.handle.Close()
|
||||||
// close the pcap handle to make the main for exit
|
// close the pcap handle to make the main for exit
|
||||||
|
|
|
@ -17,7 +17,8 @@ func (w *WiFiModule) isApSelected() bool {
|
||||||
return w.ap != nil
|
return w.ap != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WiFiModule) getRow(station *network.Station) []string {
|
func (w *WiFiModule) getRow(station *network.Station) ([]string, bool) {
|
||||||
|
include := false
|
||||||
sinceStarted := time.Since(w.Session.StartedAt)
|
sinceStarted := time.Since(w.Session.StartedAt)
|
||||||
sinceFirstSeen := time.Since(station.FirstSeen)
|
sinceFirstSeen := time.Since(station.FirstSeen)
|
||||||
|
|
||||||
|
@ -61,16 +62,27 @@ func (w *WiFiModule) getRow(station *network.Station) []string {
|
||||||
recvd = humanize.Bytes(station.Received)
|
recvd = humanize.Bytes(station.Received)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if w.source == "" {
|
||||||
|
for _, frequencies := range w.frequencies {
|
||||||
|
if frequencies == station.Frequency {
|
||||||
|
include = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
include = true
|
||||||
|
}
|
||||||
|
|
||||||
if w.isApSelected() {
|
if w.isApSelected() {
|
||||||
return []string{
|
return []string{
|
||||||
fmt.Sprintf("%d dBm", station.RSSI),
|
fmt.Sprintf("%d dBm", station.RSSI),
|
||||||
bssid,
|
bssid,
|
||||||
/* station.Vendor, */
|
/* station.Vendor, */
|
||||||
strconv.Itoa(mhz2chan(station.Frequency)),
|
strconv.Itoa(network.Dot11Freq2Chan(station.Frequency)),
|
||||||
sent,
|
sent,
|
||||||
recvd,
|
recvd,
|
||||||
seen,
|
seen,
|
||||||
}
|
}, include
|
||||||
} else {
|
} else {
|
||||||
// this is ugly, but necessary in order to have this
|
// this is ugly, but necessary in order to have this
|
||||||
// method handle both access point and clients
|
// method handle both access point and clients
|
||||||
|
@ -88,12 +100,12 @@ func (w *WiFiModule) getRow(station *network.Station) []string {
|
||||||
ssid,
|
ssid,
|
||||||
/* station.Vendor, */
|
/* station.Vendor, */
|
||||||
encryption,
|
encryption,
|
||||||
strconv.Itoa(mhz2chan(station.Frequency)),
|
strconv.Itoa(network.Dot11Freq2Chan(station.Frequency)),
|
||||||
clients,
|
clients,
|
||||||
sent,
|
sent,
|
||||||
recvd,
|
recvd,
|
||||||
seen,
|
seen,
|
||||||
}
|
}, include
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +135,9 @@ func (w *WiFiModule) Show(by string) error {
|
||||||
|
|
||||||
rows := make([][]string, 0)
|
rows := make([][]string, 0)
|
||||||
for _, s := range stations {
|
for _, s := range stations {
|
||||||
rows = append(rows, w.getRow(s))
|
if row, include := w.getRow(s); include == true {
|
||||||
|
rows = append(rows, row)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
nrows := len(rows)
|
nrows := len(rows)
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,24 @@ var (
|
||||||
IPv4Validator = regexp.MustCompile("^[0-9\\.]+/?\\d*$")
|
IPv4Validator = regexp.MustCompile("^[0-9\\.]+/?\\d*$")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func IsZeroMac(mac net.HardwareAddr) bool {
|
||||||
|
for _, b := range mac {
|
||||||
|
if b != 0x00 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsBroadcastMac(mac net.HardwareAddr) bool {
|
||||||
|
for _, b := range mac {
|
||||||
|
if b != 0xff {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func NormalizeMac(mac string) string {
|
func NormalizeMac(mac string) string {
|
||||||
var parts []string
|
var parts []string
|
||||||
if strings.ContainsRune(mac, '-') {
|
if strings.ContainsRune(mac, '-') {
|
||||||
|
|
|
@ -7,6 +7,29 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func Dot11Freq2Chan(freq int) int {
|
||||||
|
if freq <= 2472 {
|
||||||
|
return ((freq - 2412) / 5) + 1
|
||||||
|
} else if freq == 2484 {
|
||||||
|
return 14
|
||||||
|
} else if freq >= 5035 && freq <= 5865 {
|
||||||
|
return ((freq - 5035) / 5) + 7
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func Dot11Chan2Freq(channel int) int {
|
||||||
|
if channel <= 13 {
|
||||||
|
return ((channel - 1) * 5) + 2412
|
||||||
|
} else if channel == 14 {
|
||||||
|
return 2484
|
||||||
|
} else if channel <= 173 {
|
||||||
|
return ((channel - 7) * 5) + 5035
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type APNewCallback func(ap *AccessPoint)
|
type APNewCallback func(ap *AccessPoint)
|
||||||
type APLostCallback func(ap *AccessPoint)
|
type APLostCallback func(ap *AccessPoint)
|
||||||
|
|
||||||
|
@ -133,6 +156,20 @@ func (w *WiFi) Get(mac string) (*AccessPoint, bool) {
|
||||||
return ap, found
|
return ap, found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *WiFi) GetClient(mac string) (*Station, bool) {
|
||||||
|
w.Lock()
|
||||||
|
defer w.Unlock()
|
||||||
|
|
||||||
|
mac = NormalizeMac(mac)
|
||||||
|
for _, ap := range w.aps {
|
||||||
|
if client, found := ap.Get(mac); found == true {
|
||||||
|
return client, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
func (w *WiFi) Clear() error {
|
func (w *WiFi) Clear() error {
|
||||||
w.aps = make(map[string]*AccessPoint)
|
w.aps = make(map[string]*AccessPoint)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -126,3 +126,20 @@ func Dot11IsDataFor(dot11 *layers.Dot11, station net.HardwareAddr) bool {
|
||||||
// packet going to this specific BSSID?
|
// packet going to this specific BSSID?
|
||||||
return bytes.Compare(dot11.Address1, station) == 0
|
return bytes.Compare(dot11.Address1, station) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Dot11ParseDSSet(packet gopacket.Packet) (bool, int) {
|
||||||
|
channel := 0
|
||||||
|
found := false
|
||||||
|
for _, layer := range packet.Layers() {
|
||||||
|
info, ok := layer.(*layers.Dot11InformationElement)
|
||||||
|
if ok == true {
|
||||||
|
if info.ID == layers.Dot11InformationElementIDDSSet {
|
||||||
|
channel, _ = Dot11InformationElementIDDSSetDecode(info.Info)
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found, channel
|
||||||
|
}
|
||||||
|
|
|
@ -211,3 +211,11 @@ func Dot11InformationElementRSNInfoDecode(buf []byte) (rsn RSNInfo, err error) {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Dot11InformationElementIDDSSetDecode(buf []byte) (channel int, err error) {
|
||||||
|
if err = canParse("DSSet.channel", buf, 1); err == nil {
|
||||||
|
channel = int(buf[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -15,23 +15,6 @@ const (
|
||||||
DefaultPrompt = "{by}{fw}{cidr} {fb}> {env.iface.ipv4} {reset} {bold}» {reset}"
|
DefaultPrompt = "{by}{fw}{cidr} {fb}> {env.iface.ipv4} {reset} {bold}» {reset}"
|
||||||
)
|
)
|
||||||
|
|
||||||
var PromptEffects = map[string]string{
|
|
||||||
"{bold}": core.BOLD,
|
|
||||||
"{dim}": core.DIM,
|
|
||||||
"{r}": core.RED,
|
|
||||||
"{g}": core.GREEN,
|
|
||||||
"{b}": core.BLUE,
|
|
||||||
"{y}": core.YELLOW,
|
|
||||||
"{fb}": core.FG_BLACK,
|
|
||||||
"{fw}": core.FG_WHITE,
|
|
||||||
"{bdg}": core.BG_DGRAY,
|
|
||||||
"{br}": core.BG_RED,
|
|
||||||
"{bg}": core.BG_GREEN,
|
|
||||||
"{by}": core.BG_YELLOW,
|
|
||||||
"{blb}": core.BG_LBLUE, // Ziggy this is for you <3
|
|
||||||
"{reset}": core.RESET,
|
|
||||||
}
|
|
||||||
|
|
||||||
var PromptCallbacks = map[string]func(s *Session) string{
|
var PromptCallbacks = map[string]func(s *Session) string{
|
||||||
"{cidr}": func(s *Session) string {
|
"{cidr}": func(s *Session) string {
|
||||||
return s.Interface.CIDR()
|
return s.Interface.CIDR()
|
||||||
|
@ -71,7 +54,26 @@ func (p Prompt) Render(s *Session) string {
|
||||||
prompt = DefaultPrompt
|
prompt = DefaultPrompt
|
||||||
}
|
}
|
||||||
|
|
||||||
for tok, effect := range PromptEffects {
|
// these are here because if colors are disabled,
|
||||||
|
// we need the updated core.* variables
|
||||||
|
var effects = map[string]string{
|
||||||
|
"{bold}": core.BOLD,
|
||||||
|
"{dim}": core.DIM,
|
||||||
|
"{r}": core.RED,
|
||||||
|
"{g}": core.GREEN,
|
||||||
|
"{b}": core.BLUE,
|
||||||
|
"{y}": core.YELLOW,
|
||||||
|
"{fb}": core.FG_BLACK,
|
||||||
|
"{fw}": core.FG_WHITE,
|
||||||
|
"{bdg}": core.BG_DGRAY,
|
||||||
|
"{br}": core.BG_RED,
|
||||||
|
"{bg}": core.BG_GREEN,
|
||||||
|
"{by}": core.BG_YELLOW,
|
||||||
|
"{blb}": core.BG_LBLUE, // Ziggy this is for you <3
|
||||||
|
"{reset}": core.RESET,
|
||||||
|
}
|
||||||
|
|
||||||
|
for tok, effect := range effects {
|
||||||
prompt = strings.Replace(prompt, tok, effect, -1)
|
prompt = strings.Replace(prompt, tok, effect, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -138,6 +138,8 @@ func New() (*Session, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
core.InitSwag(*s.Options.NoColors)
|
||||||
|
|
||||||
if *s.Options.CpuProfile != "" {
|
if *s.Options.CpuProfile != "" {
|
||||||
if f, err := os.Create(*s.Options.CpuProfile); err != nil {
|
if f, err := os.Create(*s.Options.CpuProfile); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue