1
+ const ExtractCredentialsMsg =
2
+ """ This command extracts plaintext credentials from command-lines in Sysmon 1 and Security 4688 event logs. """
3
+
4
+ type
5
+ ExtractCredentialsCmd* = ref object of AbstractCmd
6
+ seqOfResultsTables* : seq [Table[string , string ]]
7
+
8
+ method filter* (self: ExtractCredentialsCmd, x: HayabusaJson): bool =
9
+ let eidMatched = (x.EventID == 1 and x.Channel == " Sysmon" ) or (x.EventID == 4688 and x.Channel == " Sec" )
10
+ if not eidMatched:
11
+ return false
12
+ let cmdline = x.Details[" Cmdline" ].getStr().toLower()
13
+ let commands = [" net user" , " schtasks" , " wmic" , " psexec" ]
14
+ let cmdMatched = any (commands, proc (x: string ): bool = x in cmdline)
15
+ if " net user" in cmdline:
16
+ return true # net user does not have password parameter
17
+ let passwordPram = [" -p" , " -password" , " -passwd" , " –password" , " –passwd" , " /p" , " /password" , " /passwd" ]
18
+ let passwordMatched = any (passwordPram, proc (x: string ): bool = x in cmdline)
19
+ return cmdMatched and passwordMatched
20
+
21
+ proc extractUserPass(cmd: string ): (string , string ) =
22
+ let patterns = [
23
+ (re(r " .*/user:([^\s/]+).* " ), re(r " .*/password:([^\s/]+).* " )), # for wmic
24
+ (re(r " .*/U ([^\s/]+).* " ), re(r " .*/P ([^\s/]+).* " )), # for schtasks
25
+ (re(r " .*net user ([^\s/]+) " ), re(r " .*?([^\s/]+) /add.* " )), # for net user
26
+ (re(r " .*-u ([^\s/]+).* " ), re(r " .*-p ([^\s/]+).* " )) # for psexec
27
+ ]
28
+
29
+ for (userPattern, passwordPattern) in patterns:
30
+ var username = " "
31
+ var password = " "
32
+
33
+ if cmd =~ userPattern:
34
+ username = matches[0 ]
35
+
36
+ if cmd =~ passwordPattern:
37
+ password = matches[0 ]
38
+
39
+ if username != " " and password != " " :
40
+ return (username, password)
41
+
42
+ return (" " , " " )
43
+
44
+ method analyze* (self: ExtractCredentialsCmd, x: HayabusaJson) =
45
+ var singleResultTable = initTable[string , string ]()
46
+ singleResultTable[" Timestamp" ] = x.Timestamp
47
+ singleResultTable[" Computer" ] = x.Computer
48
+ singleResultTable[" Event" ] = x.Channel & " -" & $ (x.EventID)
49
+ let cmd = x.Details[" Cmdline" ].getStr()
50
+ let (user, pass) = extractUserPass(cmd)
51
+ if user == " " and pass == " " :
52
+ return # skip if no user or password found
53
+ singleResultTable[" User" ] = user
54
+ singleResultTable[" Password" ] = pass
55
+ singleResultTable[" Cmdline" ] = cmd
56
+ self.seqOfResultsTables.add(singleResultTable)
57
+
58
+ method resultOutput* (self: ExtractCredentialsCmd) =
59
+ var savedFiles = " n/a"
60
+ var results = " n/a"
61
+ let header = [" Timestamp" , " Computer" , " Event" , " User" , " Password" , " Cmdline" ]
62
+ if self.output != " " :
63
+ # Open file to save results
64
+ var outputFile = open(self.output, fmWrite)
65
+
66
+ # # Write CSV header
67
+ outputFile.write(header.join(" ," ) & " \p " )
68
+
69
+ # # Write contents
70
+ for table in self.seqOfResultsTables:
71
+ for i, key in enumerate(header):
72
+ if table.hasKey(key):
73
+ if i < header.len() - 1 :
74
+ outputFile.write(escapeCsvField(table[key]) & " ," )
75
+ else :
76
+ outputFile.write(escapeCsvField(table[key]))
77
+ else :
78
+ outputFile.write(" ," )
79
+ outputFile.write(" \p " )
80
+ outputFile.close()
81
+ let fileSize = getFileSize(self.output)
82
+ savedFiles = self.output & " (" & formatFileSize(fileSize) & " )"
83
+ results = " Events: " & intToStr(self.seqOfResultsTables.len).insertSep(',' )
84
+ if self.displayTable:
85
+ echo " "
86
+ echo " Saved results to " & savedFiles
87
+ else :
88
+ var table: TerminalTable
89
+ table.add header
90
+ for t in self.seqOfResultsTables:
91
+ table.add t[header[0 ]], t[header[1 ]], t[header[2 ]], t[header[3 ]], t[header[4 ]], t[header[5 ]]
92
+ if self.displayTable:
93
+ echo " "
94
+ table.echoTableSepsWithStyled(seps = boxSeps)
95
+ echo " "
96
+ self.cmdResult = CmdResult(results: results, savedFiles: savedFiles)
97
+
98
+ proc extractCredentials(output: string = " " , quiet: bool = false , skipProgressBar: bool = false , timeline: string ) =
99
+ checkArgs(quiet, timeline, " informational" )
100
+ var filePaths = getTargetExtFileLists(timeline, " .jsonl" , true )
101
+ for timelinePath in filePaths:
102
+ let cmd = ExtractCredentialsCmd(
103
+ timeline: timelinePath,
104
+ skipProgressBar: skipProgressBar,
105
+ output: output,
106
+ name: " extract-credentials" ,
107
+ msg: ExtractCredentialsMsg)
108
+ cmd.analyzeJSONLFile()
0 commit comments