Noise-Canceling UEBA
All checks were successful
release-tag / release-image (push) Successful in 2m11s
All checks were successful
release-tag / release-image (push) Successful in 2m11s
This commit is contained in:
@@ -1409,16 +1409,15 @@ CREATE TABLE host_risk_scores (
|
||||
updated_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6)
|
||||
);
|
||||
|
||||
CREATE TABLE ueba_user_baseline (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
CREATE TABLE IF NOT EXISTS ueba_user_baseline (
|
||||
username VARCHAR(255) NOT NULL,
|
||||
hostname VARCHAR(255) NOT NULL,
|
||||
src_ip VARCHAR(255) NOT NULL DEFAULT '',
|
||||
workstation VARCHAR(255) NOT NULL DEFAULT '',
|
||||
first_seen TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
last_seen TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
src_ip VARCHAR(64) NOT NULL,
|
||||
workstation VARCHAR(255) NOT NULL,
|
||||
first_seen DATETIME(6) NOT NULL DEFAULT UTC_TIMESTAMP(6),
|
||||
last_seen DATETIME(6) NOT NULL DEFAULT UTC_TIMESTAMP(6),
|
||||
seen_count BIGINT NOT NULL DEFAULT 1,
|
||||
UNIQUE KEY uniq_user_context (username, hostname, src_ip, workstation)
|
||||
PRIMARY KEY (username, hostname, src_ip, workstation)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ueba_user_baseline_user
|
||||
|
||||
105
main.go
105
main.go
@@ -4610,10 +4610,15 @@ func (d *detector) runUEBANewUserContextRule(ctx context.Context) error {
|
||||
|
||||
windowEnd := time.Now().UTC()
|
||||
windowStart := windowEnd.Add(-d.cfg.UEBANewContextWindow)
|
||||
lookbackStart := windowEnd.Add(-d.cfg.UEBALookback)
|
||||
|
||||
rows, err := d.db.QueryContext(ctx, `
|
||||
SELECT e.hostname, e.target_user, e.src_ip, e.workstation, COUNT(*) AS cnt
|
||||
const q = `
|
||||
SELECT
|
||||
e.hostname,
|
||||
e.target_user,
|
||||
e.src_ip,
|
||||
e.workstation,
|
||||
MIN(e.ts) AS first_seen,
|
||||
COUNT(*) AS cnt
|
||||
FROM event_logs e
|
||||
WHERE e.channel_name = 'Security'
|
||||
AND e.event_id = 4624
|
||||
@@ -4621,23 +4626,17 @@ WHERE e.channel_name = 'Security'
|
||||
AND e.target_user <> ''
|
||||
AND e.target_user <> '-'
|
||||
AND e.target_user NOT LIKE '%$'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM ueba_user_baseline b
|
||||
WHERE b.username = e.target_user
|
||||
AND b.hostname = e.hostname
|
||||
AND b.src_ip = e.src_ip
|
||||
AND b.workstation = e.workstation
|
||||
AND b.first_seen < ?
|
||||
AND b.last_seen >= ?
|
||||
AND LOWER(e.target_user) NOT IN (
|
||||
'system',
|
||||
'localsystem',
|
||||
'local service',
|
||||
'network service',
|
||||
'anonymous logon'
|
||||
)
|
||||
GROUP BY e.hostname, e.target_user, e.src_ip, e.workstation
|
||||
`,
|
||||
windowStart,
|
||||
windowEnd,
|
||||
windowStart,
|
||||
lookbackStart,
|
||||
)
|
||||
`
|
||||
|
||||
rows, err := d.db.QueryContext(ctx, q, windowStart, windowEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -4645,12 +4644,27 @@ GROUP BY e.hostname, e.target_user, e.src_ip, e.workstation
|
||||
|
||||
for rows.Next() {
|
||||
var host, user, srcIP, workstation string
|
||||
var firstSeen time.Time
|
||||
var count int
|
||||
|
||||
if err := rows.Scan(&host, &user, &srcIP, &workstation, &count); err != nil {
|
||||
if err := rows.Scan(&host, &user, &srcIP, &workstation, &firstSeen, &count); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user = normalizeUsername(user)
|
||||
if isNoiseAccount(user) {
|
||||
continue
|
||||
}
|
||||
|
||||
isNew, err := d.touchUEBAUserContext(ctx, user, host, srcIP, workstation, firstSeen, count)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isNew {
|
||||
continue
|
||||
}
|
||||
|
||||
score := 2.0
|
||||
severity := "medium"
|
||||
|
||||
@@ -4679,8 +4693,10 @@ GROUP BY e.hostname, e.target_user, e.src_ip, e.workstation
|
||||
"user": user,
|
||||
"src_ip": srcIP,
|
||||
"workstation": workstation,
|
||||
"host": host,
|
||||
"count": count,
|
||||
"lookback": d.cfg.UEBALookback.String(),
|
||||
"first_seen": firstSeen.UTC().Format(time.RFC3339Nano),
|
||||
"window": d.cfg.UEBANewContextWindow.String(),
|
||||
}),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -5371,6 +5387,55 @@ GROUP BY e.hostname, e.target_user, e.src_ip
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
func (d *detector) touchUEBAUserContext(ctx context.Context, username, hostname, srcIP, workstation string, firstSeen time.Time, count int) (bool, error) {
|
||||
username = normalizeUsername(username)
|
||||
hostname = strings.TrimSpace(hostname)
|
||||
srcIP = strings.TrimSpace(srcIP)
|
||||
workstation = strings.TrimSpace(workstation)
|
||||
|
||||
if username == "" || hostname == "" {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if srcIP == "" {
|
||||
srcIP = "-"
|
||||
}
|
||||
if workstation == "" {
|
||||
workstation = "-"
|
||||
}
|
||||
|
||||
res, err := d.db.ExecContext(ctx, `
|
||||
INSERT INTO ueba_user_baseline
|
||||
(username, hostname, src_ip, workstation, first_seen, last_seen, seen_count)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
last_seen = VALUES(last_seen),
|
||||
seen_count = seen_count + VALUES(seen_count)
|
||||
`,
|
||||
username,
|
||||
hostname,
|
||||
srcIP,
|
||||
workstation,
|
||||
firstSeen.UTC(),
|
||||
firstSeen.UTC(),
|
||||
count,
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
affected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// MySQL:
|
||||
// 1 = neuer Insert
|
||||
// 2 = Update wegen ON DUPLICATE KEY
|
||||
// 0 = kein effektives Update, abhängig von Client/Settings möglich
|
||||
return affected == 1, nil
|
||||
}
|
||||
|
||||
func (d *detector) isDetectionSuppressed(ctx context.Context, det Detection) (bool, error) {
|
||||
var count int
|
||||
|
||||
|
||||
Reference in New Issue
Block a user