diff --git a/content/exchange/artifacts/FreeBSD.Sys.Utx.yaml b/content/exchange/artifacts/FreeBSD.Sys.Utx.yaml new file mode 100644 index 00000000000..b47f86ec622 --- /dev/null +++ b/content/exchange/artifacts/FreeBSD.Sys.Utx.yaml @@ -0,0 +1,76 @@ +name: FreeBSD.Sys.Utx +author: Herbert Bärschneider +description: | + Parse the utx file of the system (similar to wtmp on Linux). This covers user sessions, boots, shutdowns and system time changes. + Because FreeBSD discards fields for the entries of the utx file based on type (see `man getutxent`), no direct parsing of the file using "vtypes" is done (too complicated for me to define a structure for parsing), but rather native tools are used for accessing the data. + + Using a value of "time" with the "userRegex" Parameter will give you all entries related to boots, shutdowns and system time changes. + + Beware: logout times are given in localtime! Furthermore, that column is not automatically parsed into timestamp values, because the tool output is not consistently a recognizable datetime. + +type: CLIENT + +parameters: + - name: utxGlobs + default: /var/log/utx.log* + description: | + glob for covering the files that should be parsed; default covers the usual location of the utx file on FreeBSD + - name: userRegex + type: regex + default: "." + description: | + Regex for filtering the users, showing those you are interested in + - name: DateAfter + type: timestamp + description: | + timestamp used for filtering the login time + - name: DateBefore + type: timestamp + description: | + timestamp used for filtering the login time + +sources: + - precondition: + SELECT OS From info() where OS = 'freebsd' + query: | + -- timestamps given by the system command "last" are in local time, so we tell Velociraptor to handle them accordingly when converting with the VQL function "timestamp()" + LET PARSE_TZ <= "local" + + -- time test function (taken from Windows.NTFS.MFT) + LET time_test(stamp) = + if(condition= DateBefore AND DateAfter, + then= stamp < DateBefore AND stamp > DateAfter, + else= + if(condition=DateBefore, + then= stamp < DateBefore, + else= + if(condition= DateAfter, + then= stamp > DateAfter, + else= True + ))) + + -- expand the glob and get all files that are matched by it + LET Files = SELECT OSPath FROM glob(globs=split(string=utxGlobs, sep=",")) + + -- for each targeted file, parse the data out of it using system built-in command + LET UtxParsing = SELECT * FROM foreach(row=Files, + query={ + -- TODO: try the command while setting env variable "TZ" to UTC and check, if the timestamps are changed accordingly + SELECT * + FROM execve(argv=["last", "-wy", "--libxo=json", "-f", OSPath]) + WHERE log(message="stderr: " + Stderr) + }) + + -- parse the output from each file as json + LET UtxContent = SELECT parse_json(data=Stdout) as Content FROM UtxParsing + + -- pull out the interesting information from the nested json content + LET s = scope() -- a little helper to suppress "symbol not found error" for elements of the content that are sometimes missing + LET FormatedContent = SELECT * FROM foreach(row=UtxContent, + query={ + SELECT user, timestamp(string=`login-time`) AS login_time, s.`logout-time` AS logout_time, s.`session-length` AS session_length, s.tty AS tty, s.`from` AS remote, Content.`last-information`.utxdb AS utx_log FROM foreach(row=Content.`last-information`.last) + }) + + SELECT * FROM FormatedContent WHERE + user =~ userRegex + AND time_test(stamp=login_time)