new: added syn.scan.progress command and syn.scan.show-progress-every variable (ref #415)

This commit is contained in:
evilsocket 2019-01-18 15:51:11 +01:00
commit e31ce94a78
No known key found for this signature in database
GPG key ID: 1564D7F30393A456
2 changed files with 144 additions and 62 deletions

View file

@ -5,53 +5,79 @@ import (
"net" "net"
"strconv" "strconv"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/bettercap/bettercap/log" "github.com/bettercap/bettercap/log"
"github.com/bettercap/bettercap/network"
"github.com/bettercap/bettercap/packets" "github.com/bettercap/bettercap/packets"
"github.com/bettercap/bettercap/session" "github.com/bettercap/bettercap/session"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/malfunkt/iprange" "github.com/malfunkt/iprange"
"github.com/evilsocket/islazy/str" "github.com/evilsocket/islazy/str"
"github.com/evilsocket/islazy/tui"
) )
const synSourcePort = 666 const synSourcePort = 666
type synScannerStats struct {
started time.Time
numPorts uint64
numAddresses uint64
totProbes uint64
doneProbes uint64
openPorts uint64
}
type SynScanner struct { type SynScanner struct {
session.SessionModule session.SessionModule
addresses []net.IP addresses []net.IP
startPort int startPort int
endPort int endPort int
waitGroup *sync.WaitGroup progressEvery time.Duration
stats synScannerStats
waitGroup *sync.WaitGroup
} }
func NewSynScanner(s *session.Session) *SynScanner { func NewSynScanner(s *session.Session) *SynScanner {
ss := &SynScanner{ ss := &SynScanner{
SessionModule: session.NewSessionModule("syn.scan", s), SessionModule: session.NewSessionModule("syn.scan", s),
addresses: make([]net.IP, 0), addresses: make([]net.IP, 0),
startPort: 0,
endPort: 0,
waitGroup: &sync.WaitGroup{}, waitGroup: &sync.WaitGroup{},
progressEvery: time.Duration(1) * time.Second,
} }
ss.AddParam(session.NewIntParameter("syn.scan.show-progress-every",
"1",
"Period in seconds for the scanning progress reporting."))
ss.AddHandler(session.NewModuleHandler("syn.scan IP-RANGE [START-PORT] [END-PORT]", "syn.scan ([^\\s]+) ?(\\d+)?([\\s\\d]*)?", ss.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.", "Perform a syn port scanning against an IP address within the provided ports range.",
func(args []string) error { func(args []string) error {
period := 0
if ss.Running() { if ss.Running() {
return fmt.Errorf("A scan is already running, wait for it to end before starting a new one.") return fmt.Errorf("A scan is already running, wait for it to end before starting a new one.")
} else if err := ss.parseTargets(args[0]); err != nil { } else if err := ss.parseTargets(args[0]); err != nil {
return err return err
} else if err = ss.parsePorts(args); err != nil { } else if err = ss.parsePorts(args); err != nil {
return err return err
} else if err, period = ss.IntParam("syn.scan.show-progress-every"); err != nil {
return err
} else {
ss.progressEvery = time.Duration(period) * time.Second
} }
return ss.synScan() return ss.synScan()
})) }))
ss.AddHandler(session.NewModuleHandler("syn.scan.progress", "syn\\.scan\\.progress",
"Print progress of the current syn scanning session.",
func(args []string) error {
if !ss.Running() {
return fmt.Errorf("no syn.scan is running")
}
return ss.showProgress()
}))
return ss return ss
} }
@ -66,6 +92,8 @@ func (s *SynScanner) parseTargets(arg string) error {
func (s *SynScanner) parsePorts(args []string) (err error) { func (s *SynScanner) parsePorts(args []string) (err error) {
argc := len(args) argc := len(args)
s.stats.totProbes = 0
s.stats.doneProbes = 0
s.startPort = 1 s.startPort = 1
s.endPort = 65535 s.endPort = 65535
@ -111,53 +139,26 @@ func (s *SynScanner) Start() error {
return nil return nil
} }
func (s *SynScanner) inRange(ip net.IP) bool { func plural(n uint64) string {
for _, a := range s.addresses { if n > 1 {
if a.Equal(ip) { return "s"
return true
}
} }
return false return ""
} }
func (s *SynScanner) onPacket(pkt gopacket.Packet) { func (s *SynScanner) showProgress() error {
var eth layers.Ethernet progress := 100.0 * (float64(s.stats.doneProbes) / float64(s.stats.totProbes))
var ip layers.IPv4 log.Info("[%s] [%.2f%%] found %d open port%s for %d address%s, sent %d/%d packets in %s",
var tcp layers.TCP tui.Green("syn.scan"),
foundLayerTypes := []gopacket.LayerType{} progress,
s.stats.openPorts,
parser := gopacket.NewDecodingLayerParser( plural(s.stats.openPorts),
layers.LayerTypeEthernet, s.stats.numAddresses,
&eth, plural(s.stats.numAddresses),
&ip, s.stats.doneProbes,
&tcp, s.stats.totProbes,
) time.Since(s.stats.started))
return nil
err := parser.DecodeLayers(pkt.Data(), &foundLayerTypes)
if err != nil {
return
}
if s.inRange(ip.SrcIP) && tcp.DstPort == synSourcePort && tcp.SYN && tcp.ACK {
from := ip.SrcIP.String()
port := int(tcp.SrcPort)
var host *network.Endpoint
if ip.SrcIP.Equal(s.Session.Interface.IP) {
host = s.Session.Interface
} else if ip.SrcIP.Equal(s.Session.Gateway.IP) {
host = s.Session.Gateway
} else {
host = s.Session.Lan.GetByIp(from)
}
if host != nil {
ports := host.Meta.GetIntsWith("tcp-ports", port, true)
host.Meta.SetInts("tcp-ports", ports)
}
NewSynScanEvent(from, host, port).Push()
}
} }
func (s *SynScanner) synScan() error { func (s *SynScanner) synScan() error {
@ -167,22 +168,39 @@ func (s *SynScanner) synScan() error {
s.waitGroup.Add(1) s.waitGroup.Add(1)
defer s.waitGroup.Done() defer s.waitGroup.Done()
naddrs := len(s.addresses) s.stats.openPorts = 0
s.stats.numPorts = uint64(s.endPort - s.startPort + 1)
s.stats.started = time.Now()
s.stats.numAddresses = uint64(len(s.addresses))
s.stats.totProbes = s.stats.numAddresses * s.stats.numPorts
s.stats.doneProbes = 0
plural := "es" plural := "es"
if naddrs == 1 { if s.stats.numAddresses == 1 {
plural = "" plural = ""
} }
if s.startPort != s.endPort { if s.stats.numPorts > 1 {
log.Info("SYN scanning %d address%s from port %d to port %d ...", naddrs, plural, s.startPort, s.endPort) log.Info("scanning %d address%s from port %d to port %d ...", s.stats.numAddresses, plural, s.startPort, s.endPort)
} else { } else {
log.Info("SYN scanning %d address%s on port %d ...", naddrs, plural, s.startPort) log.Info("scanning %d address%s on port %d ...", s.stats.numAddresses, plural, s.startPort)
} }
// set the collector // set the collector
s.Session.Queue.OnPacket(s.onPacket) s.Session.Queue.OnPacket(s.onPacket)
defer s.Session.Queue.OnPacket(nil) defer s.Session.Queue.OnPacket(nil)
// start to show progress every second
go func() {
for {
time.Sleep(s.progressEvery)
if s.Running() {
s.showProgress()
} else {
break
}
}
}()
// start sending SYN packets and wait // start sending SYN packets and wait
for _, address := range s.addresses { for _, address := range s.addresses {
if !s.Running() { if !s.Running() {
@ -190,6 +208,7 @@ func (s *SynScanner) synScan() error {
} }
mac, err := findMAC(s.Session, address, true) mac, err := findMAC(s.Session, address, true)
if err != nil { if err != nil {
atomic.AddUint64(&s.stats.doneProbes, s.stats.numPorts)
log.Debug("Could not get MAC for %s: %s", address.String(), err) log.Debug("Could not get MAC for %s: %s", address.String(), err)
continue continue
} }
@ -199,6 +218,8 @@ func (s *SynScanner) synScan() error {
break break
} }
atomic.AddUint64(&s.stats.doneProbes, 1)
err, raw := packets.NewTCPSyn(s.Session.Interface.IP, s.Session.Interface.HW, address, mac, synSourcePort, dstPort) err, raw := packets.NewTCPSyn(s.Session.Interface.IP, s.Session.Interface.HW, address, mac, synSourcePort, dstPort)
if err != nil { if err != nil {
log.Error("Error creating SYN packet: %s", err) log.Error("Error creating SYN packet: %s", err)
@ -210,11 +231,10 @@ func (s *SynScanner) synScan() error {
} else { } else {
log.Debug("Sent %d bytes of SYN packet to %s for port %d", len(raw), address.String(), dstPort) log.Debug("Sent %d bytes of SYN packet to %s for port %d", len(raw), address.String(), dstPort)
} }
time.Sleep(time.Duration(10) * time.Millisecond)
} }
} }
nports := s.endPort - s.startPort + 1
time.Sleep(time.Duration(nports*500) * time.Millisecond)
}) })
return nil return nil

View file

@ -0,0 +1,62 @@
package modules
import (
"net"
"sync/atomic"
"github.com/bettercap/bettercap/network"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
func (s *SynScanner) isAddressInRange(ip net.IP) bool {
for _, a := range s.addresses {
if a.Equal(ip) {
return true
}
}
return false
}
func (s *SynScanner) onPacket(pkt gopacket.Packet) {
var eth layers.Ethernet
var ip layers.IPv4
var tcp layers.TCP
foundLayerTypes := []gopacket.LayerType{}
parser := gopacket.NewDecodingLayerParser(
layers.LayerTypeEthernet,
&eth,
&ip,
&tcp,
)
err := parser.DecodeLayers(pkt.Data(), &foundLayerTypes)
if err != nil {
return
}
if s.isAddressInRange(ip.SrcIP) && tcp.DstPort == synSourcePort && tcp.SYN && tcp.ACK {
atomic.AddUint64(&s.stats.openPorts, 1)
from := ip.SrcIP.String()
port := int(tcp.SrcPort)
var host *network.Endpoint
if ip.SrcIP.Equal(s.Session.Interface.IP) {
host = s.Session.Interface
} else if ip.SrcIP.Equal(s.Session.Gateway.IP) {
host = s.Session.Gateway
} else {
host = s.Session.Lan.GetByIp(from)
}
if host != nil {
ports := host.Meta.GetIntsWith("tcp-ports", port, true)
host.Meta.SetInts("tcp-ports", ports)
}
NewSynScanEvent(from, host, port).Push()
}
}