110 lines
2.6 KiB
Go
110 lines
2.6 KiB
Go
package analyzer
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"dip-ids/internal/config"
|
|
"dip-ids/internal/storage"
|
|
|
|
"github.com/google/gopacket"
|
|
"github.com/google/gopacket/layers"
|
|
)
|
|
|
|
// bruteForceDetector обнаруживает перебор паролей (brute-force).
|
|
// Алгоритм: отслеживает количество SYN-пакетов от одного источника
|
|
// к одному порту назначения за скользящее временное окно.
|
|
type bruteForceDetector struct {
|
|
mu sync.Mutex
|
|
cfg config.BruteForceConfig
|
|
ports map[int32]struct{}
|
|
// ключ: "srcIP:dstIP:dstPort"
|
|
tracker map[string][]time.Time
|
|
alerted map[string]time.Time
|
|
}
|
|
|
|
func NewBruteForceDetector(cfg config.BruteForceConfig) Detector {
|
|
ports := make(map[int32]struct{}, len(cfg.Ports))
|
|
for _, p := range cfg.Ports {
|
|
ports[p] = struct{}{}
|
|
}
|
|
return &bruteForceDetector{
|
|
cfg: cfg,
|
|
ports: ports,
|
|
tracker: make(map[string][]time.Time),
|
|
alerted: make(map[string]time.Time),
|
|
}
|
|
}
|
|
|
|
func (d *bruteForceDetector) Name() string { return "brute_force" }
|
|
|
|
func (d *bruteForceDetector) Analyze(pkt gopacket.Packet) []storage.Alert {
|
|
if !d.cfg.Enabled {
|
|
return nil
|
|
}
|
|
|
|
netLayer := pkt.NetworkLayer()
|
|
tcpLayer := pkt.Layer(layers.LayerTypeTCP)
|
|
if netLayer == nil || tcpLayer == nil {
|
|
return nil
|
|
}
|
|
|
|
tcp := tcpLayer.(*layers.TCP)
|
|
if !tcp.SYN || tcp.ACK {
|
|
return nil
|
|
}
|
|
|
|
dstPort := int32(tcp.DstPort)
|
|
if _, monitored := d.ports[dstPort]; !monitored {
|
|
return nil
|
|
}
|
|
|
|
srcIP := netLayer.NetworkFlow().Src().String()
|
|
dstIP := netLayer.NetworkFlow().Dst().String()
|
|
now := pkt.Metadata().Timestamp
|
|
if now.IsZero() {
|
|
now = time.Now()
|
|
}
|
|
|
|
key := fmt.Sprintf("%s:%s:%d", srcIP, dstIP, dstPort)
|
|
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
window := time.Duration(d.cfg.WindowSeconds) * time.Second
|
|
cutoff := now.Add(-window)
|
|
|
|
d.tracker[key] = append(d.tracker[key], now)
|
|
d.tracker[key] = filterTimes(d.tracker[key], cutoff)
|
|
|
|
count := len(d.tracker[key])
|
|
if count < d.cfg.Threshold {
|
|
return nil
|
|
}
|
|
|
|
if last, ok := d.alerted[key]; ok && now.Sub(last) < 30*time.Second {
|
|
return nil
|
|
}
|
|
d.alerted[key] = now
|
|
|
|
return []storage.Alert{{
|
|
CreatedAt: now,
|
|
Type: "brute_force",
|
|
Severity: d.cfg.Severity,
|
|
SrcIP: srcIP,
|
|
DstIP: dstIP,
|
|
DstPort: uint16(dstPort),
|
|
Protocol: "TCP",
|
|
Description: fmt.Sprintf("Brute-force detected on port %d: %d connection attempts in %ds", dstPort, count, d.cfg.WindowSeconds),
|
|
Count: count,
|
|
}}
|
|
}
|
|
|
|
func (d *bruteForceDetector) Reset() {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
d.tracker = make(map[string][]time.Time)
|
|
d.alerted = make(map[string]time.Time)
|
|
}
|