mirror of
https://github.com/bettercap/bettercap
synced 2025-07-06 04:52:10 -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
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
*.cap
|
||||
bettercap
|
||||
net/oui_compiled.go
|
31
Makefile
Normal file
31
Makefile
Normal file
|
@ -0,0 +1,31 @@
|
|||
TARGET=bettercap
|
||||
BUILD_DATE=`date +%Y-%m-%d\ %H:%M`
|
||||
BUILD_FILE=core/build.go
|
||||
|
||||
all: build
|
||||
@echo "@ Done"
|
||||
@echo -n "\n"
|
||||
|
||||
build: build_file
|
||||
@echo "@ Building ..."
|
||||
@go build $(FLAGS) -o $(TARGET) .
|
||||
|
||||
build_file: resources
|
||||
@rm -f $(BUILD_FILE)
|
||||
@echo "package core" > $(BUILD_FILE)
|
||||
@echo "const (" >> $(BUILD_FILE)
|
||||
@echo " BuildDate = \"$(BUILD_DATE)\"" >> $(BUILD_FILE)
|
||||
@echo ")" >> $(BUILD_FILE)
|
||||
|
||||
resources:
|
||||
@echo "@ Compiling resources into go files ..."
|
||||
@go-bindata -o net/oui_compiled.go -pkg net net/oui.dat
|
||||
|
||||
clean:
|
||||
@rm -rf $(TARGET) net/oui_compiled.go
|
||||
|
||||
clear_arp:
|
||||
@ip -s -s neigh flush all
|
||||
|
||||
bcast_ping:
|
||||
@ping -b 255.255.255.255
|
8
core/banner.go
Normal file
8
core/banner.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
package core
|
||||
|
||||
const (
|
||||
Name = "bettercap"
|
||||
Version = "2.0.0a"
|
||||
Author = "Simone 'evilsocket' Margaritelli"
|
||||
Website = "https://bettercap.org/"
|
||||
)
|
4
core/build.go
Normal file
4
core/build.go
Normal file
|
@ -0,0 +1,4 @@
|
|||
package core
|
||||
const (
|
||||
BuildDate = "2017-11-16 19:24"
|
||||
)
|
25
core/core.go
Normal file
25
core/core.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"github.com/op/go-logging"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var log = logging.MustGetLogger("mitm")
|
||||
|
||||
func Exec(executable string, args []string) (string, error) {
|
||||
path, err := exec.LookPath(executable)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
log.Debugf(DIM+"Exec( '%s %s' )"+RESET+"\n", path, strings.Join(args, " "))
|
||||
raw, err := exec.Command(path, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
log.Errorf(" err=%s out='%s'\n", err, raw)
|
||||
return "", err
|
||||
} else {
|
||||
return strings.Trim(string(raw), "\r\n\t "), nil
|
||||
}
|
||||
}
|
25
core/options.go
Normal file
25
core/options.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package core
|
||||
|
||||
import "flag"
|
||||
|
||||
type Options struct {
|
||||
InterfaceName *string
|
||||
Caplet *string
|
||||
Debug *bool
|
||||
Silent *bool
|
||||
NoHistory *bool
|
||||
}
|
||||
|
||||
func ParseOptions() (Options, error) {
|
||||
o := Options{
|
||||
InterfaceName: flag.String("iface", "", "Network interface to bind to."),
|
||||
Caplet: flag.String("caplet", "", "Read commands from this file instead of goin into interactive mode."),
|
||||
Debug: flag.Bool("debug", false, "Print debug messages."),
|
||||
Silent: flag.Bool("silent", false, "Suppress all logs which are not errors."),
|
||||
NoHistory: flag.Bool("no-history", false, "Disable history file."),
|
||||
}
|
||||
|
||||
flag.Parse()
|
||||
|
||||
return o, nil
|
||||
}
|
36
core/swag.go
Normal file
36
core/swag.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package core
|
||||
|
||||
// https://misc.flogisoft.com/bash/tip_colors_and_formatting
|
||||
const (
|
||||
BOLD = "\033[1m"
|
||||
DIM = "\033[2m"
|
||||
|
||||
RED = "\033[31m"
|
||||
GREEN = "\033[32m"
|
||||
YELLOW = "\033[33m"
|
||||
|
||||
RESET = "\033[0m"
|
||||
)
|
||||
|
||||
const ON = GREEN + "✔" + RESET
|
||||
const OFF = RED + "✘" + RESET
|
||||
|
||||
func Bold(s string) string {
|
||||
return BOLD + s + RESET
|
||||
}
|
||||
|
||||
func Dim(s string) string {
|
||||
return DIM + s + RESET
|
||||
}
|
||||
|
||||
func Red(s string) string {
|
||||
return RED + s + RESET
|
||||
}
|
||||
|
||||
func Green(s string) string {
|
||||
return GREEN + s + RESET
|
||||
}
|
||||
|
||||
func Yellow(s string) string {
|
||||
return YELLOW + s + RESET
|
||||
}
|
10
firewall/firewall.go
Normal file
10
firewall/firewall.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package firewall
|
||||
|
||||
type FirewallManager interface {
|
||||
IsForwardingEnabled() bool
|
||||
EnableForwarding(enabled bool) error
|
||||
EnableIcmpBcast(enabled bool) error
|
||||
EnableSendRedirects(enabled bool) error
|
||||
EnableRedirection(r *Redirection, enabled bool) error
|
||||
Restore()
|
||||
}
|
178
firewall/firewall_linux.go
Normal file
178
firewall/firewall_linux.go
Normal file
|
@ -0,0 +1,178 @@
|
|||
package firewall
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/evilsocket/bettercap/core"
|
||||
"github.com/op/go-logging"
|
||||
)
|
||||
|
||||
type LinuxFirewall struct {
|
||||
forwarding bool
|
||||
redirections map[string]*Redirection
|
||||
}
|
||||
|
||||
var log = logging.MustGetLogger("mitm")
|
||||
|
||||
const (
|
||||
IPV4ForwardingFile = "/proc/sys/net/ipv4/ip_forward"
|
||||
IPV4ICMPBcastFile = "/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts"
|
||||
IPV4SendRedirectsFile = "/proc/sys/net/ipv4/conf/all/send_redirects"
|
||||
)
|
||||
|
||||
func Make() FirewallManager {
|
||||
firewall := &LinuxFirewall{
|
||||
forwarding: false,
|
||||
redirections: make(map[string]*Redirection, 0),
|
||||
}
|
||||
|
||||
firewall.forwarding = firewall.IsForwardingEnabled()
|
||||
|
||||
log.Debugf("Created Linux Firewall ( forwarding=%v )\n", firewall.forwarding)
|
||||
|
||||
return firewall
|
||||
}
|
||||
|
||||
func (f LinuxFirewall) enableFeature(filename string, enable bool) error {
|
||||
var value string
|
||||
if enable {
|
||||
value = "1"
|
||||
} else {
|
||||
value = "0"
|
||||
}
|
||||
|
||||
fd, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
if _, err = fd.WriteString(value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f LinuxFirewall) IsForwardingEnabled() bool {
|
||||
|
||||
if out, err := ioutil.ReadFile(IPV4ForwardingFile); err != nil {
|
||||
log.Error(err)
|
||||
return false
|
||||
} else {
|
||||
return strings.Trim(string(out), "\r\n\t ") == "1"
|
||||
}
|
||||
}
|
||||
|
||||
func (f LinuxFirewall) EnableForwarding(enabled bool) error {
|
||||
return f.enableFeature(IPV4ForwardingFile, enabled)
|
||||
}
|
||||
|
||||
func (f LinuxFirewall) EnableIcmpBcast(enabled bool) error {
|
||||
return f.enableFeature(IPV4ICMPBcastFile, enabled)
|
||||
}
|
||||
|
||||
func (f LinuxFirewall) EnableSendRedirects(enabled bool) error {
|
||||
return f.enableFeature(IPV4SendRedirectsFile, enabled)
|
||||
}
|
||||
|
||||
func (f *LinuxFirewall) EnableRedirection(r *Redirection, enabled bool) error {
|
||||
var opts []string
|
||||
|
||||
rkey := r.String()
|
||||
_, found := f.redirections[rkey]
|
||||
|
||||
if enabled == true {
|
||||
if found == true {
|
||||
return fmt.Errorf("Redirection '%s' already enabled.", rkey)
|
||||
}
|
||||
|
||||
log.Debugf("Enabling redirection %s\n", rkey)
|
||||
|
||||
f.redirections[rkey] = r
|
||||
|
||||
// accept all
|
||||
if _, err := core.Exec("iptables", []string{"-P", "FORWARD", "ACCEPT"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.SrcAddress == "" {
|
||||
opts = []string{
|
||||
"-t", "nat",
|
||||
"-A", "PREROUTING",
|
||||
"-i", r.Interface,
|
||||
"-p", r.Protocol,
|
||||
"--dport", fmt.Sprintf("%d", r.SrcPort),
|
||||
"-j", "DNAT",
|
||||
"--to", fmt.Sprintf("%s:%d", r.DstAddress, r.DstPort),
|
||||
}
|
||||
} else {
|
||||
opts = []string{
|
||||
"-t", "nat",
|
||||
"-A", "PREROUTING",
|
||||
"-i", r.Interface,
|
||||
"-p", r.Protocol,
|
||||
"-d", r.SrcAddress,
|
||||
"--dport", fmt.Sprintf("%d", r.SrcPort),
|
||||
"-j", "DNAT",
|
||||
"--to", fmt.Sprintf("%s:%d", r.DstAddress, r.DstPort),
|
||||
}
|
||||
}
|
||||
if _, err := core.Exec("iptables", opts); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if found == false {
|
||||
log.Debugf("Did not remove redirection '%s' as it was already removed.\n", r.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debugf("Disabling redirection %s\n", r.String())
|
||||
|
||||
delete(f.redirections, r.String())
|
||||
|
||||
if r.SrcAddress == "" {
|
||||
opts = []string{
|
||||
"-t", "nat",
|
||||
"-D", "PREROUTING",
|
||||
"-i", r.Interface,
|
||||
"-p", r.Protocol,
|
||||
"--dport", fmt.Sprintf("%d", r.SrcPort),
|
||||
"-j", "DNAT",
|
||||
"--to", fmt.Sprintf("%s:%d", r.DstAddress, r.DstPort),
|
||||
}
|
||||
} else {
|
||||
opts = []string{
|
||||
"-t", "nat",
|
||||
"-D", "PREROUTING",
|
||||
"-i", r.Interface,
|
||||
"-p", r.Protocol,
|
||||
"-d", r.SrcAddress,
|
||||
"--dport", fmt.Sprintf("%d", r.SrcPort),
|
||||
"-j", "DNAT",
|
||||
"--to", fmt.Sprintf("%s:%d", r.DstAddress, r.DstPort),
|
||||
}
|
||||
}
|
||||
if _, err := core.Exec("iptables", opts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f LinuxFirewall) Restore() {
|
||||
log.Debugf("Restoring firewall state.\n")
|
||||
for _, r := range f.redirections {
|
||||
if err := f.EnableRedirection(r, false); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := f.EnableForwarding(f.forwarding); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
27
firewall/redirection.go
Normal file
27
firewall/redirection.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package firewall
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Redirection struct {
|
||||
Interface string
|
||||
Protocol string
|
||||
SrcAddress string
|
||||
SrcPort int
|
||||
DstAddress string
|
||||
DstPort int
|
||||
}
|
||||
|
||||
func NewRedirection(iface string, proto string, port_from int, addr_to string, port_to int) *Redirection {
|
||||
return &Redirection{
|
||||
Interface: iface,
|
||||
Protocol: proto,
|
||||
SrcAddress: "",
|
||||
SrcPort: port_from,
|
||||
DstAddress: addr_to,
|
||||
DstPort: port_to,
|
||||
}
|
||||
}
|
||||
|
||||
func (r Redirection) String() string {
|
||||
return fmt.Sprintf("[%s] (%s) %s:%d -> %s:%d", r.Interface, r.Protocol, r.SrcAddress, r.SrcPort, r.DstAddress, r.DstPort)
|
||||
}
|
57
main.go
Normal file
57
main.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/op/go-logging"
|
||||
|
||||
"github.com/evilsocket/bettercap/core"
|
||||
"github.com/evilsocket/bettercap/session"
|
||||
"github.com/evilsocket/bettercap/session/modules"
|
||||
)
|
||||
|
||||
var sess *session.Session
|
||||
var log = logging.MustGetLogger("mitm")
|
||||
var err error
|
||||
|
||||
func main() {
|
||||
if sess, err = session.New(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.Infof("Starting %s v%s\n", core.Name, core.Version)
|
||||
log.Infof("Build: date=%s os=%s arch=%s\n", core.BuildDate, runtime.GOOS, runtime.GOARCH)
|
||||
|
||||
sess.Register(session_modules.NewProber(sess))
|
||||
sess.Register(session_modules.NewDiscovery(sess))
|
||||
sess.Register(session_modules.NewArpSpoofer(sess))
|
||||
sess.Register(session_modules.NewSniffer(sess))
|
||||
sess.Register(session_modules.NewHttpProxy(sess))
|
||||
|
||||
if err = sess.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer sess.Close()
|
||||
|
||||
if *sess.Options.Caplet != "" {
|
||||
if err = sess.RunCaplet(*sess.Options.Caplet); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
for sess.Active {
|
||||
line, err := sess.ReadLine()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if line == "" || line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = sess.Run(line); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
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 "???"
|
||||
}
|
40
packets/arp.go
Normal file
40
packets/arp.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package packets
|
||||
|
||||
import (
|
||||
"github.com/google/gopacket/layers"
|
||||
"net"
|
||||
)
|
||||
|
||||
func NewARPTo(from net.IP, from_hw net.HardwareAddr, to net.IP, to_hw net.HardwareAddr, req uint16) (layers.Ethernet, layers.ARP) {
|
||||
eth := layers.Ethernet{
|
||||
SrcMAC: from_hw,
|
||||
DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
EthernetType: layers.EthernetTypeARP,
|
||||
}
|
||||
arp := layers.ARP{
|
||||
AddrType: layers.LinkTypeEthernet,
|
||||
Protocol: layers.EthernetTypeIPv4,
|
||||
HwAddressSize: 6,
|
||||
ProtAddressSize: 4,
|
||||
Operation: req,
|
||||
SourceHwAddress: from_hw,
|
||||
SourceProtAddress: from.To4(),
|
||||
DstHwAddress: to_hw,
|
||||
DstProtAddress: to.To4(),
|
||||
}
|
||||
|
||||
return eth, arp
|
||||
}
|
||||
func NewARP(from net.IP, from_hw net.HardwareAddr, to net.IP, req uint16) (layers.Ethernet, layers.ARP) {
|
||||
return NewARPTo(from, from_hw, to, []byte{0, 0, 0, 0, 0, 0}, req)
|
||||
}
|
||||
|
||||
func NewARPRequest(from net.IP, from_hw net.HardwareAddr, to net.IP) (error, []byte) {
|
||||
eth, arp := NewARP(from, from_hw, to, layers.ARPRequest)
|
||||
return Serialize(ð, &arp)
|
||||
}
|
||||
|
||||
func NewARPReply(from net.IP, from_hw net.HardwareAddr, to net.IP, to_hw net.HardwareAddr) (error, []byte) {
|
||||
eth, arp := NewARPTo(from, from_hw, to, to_hw, layers.ARPReply)
|
||||
return Serialize(ð, &arp)
|
||||
}
|
58
packets/queue.go
Normal file
58
packets/queue.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package packets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/google/gopacket/pcap"
|
||||
"github.com/op/go-logging"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var log = logging.MustGetLogger("mitm")
|
||||
|
||||
type Queue struct {
|
||||
iface string
|
||||
handle *pcap.Handle
|
||||
lock *sync.Mutex
|
||||
active bool
|
||||
}
|
||||
|
||||
func NewQueue(iface string) (*Queue, error) {
|
||||
log.Debugf("Creating packet queue for interface %s.\n", iface)
|
||||
var err error
|
||||
|
||||
q := &Queue{
|
||||
iface: iface,
|
||||
handle: nil,
|
||||
lock: &sync.Mutex{},
|
||||
active: true,
|
||||
}
|
||||
|
||||
q.handle, err = pcap.OpenLive(iface, 65536, true, pcap.BlockForever)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (q *Queue) Send(raw []byte) error {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
log.Debugf("Sending %d bytes to packet queue.\n", len(raw))
|
||||
if q.active {
|
||||
return q.handle.WritePacketData(raw)
|
||||
} else {
|
||||
return fmt.Errorf("Packet queue is not active.")
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Queue) Stop() {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
log.Debugf("Stopping packet queue.\n")
|
||||
|
||||
q.handle.Close()
|
||||
q.active = false
|
||||
}
|
20
packets/serialize.go
Normal file
20
packets/serialize.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package packets
|
||||
|
||||
import (
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
func Serialize(layers ...gopacket.SerializableLayer) (error, []byte) {
|
||||
// Set up buffer and options for serialization.
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{
|
||||
FixLengths: true,
|
||||
ComputeChecksums: true,
|
||||
}
|
||||
|
||||
if err := gopacket.SerializeLayers(buf, opts, layers...); err != nil {
|
||||
return err, nil
|
||||
}
|
||||
|
||||
return nil, buf.Bytes()
|
||||
}
|
32
packets/udp.go
Normal file
32
packets/udp.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package packets
|
||||
|
||||
import (
|
||||
"github.com/google/gopacket/layers"
|
||||
"net"
|
||||
)
|
||||
|
||||
func NewUDPProbe(from net.IP, from_hw net.HardwareAddr, to net.IP, port int) (error, []byte) {
|
||||
eth := layers.Ethernet{
|
||||
SrcMAC: from_hw,
|
||||
DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
EthernetType: layers.EthernetTypeIPv4,
|
||||
}
|
||||
|
||||
ip4 := layers.IPv4{
|
||||
Protocol: layers.IPProtocolUDP,
|
||||
Version: 4,
|
||||
TTL: 64,
|
||||
SrcIP: from,
|
||||
DstIP: to,
|
||||
}
|
||||
|
||||
udp := layers.UDP{
|
||||
SrcPort: layers.UDPPort(12345),
|
||||
DstPort: layers.UDPPort(port),
|
||||
}
|
||||
udp.Payload = []byte{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef}
|
||||
|
||||
udp.SetNetworkLayerForChecksum(&ip4)
|
||||
|
||||
return Serialize(ð, &ip4, &udp)
|
||||
}
|
28
session/command_handler.go
Normal file
28
session/command_handler.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package session
|
||||
|
||||
import "regexp"
|
||||
|
||||
type CommandHandler struct {
|
||||
Name string
|
||||
Description string
|
||||
Parser *regexp.Regexp
|
||||
Exec func(args []string, s *Session) error
|
||||
}
|
||||
|
||||
func NewCommandHandler(name string, expr string, desc string, exec func(args []string, s *Session) error) CommandHandler {
|
||||
return CommandHandler{
|
||||
Name: name,
|
||||
Description: desc,
|
||||
Parser: regexp.MustCompile(expr),
|
||||
Exec: exec,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *CommandHandler) Parse(line string) (bool, []string) {
|
||||
result := h.Parser.FindStringSubmatch(line)
|
||||
if len(result) == h.Parser.NumSubexp()+1 {
|
||||
return true, result[1:len(result)]
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
}
|
82
session/environment.go
Normal file
82
session/environment.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
package session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Environment struct {
|
||||
Padding int
|
||||
storage map[string]string
|
||||
lock *sync.Mutex
|
||||
}
|
||||
|
||||
func NewEnvironment() *Environment {
|
||||
env := &Environment{
|
||||
Padding: 0,
|
||||
storage: make(map[string]string),
|
||||
lock: &sync.Mutex{},
|
||||
}
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
func (env *Environment) Has(name string) bool {
|
||||
env.lock.Lock()
|
||||
defer env.lock.Unlock()
|
||||
|
||||
_, found := env.storage[name]
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
func (env *Environment) Set(name, value string) string {
|
||||
env.lock.Lock()
|
||||
defer env.lock.Unlock()
|
||||
|
||||
old, _ := env.storage[name]
|
||||
env.storage[name] = value
|
||||
|
||||
if len(name) > env.Padding {
|
||||
env.Padding = len(name)
|
||||
}
|
||||
|
||||
return old
|
||||
}
|
||||
|
||||
func (env *Environment) Get(name string) (bool, string) {
|
||||
env.lock.Lock()
|
||||
defer env.lock.Unlock()
|
||||
|
||||
if value, found := env.storage[name]; found == true {
|
||||
return true, value
|
||||
}
|
||||
|
||||
return false, ""
|
||||
}
|
||||
|
||||
func (env *Environment) GetInt(name string) (error, int) {
|
||||
if found, value := env.Get(name); found == true {
|
||||
if i, err := strconv.Atoi(value); err == nil {
|
||||
return nil, i
|
||||
} else {
|
||||
return err, 0
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("Not found."), 0
|
||||
}
|
||||
|
||||
func (env *Environment) Sorted() []string {
|
||||
env.lock.Lock()
|
||||
defer env.lock.Unlock()
|
||||
|
||||
var keys []string
|
||||
for k := range env.storage {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
77
session/module.go
Normal file
77
session/module.go
Normal file
|
@ -0,0 +1,77 @@
|
|||
package session
|
||||
|
||||
import "sync"
|
||||
|
||||
type Module interface {
|
||||
Name() string
|
||||
Description() string
|
||||
Author() string
|
||||
Handlers() []ModuleHandler
|
||||
Parameters() map[string]*ModuleParam
|
||||
|
||||
Running() bool
|
||||
Start() error
|
||||
Stop() error
|
||||
|
||||
OnSessionStarted(s *Session)
|
||||
OnSessionEnded(s *Session)
|
||||
}
|
||||
|
||||
type SessionModule struct {
|
||||
Session *Session
|
||||
Started bool
|
||||
StatusLock *sync.Mutex
|
||||
|
||||
handlers []ModuleHandler
|
||||
params map[string]*ModuleParam
|
||||
}
|
||||
|
||||
func NewSessionModule(s *Session) SessionModule {
|
||||
m := SessionModule{
|
||||
Session: s,
|
||||
Started: false,
|
||||
StatusLock: &sync.Mutex{},
|
||||
|
||||
handlers: make([]ModuleHandler, 0),
|
||||
params: make(map[string]*ModuleParam),
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *SessionModule) Handlers() []ModuleHandler {
|
||||
return m.handlers
|
||||
}
|
||||
|
||||
func (m *SessionModule) Parameters() map[string]*ModuleParam {
|
||||
return m.params
|
||||
}
|
||||
|
||||
func (m *SessionModule) Param(name string) *ModuleParam {
|
||||
return m.params[name]
|
||||
}
|
||||
|
||||
func (m *SessionModule) AddHandler(h ModuleHandler) {
|
||||
m.handlers = append(m.handlers, h)
|
||||
}
|
||||
|
||||
func (m *SessionModule) AddParam(p *ModuleParam) {
|
||||
m.params[p.Name] = p
|
||||
p.Register(m.Session)
|
||||
}
|
||||
|
||||
func (m *SessionModule) Running() bool {
|
||||
m.StatusLock.Lock()
|
||||
defer m.StatusLock.Unlock()
|
||||
return m.Started
|
||||
}
|
||||
|
||||
func (m *SessionModule) SetRunning(running bool) {
|
||||
m.StatusLock.Lock()
|
||||
defer m.StatusLock.Unlock()
|
||||
m.Started = running
|
||||
}
|
||||
|
||||
func (m *SessionModule) OnSessionStarted(s *Session) {
|
||||
|
||||
}
|
37
session/module_handler.go
Normal file
37
session/module_handler.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/evilsocket/bettercap/core"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type ModuleHandler struct {
|
||||
Name string
|
||||
Description string
|
||||
Parser *regexp.Regexp
|
||||
Exec func(args []string) error
|
||||
}
|
||||
|
||||
func NewModuleHandler(name string, expr string, desc string, exec func(args []string) error) ModuleHandler {
|
||||
return ModuleHandler{
|
||||
Name: name,
|
||||
Description: desc,
|
||||
Parser: regexp.MustCompile(expr),
|
||||
Exec: exec,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ModuleHandler) Help(padding int) string {
|
||||
return fmt.Sprintf(" "+core.Bold("%"+strconv.Itoa(padding)+"s")+" : %s\n", h.Name, h.Description)
|
||||
}
|
||||
|
||||
func (h *ModuleHandler) Parse(line string) (bool, []string) {
|
||||
result := h.Parser.FindStringSubmatch(line)
|
||||
if len(result) == h.Parser.NumSubexp()+1 {
|
||||
return true, result[1:len(result)]
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
}
|
105
session/module_param.go
Normal file
105
session/module_param.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
package session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/evilsocket/bettercap/core"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ParamType int
|
||||
|
||||
const (
|
||||
STRING ParamType = iota
|
||||
BOOL = iota
|
||||
INT = iota
|
||||
)
|
||||
|
||||
type ModuleParam struct {
|
||||
Name string
|
||||
Type ParamType
|
||||
Value string
|
||||
Description string
|
||||
|
||||
Validator *regexp.Regexp
|
||||
}
|
||||
|
||||
func NewModuleParameter(name string, def_value string, t ParamType, validator string, desc string) *ModuleParam {
|
||||
p := &ModuleParam{
|
||||
Name: name,
|
||||
Type: t,
|
||||
Description: desc,
|
||||
Value: def_value,
|
||||
Validator: nil,
|
||||
}
|
||||
|
||||
if validator != "" {
|
||||
p.Validator = regexp.MustCompile(validator)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func NewStringParameter(name string, def_value string, validator string, desc string) *ModuleParam {
|
||||
return NewModuleParameter(name, def_value, STRING, validator, desc)
|
||||
}
|
||||
|
||||
func NewBoolParameter(name string, def_value string, validator string, desc string) *ModuleParam {
|
||||
return NewModuleParameter(name, def_value, BOOL, validator, desc)
|
||||
}
|
||||
|
||||
func NewIntParameter(name string, def_value string, validator string, desc string) *ModuleParam {
|
||||
return NewModuleParameter(name, def_value, INT, validator, desc)
|
||||
}
|
||||
|
||||
func (p ModuleParam) Validate(value string) (error, interface{}) {
|
||||
if p.Validator != nil {
|
||||
if p.Validator.MatchString(value) == false {
|
||||
return fmt.Errorf("Parameter value '%s' does not match validator '%s'.", value, p.Validator.String()), nil
|
||||
}
|
||||
}
|
||||
|
||||
if p.Type == STRING {
|
||||
return nil, value
|
||||
} else if p.Type == BOOL {
|
||||
lvalue := strings.ToLower(value)
|
||||
if lvalue == "true" {
|
||||
return nil, true
|
||||
} else if lvalue == "false" {
|
||||
return nil, false
|
||||
} else {
|
||||
return fmt.Errorf("Can't typecast '%s' to boolean.", value), nil
|
||||
}
|
||||
} else if p.Type == INT {
|
||||
i, err := strconv.Atoi(value)
|
||||
return err, i
|
||||
}
|
||||
|
||||
return fmt.Errorf("Unhandled module parameter type %d.", p.Type), nil
|
||||
}
|
||||
|
||||
func (p ModuleParam) Get(s *Session) (error, interface{}) {
|
||||
var v string
|
||||
var found bool
|
||||
var obj interface{}
|
||||
var err error
|
||||
|
||||
if found, v = s.Env.Get(p.Name); found == false {
|
||||
v = ""
|
||||
}
|
||||
|
||||
err, obj = p.Validate(v)
|
||||
return err, obj
|
||||
|
||||
}
|
||||
|
||||
func (p ModuleParam) Help(padding int) string {
|
||||
return fmt.Sprintf(" "+core.YELLOW+"%"+strconv.Itoa(padding)+"s"+core.RESET+
|
||||
" : "+
|
||||
"%s "+core.DIM+"(default=%s"+core.RESET+")\n", p.Name, p.Description, p.Value)
|
||||
}
|
||||
|
||||
func (p ModuleParam) Register(s *Session) {
|
||||
s.Env.Set(p.Name, p.Value)
|
||||
}
|
BIN
session/modules/.discovery.go.swp
Normal file
BIN
session/modules/.discovery.go.swp
Normal file
Binary file not shown.
214
session/modules/arp_spoof.go
Normal file
214
session/modules/arp_spoof.go
Normal file
|
@ -0,0 +1,214 @@
|
|||
package session_modules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
network "github.com/evilsocket/bettercap/net"
|
||||
"github.com/evilsocket/bettercap/packets"
|
||||
"github.com/evilsocket/bettercap/session"
|
||||
"github.com/malfunkt/iprange"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ArpSpoofer struct {
|
||||
session.SessionModule
|
||||
Done chan bool
|
||||
}
|
||||
|
||||
func NewArpSpoofer(s *session.Session) *ArpSpoofer {
|
||||
p := &ArpSpoofer{
|
||||
SessionModule: session.NewSessionModule(s),
|
||||
Done: make(chan bool),
|
||||
}
|
||||
|
||||
p.AddParam(session.NewStringParameter("arp.spoof.targets", "<entire subnet>", "", "IP addresses to spoof."))
|
||||
|
||||
p.AddHandler(session.NewModuleHandler("arp.spoof (on|off)", "^arp\\.spoof\\s+(on|off)$",
|
||||
"Start/stop ARP spoofer.",
|
||||
func(args []string) error {
|
||||
if args[0] == "on" {
|
||||
return p.Start()
|
||||
} else {
|
||||
return p.Stop()
|
||||
}
|
||||
}))
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p ArpSpoofer) OnSessionStarted(s *session.Session) {
|
||||
// refresh the subnet after session has been created
|
||||
s.Env.Set("arp.spoof.targets", s.Interface.CIDR())
|
||||
}
|
||||
|
||||
func (p ArpSpoofer) OnSessionEnded(s *session.Session) {
|
||||
if p.Running() {
|
||||
p.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
func (p ArpSpoofer) Name() string {
|
||||
return "ARP Spoofer"
|
||||
}
|
||||
|
||||
func (p ArpSpoofer) Description() string {
|
||||
return "Keep spoofing selected hosts on the network."
|
||||
}
|
||||
|
||||
func (p ArpSpoofer) Author() string {
|
||||
return "Simone Margaritelli <evilsocket@protonmail.com>"
|
||||
}
|
||||
|
||||
func (p *ArpSpoofer) shouldSpoof(ip net.IP) bool {
|
||||
addr := ip.String()
|
||||
if ip.IsLoopback() == true {
|
||||
return false
|
||||
} else if addr == p.Session.Interface.IpAddress {
|
||||
return false
|
||||
} else if addr == p.Session.Gateway.IpAddress {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *ArpSpoofer) getMAC(ip net.IP, probe bool) (net.HardwareAddr, error) {
|
||||
var mac string
|
||||
var hw net.HardwareAddr
|
||||
var err error
|
||||
|
||||
// do we have this ip mac address?
|
||||
mac, err = network.ArpLookup(p.Session.Interface.Name(), ip.String(), false)
|
||||
if err != nil && probe == true {
|
||||
from := p.Session.Interface.IP
|
||||
from_hw := p.Session.Interface.HW
|
||||
|
||||
if err, probe := packets.NewUDPProbe(from, from_hw, ip, 139); err != nil {
|
||||
log.Errorf("Error while creating UDP probe packet for %s: %s\n", ip.String(), err)
|
||||
} else {
|
||||
p.Session.Queue.Send(probe)
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
mac, err = network.ArpLookup(p.Session.Interface.Name(), ip.String(), false)
|
||||
}
|
||||
|
||||
if mac == "" {
|
||||
return nil, fmt.Errorf("Could not find hardware address for %s.", ip.String())
|
||||
}
|
||||
|
||||
hw, err = net.ParseMAC(mac)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error while parsing hardware address '%s' for %s: %s", mac, ip.String(), err)
|
||||
}
|
||||
|
||||
return hw, nil
|
||||
}
|
||||
|
||||
func (p *ArpSpoofer) sendArp(addresses []net.IP, saddr net.IP, smac net.HardwareAddr, check_running bool, probe bool) {
|
||||
for _, ip := range addresses {
|
||||
if p.shouldSpoof(ip) == false {
|
||||
log.Debugf("Skipping address %s from ARP spoofing.\n", ip)
|
||||
continue
|
||||
}
|
||||
|
||||
// do we have this ip mac address?
|
||||
hw, err := p.getMAC(ip, probe)
|
||||
if err != nil {
|
||||
log.Debugf("Error while looking up hardware address for %s: %s\n", ip.String(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err, pkt := packets.NewARPReply(saddr, smac, ip, hw); err != nil {
|
||||
log.Errorf("Error while creating ARP spoof packet for %s: %s\n", ip.String(), err)
|
||||
} else {
|
||||
log.Debugf("Sending %d bytes of ARP packet to %s:%s.\n", len(pkt), ip.String(), hw.String())
|
||||
p.Session.Queue.Send(pkt)
|
||||
}
|
||||
|
||||
if check_running && p.Running() == false {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ArpSpoofer) unSpoof() error {
|
||||
var targets string
|
||||
|
||||
if err, v := p.Param("arp.spoof.targets").Get(p.Session); err != nil {
|
||||
return err
|
||||
} else {
|
||||
targets = v.(string)
|
||||
}
|
||||
|
||||
list, err := iprange.Parse(targets)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error while parsing arp.spoof.targets variable '%s': %s.", targets, err)
|
||||
}
|
||||
addresses := list.Expand()
|
||||
|
||||
from := p.Session.Gateway.IP
|
||||
from_hw := p.Session.Gateway.HW
|
||||
|
||||
log.Infof("Restoring ARP cache of %d targets (%s).\n", len(addresses), targets)
|
||||
|
||||
p.sendArp(addresses, from, from_hw, false, false)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ArpSpoofer) Start() error {
|
||||
if p.Running() == false {
|
||||
var targets string
|
||||
|
||||
if err, v := p.Param("arp.spoof.targets").Get(p.Session); err != nil {
|
||||
return err
|
||||
} else {
|
||||
targets = v.(string)
|
||||
}
|
||||
|
||||
list, err := iprange.Parse(targets)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error while parsing arp.spoof.targets variable '%s': %s.", targets, err)
|
||||
}
|
||||
addresses := list.Expand()
|
||||
|
||||
p.SetRunning(true)
|
||||
|
||||
go func() {
|
||||
|
||||
from := p.Session.Gateway.IP
|
||||
from_hw := p.Session.Interface.HW
|
||||
|
||||
log.Infof("ARP spoofer started, probing %d targets (%s).\n", len(addresses), targets)
|
||||
|
||||
for p.Running() {
|
||||
p.sendArp(addresses, from, from_hw, true, false)
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
p.Done <- true
|
||||
log.Info("ARP spoofer stopped.\n")
|
||||
}()
|
||||
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("ARP spoofer already started.")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ArpSpoofer) Stop() error {
|
||||
if p.Running() == true {
|
||||
p.SetRunning(false)
|
||||
|
||||
log.Info("Waiting for ARP spoofer to stop ...\n")
|
||||
|
||||
<-p.Done
|
||||
|
||||
p.unSpoof()
|
||||
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("ARP spoofer already stopped.")
|
||||
}
|
||||
}
|
264
session/modules/http_proxy.go
Normal file
264
session/modules/http_proxy.go
Normal file
|
@ -0,0 +1,264 @@
|
|||
package session_modules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/op/go-logging"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/elazarl/goproxy"
|
||||
"github.com/elazarl/goproxy/ext/html"
|
||||
|
||||
"github.com/evilsocket/bettercap/firewall"
|
||||
"github.com/evilsocket/bettercap/session"
|
||||
)
|
||||
|
||||
var log = logging.MustGetLogger("mitm")
|
||||
|
||||
type ProxyFilter struct {
|
||||
Type string
|
||||
Expression string
|
||||
Replace string
|
||||
Compiled *regexp.Regexp
|
||||
}
|
||||
|
||||
func tokenize(s string, sep byte, n int) (error, []string) {
|
||||
filtered := make([]string, 0)
|
||||
tokens := strings.Split(s, string(sep))
|
||||
|
||||
for _, t := range tokens {
|
||||
if t != "" {
|
||||
filtered = append(filtered, t)
|
||||
}
|
||||
}
|
||||
|
||||
if len(filtered) != n {
|
||||
return fmt.Errorf("Could not split '%s' by '%s'.", s, string(sep)), filtered
|
||||
} else {
|
||||
return nil, filtered
|
||||
}
|
||||
}
|
||||
|
||||
func NewProxyFilter(type_, expression string) (error, *ProxyFilter) {
|
||||
err, tokens := tokenize(expression, expression[0], 2)
|
||||
if err != nil {
|
||||
return err, nil
|
||||
}
|
||||
|
||||
filter := &ProxyFilter{
|
||||
Type: type_,
|
||||
Expression: tokens[0],
|
||||
Replace: tokens[1],
|
||||
Compiled: nil,
|
||||
}
|
||||
|
||||
if filter.Compiled, err = regexp.Compile(filter.Expression); err != nil {
|
||||
return err, nil
|
||||
}
|
||||
|
||||
return nil, filter
|
||||
}
|
||||
|
||||
func (f *ProxyFilter) Process(req *http.Request, response_body string) string {
|
||||
orig := response_body
|
||||
filtered := f.Compiled.ReplaceAllString(orig, f.Replace)
|
||||
|
||||
// TODO: this sucks
|
||||
if orig != filtered {
|
||||
log.Infof("%s > Applied %s-filtering to %d of response body.", req.RemoteAddr, f.Type, len(filtered))
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
type HttpProxy struct {
|
||||
session.SessionModule
|
||||
|
||||
address string
|
||||
redirection *firewall.Redirection
|
||||
server http.Server
|
||||
proxy *goproxy.ProxyHttpServer
|
||||
|
||||
pre_filter *ProxyFilter
|
||||
post_filter *ProxyFilter
|
||||
}
|
||||
|
||||
func NewHttpProxy(s *session.Session) *HttpProxy {
|
||||
p := &HttpProxy{
|
||||
SessionModule: session.NewSessionModule(s),
|
||||
proxy: goproxy.NewProxyHttpServer(),
|
||||
address: "",
|
||||
redirection: nil,
|
||||
pre_filter: nil,
|
||||
post_filter: nil,
|
||||
}
|
||||
|
||||
p.AddParam(session.NewIntParameter("http.port", "80", "", "HTTP port to redirect when the proxy is activated."))
|
||||
p.AddParam(session.NewIntParameter("http.proxy.port", "8080", "", "Port to bind the HTTP proxy to."))
|
||||
p.AddParam(session.NewStringParameter("http.proxy.address", "<interface address>", `^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$`, "Address to bind the HTTP proxy to."))
|
||||
p.AddParam(session.NewStringParameter("http.proxy.post.filter", "", "", "SED like syntax to replace things in the response ( example |</head>|<script src='...'></script></head>| )."))
|
||||
|
||||
p.AddHandler(session.NewModuleHandler("http.proxy (on|off)", "^http\\.proxy (on|off)$",
|
||||
"Start/stop HTTP proxy.",
|
||||
func(args []string) error {
|
||||
if args[0] == "on" {
|
||||
return p.Start()
|
||||
} else {
|
||||
return p.Stop()
|
||||
}
|
||||
}))
|
||||
|
||||
p.proxy.NonproxyHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
if p.doProxy(req) == true {
|
||||
req.URL.Scheme = "http"
|
||||
req.URL.Host = req.Host
|
||||
|
||||
// TODO: p.pre_filter.Process
|
||||
|
||||
p.proxy.ServeHTTP(w, req)
|
||||
} else {
|
||||
log.Infof("Skipping %s\n", req.Host)
|
||||
}
|
||||
})
|
||||
|
||||
p.proxy.OnResponse(goproxy_html.IsHtml).Do(goproxy_html.HandleString(func(body string, ctx *goproxy.ProxyCtx) string {
|
||||
if p.post_filter != nil {
|
||||
body = p.post_filter.Process(ctx.Req, body)
|
||||
}
|
||||
return body
|
||||
}))
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p HttpProxy) Name() string {
|
||||
return "HTTP Proxy"
|
||||
}
|
||||
|
||||
func (p HttpProxy) Description() string {
|
||||
return "A full featured HTTP proxy that can be used to inject malicious contents into webpages, all HTTP traffic will be redirected to it."
|
||||
}
|
||||
|
||||
func (p HttpProxy) Author() string {
|
||||
return "Simone Margaritelli <evilsocket@protonmail.com>"
|
||||
}
|
||||
|
||||
func (p HttpProxy) OnSessionStarted(s *session.Session) {
|
||||
// refresh the address after session has been created
|
||||
s.Env.Set("http.proxy.address", s.Interface.IpAddress)
|
||||
}
|
||||
|
||||
func (p HttpProxy) OnSessionEnded(s *session.Session) {
|
||||
if p.Running() {
|
||||
p.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *HttpProxy) Start() error {
|
||||
var http_port int
|
||||
var proxy_port int
|
||||
|
||||
if p.Running() == true {
|
||||
return fmt.Errorf("HTTP proxy already started.")
|
||||
}
|
||||
|
||||
if err, v := p.Param("http.proxy.address").Get(p.Session); err != nil {
|
||||
return err
|
||||
} else {
|
||||
p.address = v.(string)
|
||||
}
|
||||
|
||||
if err, v := p.Param("http.proxy.port").Get(p.Session); err != nil {
|
||||
return err
|
||||
} else {
|
||||
proxy_port = v.(int)
|
||||
}
|
||||
|
||||
if err, v := p.Param("http.port").Get(p.Session); err != nil {
|
||||
return err
|
||||
} else {
|
||||
http_port = v.(int)
|
||||
}
|
||||
|
||||
p.post_filter = nil
|
||||
if err, v := p.Param("http.proxy.post.filter").Get(p.Session); err != nil {
|
||||
return err
|
||||
} else {
|
||||
expression := v.(string)
|
||||
if expression != "" {
|
||||
if err, p.post_filter = NewProxyFilter("post", expression); err != nil {
|
||||
return err
|
||||
} else {
|
||||
log.Debug("Proxy POST filter set to '%s'.", expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if p.Session.Firewall.IsForwardingEnabled() == false {
|
||||
p.Session.Firewall.EnableForwarding(true)
|
||||
}
|
||||
|
||||
p.redirection = firewall.NewRedirection(p.Session.Interface.Name(),
|
||||
"TCP",
|
||||
http_port,
|
||||
p.address,
|
||||
proxy_port)
|
||||
|
||||
if err := p.Session.Firewall.EnableRedirection(p.redirection, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
address := fmt.Sprintf("%s:%d", p.address, proxy_port)
|
||||
log.Infof("Starting proxy on %s.\n", address)
|
||||
|
||||
p.server = http.Server{Addr: address, Handler: p.proxy}
|
||||
go func() {
|
||||
p.SetRunning(true)
|
||||
if err := p.server.ListenAndServe(); err != nil {
|
||||
p.SetRunning(false)
|
||||
log.Warning(err)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *HttpProxy) Stop() error {
|
||||
if p.Running() == true {
|
||||
p.SetRunning(false)
|
||||
p.server.Shutdown(nil)
|
||||
log.Info("HTTP proxy stopped.\n")
|
||||
if p.redirection != nil {
|
||||
if err := p.Session.Firewall.EnableRedirection(p.redirection, false); err != nil {
|
||||
return err
|
||||
}
|
||||
p.redirection = nil
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("HTTP proxy stopped.")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *HttpProxy) doProxy(req *http.Request) bool {
|
||||
blacklist := []string{
|
||||
"localhost",
|
||||
"127.0.0.1",
|
||||
p.address,
|
||||
}
|
||||
|
||||
if req.Host == "" {
|
||||
log.Errorf("Got request with empty host: %v\n", req)
|
||||
return false
|
||||
}
|
||||
|
||||
for _, blacklisted := range blacklist {
|
||||
if strings.HasPrefix(req.Host, blacklisted) {
|
||||
log.Errorf("Got request with blacklisted host: %s\n", req.Host)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
138
session/modules/net_probe.go
Normal file
138
session/modules/net_probe.go
Normal file
|
@ -0,0 +1,138 @@
|
|||
package session_modules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
// "github.com/evilsocket/bettercap/packets"
|
||||
"github.com/evilsocket/bettercap/session"
|
||||
"github.com/malfunkt/iprange"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Prober struct {
|
||||
session.SessionModule
|
||||
}
|
||||
|
||||
func NewProber(s *session.Session) *Prober {
|
||||
p := &Prober{
|
||||
SessionModule: session.NewSessionModule(s),
|
||||
}
|
||||
|
||||
p.AddParam(session.NewIntParameter("net.probe.throttle", "10", "", "If greater than 0, probe packets will be throttled by this value in milliseconds."))
|
||||
|
||||
p.AddHandler(session.NewModuleHandler("net.probe (on|off)", "^net\\.probe\\s+(on|off)$",
|
||||
"Start/stop network hosts probing in background.",
|
||||
func(args []string) error {
|
||||
if args[0] == "on" {
|
||||
return p.Start()
|
||||
} else {
|
||||
return p.Stop()
|
||||
}
|
||||
}))
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p Prober) Name() string {
|
||||
return "Network Prober"
|
||||
}
|
||||
|
||||
func (p Prober) Description() string {
|
||||
return "Keep probing for new hosts on the network by sending dummy UDP packets to every possible IP on the subnet."
|
||||
}
|
||||
|
||||
func (p Prober) Author() string {
|
||||
return "Simone Margaritelli <evilsocket@protonmail.com>"
|
||||
}
|
||||
|
||||
func (p *Prober) shouldProbe(ip net.IP) bool {
|
||||
addr := ip.String()
|
||||
if ip.IsLoopback() == true {
|
||||
return false
|
||||
} else if addr == p.Session.Interface.IpAddress {
|
||||
return false
|
||||
} else if addr == p.Session.Gateway.IpAddress {
|
||||
return false
|
||||
} else if p.Session.Targets.Has(addr) == true {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p Prober) OnSessionEnded(s *session.Session) {
|
||||
if p.Running() {
|
||||
p.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prober) sendUDP(from net.IP, from_hw net.HardwareAddr, ip net.IP) {
|
||||
name := fmt.Sprintf("%s:137", ip)
|
||||
if addr, err := net.ResolveUDPAddr("udp", name); err != nil {
|
||||
log.Errorf("Could not resolve %s.", name)
|
||||
} else if con, err := net.DialUDP("udp", nil, addr); err != nil {
|
||||
log.Errorf("Could not dial %s.", name)
|
||||
} else {
|
||||
// log.Debugf("UDP connection to %s enstablished.\n", name)
|
||||
defer con.Close()
|
||||
con.Write([]byte{0xde, 0xad, 0xbe, 0xef})
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prober) Start() error {
|
||||
if p.Running() == false {
|
||||
throttle := int(0)
|
||||
if err, v := p.Param("net.probe.throttle").Get(p.Session); err != nil {
|
||||
return err
|
||||
} else {
|
||||
throttle = v.(int)
|
||||
log.Debugf("Throttling packets of %d ms.\n", throttle)
|
||||
}
|
||||
|
||||
p.SetRunning(true)
|
||||
|
||||
go func() {
|
||||
list, err := iprange.Parse(p.Session.Interface.CIDR())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
from := p.Session.Interface.IP
|
||||
from_hw := p.Session.Interface.HW
|
||||
addresses := list.Expand()
|
||||
|
||||
log.Infof("Network prober started, probing %d possible addresses.\n", len(addresses))
|
||||
|
||||
for p.Running() {
|
||||
for _, ip := range addresses {
|
||||
if p.shouldProbe(ip) == false {
|
||||
log.Debugf("Skipping address %s from UDP probing.\n", ip)
|
||||
continue
|
||||
}
|
||||
|
||||
p.sendUDP(from, from_hw, ip)
|
||||
|
||||
if throttle > 0 {
|
||||
time.Sleep(time.Duration(throttle) * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
log.Info("Network prober stopped.\n")
|
||||
}()
|
||||
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("Network prober already started.")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prober) Stop() error {
|
||||
if p.Running() == true {
|
||||
p.SetRunning(false)
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("Network prober already stopped.")
|
||||
}
|
||||
}
|
132
session/modules/net_recon.go
Normal file
132
session/modules/net_recon.go
Normal file
|
@ -0,0 +1,132 @@
|
|||
package session_modules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/evilsocket/bettercap/net"
|
||||
"github.com/evilsocket/bettercap/session"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Discovery struct {
|
||||
session.SessionModule
|
||||
|
||||
refresh int
|
||||
before net.ArpTable
|
||||
current net.ArpTable
|
||||
quit chan bool
|
||||
}
|
||||
|
||||
func NewDiscovery(s *session.Session) *Discovery {
|
||||
d := &Discovery{
|
||||
SessionModule: session.NewSessionModule(s),
|
||||
|
||||
refresh: 1,
|
||||
before: nil,
|
||||
current: nil,
|
||||
quit: make(chan bool),
|
||||
}
|
||||
|
||||
d.AddHandler(session.NewModuleHandler("net.recon (on|off)", "^net\\.recon\\s+(on|off)$",
|
||||
"Start/stop network hosts discovery in background.",
|
||||
func(args []string) error {
|
||||
if args[0] == "on" {
|
||||
return d.Start()
|
||||
} else {
|
||||
return d.Stop()
|
||||
}
|
||||
}))
|
||||
|
||||
d.AddHandler(session.NewModuleHandler("net.show", "^net\\.show$",
|
||||
"Show current hosts list.",
|
||||
func(args []string) error {
|
||||
return d.Show()
|
||||
}))
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func (d Discovery) Name() string {
|
||||
return "Network Recon"
|
||||
}
|
||||
|
||||
func (d Discovery) Description() string {
|
||||
return "Read periodically the ARP cache in order to monitor for new hosts on the network."
|
||||
}
|
||||
|
||||
func (d Discovery) Author() string {
|
||||
return "Simone Margaritelli <evilsocket@protonmail.com>"
|
||||
}
|
||||
|
||||
func (d Discovery) OnSessionEnded(s *session.Session) {
|
||||
if d.Running() {
|
||||
d.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Discovery) Start() error {
|
||||
if d.Running() == false {
|
||||
d.SetRunning(true)
|
||||
|
||||
go func() {
|
||||
log.Info("Network discovery started.\n")
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-time.After(time.Duration(d.refresh) * time.Second):
|
||||
var err error
|
||||
|
||||
if d.current, err = net.ArpUpdate(d.Session.Interface.Name()); err != nil {
|
||||
log.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
var new net.ArpTable = make(net.ArpTable)
|
||||
var rem net.ArpTable = make(net.ArpTable)
|
||||
|
||||
if d.before != nil {
|
||||
new = net.ArpDiff(d.current, d.before)
|
||||
rem = net.ArpDiff(d.before, d.current)
|
||||
} else {
|
||||
new = d.current
|
||||
}
|
||||
|
||||
if len(new) > 0 || len(rem) > 0 {
|
||||
// refresh target pool
|
||||
for ip, mac := range new {
|
||||
d.Session.Targets.AddIfNotExist(ip, mac)
|
||||
}
|
||||
|
||||
for ip, mac := range rem {
|
||||
d.Session.Targets.Remove(ip, mac)
|
||||
}
|
||||
}
|
||||
|
||||
d.before = d.current
|
||||
|
||||
case <-d.quit:
|
||||
log.Info("Network discovery stopped.\n")
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("Network discovery already started.")
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Discovery) Show() error {
|
||||
d.Session.Targets.Dump()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Discovery) Stop() error {
|
||||
if d.Running() == true {
|
||||
d.SetRunning(false)
|
||||
d.quit <- true
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("Network discovery already stopped.")
|
||||
}
|
||||
}
|
275
session/modules/net_sniff.go
Normal file
275
session/modules/net_sniff.go
Normal file
|
@ -0,0 +1,275 @@
|
|||
package session_modules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/evilsocket/bettercap/core"
|
||||
"github.com/evilsocket/bettercap/session"
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/google/gopacket/pcap"
|
||||
"github.com/google/gopacket/pcapgo"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type SnifferContext struct {
|
||||
Handle *pcap.Handle
|
||||
DumpLocal bool
|
||||
Verbose bool
|
||||
Filter string
|
||||
Expression string
|
||||
Compiled *regexp.Regexp
|
||||
Output string
|
||||
OutputFile *os.File
|
||||
OutputWriter *pcapgo.Writer
|
||||
}
|
||||
|
||||
func NewSnifferContext() *SnifferContext {
|
||||
return &SnifferContext{
|
||||
Handle: nil,
|
||||
DumpLocal: false,
|
||||
Verbose: true,
|
||||
Filter: "",
|
||||
Expression: "",
|
||||
Compiled: nil,
|
||||
Output: "",
|
||||
OutputFile: nil,
|
||||
OutputWriter: nil,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
no = core.Red("no")
|
||||
yes = core.Green("yes")
|
||||
)
|
||||
|
||||
func (c *SnifferContext) Log() {
|
||||
log.Info("\n")
|
||||
|
||||
if c.DumpLocal {
|
||||
log.Info(" Skip local packets : " + no)
|
||||
} else {
|
||||
log.Info(" Skip local packets : " + yes)
|
||||
}
|
||||
|
||||
if c.Verbose {
|
||||
log.Info(" Verbose : " + yes)
|
||||
} else {
|
||||
log.Info(" Verbose : " + no)
|
||||
}
|
||||
|
||||
if c.Filter != "" {
|
||||
log.Info(" BPF Filter : '" + core.Yellow(c.Filter) + "'")
|
||||
}
|
||||
|
||||
if c.Expression != "" {
|
||||
log.Info(" Regular expression : '" + core.Yellow(c.Expression) + "'")
|
||||
}
|
||||
|
||||
if c.Output != "" {
|
||||
log.Info(" File output : '" + core.Yellow(c.Output) + "'")
|
||||
}
|
||||
|
||||
log.Info("\n")
|
||||
}
|
||||
|
||||
func (c *SnifferContext) Close() {
|
||||
if c.Handle != nil {
|
||||
c.Handle.Close()
|
||||
c.Handle = nil
|
||||
}
|
||||
|
||||
if c.OutputFile != nil {
|
||||
c.OutputFile.Close()
|
||||
c.OutputFile = nil
|
||||
}
|
||||
}
|
||||
|
||||
type Sniffer struct {
|
||||
session.SessionModule
|
||||
}
|
||||
|
||||
func NewSniffer(s *session.Session) *Sniffer {
|
||||
sniff := &Sniffer{
|
||||
SessionModule: session.NewSessionModule(s),
|
||||
}
|
||||
|
||||
sniff.AddParam(session.NewBoolParameter("net.sniffer.verbose", "true", "", "Print captured packets to screen."))
|
||||
sniff.AddParam(session.NewBoolParameter("net.sniffer.local", "false", "", "If true it will consider packets from/to this computer, otherwise it will skip them."))
|
||||
sniff.AddParam(session.NewStringParameter("net.sniffer.filter", "not arp", "", "BPF filter for the sniffer."))
|
||||
sniff.AddParam(session.NewStringParameter("net.sniffer.regexp", "", "", "If filled, only packets matching this regular expression will be considered."))
|
||||
sniff.AddParam(session.NewStringParameter("net.sniffer.output", "", "", "If set, the sniffer will write captured packets to this file."))
|
||||
|
||||
sniff.AddHandler(session.NewModuleHandler("net.sniffer (on|off)", "^net\\.sniffer\\s+(on|off)$",
|
||||
"Start/stop network sniffer in background.",
|
||||
func(args []string) error {
|
||||
if args[0] == "on" {
|
||||
return sniff.Start()
|
||||
} else {
|
||||
return sniff.Stop()
|
||||
}
|
||||
}))
|
||||
|
||||
return sniff
|
||||
}
|
||||
|
||||
func (s Sniffer) Name() string {
|
||||
return "Network Sniffer"
|
||||
}
|
||||
|
||||
func (s Sniffer) Description() string {
|
||||
return "Sniff packets from the network."
|
||||
}
|
||||
|
||||
func (s Sniffer) Author() string {
|
||||
return "Simone Margaritelli <evilsocket@protonmail.com>"
|
||||
}
|
||||
|
||||
func (sn Sniffer) OnSessionEnded(s *session.Session) {
|
||||
if sn.Running() {
|
||||
sn.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
func same(a, b net.HardwareAddr) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for idx, v := range a {
|
||||
if b[idx] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s Sniffer) isLocalPacket(packet gopacket.Packet) bool {
|
||||
local_hw := s.Session.Interface.HW
|
||||
eth := packet.Layer(layers.LayerTypeEthernet)
|
||||
if eth != nil {
|
||||
eth_packet, _ := eth.(*layers.Ethernet)
|
||||
if same(eth_packet.SrcMAC, local_hw) || same(eth_packet.DstMAC, local_hw) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Sniffer) GetContext() (error, *SnifferContext) {
|
||||
var err error
|
||||
|
||||
ctx := NewSnifferContext()
|
||||
|
||||
if ctx.Handle, err = pcap.OpenLive(s.Session.Interface.Name(), 65536, true, pcap.BlockForever); err != nil {
|
||||
return err, ctx
|
||||
}
|
||||
|
||||
if err, v := s.Param("net.sniffer.verbose").Get(s.Session); err != nil {
|
||||
return err, ctx
|
||||
} else {
|
||||
ctx.Verbose = v.(bool)
|
||||
}
|
||||
|
||||
if err, v := s.Param("net.sniffer.local").Get(s.Session); err != nil {
|
||||
return err, ctx
|
||||
} else {
|
||||
ctx.DumpLocal = v.(bool)
|
||||
}
|
||||
|
||||
if err, v := s.Param("net.sniffer.filter").Get(s.Session); err != nil {
|
||||
return err, ctx
|
||||
} else {
|
||||
if ctx.Filter = v.(string); ctx.Filter != "" {
|
||||
err = ctx.Handle.SetBPFFilter(ctx.Filter)
|
||||
if err != nil {
|
||||
return err, ctx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err, v := s.Param("net.sniffer.regexp").Get(s.Session); err != nil {
|
||||
return err, ctx
|
||||
} else {
|
||||
if ctx.Expression = v.(string); ctx.Expression != "" {
|
||||
if ctx.Compiled, err = regexp.Compile(ctx.Expression); err != nil {
|
||||
return err, ctx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err, v := s.Param("net.sniffer.output").Get(s.Session); err != nil {
|
||||
return err, ctx
|
||||
} else {
|
||||
if ctx.Output = v.(string); ctx.Output != "" {
|
||||
if ctx.OutputFile, err = os.Create(ctx.Output); err != nil {
|
||||
return err, ctx
|
||||
}
|
||||
|
||||
ctx.OutputWriter = pcapgo.NewWriter(ctx.OutputFile)
|
||||
ctx.OutputWriter.WriteFileHeader(65536, layers.LinkTypeEthernet)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ctx
|
||||
}
|
||||
|
||||
func (s *Sniffer) Start() error {
|
||||
if s.Running() == false {
|
||||
var err error
|
||||
var ctx *SnifferContext
|
||||
|
||||
if err, ctx = s.GetContext(); err != nil {
|
||||
if ctx != nil {
|
||||
ctx.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
s.SetRunning(true)
|
||||
|
||||
go func() {
|
||||
defer ctx.Close()
|
||||
|
||||
log.Info("Network sniffer started.\n")
|
||||
ctx.Log()
|
||||
|
||||
src := gopacket.NewPacketSource(ctx.Handle, ctx.Handle.LinkType())
|
||||
for packet := range src.Packets() {
|
||||
if s.Running() == false {
|
||||
break
|
||||
}
|
||||
|
||||
if ctx.DumpLocal == true || s.isLocalPacket(packet) == false {
|
||||
data := packet.Data()
|
||||
if ctx.Compiled == nil || ctx.Compiled.Match(data) == true {
|
||||
if ctx.Verbose {
|
||||
fmt.Println(packet.Dump())
|
||||
}
|
||||
|
||||
if ctx.OutputWriter != nil {
|
||||
ctx.OutputWriter.WritePacket(packet.Metadata().CaptureInfo, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Network sniffer stopped.\n")
|
||||
}()
|
||||
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("Network sniffer already started.")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Sniffer) Stop() error {
|
||||
if s.Running() == true {
|
||||
s.SetRunning(false)
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("Network sniffer already stopped.")
|
||||
}
|
||||
}
|
408
session/session.go
Normal file
408
session/session.go
Normal file
|
@ -0,0 +1,408 @@
|
|||
package session
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"os/user"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/chzyer/readline"
|
||||
"github.com/op/go-logging"
|
||||
|
||||
"github.com/evilsocket/bettercap/core"
|
||||
"github.com/evilsocket/bettercap/firewall"
|
||||
"github.com/evilsocket/bettercap/net"
|
||||
"github.com/evilsocket/bettercap/packets"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
Options core.Options
|
||||
Interface *net.Endpoint
|
||||
Gateway *net.Endpoint
|
||||
Firewall firewall.FirewallManager
|
||||
Env *Environment
|
||||
Targets *Targets
|
||||
Queue *packets.Queue
|
||||
Input *readline.Instance
|
||||
Active bool
|
||||
|
||||
// Watcher *discovery.Watcher
|
||||
CoreHandlers []CommandHandler
|
||||
Modules []Module
|
||||
HelpPadding int
|
||||
}
|
||||
|
||||
func New() (*Session, error) {
|
||||
var err error
|
||||
|
||||
s := &Session{
|
||||
Env: NewEnvironment(),
|
||||
Active: false,
|
||||
Queue: nil,
|
||||
|
||||
CoreHandlers: make([]CommandHandler, 0),
|
||||
Modules: make([]Module, 0),
|
||||
HelpPadding: 0,
|
||||
}
|
||||
|
||||
if s.Options, err = core.ParseOptions(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if u, err := user.Current(); err != nil {
|
||||
return nil, err
|
||||
} else if u.Uid != "0" {
|
||||
return nil, fmt.Errorf("This software must run as root.")
|
||||
}
|
||||
|
||||
// setup logging
|
||||
if *s.Options.Debug == true {
|
||||
logging.SetLevel(logging.DEBUG, "")
|
||||
} else if *s.Options.Silent == true {
|
||||
logging.SetLevel(logging.ERROR, "")
|
||||
} else {
|
||||
logging.SetLevel(logging.INFO, "")
|
||||
}
|
||||
|
||||
s.registerCoreHandlers()
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *Session) registerCoreHandlers() {
|
||||
s.CoreHandlers = append(s.CoreHandlers, NewCommandHandler("help", "^(help|\\?)$",
|
||||
"Display list of available commands.",
|
||||
func(args []string, s *Session) error {
|
||||
fmt.Println()
|
||||
fmt.Printf("Basic commands:\n\n")
|
||||
for _, h := range s.CoreHandlers {
|
||||
fmt.Printf(" "+core.Bold("%"+strconv.Itoa(s.HelpPadding)+"s")+" : %s\n", h.Name, h.Description)
|
||||
}
|
||||
|
||||
sort.Slice(s.Modules, func(i, j int) bool {
|
||||
return s.Modules[i].Name() < s.Modules[j].Name()
|
||||
})
|
||||
|
||||
for _, m := range s.Modules {
|
||||
fmt.Println()
|
||||
status := ""
|
||||
if m.Running() {
|
||||
status = core.Green("active")
|
||||
} else {
|
||||
status = core.Red("not active")
|
||||
}
|
||||
fmt.Printf("%s [%s]\n", m.Name(), status)
|
||||
fmt.Println(core.Dim(m.Description()) + "\n")
|
||||
for _, h := range m.Handlers() {
|
||||
fmt.Printf(h.Help(s.HelpPadding))
|
||||
}
|
||||
|
||||
params := m.Parameters()
|
||||
if len(params) > 0 {
|
||||
fmt.Printf("\n Parameters\n\n")
|
||||
for _, p := range params {
|
||||
fmt.Printf(p.Help(s.HelpPadding))
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}))
|
||||
|
||||
s.CoreHandlers = append(s.CoreHandlers, NewCommandHandler("active", "^active$",
|
||||
"Show information about active modules.",
|
||||
func(args []string, s *Session) error {
|
||||
for _, m := range s.Modules {
|
||||
if m.Running() == false {
|
||||
continue
|
||||
}
|
||||
fmt.Printf("[%s] %s (%s)\n", core.Green("active"), m.Name(), core.Dim(m.Description()))
|
||||
params := m.Parameters()
|
||||
if len(params) > 0 {
|
||||
for _, p := range params {
|
||||
_, p.Value = s.Env.Get(p.Name)
|
||||
fmt.Printf(" %s: '%s'\n", p.Name, core.Yellow(p.Value))
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}))
|
||||
|
||||
s.CoreHandlers = append(s.CoreHandlers, NewCommandHandler("exit", "^(q|quit|e|exit)$",
|
||||
"Close the session and exit.",
|
||||
func(args []string, s *Session) error {
|
||||
s.Active = false
|
||||
return nil
|
||||
}))
|
||||
|
||||
s.CoreHandlers = append(s.CoreHandlers, NewCommandHandler("sleep SECONDS", "^sleep\\s+(\\d+)$",
|
||||
"Sleep for the given amount of seconds.",
|
||||
func(args []string, s *Session) error {
|
||||
if secs, err := strconv.Atoi(args[0]); err == nil {
|
||||
time.Sleep(time.Duration(secs) * time.Second)
|
||||
return nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}))
|
||||
|
||||
s.CoreHandlers = append(s.CoreHandlers, NewCommandHandler("get NAME", "^get\\s+(.+)",
|
||||
"Get the value of variable NAME, use * for all.",
|
||||
func(args []string, s *Session) error {
|
||||
key := args[0]
|
||||
if key == "*" {
|
||||
prev_ns := ""
|
||||
|
||||
fmt.Println()
|
||||
for _, k := range s.Env.Sorted() {
|
||||
ns := ""
|
||||
toks := strings.Split(k, ".")
|
||||
if len(toks) > 0 {
|
||||
ns = toks[0]
|
||||
}
|
||||
|
||||
if ns != prev_ns {
|
||||
fmt.Println()
|
||||
prev_ns = ns
|
||||
}
|
||||
|
||||
fmt.Printf(" %"+strconv.Itoa(s.Env.Padding)+"s: '%s'\n", k, s.Env.storage[k])
|
||||
}
|
||||
fmt.Println()
|
||||
} else if found, value := s.Env.Get(key); found == true {
|
||||
fmt.Println()
|
||||
fmt.Printf(" %s: '%s'\n", key, value)
|
||||
fmt.Println()
|
||||
} else {
|
||||
return fmt.Errorf("%s not found", key)
|
||||
}
|
||||
|
||||
return nil
|
||||
}))
|
||||
|
||||
s.CoreHandlers = append(s.CoreHandlers, NewCommandHandler("set NAME VALUE", "^set\\s+([^\\s]+)\\s+(.+)",
|
||||
"Set the VALUE of variable NAME.",
|
||||
func(args []string, s *Session) error {
|
||||
key := args[0]
|
||||
value := args[1]
|
||||
|
||||
if value == "\"\"" {
|
||||
value = ""
|
||||
}
|
||||
|
||||
s.Env.Set(key, value)
|
||||
fmt.Printf(" %s => '%s'\n", core.Green(key), core.Yellow(value))
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
func (s *Session) setupInput() error {
|
||||
var err error
|
||||
|
||||
pcompleters := make([]readline.PrefixCompleterInterface, 0)
|
||||
for _, h := range s.CoreHandlers {
|
||||
pcompleters = append(pcompleters, readline.PcItem(h.Name))
|
||||
}
|
||||
|
||||
for _, m := range s.Modules {
|
||||
for _, h := range m.Handlers() {
|
||||
pcompleters = append(pcompleters, readline.PcItem(h.Name))
|
||||
}
|
||||
}
|
||||
|
||||
history := ""
|
||||
if *s.Options.NoHistory == false {
|
||||
history = "bettercap.history"
|
||||
}
|
||||
|
||||
cfg := readline.Config{
|
||||
HistoryFile: history,
|
||||
InterruptPrompt: "^C",
|
||||
EOFPrompt: "exit",
|
||||
HistorySearchFold: true,
|
||||
AutoComplete: readline.NewPrefixCompleter(pcompleters...),
|
||||
FuncFilterInputRune: func(r rune) (rune, bool) {
|
||||
switch r {
|
||||
// block CtrlZ feature
|
||||
case readline.CharCtrlZ:
|
||||
return r, false
|
||||
}
|
||||
return r, true
|
||||
},
|
||||
}
|
||||
|
||||
s.Input, err = readline.NewEx(&cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// now that we have the readline instance, we can set logging to its
|
||||
// console writer so the whole thing gets correctly updated when something
|
||||
// is logged to screen (it won't overlap with the prompt).
|
||||
log_be := logging.NewLogBackend(s.Input.Stderr(), "", 0)
|
||||
log_level := logging.AddModuleLevel(log_be)
|
||||
if *s.Options.Debug == true {
|
||||
log_level.SetLevel(logging.DEBUG, "")
|
||||
} else if *s.Options.Silent == true {
|
||||
log_level.SetLevel(logging.ERROR, "")
|
||||
} else {
|
||||
log_level.SetLevel(logging.INFO, "")
|
||||
}
|
||||
|
||||
logging.SetBackend(log_level)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) Close() {
|
||||
for _, m := range s.Modules {
|
||||
m.OnSessionEnded(s)
|
||||
}
|
||||
|
||||
s.Firewall.Restore()
|
||||
s.Queue.Stop()
|
||||
}
|
||||
|
||||
func (s *Session) Register(mod Module) error {
|
||||
s.Modules = append(s.Modules, mod)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) Start() error {
|
||||
var err error
|
||||
|
||||
net.OuiInit()
|
||||
|
||||
if s.Interface, err = net.FindInterface(*s.Options.InterfaceName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Env.Set("iface.name", s.Interface.Name())
|
||||
s.Env.Set("iface.address", s.Interface.IpAddress)
|
||||
s.Env.Set("iface.mac", s.Interface.HwAddress)
|
||||
|
||||
if s.Queue, err = packets.NewQueue(s.Interface.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("[%s%s%s] %s\n", core.GREEN, s.Interface.Name(), core.RESET, s.Interface)
|
||||
log.Debugf("[%ssubnet%s] %s\n", core.GREEN, core.RESET, s.Interface.CIDR())
|
||||
|
||||
if s.Gateway, err = net.FindGateway(s.Interface); err != nil {
|
||||
log.Warningf("%s\n", err)
|
||||
}
|
||||
|
||||
if s.Gateway == nil || s.Gateway.IpAddress == s.Interface.IpAddress {
|
||||
s.Gateway = s.Interface
|
||||
}
|
||||
|
||||
s.Env.Set("gateway.address", s.Gateway.IpAddress)
|
||||
s.Env.Set("gateway.mac", s.Gateway.HwAddress)
|
||||
|
||||
log.Debugf("[%sgateway%s] %s\n", core.GREEN, core.RESET, s.Gateway)
|
||||
|
||||
s.Targets = NewTargets(s.Interface, s.Gateway)
|
||||
s.Firewall = firewall.Make()
|
||||
|
||||
if err := s.setupInput(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, h := range s.CoreHandlers {
|
||||
if len(h.Name) > s.HelpPadding {
|
||||
s.HelpPadding = len(h.Name)
|
||||
}
|
||||
}
|
||||
for _, m := range s.Modules {
|
||||
for _, h := range m.Handlers() {
|
||||
if len(h.Name) > s.HelpPadding {
|
||||
s.HelpPadding = len(h.Name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range m.Parameters() {
|
||||
if len(p.Name) > s.HelpPadding {
|
||||
s.HelpPadding = len(p.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
signal.Notify(c, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-c
|
||||
fmt.Println()
|
||||
log.Warning("Got SIGTERM ...")
|
||||
s.Close()
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
s.Active = true
|
||||
|
||||
for _, m := range s.Modules {
|
||||
m.OnSessionStarted(s)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) ReadLine() (string, error) {
|
||||
s.Input.SetPrompt(core.GREEN + s.Interface.IpAddress + core.RESET + "» ")
|
||||
s.Input.Refresh()
|
||||
return s.Input.Readline()
|
||||
}
|
||||
|
||||
func (s *Session) RunCaplet(filename string) error {
|
||||
log.Infof("Reading from caplet %s ...\n", filename)
|
||||
|
||||
input, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer input.Close()
|
||||
|
||||
scanner := bufio.NewScanner(input)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line == "" || line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = s.Run(line); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) Run(line string) error {
|
||||
line = strings.TrimRight(line, " ")
|
||||
for _, h := range s.CoreHandlers {
|
||||
if parsed, args := h.Parse(line); parsed == true {
|
||||
return h.Exec(args, s)
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range s.Modules {
|
||||
for _, h := range m.Handlers() {
|
||||
if parsed, args := h.Parse(line); parsed == true {
|
||||
return h.Exec(args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("Unknown command %s%s%s, type %shelp%s for the help menu.", core.BOLD, line, core.RESET, core.BOLD, core.RESET)
|
||||
}
|
110
session/targets.go
Normal file
110
session/targets.go
Normal file
|
@ -0,0 +1,110 @@
|
|||
package session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/op/go-logging"
|
||||
|
||||
"github.com/evilsocket/bettercap/core"
|
||||
"github.com/evilsocket/bettercap/net"
|
||||
)
|
||||
|
||||
var log = logging.MustGetLogger("mitm")
|
||||
|
||||
type Targets struct {
|
||||
Interface *net.Endpoint
|
||||
Gateway *net.Endpoint
|
||||
Targets map[string]*net.Endpoint
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func NewTargets(iface, gateway *net.Endpoint) *Targets {
|
||||
return &Targets{
|
||||
Interface: iface,
|
||||
Gateway: gateway,
|
||||
Targets: make(map[string]*net.Endpoint),
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *Targets) Remove(ip, mac string) {
|
||||
tp.lock.Lock()
|
||||
defer tp.lock.Unlock()
|
||||
|
||||
if e, found := tp.Targets[mac]; found {
|
||||
log.Infof("[%slost%s] %s\n", core.RED, core.RESET, e)
|
||||
delete(tp.Targets, mac)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *Targets) shouldIgnore(ip string) bool {
|
||||
return (ip == tp.Interface.IpAddress || ip == tp.Gateway.IpAddress)
|
||||
}
|
||||
|
||||
func (tp *Targets) Has(ip string) bool {
|
||||
tp.lock.Lock()
|
||||
defer tp.lock.Unlock()
|
||||
|
||||
for _, e := range tp.Targets {
|
||||
if e.IpAddress == ip {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (tp *Targets) AddIfNotExist(ip, mac string) {
|
||||
tp.lock.Lock()
|
||||
defer tp.lock.Unlock()
|
||||
|
||||
if tp.shouldIgnore(ip) {
|
||||
return
|
||||
}
|
||||
|
||||
if t, found := tp.Targets[mac]; found {
|
||||
t.IpAddress = ip
|
||||
return
|
||||
}
|
||||
|
||||
e := net.NewEndpoint(ip, mac)
|
||||
log.Infof("[%snew%s] %s\n", core.GREEN, core.RESET, e)
|
||||
tp.Targets[mac] = e
|
||||
}
|
||||
|
||||
type tSorter []*net.Endpoint
|
||||
|
||||
func (a tSorter) Len() int { return len(a) }
|
||||
func (a tSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a tSorter) Less(i, j int) bool { return a[i].IpAddressUint32 < a[j].IpAddressUint32 }
|
||||
|
||||
func (tp *Targets) Dump() {
|
||||
tp.lock.Lock()
|
||||
defer tp.lock.Unlock()
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf(" " + core.GREEN + "interface" + core.RESET + "\n\n")
|
||||
fmt.Printf(" " + tp.Interface.String() + "\n")
|
||||
fmt.Println()
|
||||
fmt.Printf(" " + core.GREEN + "gateway" + core.RESET + "\n\n")
|
||||
fmt.Printf(" " + tp.Gateway.String() + "\n")
|
||||
|
||||
if len(tp.Targets) > 0 {
|
||||
fmt.Println()
|
||||
fmt.Printf(" " + core.GREEN + "hosts" + core.RESET + "\n\n")
|
||||
targets := make([]*net.Endpoint, 0, len(tp.Targets))
|
||||
for _, t := range tp.Targets {
|
||||
targets = append(targets, t)
|
||||
}
|
||||
|
||||
sort.Sort(tSorter(targets))
|
||||
|
||||
for _, t := range targets {
|
||||
fmt.Println(" " + t.String())
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue