Skip to content

Commit 7b061a6

Browse files
Merge pull request #216 from Yamato-Security/add-rdp-log-to-timeline-logon
feat: add RDP Logon/Logoff to `timeline-logon`
2 parents c82c9ea + 491684c commit 7b061a6

File tree

3 files changed

+110
-20
lines changed

3 files changed

+110
-20
lines changed

CHANGELOG-Japanese.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# 変更点
22

3+
## x.x.x [xxxx/xx/xx]
4+
5+
**改善:**
6+
7+
- RDPログオンとログオフの情報が`timeline-logon`タイムラインに追加された。 #209 (@fukusuket)
8+
39
## 2.7.1 [2024/10/31] Halloween Release
410

511
**バグ修正:**

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changes
22

3+
## x.x.x [xxxx/xx/xx]
4+
5+
**Enhancements:**
6+
7+
- RDP logon and logoff information has been added to the `timeline-logon` timeline. #209 (@fukusuket)
8+
39
## 2.7.1 [2024/10/31] Halloween Release
410

511
**Bug Fixes:**

src/takajopkg/timelineLogon.nim

Lines changed: 98 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ type
1111
string]] # Sequences are immutable so need to create a sequence of pointers to tables so we can update ["ElapsedTime"]
1212
seqOfLogoffEventTables*: seq[Table[string, string]] # This sequence can be immutable
1313
logoffEvents*: Table[string, string] = initTable[string, string]()
14+
rdpLogoffEvents*: Table[string, seq[string]] = initTable[string, seq[string]]()
1415
adminLogonEvents*: Table[string, string] = initTable[string, string]()
16+
EID_21_count*: int = 0 # RDP Logon
17+
EID_23_count*: int = 0 # RDP Logoff
1518
EID_4624_count* = 0 # Successful logon
1619
EID_4625_count* = 0 # Failed logon
1720
EID_4634_count* = 0 # Logoff
@@ -24,7 +27,7 @@ type
2427

2528
method filter*(self: TimelineLogonCmd, x: HayabusaJson): bool =
2629
return x.EventId == 4624 or x.EventId == 4625 or x.EventId == 4634 or
27-
x.EventId == 4647 or x.EventId == 4648 or x.EventId == 4672
30+
x.EventId == 4647 or x.EventId == 4648 or x.EventId == 4672 or x.EventId == 21 or x.EventId == 23
2831

2932
method analyze*(self: TimelineLogonCmd, x: HayabusaJson) =
3033
let ruleTitle = x.RuleTitle
@@ -174,6 +177,71 @@ method analyze*(self: TimelineLogonCmd, x: HayabusaJson) =
174177
singleResultTable["LID"] = details.extractStr("LID")
175178
self.seqOfResultsTables.add(singleResultTable)
176179

180+
#EID 21 Logon
181+
if ruleTitle == "RDP Logon":
182+
inc self.EID_21_count
183+
var singleResultTable = newTable[string, string]()
184+
singleResultTable["Event"] = ruleTitle
185+
singleResultTable["Timestamp"] = jsonLine.Timestamp
186+
singleResultTable["Channel"] = jsonLine.Channel
187+
singleResultTable["EventID"] = "21"
188+
let details = jsonLine.Details
189+
singleResultTable["TargetComputer"] = jsonLine.Computer
190+
singleResultTable["SourceIP"] = details.extractStr("SrcIP")
191+
let userDetail = details.extractStr("TgtUser")
192+
if userDetail.contains("\\"):
193+
let parts = userDetail.split("\\")
194+
singleResultTable["TargetDomainName"] = parts[0]
195+
singleResultTable["TargetUser"] = parts[1]
196+
else:
197+
singleResultTable["TargetUser"] = userDetail
198+
singleResultTable["LID"] = details.extractStr("SessID")
199+
singleResultTable["LogoffTime"] = ""
200+
singleResultTable["ElapsedTime"] = ""
201+
self.seqOfResultsTables.add(singleResultTable)
202+
203+
#EID 23 RDP Logoff
204+
if ruleTitle == "RDP Logoff":
205+
inc self.EID_23_count
206+
# If we want to calculate ElapsedTime
207+
let details = jsonLine.Details
208+
if self.calculateElapsedTime:
209+
# Create the key in the format of LID:Computer:User with a value of the timestamp
210+
var tgtUser = details.extractStr("TgtUser")
211+
if tgtUser.contains("\\"):
212+
let parts = tgtUser.split("\\")
213+
tgtUser = parts[1]
214+
let key = jsonLine.Details["SessID"].getStr() & ":" &
215+
jsonLine.Computer & ":" & tgtUser
216+
let logoffTime = jsonLine.Timestamp
217+
if self.rdpLogoffEvents.hasKey(key):
218+
self.rdpLogoffEvents[key].add(logoffTime)
219+
else:
220+
self.rdpLogoffEvents[key] = @[logoffTime]
221+
if self.outputLogoffEvents:
222+
var singleResultTable = newTable[string, string]()
223+
singleResultTable["Event"] = ruleTitle
224+
singleResultTable["Timestamp"] = jsonLine.Timestamp
225+
singleResultTable["Channel"] = jsonLine.Channel
226+
singleResultTable["TargetComputer"] = jsonLine.Computer
227+
singleResultTable["EventID"] = "23"
228+
let userDetail = details.extractStr("TgtUser")
229+
if userDetail.contains("\\"):
230+
let parts = userDetail.split("\\")
231+
singleResultTable["TargetDomainName"] = parts[0]
232+
singleResultTable["TargetUser"] = parts[1]
233+
else:
234+
singleResultTable["TargetUser"] = userDetail
235+
singleResultTable["LID"] = details.extractStr("SessID")
236+
self.seqOfResultsTables.add(singleResultTable)
237+
238+
proc calculateDuration(logonTime: string, logoffTime: string, timeFormat: string): Duration =
239+
let logonTime = if logonTime.endsWith("Z"): logonTime.replace("Z", "") else: logonTime[0 ..< logonTime.len - 7]
240+
let logoffTime = if logoffTime.endsWith("Z"): logoffTime.replace("Z", "") else: logoffTime[0 ..< logoffTime.len - 7]
241+
let parsedLogoffTime = parse(padString(logoffTime, '0', timeFormat), timeFormat)
242+
let parsedLogonTime = parse(padString(logonTime, '0', timeFormat), timeFormat)
243+
return parsedLogoffTime - parsedLogonTime
244+
177245
method resultOutput*(self: TimelineLogonCmd) =
178246
if self.displayTable:
179247
echo ""
@@ -183,28 +251,25 @@ method resultOutput*(self: TimelineLogonCmd) =
183251
# Calculating the logon elapsed time (default)
184252
if self.calculateElapsedTime:
185253
for tableOfResults in self.seqOfResultsTables:
186-
if tableOfResults["EventID"] == "4624":
187-
var logoffTime = ""
254+
if tableOfResults["EventID"] == "4624" or tableOfResults["EventID"] == "21":
188255
var logonTime = tableOfResults["Timestamp"]
189-
190256
let key = tableOfResults["LID"] & ":" & tableOfResults[
191257
"TargetComputer"] & ":" & tableOfResults["TargetUser"]
192258
if self.logoffEvents.hasKey(key):
193-
logoffTime = self.logoffEvents[key]
194-
tableOfResults[]["LogoffTime"] = logoffTime
195-
logonTime = if logonTime.endsWith("Z"): logonTime.replace(
196-
"Z", "") else: logonTime[0 ..< logonTime.len - 7]
197-
logoffTime = if logoffTime.endsWith(
198-
"Z"): logoffTime.replace("Z", "") else: logoffTime[
199-
0 ..< logofftime.len - 7]
200-
let parsedLogoffTime = parse(padString(logoffTime, '0',
201-
timeFormat), timeFormat)
202-
let parsedLogonTime = parse(padString(logonTime, '0',
203-
timeFormat), timeFormat)
204-
let duration = parsedLogoffTime - parsedLogonTime
205-
tableOfResults[]["ElapsedTime"] = formatDuration(duration)
206-
else:
207-
logoffTime = "n/a"
259+
let logoffTime = self.logoffEvents[key]
260+
if logoffTime > logonTime:
261+
tableOfResults[]["LogoffTime"] = logoffTime
262+
let duration = calculateDuration(logonTime, logoffTime, timeFormat)
263+
tableOfResults[]["ElapsedTime"] = formatDuration(duration)
264+
elif self.rdpLogoffEvents.hasKey(key):
265+
var logoffTimes = self.rdpLogoffEvents[key]
266+
logoffTimes.sort()
267+
for logoffTime in logoffTimes:
268+
if logoffTime > logonTime:
269+
tableOfResults[]["LogoffTime"] = logoffTime
270+
let duration = calculateDuration(logonTime, logoffTime, timeFormat)
271+
tableOfResults[]["ElapsedTime"] = formatDuration(duration)
272+
break
208273

209274
# Find admin logons
210275
for tableOfResults in self.seqOfResultsTables:
@@ -241,7 +306,11 @@ method resultOutput*(self: TimelineLogonCmd) =
241306
padString("EID 4648 (Explicit Logon): " & intToStr(
242307
self.EID_4648_count).insertSep(','), ' ', 80) &
243308
padString("EID 4672 (Admin Logon): " & intToStr(
244-
self.EID_4672_count).insertSep(','), ' ', 80)
309+
self.EID_4672_count).insertSep(','), ' ', 80) &
310+
padString("EID 21 (RDP Logon): " & intToStr(
311+
self.EID_21_count).insertSep(','), ' ', 80) &
312+
padString("EID 23 (RDP Logoff): " & intToStr(
313+
self.EID_23_count).insertSep(','), ' ', 80)
245314
if self.displayTable:
246315
echo ""
247316
echo "Found logon events:"
@@ -256,8 +325,13 @@ method resultOutput*(self: TimelineLogonCmd) =
256325
self.EID_4648_count).insertSep(',')
257326
echo "EID 4672 (Admin Logon): ", intToStr(
258327
self.EID_4672_count).insertSep(',')
328+
echo "EID 21 (RDP Logon): ", intToStr(
329+
self.EID_21_count).insertSep(',')
330+
echo "EID 23 (RDP Logoff): ", intToStr(
331+
self.EID_23_count).insertSep(',')
259332
echo ""
260333

334+
261335
# Save results
262336
var outputFile = open(self.output, fmWrite)
263337
let header = ["Timestamp", "Channel", "EventID", "Event", "LogoffTime",
@@ -272,6 +346,10 @@ method resultOutput*(self: TimelineLogonCmd) =
272346
outputFile.write("\p")
273347

274348
## Write contents
349+
self.seqOfResultsTables.sort(proc(a, b: TableRef[string, string]): int =
350+
if a["Timestamp"] < b["Timestamp"]: return -1
351+
if a["Timestamp"] > b["Timestamp"]: return 1
352+
return 0)
275353
for table in self.seqOfResultsTables:
276354
for key in header:
277355
if table.hasKey(key):

0 commit comments

Comments
 (0)