102 lines
2.4 KiB
Go
102 lines
2.4 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"
|
|
)
|
|
|
|
// arpSpoofDetector обнаруживает ARP-спуфинг.
|
|
// Алгоритм: ведёт таблицу соответствия IP→MAC; при обнаружении
|
|
// нового MAC для уже известного IP генерирует алерт.
|
|
type arpSpoofDetector struct {
|
|
mu sync.Mutex
|
|
cfg config.ARPSpoofConfig
|
|
ipToMAC map[string]string // IP -> MAC
|
|
alerted map[string]time.Time
|
|
}
|
|
|
|
func NewARPSpoofDetector(cfg config.ARPSpoofConfig) Detector {
|
|
return &arpSpoofDetector{
|
|
cfg: cfg,
|
|
ipToMAC: make(map[string]string),
|
|
alerted: make(map[string]time.Time),
|
|
}
|
|
}
|
|
|
|
func (d *arpSpoofDetector) Name() string { return "arp_spoof" }
|
|
|
|
func (d *arpSpoofDetector) Analyze(pkt gopacket.Packet) []storage.Alert {
|
|
if !d.cfg.Enabled {
|
|
return nil
|
|
}
|
|
|
|
arpLayer := pkt.Layer(layers.LayerTypeARP)
|
|
if arpLayer == nil {
|
|
return nil
|
|
}
|
|
|
|
arp := arpLayer.(*layers.ARP)
|
|
// Обрабатываем только ARP Reply (op=2) и Gratuitous ARP
|
|
if arp.Operation != layers.ARPReply {
|
|
return nil
|
|
}
|
|
|
|
srcIP := fmt.Sprintf("%d.%d.%d.%d", arp.SourceProtAddress[0], arp.SourceProtAddress[1],
|
|
arp.SourceProtAddress[2], arp.SourceProtAddress[3])
|
|
srcMAC := fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x",
|
|
arp.SourceHwAddress[0], arp.SourceHwAddress[1], arp.SourceHwAddress[2],
|
|
arp.SourceHwAddress[3], arp.SourceHwAddress[4], arp.SourceHwAddress[5])
|
|
|
|
now := pkt.Metadata().Timestamp
|
|
if now.IsZero() {
|
|
now = time.Now()
|
|
}
|
|
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
knownMAC, exists := d.ipToMAC[srcIP]
|
|
if !exists {
|
|
d.ipToMAC[srcIP] = srcMAC
|
|
return nil
|
|
}
|
|
|
|
if knownMAC == srcMAC {
|
|
return nil
|
|
}
|
|
|
|
// MAC изменился — ARP спуфинг
|
|
key := srcIP
|
|
if last, ok := d.alerted[key]; ok && now.Sub(last) < 60*time.Second {
|
|
return nil
|
|
}
|
|
d.alerted[key] = now
|
|
|
|
// Обновляем таблицу
|
|
d.ipToMAC[srcIP] = srcMAC
|
|
|
|
return []storage.Alert{{
|
|
CreatedAt: now,
|
|
Type: "arp_spoof",
|
|
Severity: d.cfg.Severity,
|
|
SrcIP: srcIP,
|
|
Protocol: "ARP",
|
|
Description: fmt.Sprintf("ARP spoofing: IP %s changed MAC from %s to %s", srcIP, knownMAC, srcMAC),
|
|
Count: 1,
|
|
}}
|
|
}
|
|
|
|
func (d *arpSpoofDetector) Reset() {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
d.ipToMAC = make(map[string]string)
|
|
d.alerted = make(map[string]time.Time)
|
|
}
|