From c7e4facc618dcdc369cdbfa36cf18eff762e3096 Mon Sep 17 00:00:00 2001
From: fukusuket <41001169+fukusuket@users.noreply.github.com>
Date: Sat, 17 Feb 2024 17:47:22 +0900
Subject: [PATCH 01/16] feat: show alert levels and rule names for stacking
commands refactor: move terminal related func to takajoTerminal.nim
---
src/takajo.nim | 3 +-
src/takajopkg/extractScriptblocks.nim | 12 +----
src/takajopkg/general.nim | 50 --------------------
src/takajopkg/stackCmdlines.nim | 48 ++++++++++---------
src/takajopkg/stackDNS.nim | 43 +++++++++--------
src/takajopkg/stackProcesses.nim | 48 ++++++++++---------
src/takajopkg/stackServices.nim | 6 ++-
src/takajopkg/stackTasks.nim | 6 ++-
src/takajopkg/stackUtil.nim | 30 ++++++++++++
src/takajopkg/takajoTerminal.nim | 67 +++++++++++++++++++++++++++
10 files changed, 186 insertions(+), 127 deletions(-)
create mode 100644 src/takajopkg/stackUtil.nim
create mode 100644 src/takajopkg/takajoTerminal.nim
diff --git a/src/takajo.nim b/src/takajo.nim
index cef69dfa..c045ca1e 100644
--- a/src/takajo.nim
+++ b/src/takajo.nim
@@ -11,7 +11,6 @@ import strformat
import strutils
import tables
import terminal
-import termstyle
import times
import threadpool
import uri
@@ -21,6 +20,8 @@ import std/xmlparser
import std/xmltree
import suru
import takajopkg/general
+import takajopkg/stackUtil
+import takajopkg/takajoTerminal
include takajopkg/extractScriptblocks
include takajopkg/listDomains
include takajopkg/listIpAddresses
diff --git a/src/takajopkg/extractScriptblocks.nim b/src/takajopkg/extractScriptblocks.nim
index 487e54c0..003b732e 100644
--- a/src/takajopkg/extractScriptblocks.nim
+++ b/src/takajopkg/extractScriptblocks.nim
@@ -164,16 +164,8 @@ proc extractScriptblocks(level: string = "low", output: string = "scriptblock-lo
else:
outputFile.write(escapeCsvField(val) & "\p")
for v in summaryRecords.values:
- if v[5] == "crit":
- table.add red v[0], red v[1], red v[2], red v[3], red v[4], red v[5], red v[6]
- elif v[5] == "high":
- table.add yellow v[0], yellow v[1], yellow v[2], yellow v[3], yellow v[4], yellow v[5], yellow v[6]
- elif v[5] == "med":
- table.add cyan v[0], cyan v[1], cyan v[2], cyan v[3], cyan v[4], cyan v[5], cyan v[6]
- elif v[5] == "low":
- table.add green v[0], green v[1], green v[2], green v[3], green v[4], green v[5], green v[6]
- else:
- table.add v
+ let color = levelColor(v[5])
+ table.add color v[0], color v[1], color v[2], color v[3], color v[4], color v[5], color v[6]
for i, cell in v:
if i < 6:
outputFile.write(escapeCsvField(cell) & ",")
diff --git a/src/takajopkg/general.nim b/src/takajopkg/general.nim
index c56b6d87..bc0cee62 100644
--- a/src/takajopkg/general.nim
+++ b/src/takajopkg/general.nim
@@ -1,7 +1,4 @@
import json
-import nancy
-import terminal
-import termstyle
import re
import std/os
import std/parsecsv
@@ -12,17 +9,6 @@ import std/tables
import times
from std/streams import newFileStream
-proc outputLogo*(): string =
- let logo = """
-╔════╦═══╦╗╔═╦═══╗ ╔╦═══╗
-║╔╗╔╗║╔═╗║║║╔╣╔═╗║ ║║╔═╗║
-╚╝║║╚╣║ ║║╚╝╝║║ ║║ ║║║ ║║
- ║║ ║╚═╝║╔╗╖║╚═╝╠╗║║║ ║║
- ╔╝╚╗║╔═╗║║║╚╣╔═╗║╚╝║╚═╝║
- ╚══╝╚╝ ╚╩╝╚═╩╝ ╚╩══╩═══╝
- by Yamato Security
-"""
- return logo
proc getJsonValue*(jsonResponse: JsonNode, keys: seq[string], default: string = "Unknown"): string =
var value = jsonResponse
@@ -352,42 +338,6 @@ proc isJsonConvertible*(timeline: string) : bool =
echo "Failed to open '" & timeline & "'. Please specify a valid file path.\p"
return false
-proc colorWrite*(color: ForegroundColor, ansiEscape: string, txt: string) =
- # Remove ANSI escape sequences and use stdout.styledWrite instead
- let replacedTxt = txt.replace(ansiEscape,"").replace(termClear,"")
- if "│" in replacedTxt:
- stdout.styledWrite(color, replacedTxt.replace("│ ",""))
- stdout.write "│ "
- else:
- stdout.styledWrite(color, replacedTxt)
-
-proc stdoutStyledWrite*(txt: string) =
- if txt.startsWith(termRed):
- colorWrite(fgRed, termRed,txt)
- elif txt.startsWith(termGreen):
- colorWrite(fgGreen, termGreen, txt)
- elif txt.startsWith(termYellow):
- colorWrite(fgYellow, termYellow, txt)
- elif txt.startsWith(termCyan):
- colorWrite(fgCyan, termCyan, txt)
- else:
- stdout.write txt.replace(termClear,"")
-
-proc echoTableSepsWithStyled*(table: TerminalTable, maxSize = terminalWidth(), seps = defaultSeps) =
- # This function equivalent to echoTableSeps without using ANSI escape to avoid the following issue.
- # https://github.com/PMunch/nancy/issues/4
- # https://github.com/PMunch/nancy/blob/9918716a563f64d740df6a02db42662781e94fc8/src/nancy.nim#L195C6-L195C19
- let sizes = table.getColumnSizes(maxSize - 4, padding = 3)
- printSeparator(top)
- for k, entry in table.entries(sizes):
- for _, row in entry():
- stdout.write seps.vertical & " "
- for i, cell in row():
- stdoutStyledWrite cell & (if i != sizes.high: " " & seps.vertical & " " else: "")
- stdout.write " " & seps.vertical & "\n"
- if k != table.rows - 1:
- printSeparator(center)
- printSeparator(bottom)
type VirusTotalResult* = object
resTable*: TableRef[string, string]
diff --git a/src/takajopkg/stackCmdlines.nim b/src/takajopkg/stackCmdlines.nim
index 7e77479a..a9558140 100644
--- a/src/takajopkg/stackCmdlines.nim
+++ b/src/takajopkg/stackCmdlines.nim
@@ -26,6 +26,7 @@ proc stackCmdlines(ignoreSysmon: bool = false, ignoreSecurity: bool = false, out
var
bar: SuruBar = initSuruBar()
stackCmdlines = initCountTable[string]()
+ stackCount = initTable[string, CountTable[string]]()
uniqueCmd = 0
bar[0].total = totalLines
@@ -42,34 +43,37 @@ proc stackCmdlines(ignoreSysmon: bool = false, ignoreSecurity: bool = false, out
(eventId == 4688 and not ignoreSecurity and channel == "Security"):
let command = jsonLine["Details"]["Cmdline"].getStr("N/A")
stackCmdlines.inc(command)
-
+ stackRuleCount(jsonLine, stackCount)
bar.finish()
echo ""
- stackCmdlines.sort()
-
- # Print results to screen
- var outputFileSize = 0
- if output == "":
- for commandline, count in stackCmdlines:
- inc uniqueCmd
- var commaDelimitedStr = $count & "," & commandline
- commaDelimitedStr = replace(commaDelimitedStr, ",", " | ")
- echo commaDelimitedStr
- # Save to CSV file
+ if stackCmdlines.len == 0:
+ echo "No results where found."
else:
- let outputFile = open(output, fmWrite)
- writeLine(outputFile, "Count,Cmdlines")
+ # Print results to screen
+ printAlertCount(stackCount)
+ stackCmdlines.sort()
+ var outputFileSize = 0
+ if output == "":
+ for commandline, count in stackCmdlines:
+ inc uniqueCmd
+ var commaDelimitedStr = $count & "," & commandline
+ commaDelimitedStr = replace(commaDelimitedStr, ",", " | ")
+ echo commaDelimitedStr
+ # Save to CSV file
+ else:
+ let outputFile = open(output, fmWrite)
+ writeLine(outputFile, "Count,Cmdlines")
- # Write results
- for commandline, count in stackCmdlines:
- inc uniqueCmd
- writeLine(outputFile, $count & "," & commandline)
- outputFileSize = getFileSize(outputFile)
- close(outputFile)
+ # Write results
+ for commandline, count in stackCmdlines:
+ inc uniqueCmd
+ writeLine(outputFile, $count & "," & commandline)
+ outputFileSize = getFileSize(outputFile)
+ close(outputFile)
- echo ""
- echo "Saved file: " & output & " (" & formatFileSize(outputFileSize) & ")"
+ echo ""
+ echo "Saved file: " & output & " (" & formatFileSize(outputFileSize) & ")"
let endTime = epochTime()
let elapsedTime2 = int(endTime - startTime)
diff --git a/src/takajopkg/stackDNS.nim b/src/takajopkg/stackDNS.nim
index f7e5765f..5af3d17c 100644
--- a/src/takajopkg/stackDNS.nim
+++ b/src/takajopkg/stackDNS.nim
@@ -26,6 +26,7 @@ proc stackDNS(output: string = "", quiet: bool = false, timeline: string) =
var
bar: SuruBar = initSuruBar()
stackDNS = initCountTable[string]()
+ stackCount = initTable[string, CountTable[string]]()
bar[0].total = totalLines
bar.setup()
@@ -43,32 +44,36 @@ proc stackDNS(output: string = "", quiet: bool = false, timeline: string) =
let res = jsonLine["Details"]["Result"].getStr("N/A")
let result = prog & " -> " & query & " -> " & res
stackDNS.inc(result)
+ stackRuleCount(jsonLine, stackCount)
bar.finish()
echo ""
- stackDNS.sort()
-
- # Print results to screen
- var outputFileSize = 0
- if output == "":
- for res, count in stackDNS:
- var commaDelimitedStr = $count & "," & res
- commaDelimitedStr = replace(commaDelimitedStr, ",", " | ")
- echo commaDelimitedStr
- # Save to CSV file
+ if stackDNS.len == 0:
+ echo "No results where found."
else:
- let outputFile = open(output, fmWrite)
- writeLine(outputFile, "Count,DNS query and response")
+ # Print results to screen
+ printAlertCount(stackCount)
+ stackDNS.sort()
+ var outputFileSize = 0
+ if output == "":
+ for res, count in stackDNS:
+ var commaDelimitedStr = $count & "," & res
+ commaDelimitedStr = replace(commaDelimitedStr, ",", " | ")
+ echo commaDelimitedStr
+ # Save to CSV file
+ else:
+ let outputFile = open(output, fmWrite)
+ writeLine(outputFile, "Count,DNS query and response")
- # Write results
- for res, count in stackDNS:
- writeLine(outputFile, $count & "," & res)
- outputFileSize = getFileSize(outputFile)
- close(outputFile)
+ # Write results
+ for res, count in stackDNS:
+ writeLine(outputFile, $count & "," & res)
+ outputFileSize = getFileSize(outputFile)
+ close(outputFile)
- echo ""
- echo "Saved file: " & output & " (" & formatFileSize(outputFileSize) & ")"
+ echo ""
+ echo "Saved file: " & output & " (" & formatFileSize(outputFileSize) & ")"
let endTime = epochTime()
let elapsedTime2 = int(endTime - startTime)
diff --git a/src/takajopkg/stackProcesses.nim b/src/takajopkg/stackProcesses.nim
index f73892ed..df697577 100644
--- a/src/takajopkg/stackProcesses.nim
+++ b/src/takajopkg/stackProcesses.nim
@@ -26,6 +26,7 @@ proc stackProcesses(ignoreSysmon: bool = false, ignoreSecurity: bool = false, ou
var
bar: SuruBar = initSuruBar()
stackProcesses = initCountTable[string]()
+ stackCount = initTable[string, CountTable[string]]()
uniqueProcesses = 0
bar[0].total = totalLines
@@ -42,34 +43,37 @@ proc stackProcesses(ignoreSysmon: bool = false, ignoreSecurity: bool = false, ou
(eventId == 4688 and not ignoreSecurity and channel == "Sec"):
let process = jsonLine["Details"]["Proc"].getStr("N/A")
stackProcesses.inc(process)
-
+ stackRuleCount(jsonLine, stackCount)
bar.finish()
echo ""
- stackProcesses.sort()
-
- # Print results to screen
- var outputFileSize = 0
- if output == "":
- for process, count in stackProcesses:
- inc uniqueProcesses
- var commaDelimitedStr = $count & "," & process
- commaDelimitedStr = replace(commaDelimitedStr, ",", " | ")
- echo commaDelimitedStr
- # Save to CSV file
+ if stackProcesses.len == 0:
+ echo "No results where found."
else:
- let outputFile = open(output, fmWrite)
- writeLine(outputFile, "Count,Processes")
+ # Print results to screen
+ printAlertCount(stackCount)
+ stackProcesses.sort()
+ var outputFileSize = 0
+ if output == "":
+ for process, count in stackProcesses:
+ inc uniqueProcesses
+ var commaDelimitedStr = $count & "," & process
+ commaDelimitedStr = replace(commaDelimitedStr, ",", " | ")
+ echo commaDelimitedStr
+ # Save to CSV file
+ else:
+ let outputFile = open(output, fmWrite)
+ writeLine(outputFile, "Count,Processes")
- # Write results
- for process, count in stackProcesses:
- inc uniqueProcesses
- writeLine(outputFile, $count & "," & process)
- outputFileSize = getFileSize(outputFile)
- close(outputFile)
+ # Write results
+ for process, count in stackProcesses:
+ inc uniqueProcesses
+ writeLine(outputFile, $count & "," & process)
+ outputFileSize = getFileSize(outputFile)
+ close(outputFile)
- echo ""
- echo "Saved file: " & output & " (" & formatFileSize(outputFileSize) & ")"
+ echo ""
+ echo "Saved file: " & output & " (" & formatFileSize(outputFileSize) & ")"
let endTime = epochTime()
let elapsedTime2 = int(endTime - startTime)
diff --git a/src/takajopkg/stackServices.nim b/src/takajopkg/stackServices.nim
index 078af8ac..76db3ced 100644
--- a/src/takajopkg/stackServices.nim
+++ b/src/takajopkg/stackServices.nim
@@ -26,6 +26,7 @@ proc stackServices(ignoreSysmon: bool = false, ignoreSecurity: bool = false,outp
var
bar: SuruBar = initSuruBar()
stackServices = initCountTable[string]()
+ stackCount = initTable[string, CountTable[string]]()
bar[0].total = totalLines
bar.setup()
@@ -43,12 +44,15 @@ proc stackServices(ignoreSysmon: bool = false, ignoreSecurity: bool = false,outp
let path = jsonLine["Details"]["Path"].getStr("N/A")
let res = svc & " -> " & path
stackServices.inc(res)
-
+ stackRuleCount(jsonLine, stackCount)
bar.finish()
echo ""
+
if stackServices.len == 0:
echo "No results where found."
else:
+ # Print results to screen
+ printAlertCount(stackCount)
stackServices.sort()
var stackServicesSorted = newOrderedTable[string, int]()
var stack: seq[string] = newSeq[string]()
diff --git a/src/takajopkg/stackTasks.nim b/src/takajopkg/stackTasks.nim
index 824d91a9..c2d0af9a 100644
--- a/src/takajopkg/stackTasks.nim
+++ b/src/takajopkg/stackTasks.nim
@@ -29,6 +29,7 @@ proc stackTasks(ignoreSysmon: bool = false, ignoreSecurity: bool = false, output
var
bar: SuruBar = initSuruBar()
stackTasks = initCountTable[string]()
+ stackCount = initTable[string, CountTable[string]]()
bar[0].total = totalLines
bar.setup()
@@ -57,15 +58,16 @@ proc stackTasks(ignoreSysmon: bool = false, ignoreSecurity: bool = false, output
commandAndArgs = commandAndArgs.replace("", "").replace("","")
let result = user & " -> " & name & " -> " & decodeEntity(commandAndArgs)
stackTasks.inc(result)
-
+ stackRuleCount(jsonLine, stackCount)
bar.finish()
echo ""
- stackTasks.sort()
if stackTasks.len == 0:
echo "No results where found."
else:
# Print results to screen
+ printAlertCount(stackCount)
+ stackTasks.sort()
var outputFileSize = 0
if output == "":
for task, count in stackTasks:
diff --git a/src/takajopkg/stackUtil.nim b/src/takajopkg/stackUtil.nim
new file mode 100644
index 00000000..badd8e86
--- /dev/null
+++ b/src/takajopkg/stackUtil.nim
@@ -0,0 +1,30 @@
+import json
+import nancy
+import takajoTerminal
+import std/tables
+import std/algorithm
+
+proc stackRuleCount*(jsonLine:JsonNode, stackCount: var Table[string, CountTable[string]]) =
+ let level = jsonLine["Level"].getStr("N/A")
+ if level != "info":
+ let ruleTitle = jsonLine["RuleTitle"].getStr("N/A")
+ if level notin stackCount:
+ var ct = initCountTable[string]()
+ stackCount[level] = ct
+ stackCount[level].inc(ruleTitle)
+
+proc printAlertCount*(countTable: Table[string, CountTable[string]]) =
+ if countTable.len == 0:
+ return
+ var table: TerminalTable
+ table.add ["Count", "Level", "RuleTitle"]
+ for level in ["crit", "high", "med", "low"]:
+ if level notin countTable:
+ continue
+ var result = countTable[level]
+ result.sort() # Sort by number of detections
+ for ruleTitle, count in result:
+ let color = levelColor(level)
+ table.add color count, color level, color ruleTitle
+ table.echoTableSepsWithStyled(seps = boxSeps)
+ echo ""
\ No newline at end of file
diff --git a/src/takajopkg/takajoTerminal.nim b/src/takajopkg/takajoTerminal.nim
new file mode 100644
index 00000000..fdff3772
--- /dev/null
+++ b/src/takajopkg/takajoTerminal.nim
@@ -0,0 +1,67 @@
+import nancy
+import terminal
+import termstyle
+import std/strutils
+
+proc outputLogo*(): string =
+ let logo = """
+╔════╦═══╦╗╔═╦═══╗ ╔╦═══╗
+║╔╗╔╗║╔═╗║║║╔╣╔═╗║ ║║╔═╗║
+╚╝║║╚╣║ ║║╚╝╝║║ ║║ ║║║ ║║
+ ║║ ║╚═╝║╔╗╖║╚═╝╠╗║║║ ║║
+ ╔╝╚╗║╔═╗║║║╚╣╔═╗║╚╝║╚═╝║
+ ╚══╝╚╝ ╚╩╝╚═╩╝ ╚╩══╩═══╝
+ by Yamato Security
+"""
+ return logo
+
+
+proc colorWrite*(color: ForegroundColor, ansiEscape: string, txt: string) =
+ # Remove ANSI escape sequences and use stdout.styledWrite instead
+ let replacedTxt = txt.replace(ansiEscape,"").replace(termClear,"")
+ if "│" in replacedTxt:
+ stdout.styledWrite(color, replacedTxt.replace("│ ",""))
+ stdout.write "│ "
+ else:
+ stdout.styledWrite(color, replacedTxt)
+
+
+proc stdoutStyledWrite*(txt: string) =
+ if txt.startsWith(termRed):
+ colorWrite(fgRed, termRed,txt)
+ elif txt.startsWith(termGreen):
+ colorWrite(fgGreen, termGreen, txt)
+ elif txt.startsWith(termYellow):
+ colorWrite(fgYellow, termYellow, txt)
+ elif txt.startsWith(termCyan):
+ colorWrite(fgCyan, termCyan, txt)
+ else:
+ stdout.write txt.replace(termClear,"")
+
+
+proc echoTableSepsWithStyled*(table: TerminalTable, maxSize = terminalWidth(), seps = defaultSeps) =
+ # This function equivalent to echoTableSeps without using ANSI escape to avoid the following issue.
+ # https://github.com/PMunch/nancy/issues/4
+ # https://github.com/PMunch/nancy/blob/9918716a563f64d740df6a02db42662781e94fc8/src/nancy.nim#L195C6-L195C19
+ let sizes = table.getColumnSizes(maxSize - 4, padding = 3)
+ printSeparator(top)
+ for k, entry in table.entries(sizes):
+ for _, row in entry():
+ stdout.write seps.vertical & " "
+ for i, cell in row():
+ stdoutStyledWrite cell & (if i != sizes.high: " " & seps.vertical & " " else: "")
+ stdout.write " " & seps.vertical & "\n"
+ if k != table.rows - 1:
+ printSeparator(center)
+ printSeparator(bottom)
+
+proc levelColor*(level:string): proc(ss: varargs[string, `$`]): string =
+ if level == "crit":
+ return red
+ elif level == "high":
+ return yellow
+ elif level == "med":
+ return cyan
+ elif level == "low":
+ return green
+ return white
\ No newline at end of file
From 4ebadafa271fed31917ad40a71db03d5b743e336 Mon Sep 17 00:00:00 2001
From: fukusuket <41001169+fukusuket@users.noreply.github.com>
Date: Sat, 17 Feb 2024 18:13:05 +0900
Subject: [PATCH 02/16] test: add case for integration test
---
.github/workflows/integration-test.yml | 39 ++++++++++++++++++++++++++
src/takajopkg/stackUtil.nim | 6 ++--
2 files changed, 41 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml
index 37fd22bd..feab1e4a 100644
--- a/.github/workflows/integration-test.yml
+++ b/.github/workflows/integration-test.yml
@@ -56,10 +56,34 @@ jobs:
- name: run extract-scriptblocks
run: cd takajo && ./takajo extract-scriptblocks -t timeline.jsonl
+ - name: run extract-scriptblocks(-o)
+ run: cd takajo && ./takajo extract-scriptblocks -t timeline.jsonl -o scriptblocks
+
+ - name: run list-domain(-o)
+ run: cd takajo && ./takajo list-domains -t timeline.jsonl -o domains.txt
+
+ - name: run list-domain(-s)
+ run: cd takajo && ./takajo list-domains -t timeline.jsonl -o domains.txt -s
+
+ - name: run list-hashes(-o)
+ run: cd takajo && ./takajo list-hashes -t timeline.jsonl -o case-1
+
+ - name: run list-ip-addresses(-o)
+ run: cd takajo && ./takajo list-ip-addresses -t timeline.jsonl -o ipAddresses.txt
+
+ - name: run list-ip-addresses(-i)
+ run: cd takajo && ./takajo list-ip-addresses -t timeline.jsonl -o ipAddresses.txt -i=false
+
- name: run list-undetected-evtx
+ run: cd takajo && ./takajo list-undetected-evtx -t timeline.csv -e ../hayabusa-sample-evtx
+
+ - name: run list-undetected-evtx(-o)
run: cd takajo && ./takajo list-undetected-evtx -t timeline.csv -e ../hayabusa-sample-evtx -o undetected-evtx.txt
- name: run list-unused-rules
+ run: cd takajo && ./takajo list-unused-rules -t timeline.csv -r ../hayabusa/tmp/rules
+
+ - name: run list-unused-rules(-o)
run: cd takajo && ./takajo list-unused-rules -t timeline.csv -r ../hayabusa/tmp/rules -o unused-rules.txt
- name: run split-csv-timeline
@@ -77,15 +101,30 @@ jobs:
- name: run stack-logons
run: cd takajo && ./takajo stack-logons -t timeline.jsonl
+ - name: run stack-logons(-l)
+ run: cd takajo && ./takajo stack-logons -t timeline.jsonl -l
+
+ - name: run stack-logons(-o)
+ run: cd takajo && ./takajo stack-logons -t timeline.jsonl -o logon.csv
+
- name: run stack-processes
run: cd takajo && ./takajo stack-processes -t timeline.jsonl
+ - name: run stack-processes(-o)
+ run: cd takajo && ./takajo stack-processes -t timeline.jsonl -o processes.csv
+
- name: run stack-services
run: cd takajo && ./takajo stack-services -t timeline.jsonl
+ - name: run stack-services(-o)
+ run: cd takajo && ./takajo stack-services -t timeline.jsonl -o services.csv
+
- name: run stack-tasks
run: cd takajo && ./takajo stack-tasks -t timeline.jsonl
+ - name: run stack-tasks(-o)
+ run: cd takajo && ./takajo stack-tasks -t timeline.jsonl -o tasks.csv
+
- name: run sysmon-process-tree
run: cd takajo && ./takajo sysmon-process-tree -t timeline.jsonl -p "365ABB72-3D4A-5CEB-0000-0010FA93FD00" -o process-tree.txt
diff --git a/src/takajopkg/stackUtil.nim b/src/takajopkg/stackUtil.nim
index badd8e86..72094fe7 100644
--- a/src/takajopkg/stackUtil.nim
+++ b/src/takajopkg/stackUtil.nim
@@ -7,11 +7,9 @@ import std/algorithm
proc stackRuleCount*(jsonLine:JsonNode, stackCount: var Table[string, CountTable[string]]) =
let level = jsonLine["Level"].getStr("N/A")
if level != "info":
- let ruleTitle = jsonLine["RuleTitle"].getStr("N/A")
if level notin stackCount:
- var ct = initCountTable[string]()
- stackCount[level] = ct
- stackCount[level].inc(ruleTitle)
+ stackCount[level] = initCountTable[string]()
+ stackCount[level].inc(jsonLine["RuleTitle"].getStr("N/A"))
proc printAlertCount*(countTable: Table[string, CountTable[string]]) =
if countTable.len == 0:
From ea33c27efdb9c3c61006998f5bb92a37fa186649 Mon Sep 17 00:00:00 2001
From: fukusuket <41001169+fukusuket@users.noreply.github.com>
Date: Sat, 17 Feb 2024 18:59:46 +0900
Subject: [PATCH 03/16] chg: sort by rule name
---
src/takajopkg/stackUtil.nim | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/takajopkg/stackUtil.nim b/src/takajopkg/stackUtil.nim
index 72094fe7..642b1f09 100644
--- a/src/takajopkg/stackUtil.nim
+++ b/src/takajopkg/stackUtil.nim
@@ -1,8 +1,8 @@
import json
import nancy
import takajoTerminal
-import std/tables
import std/algorithm
+import std/tables
proc stackRuleCount*(jsonLine:JsonNode, stackCount: var Table[string, CountTable[string]]) =
let level = jsonLine["Level"].getStr("N/A")
@@ -11,6 +11,11 @@ proc stackRuleCount*(jsonLine:JsonNode, stackCount: var Table[string, CountTable
stackCount[level] = initCountTable[string]()
stackCount[level].inc(jsonLine["RuleTitle"].getStr("N/A"))
+proc alertCmp(x, y: (string, int)): int =
+ result = cmp(x[1], y[1]) * -1
+ if result == 0:
+ result = cmp(x[0], y[0])
+
proc printAlertCount*(countTable: Table[string, CountTable[string]]) =
if countTable.len == 0:
return
@@ -21,7 +26,11 @@ proc printAlertCount*(countTable: Table[string, CountTable[string]]) =
continue
var result = countTable[level]
result.sort() # Sort by number of detections
+ var stack = newSeq[(string, int)]()
for ruleTitle, count in result:
+ stack.add((ruleTitle, count))
+ stack.sort(alertCmp) # Sort by rule name
+ for (ruleTitle, count) in stack:
let color = levelColor(level)
table.add color count, color level, color ruleTitle
table.echoTableSepsWithStyled(seps = boxSeps)
From e7aa58fc74c93faedcd36e817f2c1758155369a8 Mon Sep 17 00:00:00 2001
From: fukusuket <41001169+fukusuket@users.noreply.github.com>
Date: Sat, 17 Feb 2024 21:21:47 +0900
Subject: [PATCH 04/16] fix: stackCmdline not output Security Event
---
src/takajopkg/stackCmdlines.nim | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/takajopkg/stackCmdlines.nim b/src/takajopkg/stackCmdlines.nim
index a9558140..ac2b3159 100644
--- a/src/takajopkg/stackCmdlines.nim
+++ b/src/takajopkg/stackCmdlines.nim
@@ -40,7 +40,7 @@ proc stackCmdlines(ignoreSysmon: bool = false, ignoreSecurity: bool = false, out
let eventId = jsonLine["EventID"].getInt(0)
let channel = jsonLine["Channel"].getStr("N/A")
if (eventId == 1 and not ignoreSysmon and channel == "Sysmon") or
- (eventId == 4688 and not ignoreSecurity and channel == "Security"):
+ (eventId == 4688 and not ignoreSecurity and channel == "Sec"):
let command = jsonLine["Details"]["Cmdline"].getStr("N/A")
stackCmdlines.inc(command)
stackRuleCount(jsonLine, stackCount)
From 719f1e1cb800f950daace770132b4a8fdd038258 Mon Sep 17 00:00:00 2001
From: fukusuket <41001169+fukusuket@users.noreply.github.com>
Date: Sat, 17 Feb 2024 22:10:05 +0900
Subject: [PATCH 05/16] refactor: remove duplicated output logic
---
src/takajopkg/stackCmdlines.nim | 29 +------------------
src/takajopkg/stackDNS.nim | 26 +----------------
src/takajopkg/stackProcesses.nim | 30 +-------------------
src/takajopkg/stackServices.nim | 44 +----------------------------
src/takajopkg/stackTasks.nim | 25 +----------------
src/takajopkg/stackUtil.nim | 48 ++++++++++++++++++++++++++------
6 files changed, 45 insertions(+), 157 deletions(-)
diff --git a/src/takajopkg/stackCmdlines.nim b/src/takajopkg/stackCmdlines.nim
index ac2b3159..5d54cecb 100644
--- a/src/takajopkg/stackCmdlines.nim
+++ b/src/takajopkg/stackCmdlines.nim
@@ -46,34 +46,7 @@ proc stackCmdlines(ignoreSysmon: bool = false, ignoreSecurity: bool = false, out
stackRuleCount(jsonLine, stackCount)
bar.finish()
echo ""
-
- if stackCmdlines.len == 0:
- echo "No results where found."
- else:
- # Print results to screen
- printAlertCount(stackCount)
- stackCmdlines.sort()
- var outputFileSize = 0
- if output == "":
- for commandline, count in stackCmdlines:
- inc uniqueCmd
- var commaDelimitedStr = $count & "," & commandline
- commaDelimitedStr = replace(commaDelimitedStr, ",", " | ")
- echo commaDelimitedStr
- # Save to CSV file
- else:
- let outputFile = open(output, fmWrite)
- writeLine(outputFile, "Count,Cmdlines")
-
- # Write results
- for commandline, count in stackCmdlines:
- inc uniqueCmd
- writeLine(outputFile, $count & "," & commandline)
- outputFileSize = getFileSize(outputFile)
- close(outputFile)
-
- echo ""
- echo "Saved file: " & output & " (" & formatFileSize(outputFileSize) & ")"
+ outputResult(output, stackCmdlines, stackCount)
let endTime = epochTime()
let elapsedTime2 = int(endTime - startTime)
diff --git a/src/takajopkg/stackDNS.nim b/src/takajopkg/stackDNS.nim
index 5af3d17c..2e512610 100644
--- a/src/takajopkg/stackDNS.nim
+++ b/src/takajopkg/stackDNS.nim
@@ -49,31 +49,7 @@ proc stackDNS(output: string = "", quiet: bool = false, timeline: string) =
bar.finish()
echo ""
- if stackDNS.len == 0:
- echo "No results where found."
- else:
- # Print results to screen
- printAlertCount(stackCount)
- stackDNS.sort()
- var outputFileSize = 0
- if output == "":
- for res, count in stackDNS:
- var commaDelimitedStr = $count & "," & res
- commaDelimitedStr = replace(commaDelimitedStr, ",", " | ")
- echo commaDelimitedStr
- # Save to CSV file
- else:
- let outputFile = open(output, fmWrite)
- writeLine(outputFile, "Count,DNS query and response")
-
- # Write results
- for res, count in stackDNS:
- writeLine(outputFile, $count & "," & res)
- outputFileSize = getFileSize(outputFile)
- close(outputFile)
-
- echo ""
- echo "Saved file: " & output & " (" & formatFileSize(outputFileSize) & ")"
+ outputResult(output, stackDNS, stackCount)
let endTime = epochTime()
let elapsedTime2 = int(endTime - startTime)
diff --git a/src/takajopkg/stackProcesses.nim b/src/takajopkg/stackProcesses.nim
index df697577..604b822d 100644
--- a/src/takajopkg/stackProcesses.nim
+++ b/src/takajopkg/stackProcesses.nim
@@ -27,7 +27,6 @@ proc stackProcesses(ignoreSysmon: bool = false, ignoreSecurity: bool = false, ou
bar: SuruBar = initSuruBar()
stackProcesses = initCountTable[string]()
stackCount = initTable[string, CountTable[string]]()
- uniqueProcesses = 0
bar[0].total = totalLines
bar.setup()
@@ -46,34 +45,7 @@ proc stackProcesses(ignoreSysmon: bool = false, ignoreSecurity: bool = false, ou
stackRuleCount(jsonLine, stackCount)
bar.finish()
echo ""
-
- if stackProcesses.len == 0:
- echo "No results where found."
- else:
- # Print results to screen
- printAlertCount(stackCount)
- stackProcesses.sort()
- var outputFileSize = 0
- if output == "":
- for process, count in stackProcesses:
- inc uniqueProcesses
- var commaDelimitedStr = $count & "," & process
- commaDelimitedStr = replace(commaDelimitedStr, ",", " | ")
- echo commaDelimitedStr
- # Save to CSV file
- else:
- let outputFile = open(output, fmWrite)
- writeLine(outputFile, "Count,Processes")
-
- # Write results
- for process, count in stackProcesses:
- inc uniqueProcesses
- writeLine(outputFile, $count & "," & process)
- outputFileSize = getFileSize(outputFile)
- close(outputFile)
-
- echo ""
- echo "Saved file: " & output & " (" & formatFileSize(outputFileSize) & ")"
+ outputResult(output, stackProcesses, stackCount)
let endTime = epochTime()
let elapsedTime2 = int(endTime - startTime)
diff --git a/src/takajopkg/stackServices.nim b/src/takajopkg/stackServices.nim
index 76db3ced..5e12d9a0 100644
--- a/src/takajopkg/stackServices.nim
+++ b/src/takajopkg/stackServices.nim
@@ -47,49 +47,7 @@ proc stackServices(ignoreSysmon: bool = false, ignoreSecurity: bool = false,outp
stackRuleCount(jsonLine, stackCount)
bar.finish()
echo ""
-
- if stackServices.len == 0:
- echo "No results where found."
- else:
- # Print results to screen
- printAlertCount(stackCount)
- stackServices.sort()
- var stackServicesSorted = newOrderedTable[string, int]()
- var stack: seq[string] = newSeq[string]()
- var prevCount = 0
- for service, count in stackServices:
- stack.add(service)
- if prevCount == count:
- continue
- stack.sort()
- for s in stack:
- stackServicesSorted[s] = count
- stack = newSeq[string]()
- prevCount = count
- stack.sort()
- for s in stack:
- stackServicesSorted[s] = prevCount
-
- # Print results to screen
- var outputFileSize = 0
- if output == "":
- for service, count in stackServicesSorted:
- var commaDelimitedStr = $count & "," & service
- commaDelimitedStr = replace(commaDelimitedStr, ",", " | ")
- echo commaDelimitedStr
- # Save to CSV file
- else:
- let outputFile = open(output, fmWrite)
- writeLine(outputFile, "Count,Services")
-
- # Write results
- for service, count in stackServicesSorted:
- writeLine(outputFile, $count & "," & service)
- outputFileSize = getFileSize(outputFile)
- close(outputFile)
-
- echo ""
- echo "Saved file: " & output & " (" & formatFileSize(outputFileSize) & ")"
+ outputResult(output, stackServices, stackCount)
let endTime = epochTime()
let elapsedTime2 = int(endTime - startTime)
diff --git a/src/takajopkg/stackTasks.nim b/src/takajopkg/stackTasks.nim
index c2d0af9a..1713be25 100644
--- a/src/takajopkg/stackTasks.nim
+++ b/src/takajopkg/stackTasks.nim
@@ -61,30 +61,7 @@ proc stackTasks(ignoreSysmon: bool = false, ignoreSecurity: bool = false, output
stackRuleCount(jsonLine, stackCount)
bar.finish()
echo ""
-
- if stackTasks.len == 0:
- echo "No results where found."
- else:
- # Print results to screen
- printAlertCount(stackCount)
- stackTasks.sort()
- var outputFileSize = 0
- if output == "":
- for task, count in stackTasks:
- var commaDelimitedStr = $count & "," & task
- commaDelimitedStr = replace(commaDelimitedStr, ",", " | ")
- echo commaDelimitedStr
- # Save to CSV file
- else:
- let outputFile = open(output, fmWrite)
- writeLine(outputFile, "Count,Tasks")
-
- # Write results
- for task, count in stackTasks:
- writeLine(outputFile, $count & "," & task)
- outputFileSize = getFileSize(outputFile)
- close(outputFile)
- echo "Saved file: " & output & " (" & formatFileSize(outputFileSize) & ")"
+ outputResult(output, stackTasks, stackCount)
let endTime = epochTime()
let elapsedTime2 = int(endTime - startTime)
diff --git a/src/takajopkg/stackUtil.nim b/src/takajopkg/stackUtil.nim
index 642b1f09..66a8438a 100644
--- a/src/takajopkg/stackUtil.nim
+++ b/src/takajopkg/stackUtil.nim
@@ -1,7 +1,9 @@
+import general
import json
import nancy
import takajoTerminal
import std/algorithm
+import std/strutils
import std/tables
proc stackRuleCount*(jsonLine:JsonNode, stackCount: var Table[string, CountTable[string]]) =
@@ -16,7 +18,15 @@ proc alertCmp(x, y: (string, int)): int =
if result == 0:
result = cmp(x[0], y[0])
-proc printAlertCount*(countTable: Table[string, CountTable[string]]) =
+proc sortCountTable(countTable: var CountTable[string]): seq[(string, int)] =
+ countTable.sort()
+ var stack = newSeq[(string, int)]()
+ for ruleTitle, count in countTable:
+ stack.add((ruleTitle, count))
+ stack.sort(alertCmp) # Sort by rule name
+ return stack
+
+proc printAlertCount(countTable: Table[string, CountTable[string]]) =
if countTable.len == 0:
return
var table: TerminalTable
@@ -25,13 +35,35 @@ proc printAlertCount*(countTable: Table[string, CountTable[string]]) =
if level notin countTable:
continue
var result = countTable[level]
- result.sort() # Sort by number of detections
- var stack = newSeq[(string, int)]()
- for ruleTitle, count in result:
- stack.add((ruleTitle, count))
- stack.sort(alertCmp) # Sort by rule name
- for (ruleTitle, count) in stack:
+ var sortedResult = sortCountTable(result)
+ for (ruleTitle, count) in sortedResult:
let color = levelColor(level)
table.add color count, color level, color ruleTitle
table.echoTableSepsWithStyled(seps = boxSeps)
- echo ""
\ No newline at end of file
+ echo ""
+
+proc outputResult*(output:string, stackTarget: var CountTable[string], stackAlert: Table[string, CountTable[string]]) =
+ if stackTarget.len == 0:
+ echo "No results where found."
+ else:
+ # Print results to screen
+ printAlertCount(stackAlert)
+ let sortedStack = sortCountTable(stackTarget)
+ var outputFileSize = 0
+ if output == "":
+ for (key, count) in sortedStack:
+ var commaDelimitedStr = $count & "," & key
+ commaDelimitedStr = replace(commaDelimitedStr, ",", " | ")
+ echo commaDelimitedStr
+ # Save to CSV file
+ else:
+ let outputFile = open(output, fmWrite)
+ writeLine(outputFile, "Count,Processes")
+
+ # Write results
+ for (key, count) in sortedStack:
+ writeLine(outputFile, $count & "," & key)
+ outputFileSize = getFileSize(outputFile)
+ close(outputFile)
+ echo ""
+ echo "Saved file: " & output & " (" & formatFileSize(outputFileSize) & ")"
\ No newline at end of file
From 9050cca989698aee56d9200361ef0c4d31e988ea Mon Sep 17 00:00:00 2001
From: fukusuket <41001169+fukusuket@users.noreply.github.com>
Date: Sat, 17 Feb 2024 22:36:53 +0900
Subject: [PATCH 06/16] refactor: remove duplicated output logic
---
src/takajopkg/general.nim | 38 +++++++++++++++++++++++++++++++-
src/takajopkg/stackCmdlines.nim | 37 +++----------------------------
src/takajopkg/stackDNS.nim | 38 +++-----------------------------
src/takajopkg/stackLogons.nim | 19 ++--------------
src/takajopkg/stackProcesses.nim | 37 +++----------------------------
src/takajopkg/stackServices.nim | 36 +++---------------------------
src/takajopkg/stackTasks.nim | 38 +++-----------------------------
src/takajopkg/stackUtil.nim | 1 +
8 files changed, 55 insertions(+), 189 deletions(-)
diff --git a/src/takajopkg/general.nim b/src/takajopkg/general.nim
index bc0cee62..78c7a9ec 100644
--- a/src/takajopkg/general.nim
+++ b/src/takajopkg/general.nim
@@ -6,7 +6,9 @@ import std/sequtils
import std/strformat
import std/strutils
import std/tables
+import terminal
import times
+import takajoTerminal
from std/streams import newFileStream
@@ -317,7 +319,6 @@ proc extractDomain*(domain: string): string =
proc isLocalIP*(ip: string): bool =
return ip == "127.0.0.1" or ip == "-" or ip == "::1"
-
proc isJsonConvertible*(timeline: string) : bool =
var
file: File
@@ -338,6 +339,41 @@ proc isJsonConvertible*(timeline: string) : bool =
echo "Failed to open '" & timeline & "'. Please specify a valid file path.\p"
return false
+proc outputElasptedTime*(startTime: float) =
+ let endTime = epochTime()
+ let elapsedTime2 = int(endTime - startTime)
+ let hours = elapsedTime2 div 3600
+ let minutes = (elapsedTime2 mod 3600) div 60
+ let seconds = elapsedTime2 mod 60
+ echo ""
+ echo "Elapsed time: ", $hours & " hours, " & $minutes & " minutes, " & $seconds & " seconds"
+ echo ""
+
+proc checkArgs*(quiet: bool = false, timeline: string) =
+ if not quiet:
+ styledEcho(fgGreen, outputLogo())
+
+ if not os.fileExists(timeline):
+ echo "The file '" & timeline & "' does not exist. Please specify a valid file path."
+ quit(1)
+
+ if not isJsonConvertible(timeline):
+ quit(1)
+
+proc countJsonlAndStartMsg*(cmdName:string, msg:string, timeline:string):int =
+ echo "Started the Stack " & cmdName & " command"
+ echo ""
+ echo "This command will stack " & msg & "."
+ echo ""
+
+ echo "Counting total lines. Please wait."
+ echo ""
+ let totalLines = countLinesInTimeline(timeline)
+ echo "Total lines: ", totalLines
+ echo ""
+ echo "Scanning the Hayabusa timeline. Please wait."
+ echo ""
+ return totalLines
type VirusTotalResult* = object
resTable*: TableRef[string, string]
diff --git a/src/takajopkg/stackCmdlines.nim b/src/takajopkg/stackCmdlines.nim
index 5d54cecb..efb49d41 100644
--- a/src/takajopkg/stackCmdlines.nim
+++ b/src/takajopkg/stackCmdlines.nim
@@ -1,28 +1,7 @@
proc stackCmdlines(ignoreSysmon: bool = false, ignoreSecurity: bool = false, output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
- if not quiet:
- styledEcho(fgGreen, outputLogo())
-
- if not os.fileExists(timeline):
- echo "The file '" & timeline & "' does not exist. Please specify a valid file path."
- quit(1)
-
- if not isJsonConvertible(timeline):
- quit(1)
-
- echo "Started the Stack Cmdlines command"
- echo ""
- echo "This command will stack executed command lines."
- echo ""
-
- echo "Counting total lines. Please wait."
- echo ""
- let totalLines = countLinesInTimeline(timeline)
- echo "Total lines: ", totalLines
- echo ""
- echo "Scanning the Hayabusa timeline. Please wait."
- echo ""
-
+ checkArgs(quiet, timeline)
+ let totalLines = countJsonlAndStartMsg("Cmdlines", "executed command lines", timeline)
var
bar: SuruBar = initSuruBar()
stackCmdlines = initCountTable[string]()
@@ -31,7 +10,6 @@ proc stackCmdlines(ignoreSysmon: bool = false, ignoreSecurity: bool = false, out
bar[0].total = totalLines
bar.setup()
-
# Loop through JSON lines
for line in lines(timeline):
inc bar
@@ -45,14 +23,5 @@ proc stackCmdlines(ignoreSysmon: bool = false, ignoreSecurity: bool = false, out
stackCmdlines.inc(command)
stackRuleCount(jsonLine, stackCount)
bar.finish()
- echo ""
outputResult(output, stackCmdlines, stackCount)
-
- let endTime = epochTime()
- let elapsedTime2 = int(endTime - startTime)
- let hours = elapsedTime2 div 3600
- let minutes = (elapsedTime2 mod 3600) div 60
- let seconds = elapsedTime2 mod 60
- echo ""
- echo "Elapsed time: ", $hours & " hours, " & $minutes & " minutes, " & $seconds & " seconds"
- echo ""
\ No newline at end of file
+ outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackDNS.nim b/src/takajopkg/stackDNS.nim
index 2e512610..5331cb8f 100644
--- a/src/takajopkg/stackDNS.nim
+++ b/src/takajopkg/stackDNS.nim
@@ -1,28 +1,7 @@
proc stackDNS(output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
- if not quiet:
- styledEcho(fgGreen, outputLogo())
-
- if not os.fileExists(timeline):
- echo "The file '" & timeline & "' does not exist. Please specify a valid file path."
- quit(1)
-
- if not isJsonConvertible(timeline):
- quit(1)
-
- echo "Started the Stack DNS command"
- echo ""
- echo "This command will stack DNS queries and responses."
- echo ""
-
- echo "Counting total lines. Please wait."
- echo ""
- let totalLines = countLinesInTimeline(timeline)
- echo "Total lines: ", totalLines
- echo ""
- echo "Scanning the Hayabusa timeline. Please wait."
- echo ""
-
+ checkArgs(quiet, timeline)
+ let totalLines = countJsonlAndStartMsg("DNS", "DNS queries and responses", timeline)
var
bar: SuruBar = initSuruBar()
stackDNS = initCountTable[string]()
@@ -30,7 +9,6 @@ proc stackDNS(output: string = "", quiet: bool = false, timeline: string) =
bar[0].total = totalLines
bar.setup()
-
# Loop through JSON lines
for line in lines(timeline):
inc bar
@@ -47,15 +25,5 @@ proc stackDNS(output: string = "", quiet: bool = false, timeline: string) =
stackRuleCount(jsonLine, stackCount)
bar.finish()
- echo ""
-
outputResult(output, stackDNS, stackCount)
-
- let endTime = epochTime()
- let elapsedTime2 = int(endTime - startTime)
- let hours = elapsedTime2 div 3600
- let minutes = (elapsedTime2 mod 3600) div 60
- let seconds = elapsedTime2 mod 60
- echo ""
- echo "Elapsed time: ", $hours & " hours, " & $minutes & " minutes, " & $seconds & " seconds"
- echo ""
\ No newline at end of file
+ outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackLogons.nim b/src/takajopkg/stackLogons.nim
index e809540e..7b151553 100644
--- a/src/takajopkg/stackLogons.nim
+++ b/src/takajopkg/stackLogons.nim
@@ -4,15 +4,7 @@
proc stackLogons(localSrcIpAddresses = false, output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
- if not quiet:
- styledEcho(fgGreen, outputLogo())
-
- if not os.fileExists(timeline):
- echo "The file '" & timeline & "' does not exist. Please specify a valid file path."
- quit(1)
-
- if not isJsonConvertible(timeline):
- quit(1)
+ checkArgs(quiet, timeline)
echo "Started the Stack Logons command"
echo ""
@@ -105,11 +97,4 @@ proc stackLogons(localSrcIpAddresses = false, output: string = "", quiet: bool =
echo "Saved file: " & output & " (" & formatFileSize(outputFileSize) & ")"
echo ""
- let endTime = epochTime()
- let elapsedTime2 = int(endTime - startTime)
- let hours = elapsedTime2 div 3600
- let minutes = (elapsedTime2 mod 3600) div 60
- let seconds = elapsedTime2 mod 60
- echo ""
- echo "Elapsed time: ", $hours & " hours, " & $minutes & " minutes, " & $seconds & " seconds"
- echo ""
\ No newline at end of file
+ outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackProcesses.nim b/src/takajopkg/stackProcesses.nim
index 604b822d..ec3262b1 100644
--- a/src/takajopkg/stackProcesses.nim
+++ b/src/takajopkg/stackProcesses.nim
@@ -1,28 +1,7 @@
proc stackProcesses(ignoreSysmon: bool = false, ignoreSecurity: bool = false, output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
- if not quiet:
- styledEcho(fgGreen, outputLogo())
-
- if not os.fileExists(timeline):
- echo "The file '" & timeline & "' does not exist. Please specify a valid file path."
- quit(1)
-
- if not isJsonConvertible(timeline):
- quit(1)
-
- echo "Started the Stack Processes command"
- echo ""
- echo "This command will stack executed processes."
- echo ""
-
- echo "Counting total lines. Please wait."
- echo ""
- let totalLines = countLinesInTimeline(timeline)
- echo "Total lines: ", totalLines
- echo ""
- echo "Scanning the Hayabusa timeline. Please wait."
- echo ""
-
+ checkArgs(quiet, timeline)
+ let totalLines = countJsonlAndStartMsg("Processes", "executed processes", timeline)
var
bar: SuruBar = initSuruBar()
stackProcesses = initCountTable[string]()
@@ -30,7 +9,6 @@ proc stackProcesses(ignoreSysmon: bool = false, ignoreSecurity: bool = false, ou
bar[0].total = totalLines
bar.setup()
-
# Loop through JSON lines
for line in lines(timeline):
inc bar
@@ -44,14 +22,5 @@ proc stackProcesses(ignoreSysmon: bool = false, ignoreSecurity: bool = false, ou
stackProcesses.inc(process)
stackRuleCount(jsonLine, stackCount)
bar.finish()
- echo ""
outputResult(output, stackProcesses, stackCount)
-
- let endTime = epochTime()
- let elapsedTime2 = int(endTime - startTime)
- let hours = elapsedTime2 div 3600
- let minutes = (elapsedTime2 mod 3600) div 60
- let seconds = elapsedTime2 mod 60
- echo ""
- echo "Elapsed time: ", $hours & " hours, " & $minutes & " minutes, " & $seconds & " seconds"
- echo ""
\ No newline at end of file
+ outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackServices.nim b/src/takajopkg/stackServices.nim
index 5e12d9a0..bed5a6d7 100644
--- a/src/takajopkg/stackServices.nim
+++ b/src/takajopkg/stackServices.nim
@@ -1,27 +1,7 @@
proc stackServices(ignoreSysmon: bool = false, ignoreSecurity: bool = false,output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
- if not quiet:
- styledEcho(fgGreen, outputLogo())
-
- if not os.fileExists(timeline):
- echo "The file '" & timeline & "' does not exist. Please specify a valid file path."
- quit(1)
-
- if not isJsonConvertible(timeline):
- quit(1)
-
- echo "Started the Stack Services command"
- echo ""
- echo "This command will stack service names and paths."
- echo ""
-
- echo "Counting total lines. Please wait."
- echo ""
- let totalLines = countLinesInTimeline(timeline)
- echo "Total lines: ", totalLines
- echo ""
- echo "Scanning the Hayabusa timeline. Please wait."
- echo ""
+ checkArgs(quiet, timeline)
+ let totalLines = countJsonlAndStartMsg("Services", "service names and paths", timeline)
var
bar: SuruBar = initSuruBar()
@@ -30,7 +10,6 @@ proc stackServices(ignoreSysmon: bool = false, ignoreSecurity: bool = false,outp
bar[0].total = totalLines
bar.setup()
-
# Loop through JSON lines
for line in lines(timeline):
inc bar
@@ -46,14 +25,5 @@ proc stackServices(ignoreSysmon: bool = false, ignoreSecurity: bool = false,outp
stackServices.inc(res)
stackRuleCount(jsonLine, stackCount)
bar.finish()
- echo ""
outputResult(output, stackServices, stackCount)
-
- let endTime = epochTime()
- let elapsedTime2 = int(endTime - startTime)
- let hours = elapsedTime2 div 3600
- let minutes = (elapsedTime2 mod 3600) div 60
- let seconds = elapsedTime2 mod 60
- echo ""
- echo "Elapsed time: ", $hours & " hours, " & $minutes & " minutes, " & $seconds & " seconds"
- echo ""
\ No newline at end of file
+ outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackTasks.nim b/src/takajopkg/stackTasks.nim
index 1713be25..268756d8 100644
--- a/src/takajopkg/stackTasks.nim
+++ b/src/takajopkg/stackTasks.nim
@@ -3,29 +3,8 @@ proc decodeEntity(txt: string): string =
proc stackTasks(ignoreSysmon: bool = false, ignoreSecurity: bool = false, output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
- if not quiet:
- styledEcho(fgGreen, outputLogo())
-
- if not os.fileExists(timeline):
- echo "The file '" & timeline & "' does not exist. Please specify a valid file path."
- quit(1)
-
- if not isJsonConvertible(timeline):
- quit(1)
-
- echo "Started the Stack Tasks command"
- echo ""
- echo "This command will stack new scheduled tasks."
- echo ""
-
- echo "Counting total lines. Please wait."
- echo ""
- let totalLines = countLinesInTimeline(timeline)
- echo "Total lines: ", totalLines
- echo ""
- echo "Scanning the Hayabusa timeline. Please wait."
- echo ""
-
+ checkArgs(quiet, timeline)
+ let totalLines = countJsonlAndStartMsg("Tasks", "new scheduled tasks", timeline)
var
bar: SuruBar = initSuruBar()
stackTasks = initCountTable[string]()
@@ -33,8 +12,6 @@ proc stackTasks(ignoreSysmon: bool = false, ignoreSecurity: bool = false, output
bar[0].total = totalLines
bar.setup()
-
-
# Loop through JSON lines
for line in lines(timeline):
inc bar
@@ -60,14 +37,5 @@ proc stackTasks(ignoreSysmon: bool = false, ignoreSecurity: bool = false, output
stackTasks.inc(result)
stackRuleCount(jsonLine, stackCount)
bar.finish()
- echo ""
outputResult(output, stackTasks, stackCount)
-
- let endTime = epochTime()
- let elapsedTime2 = int(endTime - startTime)
- let hours = elapsedTime2 div 3600
- let minutes = (elapsedTime2 mod 3600) div 60
- let seconds = elapsedTime2 mod 60
- echo ""
- echo "Elapsed time: ", $hours & " hours, " & $minutes & " minutes, " & $seconds & " seconds"
- echo ""
\ No newline at end of file
+ outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackUtil.nim b/src/takajopkg/stackUtil.nim
index 66a8438a..66cdd638 100644
--- a/src/takajopkg/stackUtil.nim
+++ b/src/takajopkg/stackUtil.nim
@@ -43,6 +43,7 @@ proc printAlertCount(countTable: Table[string, CountTable[string]]) =
echo ""
proc outputResult*(output:string, stackTarget: var CountTable[string], stackAlert: Table[string, CountTable[string]]) =
+ echo ""
if stackTarget.len == 0:
echo "No results where found."
else:
From e0b7cf3d5ab33c58d15b747c332167b3ae126a8c Mon Sep 17 00:00:00 2001
From: fukusuket <41001169+fukusuket@users.noreply.github.com>
Date: Sun, 18 Feb 2024 21:57:09 +0900
Subject: [PATCH 07/16] chg: add columns to stack command output
---
src/takajo.nim | 5 ++
src/takajopkg/general.nim | 8 +-
src/takajopkg/stackCmdlines.nim | 15 ++--
src/takajopkg/stackDNS.nim | 15 ++--
src/takajopkg/stackLogons.nim | 2 +-
src/takajopkg/stackProcesses.nim | 15 ++--
src/takajopkg/stackServices.nim | 14 ++--
src/takajopkg/stackTasks.nim | 14 ++--
src/takajopkg/stackUtil.nim | 124 ++++++++++++++++++-------------
src/takajopkg/takajoTerminal.nim | 8 +-
10 files changed, 119 insertions(+), 101 deletions(-)
diff --git a/src/takajo.nim b/src/takajo.nim
index c045ca1e..39c69e2b 100644
--- a/src/takajo.nim
+++ b/src/takajo.nim
@@ -184,6 +184,7 @@ when isMainModule:
stackCmdlines, cmdName = "stack-cmdlines",
doc = "stack executed command lines",
help = {
+ "level": "specify the minimum alert level",
"ignoreSysmon": "exclude Sysmon 1 events",
"ignoreSecurity": "exclude Security 4688 events",
"output": "save results to a CSV file",
@@ -199,6 +200,7 @@ when isMainModule:
stackDNS, cmdName = "stack-dns",
doc = "stack DNS queries and responses",
help = {
+ "level": "specify the minimum alert level",
"output": "save results to a CSV file",
"quiet": "do not display the launch banner",
"timeline": "Hayabusa JSONL timeline (profile: any besides all-field-info*)",
@@ -218,6 +220,7 @@ when isMainModule:
stackProcesses, cmdName = "stack-processes",
doc = "stack executed processes",
help = {
+ "level": "specify the minimum alert level",
"ignoreSysmon": "exclude Sysmon 1 events",
"ignoreSecurity": "exclude Security 4688 events",
"output": "save results to a CSV file",
@@ -233,6 +236,7 @@ when isMainModule:
stackServices, cmdName = "stack-services",
doc = "stack service names and paths",
help = {
+ "level": "specify the minimum alert level",
"output": "save results to a CSV file",
"quiet": "do not display the launch banner",
"timeline": "Hayabusa JSONL timeline (profile: any besides all-field-info*)",
@@ -246,6 +250,7 @@ when isMainModule:
stackTasks, cmdName = "stack-tasks",
doc = "stack new scheduled tasks",
help = {
+ "level": "specify the minimum alert level",
"output": "save results to a CSV file",
"quiet": "do not display the launch banner",
"timeline": "Hayabusa JSONL timeline (profile: any besides all-field-info*)",
diff --git a/src/takajopkg/general.nim b/src/takajopkg/general.nim
index 78c7a9ec..ff9e476a 100644
--- a/src/takajopkg/general.nim
+++ b/src/takajopkg/general.nim
@@ -349,7 +349,7 @@ proc outputElasptedTime*(startTime: float) =
echo "Elapsed time: ", $hours & " hours, " & $minutes & " minutes, " & $seconds & " seconds"
echo ""
-proc checkArgs*(quiet: bool = false, timeline: string) =
+proc checkArgs*(quiet: bool = false, timeline: string, level:string) =
if not quiet:
styledEcho(fgGreen, outputLogo())
@@ -360,6 +360,12 @@ proc checkArgs*(quiet: bool = false, timeline: string) =
if not isJsonConvertible(timeline):
quit(1)
+ if level != "critical" and level != "high" and level != "medium" and level != "low" and level != "informational":
+ echo "You must specify a minimum level of critical, high, medium, low or informational. (default: low)"
+ echo ""
+ quit(1)
+
+
proc countJsonlAndStartMsg*(cmdName:string, msg:string, timeline:string):int =
echo "Started the Stack " & cmdName & " command"
echo ""
diff --git a/src/takajopkg/stackCmdlines.nim b/src/takajopkg/stackCmdlines.nim
index efb49d41..aa8b111e 100644
--- a/src/takajopkg/stackCmdlines.nim
+++ b/src/takajopkg/stackCmdlines.nim
@@ -1,12 +1,10 @@
-proc stackCmdlines(ignoreSysmon: bool = false, ignoreSecurity: bool = false, output: string = "", quiet: bool = false, timeline: string) =
+proc stackCmdlines(level: string = "low", ignoreSysmon: bool = false, ignoreSecurity: bool = false, output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
- checkArgs(quiet, timeline)
+ checkArgs(quiet, timeline, level)
let totalLines = countJsonlAndStartMsg("Cmdlines", "executed command lines", timeline)
var
bar: SuruBar = initSuruBar()
- stackCmdlines = initCountTable[string]()
- stackCount = initTable[string, CountTable[string]]()
- uniqueCmd = 0
+ stack = initTable[string, StackRecord]()
bar[0].total = totalLines
bar.setup()
@@ -19,9 +17,8 @@ proc stackCmdlines(ignoreSysmon: bool = false, ignoreSecurity: bool = false, out
let channel = jsonLine["Channel"].getStr("N/A")
if (eventId == 1 and not ignoreSysmon and channel == "Sysmon") or
(eventId == 4688 and not ignoreSecurity and channel == "Sec"):
- let command = jsonLine["Details"]["Cmdline"].getStr("N/A")
- stackCmdlines.inc(command)
- stackRuleCount(jsonLine, stackCount)
+ let stackKey = jsonLine["Details"]["Cmdline"].getStr("N/A")
+ stackResult(stackKey, level, jsonLine, stack)
bar.finish()
- outputResult(output, stackCmdlines, stackCount)
+ outputResult(output, "Cmdline", stack)
outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackDNS.nim b/src/takajopkg/stackDNS.nim
index 5331cb8f..c53a9348 100644
--- a/src/takajopkg/stackDNS.nim
+++ b/src/takajopkg/stackDNS.nim
@@ -1,11 +1,10 @@
-proc stackDNS(output: string = "", quiet: bool = false, timeline: string) =
+proc stackDNS(level: string = "informational", output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
- checkArgs(quiet, timeline)
+ checkArgs(quiet, timeline, level)
let totalLines = countJsonlAndStartMsg("DNS", "DNS queries and responses", timeline)
var
bar: SuruBar = initSuruBar()
- stackDNS = initCountTable[string]()
- stackCount = initTable[string, CountTable[string]]()
+ stack = initTable[string, StackRecord]()
bar[0].total = totalLines
bar.setup()
@@ -20,10 +19,8 @@ proc stackDNS(output: string = "", quiet: bool = false, timeline: string) =
let prog = jsonLine["Details"]["Proc"].getStr("N/A")
let query = jsonLine["Details"]["Query"].getStr("N/A")
let res = jsonLine["Details"]["Result"].getStr("N/A")
- let result = prog & " -> " & query & " -> " & res
- stackDNS.inc(result)
- stackRuleCount(jsonLine, stackCount)
-
+ let stackKey = prog & " -> " & query & " -> " & res
+ stackResult(stackKey, level, jsonLine, stack)
bar.finish()
- outputResult(output, stackDNS, stackCount)
+ outputResult(output, "DNS", stack)
outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackLogons.nim b/src/takajopkg/stackLogons.nim
index 7b151553..e97017a3 100644
--- a/src/takajopkg/stackLogons.nim
+++ b/src/takajopkg/stackLogons.nim
@@ -4,7 +4,7 @@
proc stackLogons(localSrcIpAddresses = false, output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
- checkArgs(quiet, timeline)
+ checkArgs(quiet, timeline, "informational")
echo "Started the Stack Logons command"
echo ""
diff --git a/src/takajopkg/stackProcesses.nim b/src/takajopkg/stackProcesses.nim
index ec3262b1..eaed40c4 100644
--- a/src/takajopkg/stackProcesses.nim
+++ b/src/takajopkg/stackProcesses.nim
@@ -1,12 +1,10 @@
-proc stackProcesses(ignoreSysmon: bool = false, ignoreSecurity: bool = false, output: string = "", quiet: bool = false, timeline: string) =
+proc stackProcesses(level: string = "low", ignoreSysmon: bool = false, ignoreSecurity: bool = false, output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
- checkArgs(quiet, timeline)
+ checkArgs(quiet, timeline, level)
let totalLines = countJsonlAndStartMsg("Processes", "executed processes", timeline)
var
bar: SuruBar = initSuruBar()
- stackProcesses = initCountTable[string]()
- stackCount = initTable[string, CountTable[string]]()
-
+ stack = initTable[string, StackRecord]()
bar[0].total = totalLines
bar.setup()
# Loop through JSON lines
@@ -18,9 +16,8 @@ proc stackProcesses(ignoreSysmon: bool = false, ignoreSecurity: bool = false, ou
let channel = jsonLine["Channel"].getStr("N/A")
if (eventId == 1 and not ignoreSysmon and channel == "Sysmon") or
(eventId == 4688 and not ignoreSecurity and channel == "Sec"):
- let process = jsonLine["Details"]["Proc"].getStr("N/A")
- stackProcesses.inc(process)
- stackRuleCount(jsonLine, stackCount)
+ let stackKey = jsonLine["Details"]["Proc"].getStr("N/A")
+ stackResult(stackKey, level, jsonLine, stack)
bar.finish()
- outputResult(output, stackProcesses, stackCount)
+ outputResult(output, "Process", stack)
outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackServices.nim b/src/takajopkg/stackServices.nim
index bed5a6d7..df093336 100644
--- a/src/takajopkg/stackServices.nim
+++ b/src/takajopkg/stackServices.nim
@@ -1,12 +1,11 @@
-proc stackServices(ignoreSysmon: bool = false, ignoreSecurity: bool = false,output: string = "", quiet: bool = false, timeline: string) =
+proc stackServices(level: string = "informational", ignoreSysmon: bool = false, ignoreSecurity: bool = false,output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
- checkArgs(quiet, timeline)
+ checkArgs(quiet, timeline, level)
let totalLines = countJsonlAndStartMsg("Services", "service names and paths", timeline)
var
bar: SuruBar = initSuruBar()
- stackServices = initCountTable[string]()
- stackCount = initTable[string, CountTable[string]]()
+ stack = initTable[string, StackRecord]()
bar[0].total = totalLines
bar.setup()
@@ -21,9 +20,8 @@ proc stackServices(ignoreSysmon: bool = false, ignoreSecurity: bool = false,outp
(eventId == 4697 and not ignoreSecurity and channel == "Sec"):
let svc = jsonLine["Details"]["Svc"].getStr("N/A")
let path = jsonLine["Details"]["Path"].getStr("N/A")
- let res = svc & " -> " & path
- stackServices.inc(res)
- stackRuleCount(jsonLine, stackCount)
+ let stackKey = svc & " -> " & path
+ stackResult(stackKey, level, jsonLine, stack)
bar.finish()
- outputResult(output, stackServices, stackCount)
+ outputResult(output, "Service", stack)
outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackTasks.nim b/src/takajopkg/stackTasks.nim
index 268756d8..c87520e2 100644
--- a/src/takajopkg/stackTasks.nim
+++ b/src/takajopkg/stackTasks.nim
@@ -1,14 +1,13 @@
proc decodeEntity(txt: string): string =
return txt.replace("&","&").replace("<","<").replace(">",">").replace(""","\"").replace("'","'")
-proc stackTasks(ignoreSysmon: bool = false, ignoreSecurity: bool = false, output: string = "", quiet: bool = false, timeline: string) =
+proc stackTasks(level: string = "informational", ignoreSysmon: bool = false, ignoreSecurity: bool = false, output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
- checkArgs(quiet, timeline)
+ checkArgs(quiet, timeline, level)
let totalLines = countJsonlAndStartMsg("Tasks", "new scheduled tasks", timeline)
var
bar: SuruBar = initSuruBar()
- stackTasks = initCountTable[string]()
- stackCount = initTable[string, CountTable[string]]()
+ stack = initTable[string, StackRecord]()
bar[0].total = totalLines
bar.setup()
@@ -33,9 +32,8 @@ proc stackTasks(ignoreSysmon: bool = false, ignoreSecurity: bool = false, output
if len(arguments) > 0:
commandAndArgs = commandAndArgs & " " & $arguments[0]
commandAndArgs = commandAndArgs.replace("", "").replace("","")
- let result = user & " -> " & name & " -> " & decodeEntity(commandAndArgs)
- stackTasks.inc(result)
- stackRuleCount(jsonLine, stackCount)
+ let stackKey = user & " -> " & name & " -> " & decodeEntity(commandAndArgs)
+ stackResult(stackKey, level, jsonLine, stack)
bar.finish()
- outputResult(output, stackTasks, stackCount)
+ outputResult(output, "Task", stack)
outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackUtil.nim b/src/takajopkg/stackUtil.nim
index 66cdd638..6f583871 100644
--- a/src/takajopkg/stackUtil.nim
+++ b/src/takajopkg/stackUtil.nim
@@ -3,68 +3,88 @@ import json
import nancy
import takajoTerminal
import std/algorithm
+import std/enumerate
+import std/sets
+import std/sequtils
import std/strutils
import std/tables
-proc stackRuleCount*(jsonLine:JsonNode, stackCount: var Table[string, CountTable[string]]) =
- let level = jsonLine["Level"].getStr("N/A")
- if level != "info":
- if level notin stackCount:
- stackCount[level] = initCountTable[string]()
- stackCount[level].inc(jsonLine["RuleTitle"].getStr("N/A"))
+let LEVEL_ORDER = {"crit": 5, "high": 4, "med": 3, "low": 2, "info": 1}.toTable
+
+type StackRecord* = object
+ key*: string
+ count* = 0
+ levelsOrder* = 0
+ levels* = initHashSet[string]()
+ ruleTitles* = initHashSet[string]()
+
+proc calcLevelOrder(x: StackRecord): int =
+ result = 0
+ if "crit" in x.levels:
+ result = LEVEL_ORDER["crit"]
+ elif "high" in x.levels:
+ result = LEVEL_ORDER["high"]
+ elif "med" in x.levels:
+ result = LEVEL_ORDER["med"]
+ elif "low" in x.levels:
+ result = LEVEL_ORDER["low"]
+ elif "info" in x.levels:
+ result = LEVEL_ORDER["info"]
-proc alertCmp(x, y: (string, int)): int =
- result = cmp(x[1], y[1]) * -1
+proc levelCmp(x, y: string): int =
+ cmp(LEVEL_ORDER[x], LEVEL_ORDER[y]) * -1
+
+proc recordCmp(x, y: StackRecord): int =
+ result = cmp(x.levelsOrder, y.levelsOrder) * -1
+ if result == 0:
+ result = cmp(x.count, y.count) * - 1
if result == 0:
- result = cmp(x[0], y[0])
+ result = cmp(x.key, y.key)
-proc sortCountTable(countTable: var CountTable[string]): seq[(string, int)] =
- countTable.sort()
- var stack = newSeq[(string, int)]()
- for ruleTitle, count in countTable:
- stack.add((ruleTitle, count))
- stack.sort(alertCmp) # Sort by rule name
- return stack
+proc buildCSVRecord(x: StackRecord): array[4, string] =
+ let levelsStr = toSeq(x.levels).sorted(levelCmp).join(",")
+ let ruleTitlesStr = toSeq(x.ruleTitles).sorted.join(",")
+ return [intToStr(x.count), x.key, levelsStr, ruleTitlesStr]
-proc printAlertCount(countTable: Table[string, CountTable[string]]) =
- if countTable.len == 0:
+proc stackResult*(key:string, minLevel:string, jsonLine:JsonNode, stack: var Table[string, StackRecord]) =
+ let level = jsonLine["Level"].getStr("N/A")
+ if not isMinLevel(level, minLevel):
return
- var table: TerminalTable
- table.add ["Count", "Level", "RuleTitle"]
- for level in ["crit", "high", "med", "low"]:
- if level notin countTable:
- continue
- var result = countTable[level]
- var sortedResult = sortCountTable(result)
- for (ruleTitle, count) in sortedResult:
- let color = levelColor(level)
- table.add color count, color level, color ruleTitle
- table.echoTableSepsWithStyled(seps = boxSeps)
- echo ""
+ var val: StackRecord
+ if key notin stack:
+ val = StackRecord(key: key)
+ else:
+ val = stack[key]
+ val.count += 1
+ val.levels.incl(level)
+ val.levelsOrder = val.calcLevelOrder()
+ val.ruleTitles.incl(jsonLine["RuleTitle"].getStr("N/A"))
+ stack[key] = val
-proc outputResult*(output:string, stackTarget: var CountTable[string], stackAlert: Table[string, CountTable[string]]) =
+proc outputResult*(output:string, culumnName: string, stack: Table[string, StackRecord]) =
echo ""
- if stackTarget.len == 0:
+ if stack.len == 0:
echo "No results where found."
else:
- # Print results to screen
- printAlertCount(stackAlert)
- let sortedStack = sortCountTable(stackTarget)
- var outputFileSize = 0
+ let stackRecords = toSeq(stack.values).sorted(recordCmp).map(buildCSVRecord)
+ let header = ["Count", culumnName, "Levels", "Alerts"]
+ var table: TerminalTable
+ table.add header
+ for row in stackRecords:
+ let color = levelColor(row[2])
+ table.add color row[0], color row[1], color row[2], color row[3]
+ table.echoTableSepsWithStyled(seps = boxSeps)
+ echo ""
if output == "":
- for (key, count) in sortedStack:
- var commaDelimitedStr = $count & "," & key
- commaDelimitedStr = replace(commaDelimitedStr, ",", " | ")
- echo commaDelimitedStr
- # Save to CSV file
- else:
- let outputFile = open(output, fmWrite)
- writeLine(outputFile, "Count,Processes")
-
- # Write results
- for (key, count) in sortedStack:
- writeLine(outputFile, $count & "," & key)
- outputFileSize = getFileSize(outputFile)
- close(outputFile)
- echo ""
- echo "Saved file: " & output & " (" & formatFileSize(outputFileSize) & ")"
\ No newline at end of file
+ return
+ let outputFile = open(output, fmWrite)
+ writeLine(outputFile, header.join(","))
+ for row in stackRecords:
+ for i, col in enumerate(row):
+ if i < row.len - 1:
+ outputFile.write(escapeCsvField(col) & ",")
+ else:
+ outputFile.write(escapeCsvField(col))
+ close(outputFile)
+ echo ""
+ echo "Saved file: " & output & " (" & formatFileSize(getFileSize(outputFile)) & ")"
\ No newline at end of file
diff --git a/src/takajopkg/takajoTerminal.nim b/src/takajopkg/takajoTerminal.nim
index fdff3772..b6e39a6e 100644
--- a/src/takajopkg/takajoTerminal.nim
+++ b/src/takajopkg/takajoTerminal.nim
@@ -56,12 +56,12 @@ proc echoTableSepsWithStyled*(table: TerminalTable, maxSize = terminalWidth(), s
printSeparator(bottom)
proc levelColor*(level:string): proc(ss: varargs[string, `$`]): string =
- if level == "crit":
+ if "crit" in level:
return red
- elif level == "high":
+ elif "high" in level:
return yellow
- elif level == "med":
+ elif "med" in level:
return cyan
- elif level == "low":
+ elif "low" in level:
return green
return white
\ No newline at end of file
From c8409d7b4b63c4acf8afb95b68441e3408f4772f Mon Sep 17 00:00:00 2001
From: fukusuket <41001169+fukusuket@users.noreply.github.com>
Date: Sun, 18 Feb 2024 22:03:11 +0900
Subject: [PATCH 08/16] ui: add level default value description
---
src/takajo.nim | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/src/takajo.nim b/src/takajo.nim
index 39c69e2b..2d33737e 100644
--- a/src/takajo.nim
+++ b/src/takajo.nim
@@ -57,12 +57,12 @@ when isMainModule:
const example_list_unused_rules = " list-unused-rules -t ../hayabusa/timeline.csv -r ../hayabusa/rules\p"
const example_split_csv_timeline = " split-csv-timeline -t ../hayabusa/timeline.csv [--makeMultiline] -o case-1-csv\p"
const example_split_json_timeline = " split-json-timeline -t ../hayabusa/timeline.jsonl -o case-1-json\p"
- const example_stack_cmdlines = " stack-cmdlines -t ../hayabusa/timeline.jsonl -o cmdlines.csv\p"
- const example_stack_dns = " stack-dns -t ../hayabusa/timeline.jsonl -o dns.csv\p"
+ const example_stack_cmdlines = " stack-cmdlines -t ../hayabusa/timeline.jsonl [--level low] -o cmdlines.csv\p"
+ const example_stack_dns = " stack-dns -t ../hayabusa/timeline.jsonl [--level infomational] -o dns.csv\p"
const example_stack_logons = " stack-logons -t ../hayabusa/timeline.jsonl -o logons.csv\p"
- const example_stack_services = " stack-services -t ../hayabusa/timeline.jsonl -o services.csv\p"
- const example_stack_tasks = " stack-tasks -t ../hayabusa/timeline.jsonl -o tasks.csv\p"
- const example_stack_processes = " stack-processes -t ../hayabusa/timeline.jsonl -o processes.csv\p"
+ const example_stack_services = " stack-services -t ../hayabusa/timeline.jsonl [--level infomational] -o services.csv\p"
+ const example_stack_tasks = " stack-tasks -t ../hayabusa/timeline.jsonl [--level infomational] -o tasks.csv\p"
+ const example_stack_processes = " stack-processes -t ../hayabusa/timeline.jsonl [--level low] -o processes.csv\p"
const example_list_hashes = " list-hashes -t ../hayabusa/case-1.jsonl -o case-1\p"
const example_sysmon_process_tree = " sysmon-process-tree -t ../hayabusa/timeline.jsonl -p [-o process-tree.txt]\p"
const example_timeline_logon = " timeline-logon -t ../hayabusa/timeline.jsonl -o logon-timeline.csv\p"
@@ -92,7 +92,7 @@ when isMainModule:
extractScriptblocks, cmdName = "extract-scriptblocks",
doc = "extract and reassemble PowerShell EID 4104 script block logs",
help = {
- "level": "specify the minimum alert level",
+ "level": "specify the minimum alert level (default: low)",
"output": "output directory (default: scriptblock-logs)",
"quiet": "do not display the launch banner",
"timeline": "Hayabusa JSONL timeline (profile: any)",
@@ -184,7 +184,7 @@ when isMainModule:
stackCmdlines, cmdName = "stack-cmdlines",
doc = "stack executed command lines",
help = {
- "level": "specify the minimum alert level",
+ "level": "specify the minimum alert level (default: low)",
"ignoreSysmon": "exclude Sysmon 1 events",
"ignoreSecurity": "exclude Security 4688 events",
"output": "save results to a CSV file",
@@ -200,7 +200,7 @@ when isMainModule:
stackDNS, cmdName = "stack-dns",
doc = "stack DNS queries and responses",
help = {
- "level": "specify the minimum alert level",
+ "level": "specify the minimum alert level (default: informational)",
"output": "save results to a CSV file",
"quiet": "do not display the launch banner",
"timeline": "Hayabusa JSONL timeline (profile: any besides all-field-info*)",
@@ -220,7 +220,7 @@ when isMainModule:
stackProcesses, cmdName = "stack-processes",
doc = "stack executed processes",
help = {
- "level": "specify the minimum alert level",
+ "level": "specify the minimum alert level (default: low)",
"ignoreSysmon": "exclude Sysmon 1 events",
"ignoreSecurity": "exclude Security 4688 events",
"output": "save results to a CSV file",
@@ -236,7 +236,7 @@ when isMainModule:
stackServices, cmdName = "stack-services",
doc = "stack service names and paths",
help = {
- "level": "specify the minimum alert level",
+ "level": "specify the minimum alert level (default: informational)",
"output": "save results to a CSV file",
"quiet": "do not display the launch banner",
"timeline": "Hayabusa JSONL timeline (profile: any besides all-field-info*)",
@@ -250,7 +250,7 @@ when isMainModule:
stackTasks, cmdName = "stack-tasks",
doc = "stack new scheduled tasks",
help = {
- "level": "specify the minimum alert level",
+ "level": "specify the minimum alert level (default: informational)",
"output": "save results to a CSV file",
"quiet": "do not display the launch banner",
"timeline": "Hayabusa JSONL timeline (profile: any besides all-field-info*)",
From 120a239ba2d28b10f358c89c672becf6605f5a07 Mon Sep 17 00:00:00 2001
From: fukusuket <41001169+fukusuket@users.noreply.github.com>
Date: Sun, 18 Feb 2024 22:17:43 +0900
Subject: [PATCH 09/16] fix: csv output error
---
src/takajopkg/stackUtil.nim | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/takajopkg/stackUtil.nim b/src/takajopkg/stackUtil.nim
index 6f583871..c34c8fcf 100644
--- a/src/takajopkg/stackUtil.nim
+++ b/src/takajopkg/stackUtil.nim
@@ -84,7 +84,8 @@ proc outputResult*(output:string, culumnName: string, stack: Table[string, Stack
if i < row.len - 1:
outputFile.write(escapeCsvField(col) & ",")
else:
- outputFile.write(escapeCsvField(col))
+ outputFile.write(escapeCsvField(col) & "\p")
+ let outputFileSize = getFileSize(outputFile)
close(outputFile)
echo ""
- echo "Saved file: " & output & " (" & formatFileSize(getFileSize(outputFile)) & ")"
\ No newline at end of file
+ echo "Saved file: " & output & " (" & formatFileSize(outputFileSize) & ")"
\ No newline at end of file
From ed3fd5118168d7c18e9800a6f351c9371c506217 Mon Sep 17 00:00:00 2001
From: fukusuket <41001169+fukusuket@users.noreply.github.com>
Date: Sun, 18 Feb 2024 22:51:39 +0900
Subject: [PATCH 10/16] test: add test case for --level option
---
.github/workflows/integration-test.yml | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml
index feab1e4a..9a62837f 100644
--- a/.github/workflows/integration-test.yml
+++ b/.github/workflows/integration-test.yml
@@ -59,6 +59,9 @@ jobs:
- name: run extract-scriptblocks(-o)
run: cd takajo && ./takajo extract-scriptblocks -t timeline.jsonl -o scriptblocks
+ - name: run extract-scriptblocks(-l)
+ run: cd takajo && ./takajo extract-scriptblocks -t timeline.jsonl -o scriptblocks -l informational
+
- name: run list-domain(-o)
run: cd takajo && ./takajo list-domains -t timeline.jsonl -o domains.txt
@@ -95,9 +98,18 @@ jobs:
- name: run stack-cmdlines
run: cd takajo && ./takajo stack-cmdlines -t timeline.jsonl
+ - name: run stack-cmdlines(-o)
+ run: cd takajo && ./takajo stack-cmdlines -t timeline.jsonl -o cmdlines.csv
+
+ - name: run stack-cmdlines(-l)
+ run: cd takajo && ./takajo stack-cmdlines -t timeline.jsonl -o cmdlines.csv -l informational
+
- name: run stack-dns
run: cd takajo && ./takajo stack-dns -t timeline.jsonl
+ - name: run stack-dns(-o)
+ run: cd takajo && ./takajo stack-dns -t timeline.jsonl -o dns.csv
+
- name: run stack-logons
run: cd takajo && ./takajo stack-logons -t timeline.jsonl
@@ -113,6 +125,9 @@ jobs:
- name: run stack-processes(-o)
run: cd takajo && ./takajo stack-processes -t timeline.jsonl -o processes.csv
+ - name: run stack-processes(-l)
+ run: cd takajo && ./takajo stack-processes -t timeline.jsonl -o processes.csv -l informational
+
- name: run stack-services
run: cd takajo && ./takajo stack-services -t timeline.jsonl
From 880ff6abb0901c0078fecbe57e1de6365d091233 Mon Sep 17 00:00:00 2001
From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com>
Date: Mon, 19 Feb 2024 06:26:02 +0800
Subject: [PATCH 11/16] fix stack services sys 7045 collect
---
src/takajo.nim | 2 +-
src/takajopkg/stackServices.nim | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/takajo.nim b/src/takajo.nim
index 2d33737e..43651b3d 100644
--- a/src/takajo.nim
+++ b/src/takajo.nim
@@ -242,7 +242,7 @@ when isMainModule:
"timeline": "Hayabusa JSONL timeline (profile: any besides all-field-info*)",
},
short = {
- "ignoreSysmon": 'y',
+ "ignoreSystem": 'y',
"ignoreSecurity": 'e'
}
],
diff --git a/src/takajopkg/stackServices.nim b/src/takajopkg/stackServices.nim
index df093336..a7b79823 100644
--- a/src/takajopkg/stackServices.nim
+++ b/src/takajopkg/stackServices.nim
@@ -1,4 +1,4 @@
-proc stackServices(level: string = "informational", ignoreSysmon: bool = false, ignoreSecurity: bool = false,output: string = "", quiet: bool = false, timeline: string) =
+proc stackServices(level: string = "informational", ignoreSystem: bool = false, ignoreSecurity: bool = false,output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
checkArgs(quiet, timeline, level)
let totalLines = countJsonlAndStartMsg("Services", "service names and paths", timeline)
@@ -16,7 +16,7 @@ proc stackServices(level: string = "informational", ignoreSysmon: bool = false,
let jsonLine = parseJson(line)
let eventId = jsonLine["EventID"].getInt(0)
let channel = jsonLine["Channel"].getStr("N/A")
- if (eventId == 7040 and not ignoreSysmon and channel == "Sysmon") or
+ if (eventId == 7045 and not ignoreSystem and channel == "Sys") or
(eventId == 4697 and not ignoreSecurity and channel == "Sec"):
let svc = jsonLine["Details"]["Svc"].getStr("N/A")
let path = jsonLine["Details"]["Path"].getStr("N/A")
From 7713ab90ba1a3f878eff51381bc01248bcc6740b Mon Sep 17 00:00:00 2001
From: fukusuket <41001169+fukusuket@users.noreply.github.com>
Date: Mon, 19 Feb 2024 22:58:18 +0900
Subject: [PATCH 12/16] chg: add channel and eid column
---
src/takajopkg/stackUtil.nim | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/src/takajopkg/stackUtil.nim b/src/takajopkg/stackUtil.nim
index c34c8fcf..376efbf2 100644
--- a/src/takajopkg/stackUtil.nim
+++ b/src/takajopkg/stackUtil.nim
@@ -14,6 +14,8 @@ let LEVEL_ORDER = {"crit": 5, "high": 4, "med": 3, "low": 2, "info": 1}.toTable
type StackRecord* = object
key*: string
count* = 0
+ eid* = ""
+ channel* = ""
levelsOrder* = 0
levels* = initHashSet[string]()
ruleTitles* = initHashSet[string]()
@@ -38,13 +40,17 @@ proc recordCmp(x, y: StackRecord): int =
result = cmp(x.levelsOrder, y.levelsOrder) * -1
if result == 0:
result = cmp(x.count, y.count) * - 1
+ if result == 0:
+ result = cmp(x.channel, y.channel)
+ if result == 0:
+ result = cmp(x.eid, y.eid)
if result == 0:
result = cmp(x.key, y.key)
-proc buildCSVRecord(x: StackRecord): array[4, string] =
+proc buildCSVRecord(x: StackRecord): array[6, string] =
let levelsStr = toSeq(x.levels).sorted(levelCmp).join(",")
let ruleTitlesStr = toSeq(x.ruleTitles).sorted.join(",")
- return [intToStr(x.count), x.key, levelsStr, ruleTitlesStr]
+ return [intToStr(x.count), x.channel, x.eid, x.key, levelsStr, ruleTitlesStr]
proc stackResult*(key:string, minLevel:string, jsonLine:JsonNode, stack: var Table[string, StackRecord]) =
let level = jsonLine["Level"].getStr("N/A")
@@ -52,7 +58,7 @@ proc stackResult*(key:string, minLevel:string, jsonLine:JsonNode, stack: var Tab
return
var val: StackRecord
if key notin stack:
- val = StackRecord(key: key)
+ val = StackRecord(key: key, eid: intToStr(jsonLine["EventID"].getInt(0)), channel: jsonLine["Channel"].getStr("N/A"))
else:
val = stack[key]
val.count += 1
@@ -67,12 +73,12 @@ proc outputResult*(output:string, culumnName: string, stack: Table[string, Stack
echo "No results where found."
else:
let stackRecords = toSeq(stack.values).sorted(recordCmp).map(buildCSVRecord)
- let header = ["Count", culumnName, "Levels", "Alerts"]
+ let header = ["Count", "Channel", "EventID", culumnName, "Levels", "Alerts"]
var table: TerminalTable
table.add header
for row in stackRecords:
- let color = levelColor(row[2])
- table.add color row[0], color row[1], color row[2], color row[3]
+ let color = levelColor(row[4])
+ table.add color row[0], color row[1], color row[2], color row[3], color row[4], color row[5]
table.echoTableSepsWithStyled(seps = boxSeps)
echo ""
if output == "":
From f6b0ed6b8f8e637b07d9bc54aa895568f44ebf38 Mon Sep 17 00:00:00 2001
From: fukusuket <41001169+fukusuket@users.noreply.github.com>
Date: Mon, 19 Feb 2024 23:42:03 +0900
Subject: [PATCH 13/16] chg: split columns
---
src/takajopkg/stackCmdlines.nim | 2 +-
src/takajopkg/stackDNS.nim | 4 ++--
src/takajopkg/stackProcesses.nim | 2 +-
src/takajopkg/stackServices.nim | 4 ++--
src/takajopkg/stackTasks.nim | 17 +++++++++--------
src/takajopkg/stackUtil.nim | 20 +++++++++++++-------
6 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/src/takajopkg/stackCmdlines.nim b/src/takajopkg/stackCmdlines.nim
index aa8b111e..33cb8abd 100644
--- a/src/takajopkg/stackCmdlines.nim
+++ b/src/takajopkg/stackCmdlines.nim
@@ -18,7 +18,7 @@ proc stackCmdlines(level: string = "low", ignoreSysmon: bool = false, ignoreSecu
if (eventId == 1 and not ignoreSysmon and channel == "Sysmon") or
(eventId == 4688 and not ignoreSecurity and channel == "Sec"):
let stackKey = jsonLine["Details"]["Cmdline"].getStr("N/A")
- stackResult(stackKey, level, jsonLine, stack)
+ stackResult(stackKey, stack, level, jsonLine)
bar.finish()
outputResult(output, "Cmdline", stack)
outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackDNS.nim b/src/takajopkg/stackDNS.nim
index c53a9348..fd43bcbd 100644
--- a/src/takajopkg/stackDNS.nim
+++ b/src/takajopkg/stackDNS.nim
@@ -20,7 +20,7 @@ proc stackDNS(level: string = "informational", output: string = "", quiet: bool
let query = jsonLine["Details"]["Query"].getStr("N/A")
let res = jsonLine["Details"]["Result"].getStr("N/A")
let stackKey = prog & " -> " & query & " -> " & res
- stackResult(stackKey, level, jsonLine, stack)
+ stackResult(stackKey, stack, level, jsonLine, @[prog, query, res])
bar.finish()
- outputResult(output, "DNS", stack)
+ outputResult(output, "DNS", stack, @["Image", "Query", "Result"])
outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackProcesses.nim b/src/takajopkg/stackProcesses.nim
index eaed40c4..26c2b129 100644
--- a/src/takajopkg/stackProcesses.nim
+++ b/src/takajopkg/stackProcesses.nim
@@ -17,7 +17,7 @@ proc stackProcesses(level: string = "low", ignoreSysmon: bool = false, ignoreSec
if (eventId == 1 and not ignoreSysmon and channel == "Sysmon") or
(eventId == 4688 and not ignoreSecurity and channel == "Sec"):
let stackKey = jsonLine["Details"]["Proc"].getStr("N/A")
- stackResult(stackKey, level, jsonLine, stack)
+ stackResult(stackKey, stack, level, jsonLine)
bar.finish()
outputResult(output, "Process", stack)
outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackServices.nim b/src/takajopkg/stackServices.nim
index a7b79823..87f341a8 100644
--- a/src/takajopkg/stackServices.nim
+++ b/src/takajopkg/stackServices.nim
@@ -21,7 +21,7 @@ proc stackServices(level: string = "informational", ignoreSystem: bool = false,
let svc = jsonLine["Details"]["Svc"].getStr("N/A")
let path = jsonLine["Details"]["Path"].getStr("N/A")
let stackKey = svc & " -> " & path
- stackResult(stackKey, level, jsonLine, stack)
+ stackResult(stackKey, stack, level, jsonLine, @["ServiceName", "Path"])
bar.finish()
- outputResult(output, "Service", stack)
+ outputResult(output, "Service", stack, @["ServiceName", "Path"])
outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackTasks.nim b/src/takajopkg/stackTasks.nim
index c87520e2..209a5310 100644
--- a/src/takajopkg/stackTasks.nim
+++ b/src/takajopkg/stackTasks.nim
@@ -24,16 +24,17 @@ proc stackTasks(level: string = "informational", ignoreSysmon: bool = false, ign
let content = jsonLine["Details"]["Content"].getStr("N/A").replace("\\r\\n", "")
let node = parseXml(content)
let commands = node.findAll("Command")
- var commandAndArgs = ""
+ var command = ""
+ var args = ""
if len(commands) > 0:
- commandAndArgs = $commands[0]
- commandAndArgs = commandAndArgs.replace("", "").replace("","")
+ command = $commands[0]
+ command = command.replace("", "").replace("","")
let arguments = node.findAll("Arguments")
if len(arguments) > 0:
- commandAndArgs = commandAndArgs & " " & $arguments[0]
- commandAndArgs = commandAndArgs.replace("", "").replace("","")
- let stackKey = user & " -> " & name & " -> " & decodeEntity(commandAndArgs)
- stackResult(stackKey, level, jsonLine, stack)
+ args = $arguments[0]
+ args = args.replace("", "").replace("","")
+ let stackKey = user & " -> " & name & " -> " & decodeEntity(command & " " & args)
+ stackResult(stackKey, stack, level, jsonLine, @[name, command, args])
bar.finish()
- outputResult(output, "Task", stack)
+ outputResult(output, "Task", stack, @["TaskName", "Command", "Arguments"])
outputElasptedTime(startTime)
\ No newline at end of file
diff --git a/src/takajopkg/stackUtil.nim b/src/takajopkg/stackUtil.nim
index 376efbf2..7a0b6d3d 100644
--- a/src/takajopkg/stackUtil.nim
+++ b/src/takajopkg/stackUtil.nim
@@ -19,6 +19,7 @@ type StackRecord* = object
levelsOrder* = 0
levels* = initHashSet[string]()
ruleTitles* = initHashSet[string]()
+ otherColumn = newSeq[string]()
proc calcLevelOrder(x: StackRecord): int =
result = 0
@@ -47,12 +48,14 @@ proc recordCmp(x, y: StackRecord): int =
if result == 0:
result = cmp(x.key, y.key)
-proc buildCSVRecord(x: StackRecord): array[6, string] =
+proc buildCSVRecord(x: StackRecord): seq[string] =
let levelsStr = toSeq(x.levels).sorted(levelCmp).join(",")
let ruleTitlesStr = toSeq(x.ruleTitles).sorted.join(",")
- return [intToStr(x.count), x.channel, x.eid, x.key, levelsStr, ruleTitlesStr]
+ if x.otherColumn.len == 0:
+ return @[intToStr(x.count), x.channel, x.eid, x.key, levelsStr, ruleTitlesStr]
+ return concat(@[intToStr(x.count), x.channel, x.eid], x.otherColumn, @[levelsStr, ruleTitlesStr])
-proc stackResult*(key:string, minLevel:string, jsonLine:JsonNode, stack: var Table[string, StackRecord]) =
+proc stackResult*(key:string, stack: var Table[string, StackRecord], minLevel:string, jsonLine:JsonNode, otherColumn:seq[string] = @[]) =
let level = jsonLine["Level"].getStr("N/A")
if not isMinLevel(level, minLevel):
return
@@ -65,20 +68,23 @@ proc stackResult*(key:string, minLevel:string, jsonLine:JsonNode, stack: var Tab
val.levels.incl(level)
val.levelsOrder = val.calcLevelOrder()
val.ruleTitles.incl(jsonLine["RuleTitle"].getStr("N/A"))
+ val.otherColumn = otherColumn
stack[key] = val
-proc outputResult*(output:string, culumnName: string, stack: Table[string, StackRecord]) =
+proc outputResult*(output:string, culumnName: string, stack: Table[string, StackRecord], otherHeader:seq[string] = newSeq[string]()) =
echo ""
if stack.len == 0:
echo "No results where found."
else:
let stackRecords = toSeq(stack.values).sorted(recordCmp).map(buildCSVRecord)
- let header = ["Count", "Channel", "EventID", culumnName, "Levels", "Alerts"]
+ var header = @["Count", "Channel", "EventID", culumnName, "Levels", "Alerts"]
+ if otherHeader.len > 0:
+ header = concat(@["Count", "Channel", "EventID"], otherHeader, @["Levels", "Alerts"])
var table: TerminalTable
table.add header
for row in stackRecords:
- let color = levelColor(row[4])
- table.add color row[0], color row[1], color row[2], color row[3], color row[4], color row[5]
+ let color = levelColor(row[^2])
+ table.add map(row, proc(col: string): string = color col)
table.echoTableSepsWithStyled(seps = boxSeps)
echo ""
if output == "":
From 2c09e7b5cdd522b44de47d481fd7b05cb12614a3 Mon Sep 17 00:00:00 2001
From: fukusuket <41001169+fukusuket@users.noreply.github.com>
Date: Mon, 19 Feb 2024 23:47:12 +0900
Subject: [PATCH 14/16] fix: service output
---
src/takajopkg/stackServices.nim | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/takajopkg/stackServices.nim b/src/takajopkg/stackServices.nim
index 87f341a8..991127ef 100644
--- a/src/takajopkg/stackServices.nim
+++ b/src/takajopkg/stackServices.nim
@@ -21,7 +21,7 @@ proc stackServices(level: string = "informational", ignoreSystem: bool = false,
let svc = jsonLine["Details"]["Svc"].getStr("N/A")
let path = jsonLine["Details"]["Path"].getStr("N/A")
let stackKey = svc & " -> " & path
- stackResult(stackKey, stack, level, jsonLine, @["ServiceName", "Path"])
+ stackResult(stackKey, stack, level, jsonLine, @[svc, path])
bar.finish()
outputResult(output, "Service", stack, @["ServiceName", "Path"])
outputElasptedTime(startTime)
\ No newline at end of file
From ad1d18945116b258b0a5d59278b4ce16eb53b7b1 Mon Sep 17 00:00:00 2001
From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com>
Date: Tue, 20 Feb 2024 14:35:50 +0800
Subject: [PATCH 15/16] change comma separator to pipe
---
src/takajopkg/stackUtil.nim | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/takajopkg/stackUtil.nim b/src/takajopkg/stackUtil.nim
index 7a0b6d3d..ce5328b4 100644
--- a/src/takajopkg/stackUtil.nim
+++ b/src/takajopkg/stackUtil.nim
@@ -49,8 +49,8 @@ proc recordCmp(x, y: StackRecord): int =
result = cmp(x.key, y.key)
proc buildCSVRecord(x: StackRecord): seq[string] =
- let levelsStr = toSeq(x.levels).sorted(levelCmp).join(",")
- let ruleTitlesStr = toSeq(x.ruleTitles).sorted.join(",")
+ let levelsStr = toSeq(x.levels).sorted(levelCmp).join(" | ")
+ let ruleTitlesStr = toSeq(x.ruleTitles).sorted.join(" | ")
if x.otherColumn.len == 0:
return @[intToStr(x.count), x.channel, x.eid, x.key, levelsStr, ruleTitlesStr]
return concat(@[intToStr(x.count), x.channel, x.eid], x.otherColumn, @[levelsStr, ruleTitlesStr])
From db904ffd8d3890b43966037cc71dd04ecf2c8a74 Mon Sep 17 00:00:00 2001
From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com>
Date: Tue, 20 Feb 2024 14:44:32 +0800
Subject: [PATCH 16/16] specify what events are used
---
src/takajopkg/stackCmdlines.nim | 2 +-
src/takajopkg/stackDNS.nim | 2 +-
src/takajopkg/stackLogons.nim | 2 +-
src/takajopkg/stackProcesses.nim | 2 +-
src/takajopkg/stackServices.nim | 2 +-
src/takajopkg/stackTasks.nim | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/takajopkg/stackCmdlines.nim b/src/takajopkg/stackCmdlines.nim
index 33cb8abd..e72a6c8e 100644
--- a/src/takajopkg/stackCmdlines.nim
+++ b/src/takajopkg/stackCmdlines.nim
@@ -1,7 +1,7 @@
proc stackCmdlines(level: string = "low", ignoreSysmon: bool = false, ignoreSecurity: bool = false, output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
checkArgs(quiet, timeline, level)
- let totalLines = countJsonlAndStartMsg("Cmdlines", "executed command lines", timeline)
+ let totalLines = countJsonlAndStartMsg("Cmdlines", "executed command lines from Sysmon 1 and Security 4688 events", timeline)
var
bar: SuruBar = initSuruBar()
stack = initTable[string, StackRecord]()
diff --git a/src/takajopkg/stackDNS.nim b/src/takajopkg/stackDNS.nim
index fd43bcbd..8c1b0152 100644
--- a/src/takajopkg/stackDNS.nim
+++ b/src/takajopkg/stackDNS.nim
@@ -1,7 +1,7 @@
proc stackDNS(level: string = "informational", output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
checkArgs(quiet, timeline, level)
- let totalLines = countJsonlAndStartMsg("DNS", "DNS queries and responses", timeline)
+ let totalLines = countJsonlAndStartMsg("DNS", "DNS queries and responses from Sysmon 22 events", timeline)
var
bar: SuruBar = initSuruBar()
stack = initTable[string, StackRecord]()
diff --git a/src/takajopkg/stackLogons.nim b/src/takajopkg/stackLogons.nim
index e97017a3..82146ae0 100644
--- a/src/takajopkg/stackLogons.nim
+++ b/src/takajopkg/stackLogons.nim
@@ -8,7 +8,7 @@ proc stackLogons(localSrcIpAddresses = false, output: string = "", quiet: bool =
echo "Started the Stack Logons command"
echo ""
- echo "This command will stack logons based on target user, target computer, source IP address and source computer."
+ echo "This command will stack logons based on target user, target computer, source IP address and source computer from Security 4624 events."
echo "Local source IP addresses are not included by default but can be enabled with -l, --localSrcIpAddresses."
echo ""
diff --git a/src/takajopkg/stackProcesses.nim b/src/takajopkg/stackProcesses.nim
index 26c2b129..1fe4e933 100644
--- a/src/takajopkg/stackProcesses.nim
+++ b/src/takajopkg/stackProcesses.nim
@@ -1,7 +1,7 @@
proc stackProcesses(level: string = "low", ignoreSysmon: bool = false, ignoreSecurity: bool = false, output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
checkArgs(quiet, timeline, level)
- let totalLines = countJsonlAndStartMsg("Processes", "executed processes", timeline)
+ let totalLines = countJsonlAndStartMsg("Processes", "executed processes from Sysmon 1 and Security 4688 events", timeline)
var
bar: SuruBar = initSuruBar()
stack = initTable[string, StackRecord]()
diff --git a/src/takajopkg/stackServices.nim b/src/takajopkg/stackServices.nim
index 991127ef..0c99fb0a 100644
--- a/src/takajopkg/stackServices.nim
+++ b/src/takajopkg/stackServices.nim
@@ -1,7 +1,7 @@
proc stackServices(level: string = "informational", ignoreSystem: bool = false, ignoreSecurity: bool = false,output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
checkArgs(quiet, timeline, level)
- let totalLines = countJsonlAndStartMsg("Services", "service names and paths", timeline)
+ let totalLines = countJsonlAndStartMsg("Services", "service names and paths from System 7045 and Security 4697 events", timeline)
var
bar: SuruBar = initSuruBar()
diff --git a/src/takajopkg/stackTasks.nim b/src/takajopkg/stackTasks.nim
index 209a5310..ed5022f0 100644
--- a/src/takajopkg/stackTasks.nim
+++ b/src/takajopkg/stackTasks.nim
@@ -4,7 +4,7 @@ proc decodeEntity(txt: string): string =
proc stackTasks(level: string = "informational", ignoreSysmon: bool = false, ignoreSecurity: bool = false, output: string = "", quiet: bool = false, timeline: string) =
let startTime = epochTime()
checkArgs(quiet, timeline, level)
- let totalLines = countJsonlAndStartMsg("Tasks", "new scheduled tasks", timeline)
+ let totalLines = countJsonlAndStartMsg("Tasks", "new scheduled tasks from Security 4698 events", timeline)
var
bar: SuruBar = initSuruBar()
stack = initTable[string, StackRecord]()