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

This commit is contained in:
Simone Margaritelli 2024-09-21 17:38:52 +02:00
parent 2966153adf
commit 26c532316a
21 changed files with 355 additions and 75 deletions

View file

@ -134,8 +134,8 @@ func (mod *EventsStream) Render(output io.Writer, e session.Event) {
mod.viewUpdateEvent(output, e)
} else if e.Tag == "gateway.change" {
mod.viewGatewayEvent(output, e)
} else if e.Tag == "mdns.service" {
mod.viewMDNSEvent(output, e)
} else if strings.HasPrefix(e.Tag, "zeroconf.") {
mod.viewZeroConfEvent(output, e)
} else if e.Tag != "tick" && e.Tag != "session.started" && e.Tag != "session.stopped" {
fmt.Fprintf(output, "[%s] [%s] %v\n", e.Time.Format(mod.timeFormat), tui.Green(e.Tag), e)
}

View file

@ -1,23 +0,0 @@
package events_stream
import (
"fmt"
"io"
"github.com/bettercap/bettercap/v2/modules/zerogod"
"github.com/bettercap/bettercap/v2/session"
"github.com/evilsocket/islazy/tui"
)
func (mod *EventsStream) viewMDNSEvent(output io.Writer, e session.Event) {
event := e.Data.(zerogod.ServiceDiscoveryEvent)
fmt.Fprintf(output, "[%s] [%s] service %s detected for %s (%s):%d with %d records\n",
e.Time.Format(mod.timeFormat),
tui.Green(e.Tag),
tui.Bold(event.Service.ServiceInstanceName()),
event.Service.AddrIPv4,
tui.Dim(event.Service.HostName),
event.Service.Port,
len(event.Service.Text),
)
}

View file

@ -0,0 +1,62 @@
package events_stream
import (
"fmt"
"io"
"strings"
"github.com/bettercap/bettercap/v2/modules/zerogod"
"github.com/bettercap/bettercap/v2/session"
"github.com/evilsocket/islazy/tui"
)
func (mod *EventsStream) viewZeroConfEvent(output io.Writer, e session.Event) {
if e.Tag == "zeroconf.service" {
event := e.Data.(zerogod.ServiceDiscoveryEvent)
fmt.Fprintf(output, "[%s] [%s] service %s detected for %s (%s):%d with %d records\n",
e.Time.Format(mod.timeFormat),
tui.Green(e.Tag),
tui.Bold(event.Service.ServiceInstanceName()),
event.Service.AddrIPv4,
tui.Dim(event.Service.HostName),
event.Service.Port,
len(event.Service.Text),
)
} else if e.Tag == "zeroconf.browsing" {
event := e.Data.(zerogod.BrowsingEvent)
source := event.Source
if event.Endpoint != nil {
source = event.Endpoint.ShortString()
}
services := make([]string, 0)
for _, q := range event.Query.Questions {
services = append(services, tui.Yellow(string(q.Name)))
}
/*
instances := make([]string, 0)
answers := append(event.Query.Answers, event.Query.Additionals...)
for _, answer := range answers {
if answer.Class == layers.DNSClassIN && answer.Type == layers.DNSTypePTR {
instances = append(instances, tui.Green(string(answer.PTR)))
} else {
instances = append(instances, tui.Green(answer.String()))
}
}
advPart := ""
if len(instances) > 0 {
advPart = fmt.Sprintf(" and advertising %s", strings.Join(instances, ", "))
}
*/
fmt.Fprintf(output, "[%s] [%s] %s is browsing for services %s\n",
e.Time.Format(mod.timeFormat),
tui.Green(e.Tag),
source,
strings.Join(services, ", "),
)
} else {
fmt.Fprintf(output, "[%s] [%s] %v\n", e.Time.Format(mod.timeFormat), tui.Green(e.Tag), e)
}
}

View file

@ -3,10 +3,14 @@ package zerogod
import (
"github.com/bettercap/bettercap/v2/session"
"github.com/bettercap/bettercap/v2/tls"
"github.com/google/gopacket"
"github.com/google/gopacket/pcap"
)
type ZeroGod struct {
session.SessionModule
sniffer *pcap.Handle
snifferCh chan gopacket.Packet
browser *Browser
advertiser *Advertiser
}
@ -14,8 +18,6 @@ type ZeroGod struct {
func NewZeroGod(s *session.Session) *ZeroGod {
mod := &ZeroGod{
SessionModule: session.NewSessionModule("zerogod", s),
browser: nil,
advertiser: nil,
}
mod.SessionModule.Requires("net.recon")
@ -115,12 +117,6 @@ func (mod *ZeroGod) Configure() (err error) {
return session.ErrAlreadyStarted(mod.Name())
}
if mod.browser != nil {
mod.browser.Stop(false)
}
mod.browser = NewBrowser()
return
}
@ -130,7 +126,7 @@ func (mod *ZeroGod) Start() (err error) {
}
// start the root discovery
if err = mod.startResolver(DNSSD_DISCOVERY_SERVICE); err != nil {
if err = mod.startDiscovery(DNSSD_DISCOVERY_SERVICE); err != nil {
return err
}
@ -144,14 +140,6 @@ func (mod *ZeroGod) Start() (err error) {
func (mod *ZeroGod) Stop() error {
return mod.SetRunning(false, func() {
mod.stopAdvertiser()
if mod.browser != nil {
mod.Debug("stopping discovery")
mod.browser.Stop(true)
mod.Debug("stopped")
mod.browser = nil
}
mod.stopDiscovery()
})
}

View file

@ -7,6 +7,7 @@ import (
"net"
"strings"
"github.com/evilsocket/islazy/ops"
"github.com/evilsocket/islazy/tui"
)
@ -30,10 +31,12 @@ var TCP_HANDLERS = map[string]Handler{
type Acceptor struct {
mod *ZeroGod
srvHost string
proto string
port uint16
service string
tlsConfig *tls.Config
listener net.Listener
tcpListener net.Listener
udpListener *net.UDPConn
running bool
context context.Context
ctxCancel context.CancelFunc
@ -53,9 +56,12 @@ type HandlerContext struct {
func NewAcceptor(mod *ZeroGod, service string, srvHost string, port uint16, tlsConfig *tls.Config, ippAttributes map[string]string) *Acceptor {
context, ctcCancel := context.WithCancel(context.Background())
proto := ops.Ternary(strings.Contains(service, "_tcp"), "tcp", "udp").(string)
acceptor := Acceptor{
mod: mod,
port: port,
proto: proto,
service: service,
context: context,
ctxCancel: ctcCancel,
@ -72,36 +78,34 @@ func NewAcceptor(mod *ZeroGod, service string, srvHost string, port uint16, tlsC
}
if acceptor.handler.Handle == nil {
mod.Warning("no protocol handler found for service %s, using generic dump handler", tui.Yellow(service))
mod.Warning("no protocol handler found for service %s, using generic %s dump handler", tui.Yellow(service), proto)
acceptor.handler.Handle = handleGenericTCP
} else {
mod.Info("found %s protocol handler", tui.Green(service))
mod.Info("found %s %s protocol handler", proto, tui.Green(service))
}
return &acceptor
}
func (a *Acceptor) Start() (err error) {
func (a *Acceptor) startTCP() (err error) {
var lc net.ListenConfig
if a.listener, err = lc.Listen(a.context, "tcp", fmt.Sprintf("0.0.0.0:%d", a.port)); err != nil {
if a.tcpListener, err = lc.Listen(a.context, "tcp", fmt.Sprintf("0.0.0.0:%d", a.port)); err != nil {
return err
}
if a.tlsConfig != nil {
a.listener = tls.NewListener(a.listener, a.tlsConfig)
a.tcpListener = tls.NewListener(a.tcpListener, a.tlsConfig)
}
a.running = true
go func() {
a.mod.Debug("tcp listener for port %d (%s) started", a.port, tui.Green(a.service))
a.mod.Debug("%s listener for port %d (%s) started", a.proto, a.port, tui.Green(a.service))
for a.running {
if conn, err := a.listener.Accept(); err != nil {
if conn, err := a.tcpListener.Accept(); err != nil {
if a.running {
a.mod.Error("%v", err)
}
} else {
a.mod.Debug("accepted connection for service %s (port %d): %v", tui.Green(a.service), a.port, conn.RemoteAddr())
a.mod.Debug("accepted %s connection for service %s (port %d): %v", a.proto, tui.Green(a.service), a.port, conn.RemoteAddr())
go a.handler.Handle(&HandlerContext{
service: a.service,
mod: a.mod,
@ -113,17 +117,60 @@ func (a *Acceptor) Start() (err error) {
})
}
}
a.mod.Debug("tcp listener for port %d (%s) stopped", a.port, tui.Green(a.service))
a.mod.Debug("%s listener for port %d (%s) stopped", a.proto, a.port, tui.Green(a.service))
}()
return nil
}
func (a *Acceptor) startUDP() (err error) {
if udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("0.0.0.0:%d", a.port)); err != nil {
return err
} else if a.udpListener, err = net.ListenUDP("udp", udpAddr); err != nil {
return err
} else {
a.running = true
go func() {
var buffer [4096]byte
a.mod.Info("%s listener for port %d (%s) started", a.proto, a.port, tui.Green(a.service))
for a.running {
if n, addr, err := a.udpListener.ReadFromUDP(buffer[0:]); err != nil {
a.mod.Warning("error reading udp packet: %v", err)
} else if n <= 0 {
a.mod.Info("empty read")
} else {
a.mod.Info("%v:\n%s", addr, Dump(buffer[0:n]))
}
}
a.mod.Info("%s listener for port %d (%s) stopped", a.proto, a.port, tui.Green(a.service))
}()
}
return nil
}
func (a *Acceptor) Start() (err error) {
if a.proto == "tcp" {
return a.startTCP()
} else {
return a.startUDP()
}
}
func (a *Acceptor) Stop() {
a.mod.Debug("stopping tcp listener for port %d", a.port)
a.mod.Debug("stopping %s listener for port %d", a.proto, a.port)
a.running = false
if a.proto == "tcp" {
a.ctxCancel()
<-a.context.Done()
a.listener.Close()
a.mod.Debug("tcp listener for port %d stopped", a.port)
a.tcpListener.Close()
} else {
a.udpListener.Close()
}
a.mod.Debug("%s listener for port %d stopped", a.proto, a.port)
}

View file

@ -5,6 +5,8 @@ import (
"errors"
"fmt"
"io/ioutil"
"math/rand"
"net"
"os"
"strings"
"time"
@ -20,6 +22,27 @@ type Advertiser struct {
Acceptors []*Acceptor
}
func isPortAvailable(port int) bool {
address := fmt.Sprintf("127.0.0.1:%d", port)
if conn, err := net.DialTimeout("tcp", address, 10*time.Millisecond); err != nil {
return true
} else if conn == nil {
return true
} else {
conn.Close()
return false
}
}
func isPortRequested(svc *ServiceData, services []*ServiceData) bool {
for _, other := range services {
if svc != other && svc.Port == other.Port {
return true
}
}
return false
}
func (mod *ZeroGod) loadTLSConfig() (*tls.Config, error) {
var certFile string
var keyFile string
@ -104,6 +127,21 @@ func (mod *ZeroGod) startAdvertiser(fileName string) error {
Acceptors: make([]*Acceptor, 0),
}
// fix ports
for _, svc := range advertiser.Services {
// if no external responder has been specified, check if port is available
if svc.Responder == "" {
for svc.Port == 0 || !isPortAvailable(svc.Port) || isPortRequested(svc, services) {
newPort := (rand.Intn(65535-1024) + 1024)
mod.Warning("port %d for service %s is not avaialble, trying %d ...",
svc.Port,
svc.FullName(),
newPort)
svc.Port = newPort
}
}
}
// paralleize initialization
svcChan := make(chan error, numServices)
for _, svc := range advertiser.Services {

View file

@ -4,7 +4,7 @@ import (
"context"
"sort"
"github.com/bettercap/bettercap/v2/zeroconf"
"github.com/bettercap/bettercap/v2/modules/zerogod/zeroconf"
"github.com/evilsocket/islazy/tui"
)

View file

@ -1,19 +1,31 @@
package zerogod
import (
"net"
"strings"
"time"
"github.com/bettercap/bettercap/v2/modules/zerogod/zeroconf"
"github.com/bettercap/bettercap/v2/network"
"github.com/bettercap/bettercap/v2/session"
"github.com/bettercap/bettercap/v2/zeroconf"
"github.com/evilsocket/islazy/tui"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
// a service has been discovered
type ServiceDiscoveryEvent struct {
Service zeroconf.ServiceEntry `json:"service"`
Endpoint *network.Endpoint `json:"endpoint"`
}
// an endpoint is browsing for specific services
type BrowsingEvent struct {
Source string `json:"source"`
Query layers.DNS `json:"service"`
Endpoint *network.Endpoint `json:"endpoint"`
}
func (mod *ZeroGod) onServiceDiscovered(svc *zeroconf.ServiceEntry) {
mod.Debug("%++v", *svc)
@ -21,8 +33,15 @@ func (mod *ZeroGod) onServiceDiscovered(svc *zeroconf.ServiceEntry) {
svcName := strings.Replace(svc.Instance, ".local", "", 1)
if !mod.browser.HasResolverFor(svcName) {
mod.Debug("discovered service %s", tui.Green(svcName))
if err := mod.startResolver(svcName); err != nil {
if ch, err := mod.browser.StartBrowsing(svcName, "local.", mod); err != nil {
mod.Error("%v", err)
} else {
// start listening on this channel
go func() {
for entry := range ch {
mod.onServiceDiscovered(entry)
}
}()
}
}
return
@ -58,17 +77,116 @@ func (mod *ZeroGod) onServiceDiscovered(svc *zeroconf.ServiceEntry) {
mod.Debug("got mdns entry for unknown ip: %++v", *svc)
}
session.I.Events.Add("mdns.service", event)
session.I.Events.Add("zeroconf.service", event)
session.I.Refresh()
}
func (mod *ZeroGod) startResolver(service string) error {
func (mod *ZeroGod) onPacket(pkt gopacket.Packet) {
mod.Debug("%++v", pkt)
netLayer := pkt.NetworkLayer()
if netLayer == nil {
mod.Warning("not network layer in packet %+v", pkt)
return
}
var srcIP net.IP
// var dstIP net.IP
switch netLayer.LayerType() {
case layers.LayerTypeIPv4:
ip := netLayer.(*layers.IPv4)
srcIP = ip.SrcIP
// dstIP = ip.DstIP
case layers.LayerTypeIPv6:
ip := netLayer.(*layers.IPv6)
srcIP = ip.SrcIP
// dstIP = ip.DstIP
default:
mod.Warning("unexpected network layer type %v in packet %+v", netLayer.LayerType(), pkt)
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)
return
}
dns := layers.DNS{}
if err := dns.DecodeFromBytes(udp.LayerPayload(), gopacket.NilDecodeFeedback); err != nil {
mod.Warning("could not decode DNS (%v) in packet %+v", err, pkt)
return
}
// since the browser is already checking for these, we are only interested in queries
numQs := len(dns.Questions)
if numQs == 0 {
mod.Debug("skipping answers only packet")
return
}
event := BrowsingEvent{
Source: srcIP.String(),
Query: dns,
Endpoint: mod.Session.Lan.GetByIp(srcIP.String()),
}
if event.Endpoint == nil {
// TODO: if nil, this is probably an IPv6 only record, try to somehow check which known IPv4 it is
// TODO: make configurable?
mod.Debug("got mdns packet from unknown ip %s: %++v", srcIP, dns)
return
}
session.I.Events.Add("zeroconf.browsing", event)
session.I.Refresh()
}
func (mod *ZeroGod) startDiscovery(service string) (err error) {
mod.Debug("starting resolver for service %s", tui.Yellow(service))
// create passive sniffer
if mod.sniffer != nil {
mod.sniffer.Close()
}
readTimeout := 500 * time.Millisecond
if mod.sniffer, err = network.CaptureWithTimeout(mod.Session.Interface.Name(), readTimeout); err != nil {
return err
} else if err = mod.sniffer.SetBPFFilter("udp and port 5353"); err != nil {
return err
}
// prepare source and start listening for packets
src := gopacket.NewPacketSource(mod.sniffer, mod.sniffer.LinkType())
mod.snifferCh = src.Packets()
// start listening for new packets
go func() {
mod.Debug("sniffer started")
for pkt := range mod.snifferCh {
if !mod.Running() {
mod.Debug("end pkt loop (pkt=%v)", pkt)
break
}
mod.onPacket(pkt)
}
mod.Debug("sniffer stopped")
}()
// create service browser
if mod.browser != nil {
mod.browser.Stop(false)
}
mod.browser = NewBrowser()
// start active browsing
if ch, err := mod.browser.StartBrowsing(service, "local.", mod); err != nil {
return err
} else {
// start listening
// start listening for new services
go func() {
for entry := range ch {
mod.onServiceDiscovered(entry)
@ -78,3 +196,21 @@ func (mod *ZeroGod) startResolver(service string) error {
return nil
}
func (mod *ZeroGod) stopDiscovery() {
if mod.browser != nil {
mod.Debug("stopping discovery")
mod.browser.Stop(true)
mod.browser = nil
mod.Debug("discovery stopped")
}
if mod.sniffer != nil {
mod.Debug("stopping sniffer")
mod.snifferCh <- nil
mod.sniffer.Close()
mod.sniffer = nil
mod.snifferCh = nil
mod.Debug("sniffer stopped")
}
}

View file

@ -5,8 +5,8 @@ import (
"strings"
"github.com/bettercap/bettercap/v2/modules/syn_scan"
"github.com/bettercap/bettercap/v2/modules/zerogod/zeroconf"
"github.com/bettercap/bettercap/v2/network"
"github.com/bettercap/bettercap/v2/zeroconf"
"github.com/evilsocket/islazy/str"
)

View file

@ -50,6 +50,7 @@ func ippClientHandler(ctx *HandlerContext) {
read, err := ctx.client.Read(buf)
if err != nil {
if err == io.EOF {
ctx.mod.Debug("EOF, client %s disconnected", clientIP)
return
}
ctx.mod.Warning("error while reading from %v: %v", clientIP, err)
@ -67,12 +68,12 @@ func ippClientHandler(ctx *HandlerContext) {
reader := bufio.NewReader(bytes.NewReader(raw_req))
http_req, err := http.ReadRequest(reader)
if err != nil {
ctx.mod.Error("error while parsing http request from %v: %v", clientIP, err)
ctx.mod.Error("error while parsing http request from %v: %v\n%s", clientIP, err, Dump(raw_req))
return
}
clientUA := http_req.UserAgent()
ctx.mod.Debug("%v -> %s", clientIP, tui.Green(clientUA))
ctx.mod.Info("%v -> %s", clientIP, tui.Green(clientUA))
ipp_body, err := ippReadRequestBody(ctx, http_req)
if err != nil {
@ -92,8 +93,14 @@ func ippClientHandler(ctx *HandlerContext) {
ipp_op_name = name
}
ctx.mod.Info("%s <- %s (%s) %s",
reqUsername := tui.Dim("<unknown>")
if value, found := ipp_req.OperationAttributes["requesting-user-name"]; found {
reqUsername = tui.Blue(value.(string))
}
ctx.mod.Info("%s <- %s@%s (%s) %s",
tui.Yellow(ctx.service),
reqUsername,
clientIP,
tui.Green(clientUA),
tui.Bold(ipp_op_name))

View file

@ -5,7 +5,7 @@ import (
"fmt"
"io/ioutil"
"github.com/bettercap/bettercap/v2/zeroconf"
"github.com/bettercap/bettercap/v2/modules/zerogod/zeroconf"
"github.com/evilsocket/islazy/str"
yaml "gopkg.in/yaml.v3"
)

View file

@ -5,7 +5,7 @@ import (
"net"
"strings"
"github.com/bettercap/bettercap/v2/zeroconf"
"github.com/bettercap/bettercap/v2/modules/zerogod/zeroconf"
"github.com/evilsocket/islazy/tui"
)

View file

@ -5,6 +5,7 @@ import (
"fmt"
"strings"
"github.com/evilsocket/islazy/ops"
"github.com/evilsocket/islazy/str"
"github.com/evilsocket/islazy/tui"
)
@ -20,7 +21,10 @@ func (mod *ZeroGod) show(filter string, withData bool) error {
for _, entry := range entries {
if endpoint := mod.Session.Lan.GetByIp(entry.Address); endpoint != nil {
fmt.Fprintf(mod.Session.Events.Stdout, "* %s (%s)\n", tui.Bold(endpoint.IpAddress), tui.Dim(endpoint.Vendor))
fmt.Fprintf(mod.Session.Events.Stdout, "* %s (%s)%s\n",
tui.Bold(endpoint.IpAddress),
tui.Dim(endpoint.Vendor),
ops.Ternary(endpoint.Hostname == "", "", " "+tui.Bold(endpoint.Hostname)))
} else {
fmt.Fprintf(mod.Session.Events.Stdout, "* %s\n", tui.Bold(entry.Address))
}

View file

@ -161,6 +161,27 @@ func (t *Endpoint) String() string {
return fmt.Sprintf("%s%s (%s) - %s", ipPart, t.HwAddress, t.Vendor, tui.Bold(name))
}
func (t *Endpoint) ShortString() string {
parts := []string{
t.IpAddress,
}
if t.Vendor != "" {
parts = append(parts, tui.Dim(fmt.Sprintf("(%s)", t.Vendor)))
}
name := t.Hostname
if t.Alias != "" {
name = t.Alias
}
if name != "" {
parts = append(parts, tui.Bold(name))
}
return strings.Join(parts, " ")
}
func (t *Endpoint) OnMeta(meta map[string]string) {
host := ""
for k, v := range meta {