mirror of
https://github.com/bettercap/bettercap
synced 2025-08-21 05:53:20 -07:00
yeah i should have done this before, i know
This commit is contained in:
commit
0091ffdbb3
33 changed files with 25678 additions and 0 deletions
90
net/arp.go
Normal file
90
net/arp.go
Normal file
|
@ -0,0 +1,90 @@
|
|||
package net
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/op/go-logging"
|
||||
|
||||
"github.com/evilsocket/bettercap/core"
|
||||
)
|
||||
|
||||
type ArpTable map[string]string
|
||||
|
||||
var (
|
||||
log = logging.MustGetLogger("mitm")
|
||||
arp_parsed = false
|
||||
arp_lock = &sync.Mutex{}
|
||||
arp_table = make(ArpTable)
|
||||
)
|
||||
|
||||
var ArpTableParser = regexp.MustCompile("^[^\\d\\.]+([\\d\\.]+).+\\s+([a-f0-9:]{17}).+\\s+(.+)$")
|
||||
var ArpTableTokens = 4
|
||||
|
||||
func ArpDiff(current, before ArpTable) ArpTable {
|
||||
diff := make(ArpTable)
|
||||
for ip, mac := range current {
|
||||
_, found := before[ip]
|
||||
if !found {
|
||||
diff[ip] = mac
|
||||
}
|
||||
}
|
||||
|
||||
return diff
|
||||
}
|
||||
|
||||
func ArpLookup(iface string, address string, refresh bool) (string, error) {
|
||||
// Refresh ARP table if first run or if a force refresh has been instructed.
|
||||
if ArpParsed() == false || refresh == true {
|
||||
if _, err := ArpUpdate(iface); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup the hardware address of this ip.
|
||||
if mac, found := arp_table[address]; found == true {
|
||||
return mac, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("Could not find mac for %s", address)
|
||||
}
|
||||
|
||||
func ArpParsed() bool {
|
||||
arp_lock.Lock()
|
||||
defer arp_lock.Unlock()
|
||||
return arp_parsed
|
||||
}
|
||||
|
||||
func ArpUpdate(iface string) (ArpTable, error) {
|
||||
arp_lock.Lock()
|
||||
defer arp_lock.Unlock()
|
||||
|
||||
// Signal we parsed the ARP table at least once.
|
||||
arp_parsed = true
|
||||
|
||||
// Run "arp -an" and parse the output.
|
||||
output, err := core.Exec("arp", []string{"-a", "-n"})
|
||||
if err != nil {
|
||||
return arp_table, err
|
||||
}
|
||||
|
||||
new_table := make(ArpTable)
|
||||
for _, line := range strings.Split(output, "\n") {
|
||||
m := ArpTableParser.FindStringSubmatch(line)
|
||||
if len(m) == ArpTableTokens {
|
||||
address := m[1]
|
||||
mac := m[2]
|
||||
ifname := m[3]
|
||||
|
||||
if ifname == iface {
|
||||
new_table[address] = mac
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arp_table = new_table
|
||||
|
||||
return arp_table, nil
|
||||
}
|
82
net/endpoint.go
Normal file
82
net/endpoint.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
package net
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/evilsocket/bettercap/core"
|
||||
)
|
||||
|
||||
type Endpoint struct {
|
||||
IP net.IP
|
||||
HW net.HardwareAddr
|
||||
IpAddress string
|
||||
SubnetBits uint32
|
||||
IpAddressUint32 uint32
|
||||
HwAddress string
|
||||
Hostname string
|
||||
Vendor string
|
||||
}
|
||||
|
||||
type OnHostResolvedAction func(e *Endpoint)
|
||||
|
||||
func NewEndpointNoResolve(ip, mac, name string, bits uint32) *Endpoint {
|
||||
hw, err := net.ParseMAC(mac)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
e := &Endpoint{
|
||||
IP: net.ParseIP(ip),
|
||||
HW: hw,
|
||||
IpAddress: ip,
|
||||
SubnetBits: bits,
|
||||
IpAddressUint32: binary.BigEndian.Uint32(net.ParseIP(ip)[12:16]),
|
||||
HwAddress: mac,
|
||||
Hostname: name,
|
||||
Vendor: OuiLookup(mac),
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func NewEndpoint(ip, mac string) *Endpoint {
|
||||
e := NewEndpointNoResolve(ip, mac, "", 0)
|
||||
|
||||
// start resolver goroutine
|
||||
go func() {
|
||||
if names, err := net.LookupAddr(e.IpAddress); err == nil {
|
||||
e.Hostname = names[0]
|
||||
log.Debugf("Endpoint %s is now known as %s\n", e.IpAddress, core.Green(e.Hostname))
|
||||
}
|
||||
}()
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (t *Endpoint) Name() string {
|
||||
return t.Hostname
|
||||
}
|
||||
|
||||
func (t *Endpoint) CIDR() string {
|
||||
shift := 32 - t.SubnetBits
|
||||
address := t.IpAddressUint32
|
||||
ip := make(net.IP, 4)
|
||||
|
||||
binary.BigEndian.PutUint32(ip, (address>>shift)<<shift)
|
||||
|
||||
return fmt.Sprintf("%s/%d", ip.String(), t.SubnetBits)
|
||||
}
|
||||
|
||||
func (t *Endpoint) String() string {
|
||||
if t.HwAddress == "" {
|
||||
return t.IpAddress
|
||||
} else if t.Vendor == "" {
|
||||
return fmt.Sprintf("%s : %s", t.IpAddress, t.HwAddress)
|
||||
} else if t.Hostname == "" {
|
||||
return fmt.Sprintf("%s : %s ( %s )", t.IpAddress, t.HwAddress, t.Vendor)
|
||||
} else {
|
||||
return fmt.Sprintf("%s : %s ( %s ) - "+core.BOLD+t.Hostname+core.RESET, t.IpAddress, t.HwAddress, t.Vendor)
|
||||
}
|
||||
}
|
98
net/net.go
Normal file
98
net/net.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package net
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/evilsocket/bettercap/core"
|
||||
)
|
||||
|
||||
var IPv4RouteParser = regexp.MustCompile("^([\\d\\.]+)\\s+([\\d\\.]+)\\s+([\\d\\.]+)\\s+([A-Z]+)\\s+\\d+\\s+\\d+\\s+\\d+\\s+(.+)$")
|
||||
var IPv4RouteTokens = 6
|
||||
|
||||
func FindInterface(name string) (*Endpoint, error) {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, iface := range ifaces {
|
||||
mac := iface.HardwareAddr.String()
|
||||
addrs, err := iface.Addrs()
|
||||
// is interface active?
|
||||
if err == nil && len(addrs) > 0 {
|
||||
if (name == "" && iface.Name != "lo") || iface.Name == name {
|
||||
var e *Endpoint = nil
|
||||
// For every address of the interface.
|
||||
for _, addr := range addrs {
|
||||
ip := addr.String()
|
||||
// Make sure this is an IPv4 address.
|
||||
if m, _ := regexp.MatchString("^[0-9\\.]+/?\\d*$", ip); m == true {
|
||||
if strings.IndexRune(ip, '/') == -1 {
|
||||
// plain ip
|
||||
e = NewEndpointNoResolve(ip, mac, iface.Name, 0)
|
||||
} else {
|
||||
// ip/bits
|
||||
parts := strings.Split(ip, "/")
|
||||
ip_part := parts[0]
|
||||
bits, err := strconv.Atoi(parts[1])
|
||||
if err == nil {
|
||||
e = NewEndpointNoResolve(ip_part, mac, iface.Name, uint32(bits))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if e != nil {
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
return nil, fmt.Errorf("Could not find default network interface.")
|
||||
} else {
|
||||
return nil, fmt.Errorf("Could not find interface '%s'.", name)
|
||||
}
|
||||
}
|
||||
|
||||
func FindGateway(iface *Endpoint) (*Endpoint, error) {
|
||||
output, err := core.Exec("route", []string{"-n", "-A", "inet"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(output, "\n") {
|
||||
m := IPv4RouteParser.FindStringSubmatch(line)
|
||||
if len(m) == IPv4RouteTokens {
|
||||
// destination := m[1]
|
||||
// mask := m[3]
|
||||
flags := m[4]
|
||||
ifname := m[5]
|
||||
|
||||
if ifname == iface.Name() && flags == "UG" {
|
||||
gateway := m[2]
|
||||
// log.Debugf("Gateway ip is %s", gateway)
|
||||
if gateway == iface.IpAddress {
|
||||
// log.Debug("Gateway == Interface")
|
||||
return iface, nil
|
||||
} else {
|
||||
// we have the address, now we need its mac
|
||||
mac, err := ArpLookup(iface.Name(), gateway, false)
|
||||
if err == nil {
|
||||
// log.Debugf("Gateway mac is %s", mac)
|
||||
return NewEndpoint(gateway, mac), nil
|
||||
} else {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Could not detect the gateway.")
|
||||
}
|
22930
net/oui.dat
Normal file
22930
net/oui.dat
Normal file
File diff suppressed because it is too large
Load diff
54
net/oui.go
Normal file
54
net/oui.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package net
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
oui = make(map[string]string)
|
||||
)
|
||||
|
||||
func OuiInit() {
|
||||
bytes, err := Asset("net/oui.dat")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
data := string(bytes)
|
||||
lines := strings.Split(data, "\n")
|
||||
|
||||
for lineno, line := range lines {
|
||||
line = strings.Trim(line, " \n\r\t")
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.SplitN(line, " ", 2)
|
||||
if len(parts) != 2 {
|
||||
log.Warningf("Skipping line %d '%s'\n", lineno+1, line)
|
||||
continue
|
||||
}
|
||||
|
||||
prefix := strings.ToLower(strings.Trim(parts[0], " \n\r\t"))
|
||||
vendor := strings.Trim(parts[1], " \n\r\t")
|
||||
|
||||
oui[prefix] = vendor
|
||||
}
|
||||
|
||||
log.Debugf("Loaded %d vendors signatures.\n", len(oui))
|
||||
}
|
||||
|
||||
func OuiLookup(mac string) string {
|
||||
octects := strings.Split(mac, ":")
|
||||
if len(octects) > 3 {
|
||||
prefix := octects[0] + octects[1] + octects[2]
|
||||
|
||||
if vendor, found := oui[prefix]; found == true {
|
||||
return vendor
|
||||
}
|
||||
} else {
|
||||
log.Warningf("Unexpected mac '%s' in net.OuiLookup\n", mac)
|
||||
}
|
||||
|
||||
return "???"
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue