package syn_scan import ( "fmt" "net" "strconv" "sync" "sync/atomic" "time" "github.com/bettercap/bettercap/packets" "github.com/bettercap/bettercap/session" "github.com/malfunkt/iprange" "github.com/evilsocket/islazy/str" ) const synSourcePort = 666 type synScannerStats struct { started time.Time numPorts uint64 numAddresses uint64 totProbes uint64 doneProbes uint64 openPorts uint64 } type SynScanner struct { session.SessionModule addresses []net.IP startPort int endPort int progressEvery time.Duration stats synScannerStats waitGroup *sync.WaitGroup } func NewSynScanner(s *session.Session) *SynScanner { mod := &SynScanner{ SessionModule: session.NewSessionModule("syn.scan", s), addresses: make([]net.IP, 0), waitGroup: &sync.WaitGroup{}, progressEvery: time.Duration(1) * time.Second, } mod.State.Store("scanning", &mod.addresses) mod.State.Store("progress", 0.0) mod.AddParam(session.NewIntParameter("syn.scan.show-progress-every", "1", "Period in seconds for the scanning progress reporting.")) mod.AddHandler(session.NewModuleHandler("syn.scan stop", "syn\\.scan (stop|off)", "Stop the current syn scanning session.", func(args []string) error { if !mod.Running() { return fmt.Errorf("no syn.scan is running") } return mod.Stop() })) mod.AddHandler(session.NewModuleHandler("syn.scan IP-RANGE START-PORT END-PORT", "syn.scan ([^\\s]+) ?(\\d+)?([\\s\\d]*)?", "Perform a syn port scanning against an IP address within the provided ports range.", func(args []string) error { period := 0 if mod.Running() { return fmt.Errorf("A scan is already running, wait for it to end before starting a new one.") } else if err := mod.parseTargets(args[0]); err != nil { return err } else if err = mod.parsePorts(args); err != nil { return err } else if err, period = mod.IntParam("syn.scan.show-progress-every"); err != nil { return err } else { mod.progressEvery = time.Duration(period) * time.Second } return mod.synScan() })) mod.AddHandler(session.NewModuleHandler("syn.scan.progress", "syn\\.scan\\.progress", "Print progress of the current syn scanning session.", func(args []string) error { if !mod.Running() { return fmt.Errorf("no syn.scan is running") } return mod.showProgress() })) return mod } func (mod *SynScanner) parseTargets(arg string) error { if list, err := iprange.Parse(arg); err != nil { return fmt.Errorf("error while parsing IP range '%s': %s", arg, err) } else { mod.addresses = list.Expand() } return nil } func (mod *SynScanner) parsePorts(args []string) (err error) { argc := len(args) mod.stats.totProbes = 0 mod.stats.doneProbes = 0 mod.startPort = 1 mod.endPort = 65535 if argc > 1 && str.Trim(args[1]) != "" { if mod.startPort, err = strconv.Atoi(str.Trim(args[1])); err != nil { return fmt.Errorf("invalid start port %s: %s", args[1], err) } else if mod.startPort > 65535 { mod.startPort = 65535 } mod.endPort = mod.startPort } if argc > 2 && str.Trim(args[2]) != "" { if mod.endPort, err = strconv.Atoi(str.Trim(args[2])); err != nil { return fmt.Errorf("invalid end port %s: %s", args[2], err) } } if mod.endPort < mod.startPort { return fmt.Errorf("end port %d is greater than start port %d", mod.endPort, mod.startPort) } return } func (mod *SynScanner) Name() string { return "syn.scan" } func (mod *SynScanner) Description() string { return "A module to perform SYN port scanning." } func (mod *SynScanner) Author() string { return "Simone Margaritelli " } func (mod *SynScanner) Configure() error { return nil } func (mod *SynScanner) Start() error { return nil } func plural(n uint64) string { if n > 1 { return "s" } return "" } func (mod *SynScanner) showProgress() error { progress := 100.0 * (float64(mod.stats.doneProbes) / float64(mod.stats.totProbes)) mod.State.Store("progress", progress) mod.Info("[%.2f%%] found %d open port%s for %d address%s, sent %d/%d packets in %s", progress, mod.stats.openPorts, plural(mod.stats.openPorts), mod.stats.numAddresses, plural(mod.stats.numAddresses), mod.stats.doneProbes, mod.stats.totProbes, time.Since(mod.stats.started)) return nil } func (mod *SynScanner) Stop() error { mod.Info("stopping ...") return mod.SetRunning(false, func() { mod.waitGroup.Wait() mod.showProgress() mod.addresses = []net.IP{} mod.State.Store("progress", 0.0) }) } func (mod *SynScanner) synScan() error { mod.SetRunning(true, func() { defer mod.SetRunning(false, func() { mod.addresses = []net.IP{} mod.State.Store("progress", 0.0) }) mod.waitGroup.Add(1) defer mod.waitGroup.Done() mod.stats.openPorts = 0 mod.stats.numPorts = uint64(mod.endPort - mod.startPort + 1) mod.stats.started = time.Now() mod.stats.numAddresses = uint64(len(mod.addresses)) mod.stats.totProbes = mod.stats.numAddresses * mod.stats.numPorts mod.stats.doneProbes = 0 plural := "es" if mod.stats.numAddresses == 1 { plural = "" } if mod.stats.numPorts > 1 { mod.Info("scanning %d address%s from port %d to port %d ...", mod.stats.numAddresses, plural, mod.startPort, mod.endPort) } else { mod.Info("scanning %d address%s on port %d ...", mod.stats.numAddresses, plural, mod.startPort) } mod.State.Store("progress", 0.0) // set the collector mod.Session.Queue.OnPacket(mod.onPacket) defer mod.Session.Queue.OnPacket(nil) // start to show progress every second go func() { for { time.Sleep(mod.progressEvery) if mod.Running() { mod.showProgress() } else { break } } }() // start sending SYN packets and wait for _, address := range mod.addresses { if !mod.Running() { break } mac, err := mod.Session.FindMAC(address, true) if err != nil { atomic.AddUint64(&mod.stats.doneProbes, mod.stats.numPorts) mod.Debug("could not get MAC for %s: %s", address.String(), err) continue } for dstPort := mod.startPort; dstPort < mod.endPort+1; dstPort++ { if !mod.Running() { break } atomic.AddUint64(&mod.stats.doneProbes, 1) err, raw := packets.NewTCPSyn(mod.Session.Interface.IP, mod.Session.Interface.HW, address, mac, synSourcePort, dstPort) if err != nil { mod.Error("error creating SYN packet: %s", err) continue } if err := mod.Session.Queue.Send(raw); err != nil { mod.Error("error sending SYN packet: %s", err) } else { mod.Debug("sent %d bytes of SYN packet to %s for port %d", len(raw), address.String(), dstPort) } time.Sleep(time.Duration(10) * time.Millisecond) } } }) return nil }