rapid7-insightidr 1.14.8: user_authentications counts the user's own logons
The keyword search matches the account name anywhere in a line, so it brought other accounts' logons: on a real run the only "new" event reported was another user's failed password. Only events whose account/user field is the user (DOMAIN\\user and user@domain normalized) are summarized now, with keyword_matches and user_matches both reported. A first authentication is recognized as Rapid7 flags it, a boolean new_authentication field, as well as the word. The event limit cannot be set below 10000 (the agent passed 1000). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015RhL2gy6Sx6Y3FEsyBE3mZ
This commit is contained in:
@@ -40,7 +40,9 @@ instructions: |
|
|||||||
assets", "Domain Credentials": call user_authentications(user=<the
|
assets", "Domain Credentials": call user_authentications(user=<the
|
||||||
account>, anchor_time=<the alert's first seen time, copied as given>)
|
account>, anchor_time=<the alert's first seen time, copied as given>)
|
||||||
ONCE. It does the search, the window and the split itself. Report its
|
ONCE. It does the search, the window and the split itself. Report its
|
||||||
"window" word for word, the totals from "all", and from
|
"window" word for word, user_matches (the user's own events —
|
||||||
|
keyword_matches includes other accounts' lines, never report those as
|
||||||
|
the user's), the totals from "all", and from
|
||||||
"new_authentications" the destination assets by name with their counts:
|
"new_authentications" the destination assets by name with their counts:
|
||||||
those are the new assets. Do not redo this search with query_log_set.
|
those are the new assets. Do not redo this search with query_log_set.
|
||||||
Other alerts with evidence_api false, or a question this does not
|
Other alerts with evidence_api false, or a question this does not
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -255,6 +255,42 @@ def _resolve_name(name):
|
|||||||
|
|
||||||
|
|
||||||
NEW_MARKERS = ("new_authentication",)
|
NEW_MARKERS = ("new_authentication",)
|
||||||
|
_TRUE = ("true", "1", "yes")
|
||||||
|
|
||||||
|
|
||||||
|
def _norm_account(v):
|
||||||
|
"""jdoe, DOMAIN\\jdoe, jdoe@domain.tld -> jdoe."""
|
||||||
|
v = str(v or "").strip().lower()
|
||||||
|
if "\\" in v:
|
||||||
|
v = v.rsplit("\\", 1)[1]
|
||||||
|
if "@" in v:
|
||||||
|
v = v.split("@", 1)[0]
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
def _is_users(fields, user):
|
||||||
|
"""The event is this user's: an account or user field holds the account.
|
||||||
|
|
||||||
|
The keyword search matches the text anywhere in the line, so it also brings
|
||||||
|
other people's logons — on a real run the only "new" event it found was
|
||||||
|
another account's. Only the account fields decide."""
|
||||||
|
want = _norm_account(user)
|
||||||
|
for k, v in fields.items():
|
||||||
|
kl = k.lower()
|
||||||
|
if ("account" in kl or "user" in kl) and "sid" not in kl and _norm_account(v) == want:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _is_new(fields):
|
||||||
|
"""Rapid7 marks a first-time authentication either with a boolean field
|
||||||
|
(new_authentication: true) or with that word as a value."""
|
||||||
|
for k, v in fields.items():
|
||||||
|
if "new_authentication" in k.lower() and str(v).strip().lower() in _TRUE:
|
||||||
|
return True
|
||||||
|
if str(v).strip().lower() in NEW_MARKERS:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
@@ -278,24 +314,29 @@ def run():
|
|||||||
name = str(inp.get("log_set_name") or "Asset Authentication").strip()
|
name = str(inp.get("log_set_name") or "Asset Authentication").strip()
|
||||||
log_set_id = _resolve_name(name)
|
log_set_id = _resolve_name(name)
|
||||||
try:
|
try:
|
||||||
limit = int(inp.get("limit") or 2000)
|
limit = max(int(inp.get("limit") or 10000), 10000)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
limit = 2000
|
limit = 10000
|
||||||
query = 'where("%s")' % user
|
query = 'where("%s")' % user
|
||||||
first = request("GET", "log_search/query/logsets/" + urllib.parse.quote(log_set_id, safe=""),
|
first = request("GET", "log_search/query/logsets/" + urllib.parse.quote(log_set_id, safe=""),
|
||||||
params={"query": query, "from": start_time, "to": end_time})
|
params={"query": query, "from": start_time, "to": end_time})
|
||||||
events = _identify(_collect(first, limit), "", None)
|
matched = _identify(_collect(first, limit), "", None)
|
||||||
new = [e for e in events
|
events = [e for e in matched if _is_users(_fields(e), user)]
|
||||||
if any(str(v).lower() in NEW_MARKERS for v in _fields(e).values())]
|
new = [e for e in events if _is_new(_fields(e))]
|
||||||
print(json.dumps({
|
print(json.dumps({
|
||||||
"user": user,
|
"user": user,
|
||||||
"log_set": name,
|
"log_set": name,
|
||||||
"query": query,
|
"query": query,
|
||||||
|
"keyword_matches": len(matched),
|
||||||
|
"user_matches": len(events),
|
||||||
|
"truncated": len(matched) >= limit,
|
||||||
"window": "%s to %s" % (_iso(start_time), _iso(end_time)),
|
"window": "%s to %s" % (_iso(start_time), _iso(end_time)),
|
||||||
"all": _summarize(events, limit),
|
"all": _summarize(events, limit),
|
||||||
"new_authentications": _summarize(new, limit),
|
"new_authentications": _summarize(new, limit),
|
||||||
"note": ("new_authentications holds only the events Rapid7 marked new_authentication: their "
|
"note": ("all and new_authentications hold only this user's events (an account field equals "
|
||||||
"destination assets are the new assets a behaviour alert counts."),
|
"the user): keyword_matches counts every line the text appeared in, other accounts "
|
||||||
|
"included. new_authentications holds the events Rapid7 marked as a first "
|
||||||
|
"authentication: their destination assets are the new assets a behaviour alert counts."),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user