Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: show alert level for stacking command #113

Merged
merged 16 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,37 @@ 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 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

- 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
Expand All @@ -71,21 +98,48 @@ 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

- 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-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

- 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

Expand Down
22 changes: 14 additions & 8 deletions src/takajo.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import strformat
import strutils
import tables
import terminal
import termstyle
import times
import threadpool
import uri
Expand All @@ -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
Expand Down Expand Up @@ -56,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 <Process GUID> [-o process-tree.txt]\p"
const example_timeline_logon = " timeline-logon -t ../hayabusa/timeline.jsonl -o logon-timeline.csv\p"
Expand Down Expand Up @@ -91,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)",
Expand Down Expand Up @@ -183,6 +184,7 @@ when isMainModule:
stackCmdlines, cmdName = "stack-cmdlines",
doc = "stack executed command lines",
help = {
"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",
Expand All @@ -198,6 +200,7 @@ when isMainModule:
stackDNS, cmdName = "stack-dns",
doc = "stack DNS queries and responses",
help = {
"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*)",
Expand All @@ -217,6 +220,7 @@ when isMainModule:
stackProcesses, cmdName = "stack-processes",
doc = "stack executed processes",
help = {
"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",
Expand All @@ -232,19 +236,21 @@ when isMainModule:
stackServices, cmdName = "stack-services",
doc = "stack service names and paths",
help = {
"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*)",
},
short = {
"ignoreSysmon": 'y',
"ignoreSystem": 'y',
"ignoreSecurity": 'e'
}
],
[
stackTasks, cmdName = "stack-tasks",
doc = "stack new scheduled tasks",
help = {
"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*)",
Expand Down
12 changes: 2 additions & 10 deletions src/takajopkg/extractScriptblocks.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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) & ",")
Expand Down
94 changes: 43 additions & 51 deletions src/takajopkg/general.nim
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
import json
import nancy
import terminal
import termstyle
import re
import std/os
import std/parsecsv
import std/sequtils
import std/strformat
import std/strutils
import std/tables
import terminal
import times
import takajoTerminal
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
Expand Down Expand Up @@ -331,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
Expand All @@ -352,42 +339,47 @@ 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)
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, level: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)

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 ""
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]
Expand Down
Loading
Loading