misc: small fix or general refactoring i did not bother commenting

This commit is contained in:
Simone Margaritelli 2024-09-22 13:23:30 +02:00
commit 5652d15426
6 changed files with 177 additions and 14 deletions

View file

@ -7,6 +7,7 @@ import (
"github.com/bettercap/bettercap/v2/modules/zerogod"
"github.com/bettercap/bettercap/v2/session"
"github.com/evilsocket/islazy/ops"
"github.com/evilsocket/islazy/tui"
)
@ -50,10 +51,11 @@ func (mod *EventsStream) viewZeroConfEvent(output io.Writer, e session.Event) {
}
*/
fmt.Fprintf(output, "[%s] [%s] %s is browsing for services %s\n",
fmt.Fprintf(output, "[%s] [%s] %s is browsing (%s) for services %s\n",
e.Time.Format(mod.timeFormat),
tui.Green(e.Tag),
source,
ops.Ternary(event.Query.QR, "RESPONSE", "QUERY"),
strings.Join(services, ", "),
)
} else {

View file

@ -105,9 +105,46 @@ func listMulticastInterfaces() []net.Interface {
return nil
}
for _, ifi := range ifaces {
// not up
if (ifi.Flags & net.FlagUp) == 0 {
continue
}
// localhost
if (ifi.Flags & net.FlagLoopback) != 0 {
continue
}
// not running
if (ifi.Flags & net.FlagRunning) == 0 {
continue
}
// vpn and similar
if (ifi.Flags & net.FlagPointToPoint) != 0 {
continue
}
// at least one ipv4 address assigned
hasIPv4 := false
if addresses, _ := ifi.Addrs(); addresses != nil {
for _, addr := range addresses {
// ipv4 or ipv4 CIDR
if ip, ipnet, err := net.ParseCIDR(addr.String()); err == nil {
if ip.To4() != nil || ipnet.IP.To4() != nil {
hasIPv4 = true
break
}
} else if ipAddr, ok := addr.(*net.IPAddr); ok {
if ipAddr.IP.To4() != nil {
hasIPv4 = true
break
}
}
}
}
if !hasIPv4 {
continue
}
if (ifi.Flags & net.FlagMulticast) > 0 {
interfaces = append(interfaces, ifi)
}

View file

@ -48,9 +48,10 @@ func Register(instance, service, domain string, port int, text []string, ifaces
if err != nil {
return nil, fmt.Errorf("could not determine host")
}
entry.HostName = strings.ReplaceAll(entry.HostName, ".local", "")
}
if !strings.HasSuffix(trimDot(entry.HostName), entry.Domain) {
if !strings.HasSuffix(trimDot(entry.HostName), trimDot(entry.Domain)) {
entry.HostName = fmt.Sprintf("%s.%s.", trimDot(entry.HostName), trimDot(entry.Domain))
}
@ -431,7 +432,7 @@ func (s *Server) composeBrowsingAnswers(resp *dns.Msg, ifIndex int) {
Ttl: s.ttl,
},
Priority: 0,
Weight: 0,
Weight: 0xffff,
Port: uint16(s.service.Port),
Target: s.service.HostName,
}
@ -463,7 +464,7 @@ func (s *Server) composeLookupAnswers(resp *dns.Msg, ttl uint32, ifIndex int, fl
Ttl: ttl,
},
Priority: 0,
Weight: 0,
Weight: 0xffff,
Port: uint16(s.service.Port),
Target: s.service.HostName,
}
@ -539,7 +540,7 @@ func (s *Server) probe() {
Ttl: s.ttl,
},
Priority: 0,
Weight: 0,
Weight: 0xffff,
Port: uint16(s.service.Port),
Target: s.service.HostName,
}

View file

@ -126,6 +126,10 @@ func NewZeroGod(s *session.Session) *ZeroGod {
"",
"If an IPP acceptor is started, this setting defines where to save documents received for printing."))
mod.AddParam(session.NewBoolParameter("zerogod.verbose",
"false",
"Log every mDNS query."))
return mod
}

View file

@ -103,9 +103,7 @@ func (mod *ZeroGod) startAdvertiser(fileName string) error {
if err != nil {
return fmt.Errorf("could not get hostname: %v", err)
}
if !strings.HasSuffix(hostName, ".") {
hostName += "."
}
hostName = strings.ReplaceAll(hostName, ".local", "")
data, err := ioutil.ReadFile(fileName)
if err != nil {

View file

@ -1,6 +1,7 @@
package zerogod
import (
"fmt"
"net"
"strings"
"time"
@ -8,6 +9,7 @@ import (
"github.com/bettercap/bettercap/v2/modules/zerogod/zeroconf"
"github.com/bettercap/bettercap/v2/network"
"github.com/bettercap/bettercap/v2/session"
"github.com/evilsocket/islazy/ops"
"github.com/evilsocket/islazy/tui"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
@ -81,6 +83,119 @@ func (mod *ZeroGod) onServiceDiscovered(svc *zeroconf.ServiceEntry) {
session.I.Refresh()
}
func (mod *ZeroGod) DNSResourceRecord2String(rr *layers.DNSResourceRecord) string {
if rr.Type == layers.DNSTypeOPT {
opts := make([]string, len(rr.OPT))
for i, opt := range rr.OPT {
opts[i] = opt.String()
}
return "OPT " + strings.Join(opts, ",")
}
if rr.Type == layers.DNSTypeURI {
return fmt.Sprintf("URI %d %d %s", rr.URI.Priority, rr.URI.Weight, string(rr.URI.Target))
}
/*
https://www.rfc-editor.org/rfc/rfc6762
Note that the cache-flush bit is NOT part of the resource record
class. The cache-flush bit is the most significant bit of the second
16-bit word of a resource record in a Resource Record Section of a
Multicast DNS message (the field conventionally referred to as the
rrclass field), and the actual resource record class is the least
significant fifteen bits of this field. There is no Multicast DNS
resource record class 0x8001. The value 0x8001 in the rrclass field
of a resource record in a Multicast DNS response message indicates a
resource record with class 1, with the cache-flush bit set. When
receiving a resource record with the cache-flush bit set,
implementations should take care to mask off that bit before storing
the resource record in memory, or otherwise ensure that it is given
the correct semantic interpretation.
*/
if rr.Class == layers.DNSClassIN || rr.Class == 0x8001 {
switch rr.Type {
case layers.DNSTypeA, layers.DNSTypeAAAA:
return rr.IP.String()
case layers.DNSTypeNS:
return "NS " + string(rr.NS)
case layers.DNSTypeCNAME:
return "CNAME " + string(rr.CNAME)
case layers.DNSTypePTR:
return "PTR " + string(rr.PTR)
case layers.DNSTypeTXT:
return "TXT \n" + Dump(rr.TXT)
case layers.DNSTypeSRV:
return fmt.Sprintf("SRV priority=%d weight=%d port=%d name=%s",
rr.SRV.Priority,
rr.SRV.Weight,
rr.SRV.Port,
string(rr.SRV.Name))
case 47: // NSEC
return "NSEC"
}
}
return fmt.Sprintf("<%v (%d), %v (%d)>", rr.Class, rr.Class, rr.Type, rr.Type)
}
func (mod *ZeroGod) logDNS(src net.IP, dns layers.DNS, isLocal bool) {
source := tui.Yellow(src.String())
if endpoint := mod.Session.Lan.GetByIp(src.String()); endpoint != nil {
if endpoint.Alias != "" {
source = tui.Bold(endpoint.Alias)
} else if endpoint.Hostname != "" {
source = tui.Bold(endpoint.Hostname)
} else if endpoint.Vendor != "" {
source = fmt.Sprintf("%s (%s)", tui.Bold(endpoint.IpAddress), tui.Dim(endpoint.Vendor))
}
}
desc := fmt.Sprintf("DNS op=%s %s from %s (r_code=%s)",
dns.OpCode.String(),
tui.Bold(ops.Ternary(dns.QR, "RESPONSE", "QUERY").(string)),
source,
dns.ResponseCode.String())
attrs := []string{}
if dns.AA {
attrs = append(attrs, "AA")
}
if dns.TC {
attrs = append(attrs, "TC")
}
if dns.RD {
attrs = append(attrs, "RD")
}
if dns.RA {
attrs = append(attrs, "RA")
}
if len(attrs) > 0 {
desc += " [" + strings.Join(attrs, ", ") + "]"
}
desc += " :\n"
for _, q := range dns.Questions {
desc += fmt.Sprintf(" Q: %s\n", q)
}
for _, a := range dns.Answers {
desc += fmt.Sprintf(" A: %s\n", mod.DNSResourceRecord2String(&a))
}
for _, a := range dns.Authorities {
desc += fmt.Sprintf(" AU: %s\n", mod.DNSResourceRecord2String(&a))
}
for _, a := range dns.Additionals {
desc += fmt.Sprintf(" AD: %s\n", mod.DNSResourceRecord2String(&a))
}
if isLocal {
desc = tui.Dim(desc)
}
mod.Info("%s", desc)
}
func (mod *ZeroGod) onPacket(pkt gopacket.Packet) {
mod.Debug("%++v", pkt)
@ -105,12 +220,6 @@ func (mod *ZeroGod) onPacket(pkt gopacket.Packet) {
return
}
// not interested in packet generated by us
if srcIP.Equal(mod.Session.Interface.IP) || srcIP.Equal(mod.Session.Interface.IPv6) {
mod.Debug("skipping local packet")
return
}
udp := pkt.Layer(layers.LayerTypeUDP)
if udp == nil {
mod.Warning("not udp layer in packet %+v", pkt)
@ -123,6 +232,18 @@ func (mod *ZeroGod) onPacket(pkt gopacket.Packet) {
return
}
isLocal := srcIP.Equal(mod.Session.Interface.IP) || srcIP.Equal(mod.Session.Interface.IPv6)
if _, verbose := mod.BoolParam("zerogod.verbose"); verbose {
mod.logDNS(srcIP, dns, isLocal)
}
// not interested in packet generated by us
if isLocal {
mod.Debug("skipping local packet")
return
}
// since the browser is already checking for these, we are only interested in queries
numQs := len(dns.Questions)
if numQs == 0 {