mirror of
https://github.com/bettercap/bettercap
synced 2025-07-11 07:37:00 -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 SRC_DIR=/gocode/src/github.com/bettercap/bettercap
|
||||
|
||||
# As Alpine Linux uses a different folder, we need this
|
||||
# ugly hack in order to compile gopacket statically
|
||||
# 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
|
||||
RUN apk add --update ca-certificates
|
||||
RUN apk add --no-cache --update bash iptables wireless-tools build-base libpcap-dev git python py-six
|
||||
|
||||
WORKDIR $SRC_DIR
|
||||
ADD . $SRC_DIR
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
### Prerequisites
|
||||
# Prerequisites
|
||||
|
||||
* [ ] I read the [README](https://github.com/bettercap/bettercap/blob/master/README.md).
|
||||
* [ ] 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.
|
||||
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.
|
||||
|
||||
### Description
|
||||
! PLEASE REMOVE THIS PART AND LEAVE ONLY THE FOLLOWING SECTIONS IN YOUR REPORT !
|
||||
---
|
||||
|
||||
*Description of the bug or feature request*
|
||||
|
||||
|
@ -17,8 +15,7 @@ Please provide:
|
|||
* OS version and architecture you are using.
|
||||
* Go version if building from sources.
|
||||
* Command line arguments you are using.
|
||||
* Caplet code you are using (if any).
|
||||
* Interactive session commands you are using (if any).
|
||||
* Caplet code you are using or the interactive session commands.
|
||||
* **Full debug output** while reproducing the issue ( `bettercap -debug ...` ).
|
||||
|
||||
### 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; }
|
||||
}
|
||||
|
||||
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() {
|
||||
bin_dep 'zip'
|
||||
|
||||
|
@ -172,19 +182,17 @@ mkdir $BUILD_FOLDER
|
|||
cd $BUILD_FOLDER
|
||||
|
||||
|
||||
build_linux_amd64 # && create_archive bettercap_linux_amd64_$VERSION.zip
|
||||
# build_linux_arm7_static && create_archive bettercap_linux_arm7_$VERSION.zip
|
||||
# build_linux_arm7hf_static && create_archive bettercap_linux_arm7hf_$VERSION.zip
|
||||
# build_linux_mips_static && create_archive bettercap_linux_mips_$VERSION.zip
|
||||
# build_linux_mipsle_static && create_archive bettercap_linux_mipsle_$VERSION.zip
|
||||
# build_linux_mips64_static && create_archive bettercap_linux_mips64_$VERSION.zip
|
||||
# build_linux_mips64le_static && create_archive bettercap_linux_mips64le_$VERSION.zip
|
||||
#
|
||||
# # these are still not static :(
|
||||
# build_macos_amd64 && create_archive bettercap_macos_amd64_$VERSION.zip
|
||||
# build_android_arm && create_archive bettercap_android_arm_$VERSION.zip
|
||||
# build_windows_amd64 && create_archive bettercap_windows_amd64_$VERSION.zip
|
||||
# sha256sum * > checksums.txt
|
||||
build_linux_amd64 && create_archive bettercap_linux_amd64_$VERSION.zip
|
||||
build_macos_amd64 && create_archive bettercap_macos_amd64_$VERSION.zip
|
||||
build_android_arm && create_archive bettercap_android_arm_$VERSION.zip
|
||||
build_windows_amd64 && create_exe_archive bettercap_windows_amd64_$VERSION.zip
|
||||
build_linux_arm7_static && create_archive bettercap_linux_arm7_$VERSION.zip
|
||||
build_linux_arm7hf_static && create_archive bettercap_linux_arm7hf_$VERSION.zip
|
||||
build_linux_mips_static && create_archive bettercap_linux_mips_$VERSION.zip
|
||||
build_linux_mipsle_static && create_archive bettercap_linux_mipsle_$VERSION.zip
|
||||
build_linux_mips64_static && create_archive bettercap_linux_mips64_$VERSION.zip
|
||||
build_linux_mips64le_static && create_archive bettercap_linux_mips64le_$VERSION.zip
|
||||
sha256sum * > checksums.txt
|
||||
|
||||
echo
|
||||
echo
|
||||
|
|
11
core/core.go
11
core/core.go
|
@ -41,6 +41,17 @@ func UniqueInts(a []int, sorted bool) []int {
|
|||
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) {
|
||||
path, err := exec.LookPath(executable)
|
||||
if err != nil {
|
||||
|
|
|
@ -5,8 +5,10 @@ import "flag"
|
|||
type Options struct {
|
||||
InterfaceName *string
|
||||
Caplet *string
|
||||
AutoStart *string
|
||||
Debug *bool
|
||||
Silent *bool
|
||||
NoColors *bool
|
||||
NoHistory *bool
|
||||
EnvFile *string
|
||||
Commands *string
|
||||
|
@ -17,9 +19,11 @@ type Options struct {
|
|||
func ParseOptions() (Options, error) {
|
||||
o := Options{
|
||||
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."),
|
||||
Debug: flag.Bool("debug", false, "Print debug messages."),
|
||||
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."),
|
||||
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."),
|
||||
|
|
12
core/swag.go
12
core/swag.go
|
@ -26,14 +26,17 @@ var (
|
|||
|
||||
RESET = "\033[0m"
|
||||
|
||||
NoColors = false
|
||||
HasColors = true
|
||||
)
|
||||
|
||||
func init() {
|
||||
NoColors = os.Getenv("TERM") == "dumb" ||
|
||||
func isDumbTerminal() bool {
|
||||
return os.Getenv("TERM") == "dumb" ||
|
||||
os.Getenv("TERM") == "" ||
|
||||
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
|
||||
if NoColors {
|
||||
}
|
||||
|
||||
func InitSwag(disableColors bool) {
|
||||
if disableColors || isDumbTerminal() {
|
||||
BOLD = ""
|
||||
DIM = ""
|
||||
RED = ""
|
||||
|
@ -48,6 +51,7 @@ func init() {
|
|||
BG_YELLOW = ""
|
||||
BG_LBLUE = ""
|
||||
RESET = ""
|
||||
HasColors = false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
30
main.go
30
main.go
|
@ -14,8 +14,6 @@ import (
|
|||
var sess *session.Session
|
||||
var err error
|
||||
|
||||
// Some modules are enabled by default in order
|
||||
// to make the interactive session useful.
|
||||
var autoEnableList = []string{
|
||||
"events.stream",
|
||||
"net.recon",
|
||||
|
@ -26,9 +24,14 @@ func main() {
|
|||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer sess.Close()
|
||||
|
||||
if core.NoColors == true {
|
||||
fmt.Printf("\n\nWARNING: This terminal does not support colors, view will be very limited.\n\n")
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
appName := fmt.Sprintf("%s v%s", core.Name, core.Version)
|
||||
|
@ -59,12 +62,6 @@ func main() {
|
|||
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
|
||||
* 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.
|
||||
if *sess.Options.Caplet != "" {
|
||||
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/tls"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
|
@ -146,10 +147,22 @@ func (api *RestAPI) Configure() error {
|
|||
|
||||
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/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
|
||||
|
||||
|
|
|
@ -6,21 +6,11 @@ import (
|
|||
"net/http"
|
||||
"strconv"
|
||||
"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
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
type CommandRequest struct {
|
||||
|
@ -67,6 +57,71 @@ func (api *RestAPI) showSession(w http.ResponseWriter, r *http.Request) {
|
|||
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) {
|
||||
var err error
|
||||
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) {
|
||||
var err error
|
||||
|
||||
if api.useWebsocket {
|
||||
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)
|
||||
api.startStreamingEvents(w, r)
|
||||
} else {
|
||||
|
||||
events := session.I.Events.Sorted()
|
||||
nevents := len(events)
|
||||
nmax := nevents
|
||||
|
@ -210,12 +174,49 @@ func (api *RestAPI) sessionRoute(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
if api.checkAuth(r) == false {
|
||||
setAuthFailed(w, r)
|
||||
} else if r.Method == "GET" {
|
||||
api.showSession(w, r)
|
||||
return
|
||||
} else if r.Method == "POST" {
|
||||
api.runSessionCommand(w, r)
|
||||
} else {
|
||||
return
|
||||
} else if r.Method != "GET" {
|
||||
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 {
|
||||
setAuthFailed(w, r)
|
||||
} else if r.Method == "GET" {
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method == "GET" {
|
||||
api.showEvents(w, r)
|
||||
} else if r.Method == "DELETE" {
|
||||
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 (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
|
@ -12,6 +13,7 @@ import (
|
|||
|
||||
type EventsStream struct {
|
||||
session.SessionModule
|
||||
output *os.File
|
||||
ignoreList *IgnoreList
|
||||
waitFor string
|
||||
waitChan chan *session.Event
|
||||
|
@ -22,6 +24,7 @@ type EventsStream struct {
|
|||
func NewEventsStream(s *session.Session) *EventsStream {
|
||||
stream := &EventsStream{
|
||||
SessionModule: session.NewSessionModule("events.stream", s),
|
||||
output: os.Stdout,
|
||||
quit: make(chan bool),
|
||||
waitChan: make(chan *session.Event),
|
||||
waitFor: "",
|
||||
|
@ -104,6 +107,11 @@ func NewEventsStream(s *session.Session) *EventsStream {
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -119,11 +127,25 @@ func (s EventsStream) Author() string {
|
|||
return "Simone Margaritelli <evilsocket@protonmail.com>"
|
||||
}
|
||||
|
||||
func (s *EventsStream) Configure() error {
|
||||
return nil
|
||||
func (s *EventsStream) Configure() (err error) {
|
||||
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 {
|
||||
if err := s.Configure(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.SetRunning(true, func() {
|
||||
s.eventListener = s.Session.Events.Listen()
|
||||
for {
|
||||
|
@ -196,5 +218,8 @@ func (s *EventsStream) startWaitingFor(tag string, timeout int) error {
|
|||
func (s *EventsStream) Stop() error {
|
||||
return s.SetRunning(false, func() {
|
||||
s.quit <- true
|
||||
if s.output != os.Stdout {
|
||||
s.output.Close()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/bettercap/bettercap/core"
|
||||
|
@ -13,15 +14,15 @@ import (
|
|||
|
||||
const eventTimeFormat = "15:04:05"
|
||||
|
||||
func (s EventsStream) viewLogEvent(e session.Event) {
|
||||
fmt.Printf("[%s] [%s] [%s] %s\n",
|
||||
func (s *EventsStream) viewLogEvent(e session.Event) {
|
||||
fmt.Fprintf(s.output, "[%s] [%s] [%s] %s\n",
|
||||
e.Time.Format(eventTimeFormat),
|
||||
core.Green(e.Tag),
|
||||
e.Label(),
|
||||
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.") {
|
||||
ap := e.Data.(*network.AccessPoint)
|
||||
|
@ -35,7 +36,7 @@ func (s EventsStream) viewWiFiEvent(e session.Event) {
|
|||
}
|
||||
|
||||
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),
|
||||
core.Green(e.Tag),
|
||||
core.Bold(ap.ESSID()),
|
||||
|
@ -43,13 +44,13 @@ func (s EventsStream) viewWiFiEvent(e session.Event) {
|
|||
core.Green(ap.BSSID()),
|
||||
core.Dim(vend))
|
||||
} 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),
|
||||
core.Green(e.Tag),
|
||||
core.Red(ap.ESSID()),
|
||||
ap.BSSID())
|
||||
} else {
|
||||
fmt.Printf("[%s] [%s] %s\n",
|
||||
fmt.Fprintf(s.output, "[%s] [%s] %s\n",
|
||||
e.Time.Format(eventTimeFormat),
|
||||
core.Green(e.Tag),
|
||||
ap.String())
|
||||
|
@ -67,7 +68,7 @@ func (s EventsStream) viewWiFiEvent(e session.Event) {
|
|||
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),
|
||||
core.Green(e.Tag),
|
||||
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)
|
||||
vend := ""
|
||||
name := ""
|
||||
|
@ -93,7 +94,7 @@ func (s EventsStream) viewEndpointEvent(e session.Event) {
|
|||
}
|
||||
|
||||
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),
|
||||
core.Green(e.Tag),
|
||||
core.Bold(t.IpAddress),
|
||||
|
@ -101,27 +102,27 @@ func (s EventsStream) viewEndpointEvent(e session.Event) {
|
|||
core.Green(t.HwAddress),
|
||||
core.Dim(vend))
|
||||
} 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),
|
||||
core.Green(e.Tag),
|
||||
core.Red(t.IpAddress),
|
||||
core.Dim(vend))
|
||||
} else {
|
||||
fmt.Printf("[%s] [%s] %s\n",
|
||||
fmt.Fprintf(s.output, "[%s] [%s] %s\n",
|
||||
e.Time.Format(eventTimeFormat),
|
||||
core.Green(e.Tag),
|
||||
t.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (s EventsStream) viewModuleEvent(e session.Event) {
|
||||
fmt.Printf("[%s] [%s] %s\n",
|
||||
func (s *EventsStream) viewModuleEvent(e session.Event) {
|
||||
fmt.Fprintf(s.output, "[%s] [%s] %s\n",
|
||||
e.Time.Format(eventTimeFormat),
|
||||
core.Green(e.Tag),
|
||||
e.Data)
|
||||
}
|
||||
|
||||
func (s EventsStream) viewSnifferEvent(e session.Event) {
|
||||
func (s *EventsStream) viewSnifferEvent(e session.Event) {
|
||||
se := e.Data.(SnifferEvent)
|
||||
misc := ""
|
||||
|
||||
|
@ -150,16 +151,16 @@ func (s EventsStream) viewSnifferEvent(e session.Event) {
|
|||
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),
|
||||
core.Green(e.Tag),
|
||||
se.Message,
|
||||
misc)
|
||||
}
|
||||
|
||||
func (s EventsStream) viewSynScanEvent(e session.Event) {
|
||||
func (s *EventsStream) viewSynScanEvent(e session.Event) {
|
||||
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),
|
||||
core.Green(e.Tag),
|
||||
se.Port,
|
||||
|
@ -182,10 +183,10 @@ func (s *EventsStream) View(e session.Event, refresh bool) {
|
|||
} else if strings.HasPrefix(e.Tag, "syn.scan.") {
|
||||
s.viewSynScanEvent(e)
|
||||
} 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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"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" {
|
||||
dev := e.Data.(*network.BLEDevice)
|
||||
name := dev.Device.Name()
|
||||
|
@ -23,7 +23,7 @@ func (s EventsStream) viewBLEEvent(e session.Event) {
|
|||
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),
|
||||
core.Green(e.Tag),
|
||||
name,
|
||||
|
@ -41,14 +41,14 @@ func (s EventsStream) viewBLEEvent(e session.Event) {
|
|||
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),
|
||||
core.Green(e.Tag),
|
||||
name,
|
||||
dev.Device.ID(),
|
||||
vend)
|
||||
} /* else {
|
||||
fmt.Printf("[%s] [%s]\n",
|
||||
fmt.Fprintf(s.output,"[%s] [%s]\n",
|
||||
e.Time.Format(eventTimeFormat),
|
||||
core.Green(e.Tag))
|
||||
} */
|
||||
|
|
|
@ -6,6 +6,6 @@ import (
|
|||
"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"
|
||||
|
||||
"github.com/bettercap/bettercap/log"
|
||||
"github.com/bettercap/bettercap/network"
|
||||
"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 {
|
||||
clients := ap.Clients()
|
||||
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 {
|
||||
if w.Running() == false {
|
||||
break
|
||||
|
@ -81,7 +82,7 @@ func (w *WiFiModule) startDeauth(to net.HardwareAddr) error {
|
|||
break
|
||||
} else if c, found := ap.Get(bssid); found == true {
|
||||
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)
|
||||
})
|
||||
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 (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -30,16 +32,18 @@ type WiFiProbe struct {
|
|||
type WiFiModule struct {
|
||||
session.SessionModule
|
||||
|
||||
handle *pcap.Handle
|
||||
channel int
|
||||
hopPeriod time.Duration
|
||||
frequencies []int
|
||||
ap *network.AccessPoint
|
||||
stickChan int
|
||||
skipBroken bool
|
||||
pktSourceChan chan gopacket.Packet
|
||||
writes *sync.WaitGroup
|
||||
reads *sync.WaitGroup
|
||||
handle *pcap.Handle
|
||||
source string
|
||||
channel int
|
||||
hopPeriod time.Duration
|
||||
frequencies []int
|
||||
ap *network.AccessPoint
|
||||
stickChan int
|
||||
skipBroken bool
|
||||
pktSourceChan chan gopacket.Packet
|
||||
pktSourceChanClosed bool
|
||||
writes *sync.WaitGroup
|
||||
reads *sync.WaitGroup
|
||||
}
|
||||
|
||||
func NewWiFiModule(s *session.Session) *WiFiModule {
|
||||
|
@ -74,7 +78,7 @@ func NewWiFiModule(s *session.Session) *WiFiModule {
|
|||
return err
|
||||
} else if ap, found := w.Session.WiFi.Get(bssid.String()); found == true {
|
||||
w.ap = ap
|
||||
w.stickChan = mhz2chan(ap.Frequency)
|
||||
w.stickChan = network.Dot11Freq2Chan(ap.Frequency)
|
||||
return nil
|
||||
}
|
||||
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 {
|
||||
w.ap = nil
|
||||
w.stickChan = 0
|
||||
var err error
|
||||
if w.frequencies, err = network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
|
||||
|
@ -110,13 +118,43 @@ func NewWiFiModule(s *session.Session) *WiFiModule {
|
|||
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",
|
||||
"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",
|
||||
"true",
|
||||
|
@ -137,35 +175,34 @@ func (w WiFiModule) Author() string {
|
|||
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 {
|
||||
var hopPeriod int
|
||||
var err error
|
||||
|
||||
ihandle, err := pcap.NewInactiveHandle(w.Session.Interface.Name())
|
||||
if err != nil {
|
||||
if err, w.source = w.StringParam("wifi.source.file"); err != nil {
|
||||
return err
|
||||
}
|
||||
defer ihandle.CleanUp()
|
||||
|
||||
if err = ihandle.SetRFMon(true); err != nil {
|
||||
return fmt.Errorf("Error while setting interface %s in monitor mode: %s", core.Bold(w.Session.Interface.Name()), err)
|
||||
} else if err = ihandle.SetSnapLen(65536); err != nil {
|
||||
return err
|
||||
} else if err = ihandle.SetTimeout(pcap.BlockForever); err != nil {
|
||||
return err
|
||||
} else if w.handle, err = ihandle.Activate(); 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())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ihandle.CleanUp()
|
||||
|
||||
if err = ihandle.SetRFMon(true); err != nil {
|
||||
return fmt.Errorf("Error while setting interface %s in monitor mode: %s", core.Bold(w.Session.Interface.Name()), err)
|
||||
} else if err = ihandle.SetSnapLen(65536); err != nil {
|
||||
return err
|
||||
} else if err = ihandle.SetTimeout(pcap.BlockForever); err != nil {
|
||||
return err
|
||||
} else if w.handle, err = ihandle.Activate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err, w.skipBroken = w.BoolParam("wifi.skip-broken"); err != nil {
|
||||
|
@ -176,69 +213,39 @@ func (w *WiFiModule) Configure() error {
|
|||
|
||||
w.hopPeriod = time.Duration(hopPeriod) * time.Millisecond
|
||||
|
||||
if err, w.channel = w.IntParam("wifi.recon.channel"); err == nil {
|
||||
if err = network.SetInterfaceChannel(w.Session.Interface.Name(), w.channel); err != nil {
|
||||
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
|
||||
// this OS supports switching channel programmatically.
|
||||
if err = network.SetInterfaceChannel(w.Session.Interface.Name(), 1); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("WiFi recon active with channel hopping.")
|
||||
}
|
||||
if w.source == "" {
|
||||
// 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
|
||||
}
|
||||
|
||||
if frequencies, err := network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil {
|
||||
return err
|
||||
} else {
|
||||
w.frequencies = frequencies
|
||||
// we need to start somewhere, this is just to check if
|
||||
// this OS supports switching channel programmatically.
|
||||
if err = network.SetInterfaceChannel(w.Session.Interface.Name(), 1); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("WiFi recon active with channel hopping.")
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
// search for Dot11InformationElementIDSSID
|
||||
if ok, ssid := packets.Dot11ParseIDSSID(packet); ok == true {
|
||||
if isZeroBSSID(dot11.Address3) == false && isBroadcastBSSID(dot11.Address3) == false {
|
||||
bssid := dot11.Address3.String()
|
||||
frequency := int(radiotap.ChannelFrequency)
|
||||
from := dot11.Address3
|
||||
if network.IsZeroMac(from) == false && network.IsBroadcastMac(from) == false {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
w.reads.Add(1)
|
||||
defer w.reads.Done()
|
||||
|
@ -386,7 +359,7 @@ func (w *WiFiModule) Start() error {
|
|||
|
||||
w.SetRunning(true, func() {
|
||||
// start channel hopper if needed
|
||||
if w.channel == 0 {
|
||||
if w.channel == 0 && w.source == "" {
|
||||
go w.channelHopper()
|
||||
}
|
||||
|
||||
|
@ -423,6 +396,7 @@ func (w *WiFiModule) Start() error {
|
|||
w.updateStats(dot11, packet)
|
||||
}
|
||||
}
|
||||
w.pktSourceChanClosed = true
|
||||
})
|
||||
|
||||
return nil
|
||||
|
@ -433,7 +407,9 @@ func (w *WiFiModule) Stop() error {
|
|||
// wait any pending write operation
|
||||
w.writes.Wait()
|
||||
// signal the main for loop we want to exit
|
||||
w.pktSourceChan <- nil
|
||||
if w.pktSourceChanClosed == false {
|
||||
w.pktSourceChan <- nil
|
||||
}
|
||||
// close the pcap handle to make the main for exit
|
||||
w.handle.Close()
|
||||
// close the pcap handle to make the main for exit
|
||||
|
|
|
@ -17,7 +17,8 @@ func (w *WiFiModule) isApSelected() bool {
|
|||
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)
|
||||
sinceFirstSeen := time.Since(station.FirstSeen)
|
||||
|
||||
|
@ -61,16 +62,27 @@ func (w *WiFiModule) getRow(station *network.Station) []string {
|
|||
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() {
|
||||
return []string{
|
||||
fmt.Sprintf("%d dBm", station.RSSI),
|
||||
bssid,
|
||||
/* station.Vendor, */
|
||||
strconv.Itoa(mhz2chan(station.Frequency)),
|
||||
strconv.Itoa(network.Dot11Freq2Chan(station.Frequency)),
|
||||
sent,
|
||||
recvd,
|
||||
seen,
|
||||
}
|
||||
}, include
|
||||
} else {
|
||||
// this is ugly, but necessary in order to have this
|
||||
// method handle both access point and clients
|
||||
|
@ -88,12 +100,12 @@ func (w *WiFiModule) getRow(station *network.Station) []string {
|
|||
ssid,
|
||||
/* station.Vendor, */
|
||||
encryption,
|
||||
strconv.Itoa(mhz2chan(station.Frequency)),
|
||||
strconv.Itoa(network.Dot11Freq2Chan(station.Frequency)),
|
||||
clients,
|
||||
sent,
|
||||
recvd,
|
||||
seen,
|
||||
}
|
||||
}, include
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,7 +135,9 @@ func (w *WiFiModule) Show(by string) error {
|
|||
|
||||
rows := make([][]string, 0)
|
||||
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)
|
||||
|
||||
|
|
|
@ -26,6 +26,24 @@ var (
|
|||
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 {
|
||||
var parts []string
|
||||
if strings.ContainsRune(mac, '-') {
|
||||
|
|
|
@ -7,6 +7,29 @@ import (
|
|||
"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 APLostCallback func(ap *AccessPoint)
|
||||
|
||||
|
@ -133,6 +156,20 @@ func (w *WiFi) Get(mac string) (*AccessPoint, bool) {
|
|||
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 {
|
||||
w.aps = make(map[string]*AccessPoint)
|
||||
return nil
|
||||
|
|
|
@ -126,3 +126,20 @@ func Dot11IsDataFor(dot11 *layers.Dot11, station net.HardwareAddr) bool {
|
|||
// packet going to this specific BSSID?
|
||||
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
|
||||
}
|
||||
|
||||
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}"
|
||||
)
|
||||
|
||||
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{
|
||||
"{cidr}": func(s *Session) string {
|
||||
return s.Interface.CIDR()
|
||||
|
@ -71,7 +54,26 @@ func (p Prompt) Render(s *Session) string {
|
|||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -138,6 +138,8 @@ func New() (*Session, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
core.InitSwag(*s.Options.NoColors)
|
||||
|
||||
if *s.Options.CpuProfile != "" {
|
||||
if f, err := os.Create(*s.Options.CpuProfile); err != nil {
|
||||
return nil, err
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue