From 15cf4bccfe7efdc75f99a527beada1a76077ef1f Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Sat, 23 Nov 2024 15:22:45 +0900 Subject: [PATCH 1/4] feat: add rdp log to timeline-logon cmd --- src/takajopkg/timelineLogon.nim | 100 ++++++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 17 deletions(-) diff --git a/src/takajopkg/timelineLogon.nim b/src/takajopkg/timelineLogon.nim index fdf77d1..2415831 100644 --- a/src/takajopkg/timelineLogon.nim +++ b/src/takajopkg/timelineLogon.nim @@ -12,6 +12,8 @@ type seqOfLogoffEventTables*: seq[Table[string, string]] # This sequence can be immutable logoffEvents*: Table[string, string] = initTable[string, string]() adminLogonEvents*: Table[string, string] = initTable[string, string]() + EID_21_count*: int = 0 # RDP Logon + EID_23_count*: int = 0 # RDP Logoff EID_4624_count* = 0 # Successful logon EID_4625_count* = 0 # Failed logon EID_4634_count* = 0 # Logoff @@ -24,7 +26,7 @@ type method filter*(self: TimelineLogonCmd, x: HayabusaJson): bool = return x.EventId == 4624 or x.EventId == 4625 or x.EventId == 4634 or - x.EventId == 4647 or x.EventId == 4648 or x.EventId == 4672 + x.EventId == 4647 or x.EventId == 4648 or x.EventId == 4672 or x.EventId == 21 or x.EventId == 23 method analyze*(self: TimelineLogonCmd, x: HayabusaJson) = let ruleTitle = x.RuleTitle @@ -174,6 +176,62 @@ method analyze*(self: TimelineLogonCmd, x: HayabusaJson) = singleResultTable["LID"] = details.extractStr("LID") self.seqOfResultsTables.add(singleResultTable) + #EID 21 Logon + if ruleTitle == "RDP Logon": + inc self.EID_21_count + var singleResultTable = newTable[string, string]() + singleResultTable["Event"] = ruleTitle + singleResultTable["Timestamp"] = jsonLine.Timestamp + singleResultTable["Channel"] = jsonLine.Channel + singleResultTable["EventID"] = "21" + let details = jsonLine.Details + singleResultTable["TargetComputer"] = jsonLine.Computer + singleResultTable["SourceIP"] = details.extractStr("SrcIP") + let userDetail = details.extractStr("TgtUser") + if userDetail.contains("\\"): + let parts = userDetail.split("\\") + singleResultTable["TargetDomainName"] = parts[0] + singleResultTable["TargetUser"] = parts[1] + else: + singleResultTable["TargetUser"] = userDetail + singleResultTable["LID"] = details.extractStr("SessID") + singleResultTable["LogoffTime"] = "" + singleResultTable["ElapsedTime"] = "" + self.seqOfResultsTables.add(singleResultTable) + + #EID 23 RDP Logoff + if ruleTitle == "RDP Logoff": + inc self.EID_23_count + # If we want to calculate ElapsedTime + let details = jsonLine.Details + if self.calculateElapsedTime: + # Create the key in the format of LID:Computer:User with a value of the timestamp + var tgtUser = details.extractStr("TgtUser") + if tgtUser.contains("\\"): + let parts = tgtUser.split("\\") + tgtUser = parts[1] + let key = jsonLine.Details["SessID"].getStr() & ":" & + jsonLine.Computer & ":" & tgtUser + let logoffTime = jsonLine.Timestamp + self.logoffEvents[key] = logoffTime + if self.outputLogoffEvents: + var singleResultTable = newTable[string, string]() + singleResultTable["Event"] = ruleTitle + singleResultTable["Timestamp"] = jsonLine.Timestamp + singleResultTable["Channel"] = jsonLine.Channel + singleResultTable["TargetComputer"] = jsonLine.Computer + singleResultTable["EventID"] = "23" + let userDetail = details.extractStr("TgtUser") + if userDetail.contains("\\"): + let parts = userDetail.split("\\") + singleResultTable["TargetDomainName"] = parts[0] + singleResultTable["TargetUser"] = parts[1] + else: + singleResultTable["TargetUser"] = userDetail + singleResultTable["LID"] = details.extractStr("SessID") + self.seqOfResultsTables.add(singleResultTable) + + method resultOutput*(self: TimelineLogonCmd) = if self.displayTable: echo "" @@ -183,7 +241,7 @@ method resultOutput*(self: TimelineLogonCmd) = # Calculating the logon elapsed time (default) if self.calculateElapsedTime: for tableOfResults in self.seqOfResultsTables: - if tableOfResults["EventID"] == "4624": + if tableOfResults["EventID"] == "4624" or tableOfResults["EventID"] == "21": var logoffTime = "" var logonTime = tableOfResults["Timestamp"] @@ -191,20 +249,19 @@ method resultOutput*(self: TimelineLogonCmd) = "TargetComputer"] & ":" & tableOfResults["TargetUser"] if self.logoffEvents.hasKey(key): logoffTime = self.logoffEvents[key] - tableOfResults[]["LogoffTime"] = logoffTime - logonTime = if logonTime.endsWith("Z"): logonTime.replace( - "Z", "") else: logonTime[0 ..< logonTime.len - 7] - logoffTime = if logoffTime.endsWith( - "Z"): logoffTime.replace("Z", "") else: logoffTime[ - 0 ..< logofftime.len - 7] - let parsedLogoffTime = parse(padString(logoffTime, '0', - timeFormat), timeFormat) - let parsedLogonTime = parse(padString(logonTime, '0', - timeFormat), timeFormat) - let duration = parsedLogoffTime - parsedLogonTime - tableOfResults[]["ElapsedTime"] = formatDuration(duration) - else: - logoffTime = "n/a" + if logoffTime > logonTime: + tableOfResults[]["LogoffTime"] = logoffTime + logonTime = if logonTime.endsWith("Z"): logonTime.replace( + "Z", "") else: logonTime[0 ..< logonTime.len - 7] + logoffTime = if logoffTime.endsWith( + "Z"): logoffTime.replace("Z", "") else: logoffTime[ + 0 ..< logofftime.len - 7] + let parsedLogoffTime = parse(padString(logoffTime, '0', + timeFormat), timeFormat) + let parsedLogonTime = parse(padString(logonTime, '0', + timeFormat), timeFormat) + let duration = parsedLogoffTime - parsedLogonTime + tableOfResults[]["ElapsedTime"] = formatDuration(duration) # Find admin logons for tableOfResults in self.seqOfResultsTables: @@ -241,7 +298,11 @@ method resultOutput*(self: TimelineLogonCmd) = padString("EID 4648 (Explicit Logon): " & intToStr( self.EID_4648_count).insertSep(','), ' ', 80) & padString("EID 4672 (Admin Logon): " & intToStr( - self.EID_4672_count).insertSep(','), ' ', 80) + self.EID_4672_count).insertSep(','), ' ', 80) & + padString("EID 21 (RDP Logon): " & intToStr( + self.EID_21_count).insertSep(','), ' ', 80) & + padString("EID 23 (RDP Logoff): " & intToStr( + self.EID_23_count).insertSep(','), ' ', 80) if self.displayTable: echo "" echo "Found logon events:" @@ -256,8 +317,13 @@ method resultOutput*(self: TimelineLogonCmd) = self.EID_4648_count).insertSep(',') echo "EID 4672 (Admin Logon): ", intToStr( self.EID_4672_count).insertSep(',') + echo "EID 21 (RDP Logon): ", intToStr( + self.EID_21_count).insertSep(',') + echo "EID 23 (RDP Logoff): ", intToStr( + self.EID_23_count).insertSep(',') echo "" + # Save results var outputFile = open(self.output, fmWrite) let header = ["Timestamp", "Channel", "EventID", "Event", "LogoffTime", From ebf75d08511c43f11181ba6d8d374b329c8ac3a2 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Thu, 28 Nov 2024 23:08:40 +0900 Subject: [PATCH 2/4] fix: rdp logoff elapsed time calculation --- src/takajopkg/timelineLogon.nim | 40 +++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/takajopkg/timelineLogon.nim b/src/takajopkg/timelineLogon.nim index 2415831..f1562d4 100644 --- a/src/takajopkg/timelineLogon.nim +++ b/src/takajopkg/timelineLogon.nim @@ -11,6 +11,7 @@ type string]] # Sequences are immutable so need to create a sequence of pointers to tables so we can update ["ElapsedTime"] seqOfLogoffEventTables*: seq[Table[string, string]] # This sequence can be immutable logoffEvents*: Table[string, string] = initTable[string, string]() + rdpLogoffEvents*: Table[string, seq[string]] = initTable[string, seq[string]]() adminLogonEvents*: Table[string, string] = initTable[string, string]() EID_21_count*: int = 0 # RDP Logon EID_23_count*: int = 0 # RDP Logoff @@ -213,7 +214,10 @@ method analyze*(self: TimelineLogonCmd, x: HayabusaJson) = let key = jsonLine.Details["SessID"].getStr() & ":" & jsonLine.Computer & ":" & tgtUser let logoffTime = jsonLine.Timestamp - self.logoffEvents[key] = logoffTime + if self.rdpLogoffEvents.hasKey(key): + self.rdpLogoffEvents[key].add(logoffTime) + else: + self.rdpLogoffEvents[key] = @[logoffTime] if self.outputLogoffEvents: var singleResultTable = newTable[string, string]() singleResultTable["Event"] = ruleTitle @@ -231,6 +235,12 @@ method analyze*(self: TimelineLogonCmd, x: HayabusaJson) = singleResultTable["LID"] = details.extractStr("SessID") self.seqOfResultsTables.add(singleResultTable) +proc calculateDuration(logonTime: string, logoffTime: string, timeFormat: string): Duration = + let logonTime = if logonTime.endsWith("Z"): logonTime.replace("Z", "") else: logonTime[0 ..< logonTime.len - 7] + let logoffTime = if logoffTime.endsWith("Z"): logoffTime.replace("Z", "") else: logoffTime[0 ..< logoffTime.len - 7] + let parsedLogoffTime = parse(padString(logoffTime, '0', timeFormat), timeFormat) + let parsedLogonTime = parse(padString(logonTime, '0', timeFormat), timeFormat) + return parsedLogoffTime - parsedLogonTime method resultOutput*(self: TimelineLogonCmd) = if self.displayTable: @@ -242,26 +252,24 @@ method resultOutput*(self: TimelineLogonCmd) = if self.calculateElapsedTime: for tableOfResults in self.seqOfResultsTables: if tableOfResults["EventID"] == "4624" or tableOfResults["EventID"] == "21": - var logoffTime = "" var logonTime = tableOfResults["Timestamp"] - let key = tableOfResults["LID"] & ":" & tableOfResults[ "TargetComputer"] & ":" & tableOfResults["TargetUser"] if self.logoffEvents.hasKey(key): - logoffTime = self.logoffEvents[key] + let logoffTime = self.logoffEvents[key] if logoffTime > logonTime: tableOfResults[]["LogoffTime"] = logoffTime - logonTime = if logonTime.endsWith("Z"): logonTime.replace( - "Z", "") else: logonTime[0 ..< logonTime.len - 7] - logoffTime = if logoffTime.endsWith( - "Z"): logoffTime.replace("Z", "") else: logoffTime[ - 0 ..< logofftime.len - 7] - let parsedLogoffTime = parse(padString(logoffTime, '0', - timeFormat), timeFormat) - let parsedLogonTime = parse(padString(logonTime, '0', - timeFormat), timeFormat) - let duration = parsedLogoffTime - parsedLogonTime + let duration = calculateDuration(logonTime, logoffTime, timeFormat) tableOfResults[]["ElapsedTime"] = formatDuration(duration) + elif self.rdpLogoffEvents.hasKey(key): + var logoffTimes = self.rdpLogoffEvents[key] + logoffTimes.sort() + for logoffTime in logoffTimes: + if logoffTime > logonTime: + tableOfResults[]["LogoffTime"] = logoffTime + let duration = calculateDuration(logonTime, logoffTime, timeFormat) + tableOfResults[]["ElapsedTime"] = formatDuration(duration) + break # Find admin logons for tableOfResults in self.seqOfResultsTables: @@ -338,6 +346,10 @@ method resultOutput*(self: TimelineLogonCmd) = outputFile.write("\p") ## Write contents + self.seqOfResultsTables.sort(proc(a, b: TableRef[string, string]): int = + if a["Timestamp"] < b["Timestamp"]: return -1 + if a["Timestamp"] > b["Timestamp"]: return 1 + return 0) for table in self.seqOfResultsTables: for key in header: if table.hasKey(key): From 42a278aaa66a430ecbf9f5667a5e39354ef5740e Mon Sep 17 00:00:00 2001 From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Date: Fri, 29 Nov 2024 08:40:22 +0900 Subject: [PATCH 3/4] update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c439c61..5bca209 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changes +## x.x.x [xxxx/xx/xx] + +**Enhancements:** + +- RDP logon and logoff information has been added to the `timeline-logon` timeline. #209 (@fukusuket) + ## 2.7.1 [2024/10/31] Halloween Release **Bug Fixes:** From 491684ce3e345e6c5f94595b615b341fac17338f Mon Sep 17 00:00:00 2001 From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Date: Fri, 29 Nov 2024 08:40:33 +0900 Subject: [PATCH 4/4] update changelog --- CHANGELOG-Japanese.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG-Japanese.md b/CHANGELOG-Japanese.md index 52840d7..4056115 100644 --- a/CHANGELOG-Japanese.md +++ b/CHANGELOG-Japanese.md @@ -1,5 +1,11 @@ # 変更点 +## x.x.x [xxxx/xx/xx] + +**改善:** + +- RDPログオンとログオフの情報が`timeline-logon`タイムラインに追加された。 #209 (@fukusuket) + ## 2.7.1 [2024/10/31] Halloween Release **バグ修正:**