mirror of
https://github.com/bettercap/bettercap
synced 2025-07-06 04:52:10 -07:00
new: implemented syn.scan module (closes #67)
This commit is contained in:
parent
d6fe8fc663
commit
ce76c7258d
8 changed files with 338 additions and 3 deletions
20
core/core.go
20
core/core.go
|
@ -6,6 +6,7 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,6 +22,25 @@ func TrimRight(s string) string {
|
||||||
return strings.TrimRight(s, defaultTrimSet)
|
return strings.TrimRight(s, defaultTrimSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UniqueInts(a []int, sorted bool) []int {
|
||||||
|
tmp := make(map[int]bool)
|
||||||
|
uniq := make([]int, 0)
|
||||||
|
|
||||||
|
for _, n := range a {
|
||||||
|
tmp[n] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for n, _ := range tmp {
|
||||||
|
uniq = append(uniq, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sorted {
|
||||||
|
sort.Ints(uniq)
|
||||||
|
}
|
||||||
|
|
||||||
|
return uniq
|
||||||
|
}
|
||||||
|
|
||||||
func Exec(executable string, args []string) (string, error) {
|
func Exec(executable string, args []string) (string, error) {
|
||||||
path, err := exec.LookPath(executable)
|
path, err := exec.LookPath(executable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
1
main.go
1
main.go
|
@ -50,6 +50,7 @@ func main() {
|
||||||
sess.Register(modules.NewRestAPI(sess))
|
sess.Register(modules.NewRestAPI(sess))
|
||||||
sess.Register(modules.NewWOL(sess))
|
sess.Register(modules.NewWOL(sess))
|
||||||
sess.Register(modules.NewWiFiRecon(sess))
|
sess.Register(modules.NewWiFiRecon(sess))
|
||||||
|
sess.Register(modules.NewSynScanner(sess))
|
||||||
|
|
||||||
if err = sess.Start(); err != nil {
|
if err = sess.Start(); err != nil {
|
||||||
log.Fatal("%s", err)
|
log.Fatal("%s", err)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/evilsocket/bettercap-ng/log"
|
"github.com/evilsocket/bettercap-ng/log"
|
||||||
network "github.com/evilsocket/bettercap-ng/network"
|
"github.com/evilsocket/bettercap-ng/network"
|
||||||
"github.com/evilsocket/bettercap-ng/packets"
|
"github.com/evilsocket/bettercap-ng/packets"
|
||||||
"github.com/evilsocket/bettercap-ng/session"
|
"github.com/evilsocket/bettercap-ng/session"
|
||||||
|
|
||||||
|
|
254
modules/syn_scan.go
Normal file
254
modules/syn_scan.go
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
package modules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/evilsocket/bettercap-ng/core"
|
||||||
|
"github.com/evilsocket/bettercap-ng/log"
|
||||||
|
"github.com/evilsocket/bettercap-ng/network"
|
||||||
|
"github.com/evilsocket/bettercap-ng/packets"
|
||||||
|
"github.com/evilsocket/bettercap-ng/session"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
|
||||||
|
"github.com/malfunkt/iprange"
|
||||||
|
)
|
||||||
|
|
||||||
|
const synSourcePort = 666
|
||||||
|
|
||||||
|
type SynScanner struct {
|
||||||
|
session.SessionModule
|
||||||
|
addresses []net.IP
|
||||||
|
startPort int
|
||||||
|
endPort int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSynScanner(s *session.Session) *SynScanner {
|
||||||
|
ss := &SynScanner{
|
||||||
|
SessionModule: session.NewSessionModule("syn.scan", s),
|
||||||
|
addresses: make([]net.IP, 0),
|
||||||
|
startPort: 0,
|
||||||
|
endPort: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
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.",
|
||||||
|
func(args []string) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
list, err := iprange.Parse(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error while parsing IP range '%s': %s", args[0], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ss.addresses = list.Expand()
|
||||||
|
ss.startPort = 0
|
||||||
|
ss.endPort = 0
|
||||||
|
|
||||||
|
if ss.startPort, err = strconv.Atoi(core.Trim(args[1])); err != nil {
|
||||||
|
return fmt.Errorf("Invalid START-PORT: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ss.startPort > 65535 {
|
||||||
|
ss.startPort = 65535
|
||||||
|
}
|
||||||
|
ss.endPort = ss.startPort
|
||||||
|
|
||||||
|
argc := len(args)
|
||||||
|
if argc > 2 && core.Trim(args[2]) != "" {
|
||||||
|
if ss.endPort, err = strconv.Atoi(core.Trim(args[2])); err != nil {
|
||||||
|
return fmt.Errorf("Invalid END-PORT: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ss.endPort < ss.startPort {
|
||||||
|
return fmt.Errorf("END-PORT is greater than START-PORT")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.synScan()
|
||||||
|
}))
|
||||||
|
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SynScanner) Name() string {
|
||||||
|
return "syn.scan"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SynScanner) Description() string {
|
||||||
|
return "A module to perform SYN port scanning."
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SynScanner) Author() string {
|
||||||
|
return "Simone Margaritelli <evilsocket@protonmail.com>"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SynScanner) Configure() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SynScanner) Start() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SynScanner) Stop() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SynScanner) getMAC(ip net.IP, probe bool) (net.HardwareAddr, error) {
|
||||||
|
var mac string
|
||||||
|
var hw net.HardwareAddr
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// do we have this ip mac address?
|
||||||
|
mac, err = network.ArpLookup(s.Session.Interface.Name(), ip.String(), false)
|
||||||
|
if err != nil && probe == true {
|
||||||
|
from := s.Session.Interface.IP
|
||||||
|
from_hw := s.Session.Interface.HW
|
||||||
|
|
||||||
|
if err, probe := packets.NewUDPProbe(from, from_hw, ip, 139); err != nil {
|
||||||
|
log.Error("Error while creating UDP probe packet for %s: %s", ip.String(), err)
|
||||||
|
} else {
|
||||||
|
s.Session.Queue.Send(probe)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
|
||||||
|
mac, err = network.ArpLookup(s.Session.Interface.Name(), ip.String(), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if mac == "" {
|
||||||
|
return nil, fmt.Errorf("Could not find hardware address for %s.", ip.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
mac = network.NormalizeMac(mac)
|
||||||
|
hw, err = net.ParseMAC(mac)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error while parsing hardware address '%s' for %s: %s", mac, ip.String(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SynScanner) inRange(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,
|
||||||
|
ð,
|
||||||
|
&ip,
|
||||||
|
&tcp,
|
||||||
|
)
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
log.Info("Found open port %d for %s", tcp.SrcPort, core.Bold(from))
|
||||||
|
|
||||||
|
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 {
|
||||||
|
sports := strings.Split(host.Meta.Get("tcp-ports").(string), ",")
|
||||||
|
ports := []int{int(tcp.SrcPort)}
|
||||||
|
|
||||||
|
for _, s := range sports {
|
||||||
|
n, err := strconv.Atoi(s)
|
||||||
|
if err == nil {
|
||||||
|
ports = append(ports, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ports = core.UniqueInts(ports, true)
|
||||||
|
list := make([]string, len(ports))
|
||||||
|
for i, p := range ports {
|
||||||
|
list[i] = fmt.Sprintf("%d", p)
|
||||||
|
}
|
||||||
|
|
||||||
|
host.Meta.Set("tcp-ports", strings.Join(list, ","))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SynScanner) synScan() error {
|
||||||
|
if s.Running() == true {
|
||||||
|
return fmt.Errorf("A scan is already running, wait for it to end before starting a new one.")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.SetRunning(true, func() {
|
||||||
|
defer s.SetRunning(false, nil)
|
||||||
|
|
||||||
|
naddrs := len(s.addresses)
|
||||||
|
plural := "es"
|
||||||
|
if naddrs == 1 {
|
||||||
|
plural = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.startPort != s.endPort {
|
||||||
|
log.Info("SYN scanning %d address%s from port %d to port %d ...", naddrs, plural, s.startPort, s.endPort)
|
||||||
|
} else {
|
||||||
|
log.Info("SYN scanning %d address%s on port %d ...", naddrs, plural, s.startPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the collector
|
||||||
|
s.Session.Queue.OnPacket(s.onPacket)
|
||||||
|
defer s.Session.Queue.OnPacket(nil)
|
||||||
|
|
||||||
|
// start sending SYN packets and wait
|
||||||
|
for _, address := range s.addresses {
|
||||||
|
mac, err := s.getMAC(address, true)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Could not get MAC for %s: %s", address.String(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for dstPort := s.startPort; dstPort < s.endPort+1; dstPort++ {
|
||||||
|
err, raw := packets.NewTCPSyn(s.Session.Interface.IP, s.Session.Interface.HW, address, mac, synSourcePort, dstPort)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error creating SYN packet: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Session.Queue.Send(raw); err != nil {
|
||||||
|
log.Error("Error sending SYN packet: %s", err)
|
||||||
|
} else {
|
||||||
|
log.Debug("Sent %d bytes of SYN packet to %s for port %d", len(raw), address.String(), dstPort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nports := s.endPort - s.startPort + 1
|
||||||
|
time.Sleep(time.Duration(nports*500) * time.Millisecond)
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -43,6 +43,16 @@ func (m *Meta) Get(name string) interface{} {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Meta) GetOr(name string, dflt interface{}) interface{} {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
if v, found := m.m[name]; found == true {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return dflt
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Meta) Each(cb func(name string, value interface{})) {
|
func (m *Meta) Each(cb func(name string, value interface{})) {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
|
@ -32,8 +32,10 @@ type Stats struct {
|
||||||
Errors uint64
|
Errors uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PacketCallback func(pkt gopacket.Packet)
|
||||||
|
|
||||||
type Queue struct {
|
type Queue struct {
|
||||||
sync.Mutex
|
sync.RWMutex
|
||||||
|
|
||||||
Activities chan Activity `json:"-"`
|
Activities chan Activity `json:"-"`
|
||||||
|
|
||||||
|
@ -44,6 +46,7 @@ type Queue struct {
|
||||||
iface *network.Endpoint
|
iface *network.Endpoint
|
||||||
handle *pcap.Handle
|
handle *pcap.Handle
|
||||||
source *gopacket.PacketSource
|
source *gopacket.PacketSource
|
||||||
|
pktCb PacketCallback
|
||||||
active bool
|
active bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +58,7 @@ func NewQueue(iface *network.Endpoint) (q *Queue, err error) {
|
||||||
|
|
||||||
iface: iface,
|
iface: iface,
|
||||||
active: !iface.IsMonitor(),
|
active: !iface.IsMonitor(),
|
||||||
|
pktCb: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
if q.active == true {
|
if q.active == true {
|
||||||
|
@ -69,6 +73,21 @@ func NewQueue(iface *network.Endpoint) (q *Queue, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *Queue) OnPacket(cb PacketCallback) {
|
||||||
|
q.Lock()
|
||||||
|
defer q.Unlock()
|
||||||
|
q.pktCb = cb
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) onPacketCallback(pkt gopacket.Packet) {
|
||||||
|
q.RLock()
|
||||||
|
defer q.RUnlock()
|
||||||
|
|
||||||
|
if q.pktCb != nil {
|
||||||
|
q.pktCb(pkt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (q *Queue) trackProtocols(pkt gopacket.Packet) {
|
func (q *Queue) trackProtocols(pkt gopacket.Packet) {
|
||||||
// gather protocols stats
|
// gather protocols stats
|
||||||
pktLayers := pkt.Layers()
|
pktLayers := pkt.Layers()
|
||||||
|
@ -130,6 +149,8 @@ func (q *Queue) worker() {
|
||||||
atomic.AddUint64(&q.Stats.PktReceived, 1)
|
atomic.AddUint64(&q.Stats.PktReceived, 1)
|
||||||
atomic.AddUint64(&q.Stats.Received, pktSize)
|
atomic.AddUint64(&q.Stats.Received, pktSize)
|
||||||
|
|
||||||
|
q.onPacketCallback(pkt)
|
||||||
|
|
||||||
// decode eth and ipv4 layers
|
// decode eth and ipv4 layers
|
||||||
leth := pkt.Layer(layers.LayerTypeEthernet)
|
leth := pkt.Layer(layers.LayerTypeEthernet)
|
||||||
lip4 := pkt.Layer(layers.LayerTypeIPv4)
|
lip4 := pkt.Layer(layers.LayerTypeIPv4)
|
||||||
|
|
29
packets/tcp.go
Normal file
29
packets/tcp.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package packets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewTCPSyn(from net.IP, from_hw net.HardwareAddr, to net.IP, to_hw net.HardwareAddr, srcPort int, dstPort int) (error, []byte) {
|
||||||
|
eth := layers.Ethernet{
|
||||||
|
SrcMAC: from_hw,
|
||||||
|
DstMAC: to_hw,
|
||||||
|
EthernetType: layers.EthernetTypeIPv4,
|
||||||
|
}
|
||||||
|
ip4 := layers.IPv4{
|
||||||
|
Protocol: layers.IPProtocolTCP,
|
||||||
|
Version: 4,
|
||||||
|
TTL: 64,
|
||||||
|
SrcIP: from,
|
||||||
|
DstIP: to,
|
||||||
|
}
|
||||||
|
tcp := layers.TCP{
|
||||||
|
SrcPort: layers.TCPPort(srcPort),
|
||||||
|
DstPort: layers.TCPPort(dstPort),
|
||||||
|
SYN: true,
|
||||||
|
}
|
||||||
|
tcp.SetNetworkLayerForChecksum(&ip4)
|
||||||
|
|
||||||
|
return Serialize(ð, &ip4, &tcp)
|
||||||
|
}
|
|
@ -484,5 +484,5 @@ func (s *Session) Run(line string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("Unknown command %s%s%s, type %shelp%s for the help menu.", core.BOLD, line, core.RESET, core.BOLD, core.RESET)
|
return fmt.Errorf("Unknown or invalid syntax \"%s%s%s\", type %shelp%s for the help menu.", core.BOLD, line, core.RESET, core.BOLD, core.RESET)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue