mirror of
https://github.com/bettercap/bettercap
synced 2025-07-06 04:52:10 -07:00
334 lines
8 KiB
Go
334 lines
8 KiB
Go
package network
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/bettercap/bettercap/core"
|
|
|
|
"github.com/evilsocket/islazy/data"
|
|
"github.com/evilsocket/islazy/str"
|
|
"github.com/evilsocket/islazy/tui"
|
|
|
|
"github.com/malfunkt/iprange"
|
|
)
|
|
|
|
var ErrNoIfaces = errors.New("No active interfaces found.")
|
|
var ErrNoGateway = errors.New("Could not detect gateway.")
|
|
|
|
const (
|
|
MonitorModeAddress = "0.0.0.0"
|
|
BroadcastSuffix = ".255"
|
|
BroadcastMac = "ff:ff:ff:ff:ff:ff"
|
|
IPv4MulticastStart = "01:00:5e:00:00:00"
|
|
IPv4MulticastEnd = "01:00:5e:7f:ff:ff"
|
|
)
|
|
|
|
var (
|
|
BroadcastHw = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
|
IPv4Validator = regexp.MustCompile(`^[0-9\.]+/?\d*$`)
|
|
IPv4RangeValidator = regexp.MustCompile(`^[0-9\.\-]+/?\d*$`)
|
|
MACValidator = regexp.MustCompile(`(?i)^[a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2}$`)
|
|
// lulz this sounds like a hamburger
|
|
macParser = regexp.MustCompile(`(?i)([a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2})`)
|
|
aliasParser = regexp.MustCompile(`(?i)([a-z_][a-z_0-9]+)`)
|
|
)
|
|
|
|
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, '-') {
|
|
parts = strings.Split(mac, "-")
|
|
} else {
|
|
parts = strings.Split(mac, ":")
|
|
}
|
|
|
|
for i, p := range parts {
|
|
if len(p) < 2 {
|
|
parts[i] = "0" + p
|
|
}
|
|
}
|
|
return strings.ToLower(strings.Join(parts, ":"))
|
|
}
|
|
|
|
func ParseMACs(targets string) (macs []net.HardwareAddr, err error) {
|
|
macs = make([]net.HardwareAddr, 0)
|
|
if targets = str.Trim(targets); targets == "" {
|
|
return
|
|
}
|
|
|
|
for _, mac := range macParser.FindAllString(targets, -1) {
|
|
mac = NormalizeMac(mac)
|
|
hw, err := net.ParseMAC(mac)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error while parsing MAC '%s': %s", mac, err)
|
|
}
|
|
|
|
macs = append(macs, hw)
|
|
targets = strings.Replace(targets, mac, "", -1)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func ParseTargets(targets string, aliasMap *data.UnsortedKV) (ips []net.IP, macs []net.HardwareAddr, err error) {
|
|
ips = make([]net.IP, 0)
|
|
macs = make([]net.HardwareAddr, 0)
|
|
|
|
if targets = str.Trim(targets); targets == "" {
|
|
return
|
|
}
|
|
|
|
// first isolate MACs and parse them
|
|
for _, mac := range macParser.FindAllString(targets, -1) {
|
|
normalizedMac := NormalizeMac(mac)
|
|
hw, err := net.ParseMAC(normalizedMac)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error while parsing MAC '%s': %s", normalizedMac, err)
|
|
}
|
|
|
|
macs = append(macs, hw)
|
|
targets = strings.Replace(targets, mac, "", -1)
|
|
}
|
|
targets = strings.Trim(targets, ", ")
|
|
|
|
// fmt.Printf("targets=%s macs=%#v\n", targets, macs)
|
|
|
|
// check and resolve aliases
|
|
for _, targetAlias := range aliasParser.FindAllString(targets, -1) {
|
|
found := false
|
|
aliasMap.Each(func(mac, alias string) bool {
|
|
if alias == targetAlias {
|
|
if hw, err := net.ParseMAC(mac); err == nil {
|
|
macs = append(macs, hw)
|
|
targets = strings.Replace(targets, targetAlias, "", -1)
|
|
found = true
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
})
|
|
|
|
if !found {
|
|
return nil, nil, fmt.Errorf("could not resolve alias %s", targetAlias)
|
|
}
|
|
}
|
|
targets = strings.Trim(targets, ", ")
|
|
|
|
// parse what's left
|
|
if targets != "" {
|
|
list, err := iprange.ParseList(targets)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error while parsing address list '%s': %s", targets, err)
|
|
}
|
|
|
|
ips = list.Expand()
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func ParseEndpoints(targets string, lan *LAN) ([]*Endpoint, error) {
|
|
ips, macs, err := ParseTargets(targets, lan.Aliases())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tmp := make(map[string]*Endpoint)
|
|
for _, ip := range ips {
|
|
if e := lan.GetByIp(ip.String()); e != nil {
|
|
tmp[e.HW.String()] = e
|
|
}
|
|
}
|
|
|
|
for _, mac := range macs {
|
|
if e, found := lan.Get(mac.String()); found {
|
|
tmp[e.HW.String()] = e
|
|
}
|
|
}
|
|
|
|
ret := make([]*Endpoint, 0)
|
|
for _, e := range tmp {
|
|
ret = append(ret, e)
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
func buildEndpointFromInterface(iface net.Interface) (*Endpoint, error) {
|
|
addrs, err := iface.Addrs()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ifName := getInterfaceName(iface)
|
|
|
|
e := NewEndpointNoResolve(MonitorModeAddress, iface.HardwareAddr.String(), ifName, 0)
|
|
|
|
e.Index = iface.Index
|
|
|
|
for _, a := range addrs {
|
|
address := a.String()
|
|
if IPv4Validator.MatchString(address) {
|
|
if !strings.ContainsRune(address, '/') {
|
|
// plain ip
|
|
e.SetIP(address)
|
|
} else {
|
|
// ip/bits
|
|
e.SetNetwork(address)
|
|
}
|
|
} else {
|
|
// ipv6/xxx
|
|
e.SetIPv6(address)
|
|
}
|
|
}
|
|
|
|
return e, nil
|
|
}
|
|
|
|
func matchByAddress(iface net.Interface, name string) bool {
|
|
ifMac := iface.HardwareAddr.String()
|
|
if NormalizeMac(ifMac) == NormalizeMac(name) {
|
|
return true
|
|
}
|
|
|
|
addrs, err := iface.Addrs()
|
|
if err == nil {
|
|
for _, addr := range addrs {
|
|
ip := addr.String()
|
|
if ip == name || strings.HasPrefix(ip, name) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func findInterfaceByName(name string, ifaces []net.Interface) (*Endpoint, error) {
|
|
for _, iface := range ifaces {
|
|
ifName := getInterfaceName(iface)
|
|
if ifName == name || matchByAddress(iface, name) {
|
|
return buildEndpointFromInterface(iface)
|
|
}
|
|
}
|
|
|
|
return nil, fmt.Errorf("no interface matching '%s' found.", name)
|
|
}
|
|
|
|
func FindInterface(name string) (*Endpoint, error) {
|
|
ifaces, err := net.Interfaces()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
name = str.Trim(name)
|
|
if name != "" {
|
|
return findInterfaceByName(name, ifaces)
|
|
}
|
|
|
|
// user did not provide an interface name,
|
|
// return the first one with a valid ipv4
|
|
// address
|
|
for _, iface := range ifaces {
|
|
addrs, err := iface.Addrs()
|
|
if err != nil {
|
|
fmt.Printf("wtf of the day: %s", err)
|
|
continue
|
|
}
|
|
|
|
for _, address := range addrs {
|
|
ip := address.String()
|
|
if !strings.Contains(ip, "127.0.0.1") && IPv4Validator.MatchString(ip) {
|
|
return buildEndpointFromInterface(iface)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, ErrNoIfaces
|
|
}
|
|
|
|
func SetWiFiRegion(region string) error {
|
|
if core.HasBinary("iw") {
|
|
if out, err := core.Exec("iw", []string{"reg", "set", region}); err != nil {
|
|
return err
|
|
} else if out != "" {
|
|
return fmt.Errorf("unexpected output while setting WiFi region %s: %s", region, out)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ActivateInterface(name string) error {
|
|
if out, err := core.Exec("ifconfig", []string{name, "up"}); err != nil {
|
|
return err
|
|
} else if out != "" {
|
|
return fmt.Errorf("unexpected output while activating interface %s: %s", name, out)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func SetInterfaceTxPower(name string, txpower int) error {
|
|
if core.HasBinary("iw") {
|
|
Debug("SetInterfaceTxPower(%s, %d) iw based", name, txpower)
|
|
if _, err := core.Exec("iw", []string{"dev", name, "set", "txpower", "fixed", fmt.Sprintf("%d",
|
|
txpower)}); err != nil {
|
|
return err
|
|
}
|
|
} else if core.HasBinary("iwconfig") {
|
|
Debug("SetInterfaceTxPower(%s, %d) iwconfig based", name, txpower)
|
|
if out, err := core.Exec("iwconfig", []string{name, "txpower", fmt.Sprintf("%d", txpower)}); err != nil {
|
|
return err
|
|
} else if out != "" {
|
|
return fmt.Errorf("unexpected output while setting txpower to %d for interface %s: %s", txpower, name, out)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func GatewayProvidedByUser(iface *Endpoint, gateway string) (*Endpoint, error) {
|
|
if IPv4Validator.MatchString(gateway) {
|
|
Debug("valid gateway ip %s", gateway)
|
|
// we have the address, now we need its mac
|
|
if mac, err := ArpLookup(iface.Name(), gateway, false); err != nil {
|
|
return nil, err
|
|
} else {
|
|
Debug("gateway is %s[%s]", gateway, mac)
|
|
return NewEndpoint(gateway, mac), nil
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("Provided gateway %s not a valid IPv4 address! Revert to find default gateway.", gateway)
|
|
}
|
|
|
|
func ColorRSSI(n int) string {
|
|
// ref. https://www.metageek.com/training/resources/understanding-rssi-2.html
|
|
rssi := fmt.Sprintf("%d dBm", n)
|
|
if n >= -67 {
|
|
rssi = tui.Green(rssi)
|
|
} else if n >= -70 {
|
|
rssi = tui.Dim(tui.Green(rssi))
|
|
} else if n >= -80 {
|
|
rssi = tui.Yellow(rssi)
|
|
} else {
|
|
rssi = tui.Dim(tui.Red(rssi))
|
|
}
|
|
return rssi
|
|
}
|