mirror of
https://github.com/bettercap/bettercap
synced 2025-08-21 14:03:17 -07:00
Refactoring modules
This commit is contained in:
parent
c0d3c314fc
commit
ed652622e2
89 changed files with 186 additions and 138 deletions
224
modules/net_sniff/net_sniff.go
Normal file
224
modules/net_sniff/net_sniff.go
Normal file
|
@ -0,0 +1,224 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bettercap/bettercap/log"
|
||||
"github.com/bettercap/bettercap/session"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
type Sniffer struct {
|
||||
session.SessionModule
|
||||
Stats *SnifferStats
|
||||
Ctx *SnifferContext
|
||||
pktSourceChan chan gopacket.Packet
|
||||
|
||||
fuzzActive bool
|
||||
fuzzSilent bool
|
||||
fuzzLayers []string
|
||||
fuzzRate float64
|
||||
fuzzRatio float64
|
||||
}
|
||||
|
||||
func NewSniffer(s *session.Session) *Sniffer {
|
||||
sniff := &Sniffer{
|
||||
SessionModule: session.NewSessionModule("net.sniff", s),
|
||||
Stats: nil,
|
||||
}
|
||||
|
||||
sniff.AddParam(session.NewBoolParameter("net.sniff.verbose",
|
||||
"false",
|
||||
"If true, every captured and parsed packet will be sent to the events.stream for displaying, otherwise only the ones parsed at the application layer (sni, http, etc)."))
|
||||
|
||||
sniff.AddParam(session.NewBoolParameter("net.sniff.local",
|
||||
"false",
|
||||
"If true it will consider packets from/to this computer, otherwise it will skip them."))
|
||||
|
||||
sniff.AddParam(session.NewStringParameter("net.sniff.filter",
|
||||
"not arp",
|
||||
"",
|
||||
"BPF filter for the sniffer."))
|
||||
|
||||
sniff.AddParam(session.NewStringParameter("net.sniff.regexp",
|
||||
"",
|
||||
"",
|
||||
"If set, only packets matching this regular expression will be considered."))
|
||||
|
||||
sniff.AddParam(session.NewStringParameter("net.sniff.output",
|
||||
"",
|
||||
"",
|
||||
"If set, the sniffer will write captured packets to this file."))
|
||||
|
||||
sniff.AddParam(session.NewStringParameter("net.sniff.source",
|
||||
"",
|
||||
"",
|
||||
"If set, the sniffer will read from this pcap file instead of the current interface."))
|
||||
|
||||
sniff.AddHandler(session.NewModuleHandler("net.sniff stats", "",
|
||||
"Print sniffer session configuration and statistics.",
|
||||
func(args []string) error {
|
||||
if sniff.Stats == nil {
|
||||
return fmt.Errorf("No stats yet.")
|
||||
}
|
||||
|
||||
sniff.Ctx.Log(sniff.Session)
|
||||
|
||||
return sniff.Stats.Print()
|
||||
}))
|
||||
|
||||
sniff.AddHandler(session.NewModuleHandler("net.sniff on", "",
|
||||
"Start network sniffer in background.",
|
||||
func(args []string) error {
|
||||
return sniff.Start()
|
||||
}))
|
||||
|
||||
sniff.AddHandler(session.NewModuleHandler("net.sniff off", "",
|
||||
"Stop network sniffer in background.",
|
||||
func(args []string) error {
|
||||
return sniff.Stop()
|
||||
}))
|
||||
|
||||
sniff.AddHandler(session.NewModuleHandler("net.fuzz on", "",
|
||||
"Enable fuzzing for every sniffed packet containing the sapecified layers.",
|
||||
func(args []string) error {
|
||||
return sniff.StartFuzzing()
|
||||
}))
|
||||
|
||||
sniff.AddHandler(session.NewModuleHandler("net.fuzz off", "",
|
||||
"Disable fuzzing",
|
||||
func(args []string) error {
|
||||
return sniff.StopFuzzing()
|
||||
}))
|
||||
|
||||
sniff.AddParam(session.NewStringParameter("net.fuzz.layers",
|
||||
"Payload",
|
||||
"",
|
||||
"Types of layer to fuzz."))
|
||||
|
||||
sniff.AddParam(session.NewDecimalParameter("net.fuzz.rate",
|
||||
"1.0",
|
||||
"Rate in the [0.0,1.0] interval of packets to fuzz."))
|
||||
|
||||
sniff.AddParam(session.NewDecimalParameter("net.fuzz.ratio",
|
||||
"0.4",
|
||||
"Rate in the [0.0,1.0] interval of bytes to fuzz for each packet."))
|
||||
|
||||
sniff.AddParam(session.NewBoolParameter("net.fuzz.silent",
|
||||
"false",
|
||||
"If true it will not report fuzzed packets."))
|
||||
|
||||
return sniff
|
||||
}
|
||||
|
||||
func (s Sniffer) Name() string {
|
||||
return "net.sniff"
|
||||
}
|
||||
|
||||
func (s Sniffer) Description() string {
|
||||
return "Sniff packets from the network."
|
||||
}
|
||||
|
||||
func (s Sniffer) Author() string {
|
||||
return "Simone Margaritelli <evilsocket@protonmail.com>"
|
||||
}
|
||||
|
||||
func (s Sniffer) isLocalPacket(packet gopacket.Packet) bool {
|
||||
ipl := packet.Layer(layers.LayerTypeIPv4)
|
||||
if ipl != nil {
|
||||
ip, _ := ipl.(*layers.IPv4)
|
||||
if ip.SrcIP.Equal(s.Session.Interface.IP) || ip.DstIP.Equal(s.Session.Interface.IP) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Sniffer) onPacketMatched(pkt gopacket.Packet) {
|
||||
if mainParser(pkt, s.Ctx.Verbose) {
|
||||
s.Stats.NumDumped++
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Sniffer) Configure() error {
|
||||
var err error
|
||||
|
||||
if s.Running() {
|
||||
return session.ErrAlreadyStarted
|
||||
} else if err, s.Ctx = s.GetContext(); err != nil {
|
||||
if s.Ctx != nil {
|
||||
s.Ctx.Close()
|
||||
s.Ctx = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Sniffer) Start() error {
|
||||
if err := s.Configure(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.SetRunning(true, func() {
|
||||
s.Stats = NewSnifferStats()
|
||||
|
||||
src := gopacket.NewPacketSource(s.Ctx.Handle, s.Ctx.Handle.LinkType())
|
||||
s.pktSourceChan = src.Packets()
|
||||
for packet := range s.pktSourceChan {
|
||||
if !s.Running() {
|
||||
log.Debug("end pkt loop (pkt=%v filter='%s')", packet, s.Ctx.Filter)
|
||||
break
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
if s.Stats.FirstPacket.IsZero() {
|
||||
s.Stats.FirstPacket = now
|
||||
}
|
||||
s.Stats.LastPacket = now
|
||||
|
||||
isLocal := s.isLocalPacket(packet)
|
||||
if isLocal {
|
||||
s.Stats.NumLocal++
|
||||
}
|
||||
|
||||
if s.fuzzActive {
|
||||
s.doFuzzing(packet)
|
||||
}
|
||||
|
||||
if s.Ctx.DumpLocal || !isLocal {
|
||||
data := packet.Data()
|
||||
if s.Ctx.Compiled == nil || s.Ctx.Compiled.Match(data) {
|
||||
s.Stats.NumMatched++
|
||||
|
||||
s.onPacketMatched(packet)
|
||||
|
||||
if s.Ctx.OutputWriter != nil {
|
||||
s.Ctx.OutputWriter.WritePacket(packet.Metadata().CaptureInfo, data)
|
||||
s.Stats.NumWrote++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.pktSourceChan = nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Sniffer) Stop() error {
|
||||
return s.SetRunning(false, func() {
|
||||
log.Debug("stopping sniffer")
|
||||
if s.pktSourceChan != nil {
|
||||
log.Debug("sending nil")
|
||||
s.pktSourceChan <- nil
|
||||
log.Debug("nil sent")
|
||||
}
|
||||
log.Debug("closing ctx")
|
||||
s.Ctx.Close()
|
||||
log.Debug("ctx closed")
|
||||
})
|
||||
}
|
138
modules/net_sniff/net_sniff_context.go
Normal file
138
modules/net_sniff/net_sniff_context.go
Normal file
|
@ -0,0 +1,138 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/bettercap/bettercap/log"
|
||||
"github.com/bettercap/bettercap/session"
|
||||
|
||||
"github.com/google/gopacket/pcap"
|
||||
"github.com/google/gopacket/pcapgo"
|
||||
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
)
|
||||
|
||||
type SnifferContext struct {
|
||||
Handle *pcap.Handle
|
||||
Source string
|
||||
DumpLocal bool
|
||||
Verbose bool
|
||||
Filter string
|
||||
Expression string
|
||||
Compiled *regexp.Regexp
|
||||
Output string
|
||||
OutputFile *os.File
|
||||
OutputWriter *pcapgo.Writer
|
||||
}
|
||||
|
||||
func (s *Sniffer) GetContext() (error, *SnifferContext) {
|
||||
var err error
|
||||
|
||||
ctx := NewSnifferContext()
|
||||
|
||||
if err, ctx.Source = s.StringParam("net.sniff.source"); err != nil {
|
||||
return err, ctx
|
||||
}
|
||||
|
||||
if ctx.Source == "" {
|
||||
/*
|
||||
* We don't want to pcap.BlockForever otherwise pcap_close(handle)
|
||||
* could hang waiting for a timeout to expire ...
|
||||
*/
|
||||
readTimeout := 500 * time.Millisecond
|
||||
if ctx.Handle, err = pcap.OpenLive(s.Session.Interface.Name(), 65536, true, readTimeout); err != nil {
|
||||
return err, ctx
|
||||
}
|
||||
} else {
|
||||
if ctx.Handle, err = pcap.OpenOffline(ctx.Source); err != nil {
|
||||
return err, ctx
|
||||
}
|
||||
}
|
||||
|
||||
if err, ctx.Verbose = s.BoolParam("net.sniff.verbose"); err != nil {
|
||||
return err, ctx
|
||||
}
|
||||
|
||||
if err, ctx.DumpLocal = s.BoolParam("net.sniff.local"); err != nil {
|
||||
return err, ctx
|
||||
}
|
||||
|
||||
if err, ctx.Filter = s.StringParam("net.sniff.filter"); err != nil {
|
||||
return err, ctx
|
||||
} else if ctx.Filter != "" {
|
||||
err = ctx.Handle.SetBPFFilter(ctx.Filter)
|
||||
if err != nil {
|
||||
return err, ctx
|
||||
}
|
||||
}
|
||||
|
||||
if err, ctx.Expression = s.StringParam("net.sniff.regexp"); err != nil {
|
||||
return err, ctx
|
||||
} else if ctx.Expression != "" {
|
||||
if ctx.Compiled, err = regexp.Compile(ctx.Expression); err != nil {
|
||||
return err, ctx
|
||||
}
|
||||
}
|
||||
|
||||
if err, ctx.Output = s.StringParam("net.sniff.output"); err != nil {
|
||||
return err, ctx
|
||||
} else if ctx.Output != "" {
|
||||
if ctx.OutputFile, err = os.Create(ctx.Output); err != nil {
|
||||
return err, ctx
|
||||
}
|
||||
|
||||
ctx.OutputWriter = pcapgo.NewWriter(ctx.OutputFile)
|
||||
ctx.OutputWriter.WriteFileHeader(65536, ctx.Handle.LinkType())
|
||||
}
|
||||
|
||||
return nil, ctx
|
||||
}
|
||||
|
||||
func NewSnifferContext() *SnifferContext {
|
||||
return &SnifferContext{
|
||||
Handle: nil,
|
||||
DumpLocal: false,
|
||||
Verbose: false,
|
||||
Filter: "",
|
||||
Expression: "",
|
||||
Compiled: nil,
|
||||
Output: "",
|
||||
OutputFile: nil,
|
||||
OutputWriter: nil,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
no = tui.Red("no")
|
||||
yes = tui.Green("yes")
|
||||
yn = map[bool]string{
|
||||
true: yes,
|
||||
false: no,
|
||||
}
|
||||
)
|
||||
|
||||
func (c *SnifferContext) Log(sess *session.Session) {
|
||||
log.Info("Skip local packets : %s", yn[c.DumpLocal])
|
||||
log.Info("Verbose : %s", yn[c.Verbose])
|
||||
log.Info("BPF Filter : '%s'", tui.Yellow(c.Filter))
|
||||
log.Info("Regular expression : '%s'", tui.Yellow(c.Expression))
|
||||
log.Info("File output : '%s'", tui.Yellow(c.Output))
|
||||
}
|
||||
|
||||
func (c *SnifferContext) Close() {
|
||||
if c.Handle != nil {
|
||||
log.Debug("closing handle")
|
||||
c.Handle.Close()
|
||||
log.Debug("handle closed")
|
||||
c.Handle = nil
|
||||
}
|
||||
|
||||
if c.OutputFile != nil {
|
||||
log.Debug("closing output")
|
||||
c.OutputFile.Close()
|
||||
log.Debug("output closed")
|
||||
c.OutputFile = nil
|
||||
}
|
||||
}
|
66
modules/net_sniff/net_sniff_dns.go
Normal file
66
modules/net_sniff/net_sniff_dns.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"strings"
|
||||
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
)
|
||||
|
||||
func dnsParser(ip *layers.IPv4, pkt gopacket.Packet, udp *layers.UDP) bool {
|
||||
dns, parsed := pkt.Layer(layers.LayerTypeDNS).(*layers.DNS)
|
||||
if !parsed {
|
||||
return false
|
||||
}
|
||||
|
||||
if dns.OpCode != layers.DNSOpCodeQuery {
|
||||
return false
|
||||
}
|
||||
|
||||
m := make(map[string][]string)
|
||||
answers := [][]layers.DNSResourceRecord{
|
||||
dns.Answers,
|
||||
dns.Authorities,
|
||||
dns.Additionals,
|
||||
}
|
||||
|
||||
for _, list := range answers {
|
||||
for _, a := range list {
|
||||
if a.IP == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
hostname := string(a.Name)
|
||||
if _, found := m[hostname]; !found {
|
||||
m[hostname] = make([]string, 0)
|
||||
}
|
||||
|
||||
m[hostname] = append(m[hostname], vIP(a.IP))
|
||||
}
|
||||
}
|
||||
|
||||
if len(m) == 0 && dns.ResponseCode != layers.DNSResponseCodeNoErr {
|
||||
for _, a := range dns.Questions {
|
||||
m[string(a.Name)] = []string{tui.Red(dns.ResponseCode.String())}
|
||||
}
|
||||
}
|
||||
|
||||
for hostname, ips := range m {
|
||||
NewSnifferEvent(
|
||||
pkt.Metadata().Timestamp,
|
||||
"dns",
|
||||
ip.SrcIP.String(),
|
||||
ip.DstIP.String(),
|
||||
nil,
|
||||
"%s %s > %s : %s is %s",
|
||||
tui.Wrap(tui.BACKDARKGRAY+tui.FOREWHITE, "dns"),
|
||||
vIP(ip.SrcIP),
|
||||
vIP(ip.DstIP),
|
||||
tui.Yellow(hostname),
|
||||
tui.Dim(strings.Join(ips, ", ")),
|
||||
).Push()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
26
modules/net_sniff/net_sniff_dot11.go
Normal file
26
modules/net_sniff/net_sniff_dot11.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
func onDOT11(radiotap *layers.RadioTap, dot11 *layers.Dot11, pkt gopacket.Packet, verbose bool) {
|
||||
NewSnifferEvent(
|
||||
pkt.Metadata().Timestamp,
|
||||
"802.11",
|
||||
"-",
|
||||
"-",
|
||||
len(pkt.Data()),
|
||||
"%s %s proto=%d a1=%s a2=%s a3=%s a4=%s seqn=%d frag=%d",
|
||||
dot11.Type,
|
||||
dot11.Flags,
|
||||
dot11.Proto,
|
||||
dot11.Address1,
|
||||
dot11.Address2,
|
||||
dot11.Address3,
|
||||
dot11.Address4,
|
||||
dot11.SequenceNumber,
|
||||
dot11.FragmentNumber,
|
||||
).Push()
|
||||
}
|
35
modules/net_sniff/net_sniff_event.go
Normal file
35
modules/net_sniff/net_sniff_event.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bettercap/bettercap/session"
|
||||
)
|
||||
|
||||
type SniffData map[string]interface{}
|
||||
|
||||
type SnifferEvent struct {
|
||||
PacketTime time.Time `json:"time"`
|
||||
Protocol string `json:"protocol"`
|
||||
Source string `json:"from"`
|
||||
Destination string `json:"to"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
func NewSnifferEvent(t time.Time, proto string, src string, dst string, data interface{}, format string, args ...interface{}) SnifferEvent {
|
||||
return SnifferEvent{
|
||||
PacketTime: t,
|
||||
Protocol: proto,
|
||||
Source: src,
|
||||
Destination: dst,
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func (e SnifferEvent) Push() {
|
||||
session.I.Events.Add("net.sniff."+e.Protocol, e)
|
||||
session.I.Refresh()
|
||||
}
|
42
modules/net_sniff/net_sniff_ftp.go
Normal file
42
modules/net_sniff/net_sniff_ftp.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
|
||||
"github.com/evilsocket/islazy/str"
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
)
|
||||
|
||||
var (
|
||||
ftpRe = regexp.MustCompile(`^(USER|PASS) (.+)[\n\r]+$`)
|
||||
)
|
||||
|
||||
func ftpParser(ip *layers.IPv4, pkt gopacket.Packet, tcp *layers.TCP) bool {
|
||||
data := string(tcp.Payload)
|
||||
|
||||
if matches := ftpRe.FindAllStringSubmatch(data, -1); matches != nil {
|
||||
what := str.Trim(matches[0][1])
|
||||
cred := str.Trim(matches[0][2])
|
||||
NewSnifferEvent(
|
||||
pkt.Metadata().Timestamp,
|
||||
"ftp",
|
||||
ip.SrcIP.String(),
|
||||
ip.DstIP.String(),
|
||||
nil,
|
||||
"%s %s > %s:%s - %s %s",
|
||||
tui.Wrap(tui.BACKYELLOW+tui.FOREWHITE, "ftp"),
|
||||
vIP(ip.SrcIP),
|
||||
vIP(ip.DstIP),
|
||||
vPort(tcp.DstPort),
|
||||
tui.Bold(what),
|
||||
tui.Yellow(cred),
|
||||
).Push()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
121
modules/net_sniff/net_sniff_fuzz.go
Normal file
121
modules/net_sniff/net_sniff_fuzz.go
Normal file
|
@ -0,0 +1,121 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strings"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
|
||||
"github.com/bettercap/bettercap/log"
|
||||
|
||||
"github.com/evilsocket/islazy/str"
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
)
|
||||
|
||||
var mutators = []func(byte) byte{
|
||||
func(b byte) byte {
|
||||
return byte(rand.Intn(256) & 0xff)
|
||||
},
|
||||
func(b byte) byte {
|
||||
return byte(b << uint(rand.Intn(9)))
|
||||
},
|
||||
func(b byte) byte {
|
||||
return byte(b >> uint(rand.Intn(9)))
|
||||
},
|
||||
}
|
||||
|
||||
func (s *Sniffer) fuzz(data []byte) int {
|
||||
changes := 0
|
||||
for off, b := range data {
|
||||
if rand.Float64() > s.fuzzRatio {
|
||||
continue
|
||||
}
|
||||
|
||||
data[off] = mutators[rand.Intn(len(mutators))](b)
|
||||
changes++
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
func (s *Sniffer) doFuzzing(pkt gopacket.Packet) {
|
||||
if rand.Float64() > s.fuzzRate {
|
||||
return
|
||||
}
|
||||
|
||||
layersChanged := 0
|
||||
bytesChanged := 0
|
||||
|
||||
for _, fuzzLayerType := range s.fuzzLayers {
|
||||
for _, layer := range pkt.Layers() {
|
||||
if layer.LayerType().String() == fuzzLayerType {
|
||||
fuzzData := layer.LayerContents()
|
||||
changes := s.fuzz(fuzzData)
|
||||
if changes > 0 {
|
||||
layersChanged++
|
||||
bytesChanged += changes
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if bytesChanged > 0 {
|
||||
logFn := log.Info
|
||||
if s.fuzzSilent {
|
||||
logFn = log.Debug
|
||||
}
|
||||
logFn("[%s] changed %d bytes in %d layers.", tui.Green("net.fuzz"), bytesChanged, layersChanged)
|
||||
if err := s.Session.Queue.Send(pkt.Data()); err != nil {
|
||||
log.Error("error sending fuzzed packet: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Sniffer) configureFuzzing() (err error) {
|
||||
layers := ""
|
||||
|
||||
if err, layers = s.StringParam("net.fuzz.layers"); err != nil {
|
||||
return
|
||||
} else {
|
||||
s.fuzzLayers = str.Comma(layers)
|
||||
}
|
||||
|
||||
if err, s.fuzzRate = s.DecParam("net.fuzz.rate"); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err, s.fuzzRatio = s.DecParam("net.fuzz.ratio"); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err, s.fuzzSilent = s.BoolParam("net.fuzz.silent"); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Sniffer) StartFuzzing() error {
|
||||
if s.fuzzActive {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.configureFuzzing(); err != nil {
|
||||
return err
|
||||
} else if !s.Running() {
|
||||
if err := s.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s.fuzzActive = true
|
||||
|
||||
log.Info("[%s] active on layer types %s (rate:%f ratio:%f)", tui.Green("net.fuzz"), strings.Join(s.fuzzLayers, ","), s.fuzzRate, s.fuzzRatio)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Sniffer) StopFuzzing() error {
|
||||
s.fuzzActive = false
|
||||
return nil
|
||||
}
|
159
modules/net_sniff/net_sniff_http.go
Normal file
159
modules/net_sniff/net_sniff_http.go
Normal file
|
@ -0,0 +1,159 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
)
|
||||
|
||||
type HTTPRequest struct {
|
||||
Method string `json:"method"`
|
||||
Proto string `json:"proto"`
|
||||
Host string `json:"host"`
|
||||
URL string `json:"url:"`
|
||||
Headers http.Header `json:"headers"`
|
||||
ContentType string `json:"content_type"`
|
||||
Body []byte `json:"body"`
|
||||
}
|
||||
|
||||
func (r HTTPRequest) IsType(ctype string) bool {
|
||||
return strings.Contains(r.ContentType, ctype)
|
||||
}
|
||||
|
||||
type HTTPResponse struct {
|
||||
Protocol string `json:"protocol"`
|
||||
Status string `json:"status"`
|
||||
StatusCode int `json:"status_code"`
|
||||
Headers http.Header `json:"headers"`
|
||||
Body []byte `json:"body"`
|
||||
ContentLength int64 `json:"content_length"`
|
||||
ContentType string `json:"content_type"`
|
||||
TransferEncoding []string `json:"transfer_encoding"`
|
||||
}
|
||||
|
||||
func (r HTTPResponse) IsType(ctype string) bool {
|
||||
return strings.Contains(r.ContentType, ctype)
|
||||
}
|
||||
|
||||
func toSerializableRequest(req *http.Request) HTTPRequest {
|
||||
body := []byte(nil)
|
||||
ctype := "?"
|
||||
if req.Body != nil {
|
||||
body, _ = ioutil.ReadAll(req.Body)
|
||||
}
|
||||
|
||||
for name, values := range req.Header {
|
||||
if strings.ToLower(name) == "content-type" {
|
||||
for _, value := range values {
|
||||
ctype = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HTTPRequest{
|
||||
Method: req.Method,
|
||||
Proto: req.Proto,
|
||||
Host: req.Host,
|
||||
URL: req.URL.String(),
|
||||
Headers: req.Header,
|
||||
ContentType: ctype,
|
||||
Body: body,
|
||||
}
|
||||
}
|
||||
|
||||
func toSerializableResponse(res *http.Response) HTTPResponse {
|
||||
body := []byte(nil)
|
||||
ctype := "?"
|
||||
cenc := ""
|
||||
for name, values := range res.Header {
|
||||
name = strings.ToLower(name)
|
||||
if name == "content-type" {
|
||||
for _, value := range values {
|
||||
ctype = value
|
||||
}
|
||||
} else if name == "content-encoding" {
|
||||
for _, value := range values {
|
||||
cenc = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if res.Body != nil {
|
||||
body, _ = ioutil.ReadAll(res.Body)
|
||||
}
|
||||
|
||||
// attempt decompression, but since this has been parsed by just
|
||||
// a tcp packet, it will probably fail
|
||||
if body != nil && strings.Contains(cenc, "gzip") {
|
||||
buffer := bytes.NewBuffer(body)
|
||||
uncompressed := bytes.Buffer{}
|
||||
if reader, err := gzip.NewReader(buffer); err == nil {
|
||||
if _, err = uncompressed.ReadFrom(reader); err == nil {
|
||||
body = uncompressed.Bytes()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HTTPResponse{
|
||||
Protocol: res.Proto,
|
||||
Status: res.Status,
|
||||
StatusCode: res.StatusCode,
|
||||
Headers: res.Header,
|
||||
Body: body,
|
||||
ContentLength: res.ContentLength,
|
||||
ContentType: ctype,
|
||||
TransferEncoding: res.TransferEncoding,
|
||||
}
|
||||
}
|
||||
|
||||
func httpParser(ip *layers.IPv4, pkt gopacket.Packet, tcp *layers.TCP) bool {
|
||||
data := tcp.Payload
|
||||
if req, err := http.ReadRequest(bufio.NewReader(bytes.NewReader(data))); err == nil {
|
||||
NewSnifferEvent(
|
||||
pkt.Metadata().Timestamp,
|
||||
"http.request",
|
||||
ip.SrcIP.String(),
|
||||
req.Host,
|
||||
toSerializableRequest(req),
|
||||
"%s %s %s %s%s",
|
||||
tui.Wrap(tui.BACKRED+tui.FOREBLACK, "http"),
|
||||
vIP(ip.SrcIP),
|
||||
tui.Wrap(tui.BACKLIGHTBLUE+tui.FOREBLACK, req.Method),
|
||||
tui.Yellow(req.Host),
|
||||
vURL(req.URL.String()),
|
||||
).Push()
|
||||
|
||||
return true
|
||||
} else if res, err := http.ReadResponse(bufio.NewReader(bytes.NewReader(data)), nil); err == nil {
|
||||
sres := toSerializableResponse(res)
|
||||
NewSnifferEvent(
|
||||
pkt.Metadata().Timestamp,
|
||||
"http.response",
|
||||
ip.SrcIP.String(),
|
||||
ip.DstIP.String(),
|
||||
sres,
|
||||
"%s %s:%d %s -> %s (%s %s)",
|
||||
tui.Wrap(tui.BACKRED+tui.FOREBLACK, "http"),
|
||||
vIP(ip.SrcIP),
|
||||
tcp.SrcPort,
|
||||
tui.Bold(res.Status),
|
||||
vIP(ip.DstIP),
|
||||
tui.Dim(humanize.Bytes(uint64(len(sres.Body)))),
|
||||
tui.Yellow(sres.ContentType),
|
||||
).Push()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
43
modules/net_sniff/net_sniff_krb5.go
Normal file
43
modules/net_sniff/net_sniff_krb5.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/bettercap/bettercap/packets"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
)
|
||||
|
||||
func krb5Parser(ip *layers.IPv4, pkt gopacket.Packet, udp *layers.UDP) bool {
|
||||
if udp.DstPort != 88 {
|
||||
return false
|
||||
}
|
||||
|
||||
var req packets.Krb5Request
|
||||
_, err := asn1.UnmarshalWithParams(udp.Payload, &req, packets.Krb5AsReqParam)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if s, err := req.String(); err == nil {
|
||||
NewSnifferEvent(
|
||||
pkt.Metadata().Timestamp,
|
||||
"krb5",
|
||||
ip.SrcIP.String(),
|
||||
ip.DstIP.String(),
|
||||
nil,
|
||||
"%s %s -> %s : %s",
|
||||
tui.Wrap(tui.BACKRED+tui.FOREBLACK, "krb-as-req"),
|
||||
vIP(ip.SrcIP),
|
||||
vIP(ip.DstIP),
|
||||
s,
|
||||
).Push()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
65
modules/net_sniff/net_sniff_mdns.go
Normal file
65
modules/net_sniff/net_sniff_mdns.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/bettercap/bettercap/packets"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
)
|
||||
|
||||
func mdnsParser(ip *layers.IPv4, pkt gopacket.Packet, udp *layers.UDP) bool {
|
||||
if udp.SrcPort == packets.MDNSPort && udp.DstPort == packets.MDNSPort {
|
||||
dns := layers.DNS{}
|
||||
if err := dns.DecodeFromBytes(udp.Payload, gopacket.NilDecodeFeedback); err == nil && dns.OpCode == layers.DNSOpCodeQuery {
|
||||
for _, q := range dns.Questions {
|
||||
NewSnifferEvent(
|
||||
pkt.Metadata().Timestamp,
|
||||
"mdns",
|
||||
ip.SrcIP.String(),
|
||||
ip.DstIP.String(),
|
||||
nil,
|
||||
"%s %s : %s query for %s",
|
||||
tui.Wrap(tui.BACKDARKGRAY+tui.FOREWHITE, "mdns"),
|
||||
vIP(ip.SrcIP),
|
||||
tui.Dim(q.Type.String()),
|
||||
tui.Yellow(string(q.Name)),
|
||||
).Push()
|
||||
}
|
||||
|
||||
m := make(map[string][]string)
|
||||
answers := append(dns.Answers, dns.Additionals...)
|
||||
answers = append(answers, dns.Authorities...)
|
||||
for _, answer := range answers {
|
||||
if answer.Type == layers.DNSTypeA || answer.Type == layers.DNSTypeAAAA {
|
||||
hostname := string(answer.Name)
|
||||
if _, found := m[hostname]; !found {
|
||||
m[hostname] = make([]string, 0)
|
||||
}
|
||||
m[hostname] = append(m[hostname], answer.IP.String())
|
||||
}
|
||||
}
|
||||
|
||||
for hostname, ips := range m {
|
||||
NewSnifferEvent(
|
||||
pkt.Metadata().Timestamp,
|
||||
"mdns",
|
||||
ip.SrcIP.String(),
|
||||
ip.DstIP.String(),
|
||||
nil,
|
||||
"%s %s : %s is %s",
|
||||
tui.Wrap(tui.BACKDARKGRAY+tui.FOREWHITE, "mdns"),
|
||||
vIP(ip.SrcIP),
|
||||
tui.Yellow(hostname),
|
||||
tui.Dim(strings.Join(ips, ", ")),
|
||||
).Push()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
67
modules/net_sniff/net_sniff_ntlm.go
Normal file
67
modules/net_sniff/net_sniff_ntlm.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/bettercap/bettercap/packets"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
)
|
||||
|
||||
var (
|
||||
ntlmRe = regexp.MustCompile("(WWW-|Proxy-|)(Authenticate|Authorization): (NTLM|Negotiate)")
|
||||
challRe = regexp.MustCompile("(WWW-|Proxy-|)(Authenticate): (NTLM|Negotiate)")
|
||||
respRe = regexp.MustCompile("(WWW-|Proxy-|)(Authorization): (NTLM|Negotiate)")
|
||||
ntlm = packets.NewNTLMState()
|
||||
)
|
||||
|
||||
func isNtlm(s string) bool {
|
||||
return ntlmRe.FindString(s) != ""
|
||||
}
|
||||
|
||||
func isChallenge(s string) bool {
|
||||
return challRe.FindString(s) != ""
|
||||
}
|
||||
|
||||
func isResponse(s string) bool {
|
||||
return respRe.FindString(s) != ""
|
||||
}
|
||||
|
||||
func ntlmParser(ip *layers.IPv4, pkt gopacket.Packet, tcp *layers.TCP) bool {
|
||||
data := tcp.Payload
|
||||
ok := false
|
||||
|
||||
for _, line := range strings.Split(string(data), "\r\n") {
|
||||
if isNtlm(line) {
|
||||
tokens := strings.Split(line, " ")
|
||||
if len(tokens) != 3 {
|
||||
continue
|
||||
}
|
||||
if isChallenge(line) {
|
||||
ok = true
|
||||
ntlm.AddServerResponse(tcp.Ack, tokens[2])
|
||||
} else if isResponse(line) {
|
||||
ok = true
|
||||
ntlm.AddClientResponse(tcp.Seq, tokens[2], func(data packets.NTLMChallengeResponseParsed) {
|
||||
NewSnifferEvent(
|
||||
pkt.Metadata().Timestamp,
|
||||
"ntlm.response",
|
||||
ip.SrcIP.String(),
|
||||
ip.DstIP.String(),
|
||||
nil,
|
||||
"%s %s > %s | %s",
|
||||
tui.Wrap(tui.BACKDARKGRAY+tui.FOREWHITE, "ntlm.response"),
|
||||
vIP(ip.SrcIP),
|
||||
vIP(ip.DstIP),
|
||||
data.LcString(),
|
||||
).Push()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok
|
||||
}
|
67
modules/net_sniff/net_sniff_parsers.go
Normal file
67
modules/net_sniff/net_sniff_parsers.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bettercap/bettercap/log"
|
||||
"github.com/bettercap/bettercap/packets"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
)
|
||||
|
||||
func onUNK(ip *layers.IPv4, pkt gopacket.Packet, verbose bool) {
|
||||
if verbose {
|
||||
NewSnifferEvent(
|
||||
pkt.Metadata().Timestamp,
|
||||
pkt.TransportLayer().LayerType().String(),
|
||||
vIP(ip.SrcIP),
|
||||
vIP(ip.DstIP),
|
||||
SniffData{
|
||||
"Size": len(ip.Payload),
|
||||
},
|
||||
"%s %s > %s %s",
|
||||
tui.Wrap(tui.BACKDARKGRAY+tui.FOREWHITE, pkt.TransportLayer().LayerType().String()),
|
||||
vIP(ip.SrcIP),
|
||||
vIP(ip.DstIP),
|
||||
tui.Dim(fmt.Sprintf("%d bytes", len(ip.Payload))),
|
||||
).Push()
|
||||
}
|
||||
}
|
||||
|
||||
func mainParser(pkt gopacket.Packet, verbose bool) bool {
|
||||
// simple networking sniffing mode?
|
||||
nlayer := pkt.NetworkLayer()
|
||||
if nlayer != nil {
|
||||
if nlayer.LayerType() != layers.LayerTypeIPv4 {
|
||||
log.Debug("Unexpected layer type %s, skipping packet.", nlayer.LayerType())
|
||||
log.Debug("%s", pkt.Dump())
|
||||
return false
|
||||
}
|
||||
|
||||
ip := nlayer.(*layers.IPv4)
|
||||
|
||||
tlayer := pkt.TransportLayer()
|
||||
if tlayer == nil {
|
||||
log.Debug("Missing transport layer skipping packet.")
|
||||
log.Debug("%s", pkt.Dump())
|
||||
return false
|
||||
}
|
||||
|
||||
if tlayer.LayerType() == layers.LayerTypeTCP {
|
||||
onTCP(ip, pkt, verbose)
|
||||
} else if tlayer.LayerType() == layers.LayerTypeUDP {
|
||||
onUDP(ip, pkt, verbose)
|
||||
} else {
|
||||
onUNK(ip, pkt, verbose)
|
||||
}
|
||||
return true
|
||||
} else if ok, radiotap, dot11 := packets.Dot11Parse(pkt); ok {
|
||||
// are we sniffing in monitor mode?
|
||||
onDOT11(radiotap, dot11, pkt, verbose)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
48
modules/net_sniff/net_sniff_sni.go
Normal file
48
modules/net_sniff/net_sniff_sni.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"regexp"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
)
|
||||
|
||||
// poor man's TLS Client Hello with SNI extension parser :P
|
||||
var sniRe = regexp.MustCompile("\x00\x00.{4}\x00.{2}([a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,6})\x00")
|
||||
|
||||
func sniParser(ip *layers.IPv4, pkt gopacket.Packet, tcp *layers.TCP) bool {
|
||||
data := tcp.Payload
|
||||
dataSize := len(data)
|
||||
|
||||
if dataSize < 2 || data[0] != 0x16 || data[1] != 0x03 {
|
||||
return false
|
||||
}
|
||||
|
||||
m := sniRe.FindSubmatch(data)
|
||||
if len(m) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
domain := string(m[1])
|
||||
if tcp.DstPort != 443 {
|
||||
domain = fmt.Sprintf("%s:%d", domain, tcp.DstPort)
|
||||
}
|
||||
|
||||
NewSnifferEvent(
|
||||
pkt.Metadata().Timestamp,
|
||||
"https",
|
||||
ip.SrcIP.String(),
|
||||
domain,
|
||||
nil,
|
||||
"%s %s > %s",
|
||||
tui.Wrap(tui.BACKYELLOW+tui.FOREWHITE, "sni"),
|
||||
vIP(ip.SrcIP),
|
||||
tui.Yellow("https://"+domain),
|
||||
).Push()
|
||||
|
||||
return true
|
||||
}
|
50
modules/net_sniff/net_sniff_stats.go
Normal file
50
modules/net_sniff/net_sniff_stats.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"github.com/bettercap/bettercap/log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SnifferStats struct {
|
||||
NumLocal uint64
|
||||
NumMatched uint64
|
||||
NumDumped uint64
|
||||
NumWrote uint64
|
||||
Started time.Time
|
||||
FirstPacket time.Time
|
||||
LastPacket time.Time
|
||||
}
|
||||
|
||||
func NewSnifferStats() *SnifferStats {
|
||||
return &SnifferStats{
|
||||
NumLocal: 0,
|
||||
NumMatched: 0,
|
||||
NumDumped: 0,
|
||||
NumWrote: 0,
|
||||
Started: time.Now(),
|
||||
FirstPacket: time.Time{},
|
||||
LastPacket: time.Time{},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SnifferStats) Print() error {
|
||||
first := "never"
|
||||
last := "never"
|
||||
|
||||
if !s.FirstPacket.IsZero() {
|
||||
first = s.FirstPacket.String()
|
||||
}
|
||||
if !s.LastPacket.IsZero() {
|
||||
last = s.LastPacket.String()
|
||||
}
|
||||
|
||||
log.Info("Sniffer Started : %s", s.Started)
|
||||
log.Info("First Packet Seen : %s", first)
|
||||
log.Info("Last Packet Seen : %s", last)
|
||||
log.Info("Local Packets : %d", s.NumLocal)
|
||||
log.Info("Matched Packets : %d", s.NumMatched)
|
||||
log.Info("Dumped Packets : %d", s.NumDumped)
|
||||
log.Info("Wrote Packets : %d", s.NumWrote)
|
||||
|
||||
return nil
|
||||
}
|
46
modules/net_sniff/net_sniff_tcp.go
Normal file
46
modules/net_sniff/net_sniff_tcp.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
)
|
||||
|
||||
var tcpParsers = []func(*layers.IPv4, gopacket.Packet, *layers.TCP) bool{
|
||||
sniParser,
|
||||
ntlmParser,
|
||||
httpParser,
|
||||
ftpParser,
|
||||
teamViewerParser,
|
||||
}
|
||||
|
||||
func onTCP(ip *layers.IPv4, pkt gopacket.Packet, verbose bool) {
|
||||
tcp := pkt.Layer(layers.LayerTypeTCP).(*layers.TCP)
|
||||
for _, parser := range tcpParsers {
|
||||
if parser(ip, pkt, tcp) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if verbose {
|
||||
NewSnifferEvent(
|
||||
pkt.Metadata().Timestamp,
|
||||
"tcp",
|
||||
fmt.Sprintf("%s:%s", ip.SrcIP, vPort(tcp.SrcPort)),
|
||||
fmt.Sprintf("%s:%s", ip.DstIP, vPort(tcp.DstPort)),
|
||||
SniffData{
|
||||
"Size": len(ip.Payload),
|
||||
},
|
||||
"%s %s:%s > %s:%s %s",
|
||||
tui.Wrap(tui.BACKLIGHTBLUE+tui.FOREBLACK, "tcp"),
|
||||
vIP(ip.SrcIP),
|
||||
vPort(tcp.SrcPort),
|
||||
vIP(ip.DstIP),
|
||||
vPort(tcp.DstPort),
|
||||
tui.Dim(fmt.Sprintf("%d bytes", len(ip.Payload))),
|
||||
).Push()
|
||||
}
|
||||
}
|
32
modules/net_sniff/net_sniff_teamviewer.go
Normal file
32
modules/net_sniff/net_sniff_teamviewer.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"github.com/bettercap/bettercap/packets"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
)
|
||||
|
||||
func teamViewerParser(ip *layers.IPv4, pkt gopacket.Packet, tcp *layers.TCP) bool {
|
||||
if tcp.SrcPort == packets.TeamViewerPort || tcp.DstPort == packets.TeamViewerPort {
|
||||
if tv := packets.ParseTeamViewer(tcp.Payload); tv != nil {
|
||||
NewSnifferEvent(
|
||||
pkt.Metadata().Timestamp,
|
||||
"teamviewer",
|
||||
ip.SrcIP.String(),
|
||||
ip.DstIP.String(),
|
||||
nil,
|
||||
"%s %s %s > %s",
|
||||
tui.Wrap(tui.BACKYELLOW+tui.FOREWHITE, "teamviewer"),
|
||||
vIP(ip.SrcIP),
|
||||
tui.Yellow(tv.Command),
|
||||
vIP(ip.DstIP),
|
||||
).Push()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
45
modules/net_sniff/net_sniff_udp.go
Normal file
45
modules/net_sniff/net_sniff_udp.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
)
|
||||
|
||||
var udpParsers = []func(*layers.IPv4, gopacket.Packet, *layers.UDP) bool{
|
||||
dnsParser,
|
||||
mdnsParser,
|
||||
krb5Parser,
|
||||
upnpParser,
|
||||
}
|
||||
|
||||
func onUDP(ip *layers.IPv4, pkt gopacket.Packet, verbose bool) {
|
||||
udp := pkt.Layer(layers.LayerTypeUDP).(*layers.UDP)
|
||||
for _, parser := range udpParsers {
|
||||
if parser(ip, pkt, udp) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if verbose {
|
||||
NewSnifferEvent(
|
||||
pkt.Metadata().Timestamp,
|
||||
"udp",
|
||||
fmt.Sprintf("%s:%s", ip.SrcIP, vPort(udp.SrcPort)),
|
||||
fmt.Sprintf("%s:%s", ip.DstIP, vPort(udp.DstPort)),
|
||||
SniffData{
|
||||
"Size": len(ip.Payload),
|
||||
},
|
||||
"%s %s:%s > %s:%s %s",
|
||||
tui.Wrap(tui.BACKDARKGRAY+tui.FOREWHITE, "udp"),
|
||||
vIP(ip.SrcIP),
|
||||
vPort(udp.SrcPort),
|
||||
vIP(ip.DstIP),
|
||||
vPort(udp.DstPort),
|
||||
tui.Dim(fmt.Sprintf("%d bytes", len(ip.Payload))),
|
||||
).Push()
|
||||
}
|
||||
}
|
39
modules/net_sniff/net_sniff_upnp.go
Normal file
39
modules/net_sniff/net_sniff_upnp.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bettercap/bettercap/packets"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
|
||||
"github.com/evilsocket/islazy/str"
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
)
|
||||
|
||||
func upnpParser(ip *layers.IPv4, pkt gopacket.Packet, udp *layers.UDP) bool {
|
||||
if data := packets.UPNPGetMeta(pkt); data != nil && len(data) > 0 {
|
||||
s := ""
|
||||
for name, value := range data {
|
||||
s += fmt.Sprintf("%s:%s ", tui.Blue(name), tui.Yellow(value))
|
||||
}
|
||||
|
||||
NewSnifferEvent(
|
||||
pkt.Metadata().Timestamp,
|
||||
"upnp",
|
||||
ip.SrcIP.String(),
|
||||
ip.DstIP.String(),
|
||||
nil,
|
||||
"%s %s -> %s : %s",
|
||||
tui.Wrap(tui.BACKRED+tui.FOREBLACK, "upnp"),
|
||||
vIP(ip.SrcIP),
|
||||
vIP(ip.DstIP),
|
||||
str.Trim(s),
|
||||
).Push()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
54
modules/net_sniff/net_sniff_views.go
Normal file
54
modules/net_sniff/net_sniff_views.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package net_sniff
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/bettercap/bettercap/session"
|
||||
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
func vIP(ip net.IP) string {
|
||||
if session.I.Interface.IP.Equal(ip) {
|
||||
return tui.Dim("local")
|
||||
} else if session.I.Gateway.IP.Equal(ip) {
|
||||
return "gateway"
|
||||
}
|
||||
|
||||
address := ip.String()
|
||||
host := session.I.Lan.GetByIp(address)
|
||||
if host != nil {
|
||||
if host.Hostname != "" {
|
||||
return host.Hostname
|
||||
}
|
||||
}
|
||||
|
||||
return address
|
||||
}
|
||||
|
||||
func vPort(p interface{}) string {
|
||||
sp := fmt.Sprintf("%d", p)
|
||||
if tcp, ok := p.(layers.TCPPort); ok {
|
||||
if name, found := layers.TCPPortNames[tcp]; found {
|
||||
sp = tui.Yellow(name)
|
||||
}
|
||||
} else if udp, ok := p.(layers.UDPPort); ok {
|
||||
if name, found := layers.UDPPortNames[udp]; found {
|
||||
sp = tui.Yellow(name)
|
||||
}
|
||||
}
|
||||
|
||||
return sp
|
||||
}
|
||||
|
||||
var maxUrlSize = 80
|
||||
|
||||
func vURL(u string) string {
|
||||
ul := len(u)
|
||||
if ul > maxUrlSize {
|
||||
u = fmt.Sprintf("%s...", u[0:maxUrlSize-3])
|
||||
}
|
||||
return u
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue