mirror of
https://github.com/bettercap/bettercap
synced 2025-07-06 04:52:10 -07:00
new: dns.spoof now supports a hosts file with multiple mappings (closes #344)
This commit is contained in:
parent
3ed4db132c
commit
d16b0c7cf5
4 changed files with 131 additions and 43 deletions
|
@ -14,15 +14,12 @@ import (
|
||||||
"github.com/google/gopacket"
|
"github.com/google/gopacket"
|
||||||
"github.com/google/gopacket/layers"
|
"github.com/google/gopacket/layers"
|
||||||
"github.com/google/gopacket/pcap"
|
"github.com/google/gopacket/pcap"
|
||||||
|
|
||||||
"github.com/gobwas/glob"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DNSSpoofer struct {
|
type DNSSpoofer struct {
|
||||||
session.SessionModule
|
session.SessionModule
|
||||||
Handle *pcap.Handle
|
Handle *pcap.Handle
|
||||||
Domains []glob.Glob
|
Hosts Hosts
|
||||||
Address net.IP
|
|
||||||
All bool
|
All bool
|
||||||
waitGroup *sync.WaitGroup
|
waitGroup *sync.WaitGroup
|
||||||
pktSourceChan chan gopacket.Packet
|
pktSourceChan chan gopacket.Packet
|
||||||
|
@ -33,13 +30,18 @@ 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,
|
||||||
Domains: make([]glob.Glob, 0),
|
Hosts: Hosts{},
|
||||||
waitGroup: &sync.WaitGroup{},
|
waitGroup: &sync.WaitGroup{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spoof.AddParam(session.NewStringParameter("dns.spoof.hosts",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"If not empty, this hosts file will be used to map domains to IP addresses."))
|
||||||
|
|
||||||
spoof.AddParam(session.NewStringParameter("dns.spoof.domains",
|
spoof.AddParam(session.NewStringParameter("dns.spoof.domains",
|
||||||
"*",
|
"",
|
||||||
``,
|
"",
|
||||||
"Comma separated values of domain names to spoof."))
|
"Comma separated values of domain names to spoof."))
|
||||||
|
|
||||||
spoof.AddParam(session.NewStringParameter("dns.spoof.address",
|
spoof.AddParam(session.NewStringParameter("dns.spoof.address",
|
||||||
|
@ -80,43 +82,46 @@ func (s DNSSpoofer) Author() string {
|
||||||
|
|
||||||
func (s *DNSSpoofer) Configure() error {
|
func (s *DNSSpoofer) Configure() error {
|
||||||
var err error
|
var err error
|
||||||
var addr string
|
var hostsFile string
|
||||||
var domains []string
|
var domains []string
|
||||||
|
var address net.IP
|
||||||
|
|
||||||
if s.Running() {
|
if s.Running() {
|
||||||
return session.ErrAlreadyStarted
|
return session.ErrAlreadyStarted
|
||||||
}
|
} else if s.Handle, err = pcap.OpenLive(s.Session.Interface.Name(), 65536, true, pcap.BlockForever); err != nil {
|
||||||
|
|
||||||
if s.Handle, err = pcap.OpenLive(s.Session.Interface.Name(), 65536, true, pcap.BlockForever); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
} else if err = s.Handle.SetBPFFilter("udp"); err != nil {
|
||||||
|
|
||||||
err = s.Handle.SetBPFFilter("udp")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
} else if err, s.All = s.BoolParam("dns.spoof.all"); err != nil {
|
||||||
|
|
||||||
if err, s.All = s.BoolParam("dns.spoof.all"); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
} else if err, address = s.IPParam("dns.spoof.address"); err != nil {
|
||||||
|
return err
|
||||||
if err, domains = s.ListParam("dns.spoof.domains"); err != nil {
|
} else if err, domains = s.ListParam("dns.spoof.domains"); err != nil {
|
||||||
|
return err
|
||||||
|
} else if err, hostsFile = s.StringParam("dns.spoof.hosts"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
if expr, err := glob.Compile(domain); err != nil {
|
s.Hosts = append(s.Hosts, NewHostEntry(domain, address))
|
||||||
return fmt.Errorf("'%s' is not a valid domain glob expression: %s", domain, err)
|
}
|
||||||
|
|
||||||
|
if hostsFile != "" {
|
||||||
|
log.Info("loading hosts from file %s ...", hostsFile)
|
||||||
|
if err, hosts := HostsFromFile(hostsFile); err != nil {
|
||||||
|
return fmt.Errorf("error reading hosts from file %s: %v", hostsFile, err)
|
||||||
} else {
|
} else {
|
||||||
s.Domains = append(s.Domains, expr)
|
s.Hosts = append(s.Hosts, hosts...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err, addr = s.StringParam("dns.spoof.address"); err != nil {
|
if len(s.Hosts) == 0 {
|
||||||
return err
|
return fmt.Errorf("at least dns.spoof.hosts or dns.spoof.domains must be filled")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Address = net.ParseIP(addr)
|
for _, entry := range s.Hosts {
|
||||||
|
log.Info("[%s] %s -> %s", core.Green("dns.spoof"), entry.Host, entry.Address)
|
||||||
|
}
|
||||||
|
|
||||||
if !s.Session.Firewall.IsForwardingEnabled() {
|
if !s.Session.Firewall.IsForwardingEnabled() {
|
||||||
log.Info("Enabling forwarding.")
|
log.Info("Enabling forwarding.")
|
||||||
|
@ -126,8 +131,8 @@ func (s *DNSSpoofer) Configure() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp *layers.UDP, domain string, req *layers.DNS, target net.HardwareAddr) {
|
func (s *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)", s.Address)
|
redir := fmt.Sprintf("(->%s)", address.String())
|
||||||
who := target.String()
|
who := target.String()
|
||||||
|
|
||||||
if t, found := s.Session.Lan.Get(target.String()); found {
|
if t, found := s.Session.Lan.Get(target.String()); found {
|
||||||
|
@ -177,7 +182,7 @@ func (s *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp *
|
||||||
Type: q.Type,
|
Type: q.Type,
|
||||||
Class: q.Class,
|
Class: q.Class,
|
||||||
TTL: 1024,
|
TTL: 1024,
|
||||||
IP: s.Address,
|
IP: address,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,15 +247,6 @@ func (s *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DNSSpoofer) shouldSpoof(domain string) bool {
|
|
||||||
for _, expr := range s.Domains {
|
|
||||||
if expr.Match(domain) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DNSSpoofer) onPacket(pkt gopacket.Packet) {
|
func (s *DNSSpoofer) onPacket(pkt gopacket.Packet) {
|
||||||
typeEth := pkt.Layer(layers.LayerTypeEthernet)
|
typeEth := pkt.Layer(layers.LayerTypeEthernet)
|
||||||
typeUDP := pkt.Layer(layers.LayerTypeUDP)
|
typeUDP := pkt.Layer(layers.LayerTypeUDP)
|
||||||
|
@ -265,8 +261,8 @@ func (s *DNSSpoofer) onPacket(pkt gopacket.Packet) {
|
||||||
udp := typeUDP.(*layers.UDP)
|
udp := typeUDP.(*layers.UDP)
|
||||||
for _, q := range dns.Questions {
|
for _, q := range dns.Questions {
|
||||||
qName := string(q.Name)
|
qName := string(q.Name)
|
||||||
if s.shouldSpoof(qName) {
|
if address := s.Hosts.Resolve(qName); address != nil {
|
||||||
s.dnsReply(pkt, eth, udp, qName, dns, eth.SrcMAC)
|
s.dnsReply(pkt, eth, udp, qName, address, dns, eth.SrcMAC)
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
log.Debug("skipping domain %s", qName)
|
log.Debug("skipping domain %s", qName)
|
||||||
|
|
83
modules/dns_spoof_hosts.go
Normal file
83
modules/dns_spoof_hosts.go
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
package modules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bettercap/bettercap/core"
|
||||||
|
|
||||||
|
"github.com/gobwas/glob"
|
||||||
|
)
|
||||||
|
|
||||||
|
var hostsSplitter = regexp.MustCompile(`\s+`)
|
||||||
|
|
||||||
|
type HostEntry struct {
|
||||||
|
Host string
|
||||||
|
Suffix string
|
||||||
|
Expr glob.Glob
|
||||||
|
Address net.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e HostEntry) Matches(host string) bool {
|
||||||
|
return e.Host == host || strings.HasSuffix(host, e.Suffix) || (e.Expr != nil && e.Expr.Match(host))
|
||||||
|
}
|
||||||
|
|
||||||
|
type Hosts []HostEntry
|
||||||
|
|
||||||
|
func NewHostEntry(host string, address net.IP) HostEntry {
|
||||||
|
entry := HostEntry{
|
||||||
|
Host: host,
|
||||||
|
Address: address,
|
||||||
|
}
|
||||||
|
|
||||||
|
if host[0] == '.' {
|
||||||
|
entry.Suffix = host
|
||||||
|
} else {
|
||||||
|
entry.Suffix = "." + host
|
||||||
|
}
|
||||||
|
|
||||||
|
if expr, err := glob.Compile(host); err == nil {
|
||||||
|
entry.Expr = expr
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
|
func HostsFromFile(filename string) (err error, entries []HostEntry) {
|
||||||
|
input, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer input.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(input)
|
||||||
|
scanner.Split(bufio.ScanLines)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := core.Trim(scanner.Text())
|
||||||
|
if line == "" || line[0] == '#' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if parts := hostsSplitter.Split(line, 2); len(parts) == 2 {
|
||||||
|
address := net.ParseIP(parts[0])
|
||||||
|
domain := parts[1]
|
||||||
|
entries = append(entries, NewHostEntry(domain, address))
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("'%s' invalid hosts line", line), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Hosts) Resolve(host string) net.IP {
|
||||||
|
for _, entry := range h {
|
||||||
|
if entry.Matches(host) {
|
||||||
|
return entry.Address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -62,8 +62,8 @@ func (p *EventPool) Listen() <-chan Event {
|
||||||
// make sure, without blocking, the new listener
|
// make sure, without blocking, the new listener
|
||||||
// will receive all the queued events
|
// will receive all the queued events
|
||||||
go func() {
|
go func() {
|
||||||
for _, e := range p.events {
|
for i := len(p.events) - 1; i >= 0; i-- {
|
||||||
l <- e
|
l <- p.events[i]
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package session
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -86,6 +87,14 @@ func (m SessionModule) StringParam(name string) (error, string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m SessionModule) IPParam(name string) (error, net.IP) {
|
||||||
|
if err, v := m.StringParam(name); err != nil {
|
||||||
|
return err, nil
|
||||||
|
} else {
|
||||||
|
return nil, net.ParseIP(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m SessionModule) IntParam(name string) (error, int) {
|
func (m SessionModule) IntParam(name string) (error, int) {
|
||||||
if p, found := m.params[name]; found {
|
if p, found := m.params[name]; found {
|
||||||
if err, v := p.Get(m.Session); err != nil {
|
if err, v := p.Get(m.Session); err != nil {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue