mirror of
https://github.com/bettercap/bettercap
synced 2025-07-06 04:52:10 -07:00
When a MAC address with uppercase letters was provided, parsing would return an error because the parsing logic would only attempt to remove normalized versions (all lowercase) from the target list. This would leave the address with uppercase letters in the target list, which it would then try to interpet as an Alias. This fixes the bug by using the original address form when removing it from the target list.
333 lines
8.1 KiB
Go
333 lines
8.1 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, ", ")
|
|
|
|
// 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) {
|
|
Debug("GatewayProvidedByUser(%s) [cmd=%v opts=%v parser=%v]", gateway, IPv4RouteCmd, IPv4RouteCmdOpts, IPv4RouteParser)
|
|
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
|
|
}
|