From 8b058568c49b92f423088f9997ed08d83a8058cf Mon Sep 17 00:00:00 2001 From: stuartjash Date: Wed, 24 Aug 2022 07:11:22 -0700 Subject: [PATCH 01/31] removed commented code --- aftermath/Command.swift | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/aftermath/Command.swift b/aftermath/Command.swift index 5e5b60b..6a25cc8 100644 --- a/aftermath/Command.swift +++ b/aftermath/Command.swift @@ -139,15 +139,12 @@ class Command { mainModule.log("Finished running Aftermath collection") - // Copy from cache to /tmp -// let dir = else { -// mainModule.log("Output directory not provided") -// return -// } guard isDirectoryThatExists(path: Self.outputDir) else { mainModule.log("Output directory is not a valid directory that exists") return } + + // Copy from cache to /tmp CaseFiles.MoveCaseDir(outputDir: Self.outputDir) // End Aftermath From 05a275e1b39ad8e42c1ae166591808989615dbd2 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Wed, 24 Aug 2022 08:51:18 -0700 Subject: [PATCH 02/31] changed install_history to csv and parsed' --- systemRecon/SystemReconModule.swift | 59 ++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/systemRecon/SystemReconModule.swift b/systemRecon/SystemReconModule.swift index 9db834a..cec0ab6 100644 --- a/systemRecon/SystemReconModule.swift +++ b/systemRecon/SystemReconModule.swift @@ -54,6 +54,8 @@ class SystemReconModule: AftermathModule, AMProto { func installHistory(saveFile: URL) { let installPath = "/Library/Receipts/InstallHistory.plist" + self.addTextToFile(atUrl: saveFile, text: "ProcessName,Datetime,ContentType,DisplayName,DisplayVersion,PackageIdentifers") + let data = filemanager.contents(atPath: installPath) let installDict = try! PropertyListSerialization.propertyList(from: data!, options: [], format: nil) as! Array<[String: Any]> @@ -66,53 +68,76 @@ class SystemReconModule: AftermathModule, AMProto { var processName:String = "" for data in installDict { - let dateFormater = DateFormatter() - dateFormater.dateFormat = "yyyy-mm-dd hh:mm:ss" + let dateFormatter = DateFormatter() + dateFormatter.locale = Locale(identifier: "en_US") + dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" +// +// var procName: String +// var dateString: String +// var type: String +// var name: String +// var version: String +// var package: String if data["processName"] != nil { processName = data["processName"]! as! String - installHistoryArray.append("ProcessName: \(processName)") +// procName = processName +// installHistoryArray.append("ProcessName: \(processName)") } else { - installHistoryArray.append("ProcessName: ") +// installHistoryArray.append("ProcessName: ") + processName = "unknown" } if data["date"] != nil { - date = dateFormater.string(from: data["date"]! as! Date) - installHistoryArray.append("Date: \(date)") + date = dateFormatter.string(from: data["date"]! as! Date) +// dateString = date +// installHistoryArray.append("Date: \(date)") } else { - installHistoryArray.append("Date: ") +// installHistoryArray.append("Date: ") + date = "unknown" } if data["contentType"] != nil { contentType = data["contentType"]! as! String - installHistoryArray.append("ContentType: \(contentType)") +// type = contentType +// installHistoryArray.append("ContentType: \(contentType)") } else { - installHistoryArray.append("ContentType: ") +// installHistoryArray.append("ContentType: ") + contentType = "unknown" } if data["displayName"] != nil { displayName = data["displayName"]! as! String - installHistoryArray.append("DisplayName: \(displayName)") +// name = displayName +// installHistoryArray.append("DisplayName: \(displayName)") } else { - installHistoryArray.append("DisplayName: ") +// installHistoryArray.append("DisplayName: ") + displayName = "unknown" } if data["displayVersion"] != nil { displayVersion = data["displayVersion"]! as! String - installHistoryArray.append("DisplayVersion: \(displayVersion)") +// version = displayName +// installHistoryArray.append("DisplayVersion: \(displayVersion)") } else { - installHistoryArray.append("DisplayVersion: ") +// installHistoryArray.append("DisplayVersion: ") + displayVersion = "unknown" } if data["packageIdentifiers"] != nil { packageIdentifiers = data["packageIdentifiers"]! as! Array - installHistoryArray.append("PackageIdentifiers: \(packageIdentifiers.joined(separator: ", "))\n") +// package = packageIdentifiers.joined(separator: ",") +// installHistoryArray.append("PackageIdentifiers: \(packageIdentifiers.joined(separator: ", "))\n") } else { - installHistoryArray.append("PackageIdentifiers: \n") +// installHistoryArray.append("PackageIdentifiers: \n") + packageIdentifiers = ["unknown"] } + self.addTextToFile(atUrl: saveFile, text: "\(processName),\(date),\(contentType),\(displayName),\(displayVersion),\(packageIdentifiers.joined(separator: ","))") + } - self.addTextToFile(atUrl: saveFile, text: installHistoryArray.joined(separator: "\n")) +// self.addTextToFile(atUrl: saveFile, text: installHistoryArray.joined(separator: "\n")) } func runningApps(saveFile: URL) { @@ -219,7 +244,7 @@ class SystemReconModule: AftermathModule, AMProto { let runningAppsFile = self.createNewCaseFile(dirUrl: moduleDirRoot, filename: "running_apps.txt") let interfacesFile = self.createNewCaseFile(dirUrl: moduleDirRoot, filename: "interfaces.txt") let environmentVariablesFile = self.createNewCaseFile(dirUrl: moduleDirRoot, filename: "environment_variables.txt") - let installHistoryFile = self.createNewCaseFile(dirUrl: moduleDirRoot, filename: "install_history.txt") + let installHistoryFile = self.createNewCaseFile(dirUrl: moduleDirRoot, filename: "install_history.csv") let installedUsersFile = self.createNewCaseFile(dirUrl: moduleDirRoot, filename: "users.txt") systemInformation(saveFile: systemInformationFile) From 40b2703eed76622eee9652f48d73a58fe38864b4 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Wed, 24 Aug 2022 14:00:27 -0700 Subject: [PATCH 03/31] minor updates --- aftermath/Command.swift | 2 +- aftermath/Module.swift | 1 - analysis/Storyline.swift | 6 ++++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/aftermath/Command.swift b/aftermath/Command.swift index 6a25cc8..e5f1de0 100644 --- a/aftermath/Command.swift +++ b/aftermath/Command.swift @@ -184,7 +184,7 @@ class Command { } static func printHelp() { - print("-o -> specify an output location for Aftermath results") + print("-o -> specify an output location for Aftermath results (defaults to /tmp)") print(" usage: -o Users/user/Desktop") print("--analyze -> Analyze the results of the Aftermath results") print(" usage: --analyze ") diff --git a/aftermath/Module.swift b/aftermath/Module.swift index 580834a..63dc33e 100644 --- a/aftermath/Module.swift +++ b/aftermath/Module.swift @@ -304,6 +304,5 @@ class AftermathModule { enum SystemUsers: String, CaseIterable { case nobody = "nobody" case daemon = "daemon" - case empty = "empty" } } diff --git a/analysis/Storyline.swift b/analysis/Storyline.swift index bc199ab..16d9b52 100644 --- a/analysis/Storyline.swift +++ b/analysis/Storyline.swift @@ -26,6 +26,8 @@ class Storyline: AftermathModule { for (title,p) in safariPaths { + if !filemanager.fileExists(atPath: p) { continue } + let csvContents = Aftermath.readCSVRows(path: p) for r in csvContents.rows { @@ -56,6 +58,8 @@ class Storyline: AftermathModule { for (title,p) in chromePaths { + if !filemanager.fileExists(atPath: p) { continue } + let csvContents = Aftermath.readCSVRows(path: p) for r in csvContents.rows { @@ -85,6 +89,8 @@ class Storyline: AftermathModule { for (title,p) in chromePaths { + if !filemanager.fileExists(atPath: p) { continue } + let csvContents = Aftermath.readCSVRows(path: p) for r in csvContents.rows { From 2ce996d0486ae384505016951271ea5abbfd5a7e Mon Sep 17 00:00:00 2001 From: stuartjash Date: Fri, 26 Aug 2022 08:26:06 -0700 Subject: [PATCH 04/31] added pretty arg. defaults to non-pretty --- aftermath/Command.swift | 11 +++++++---- aftermath/Module.swift | 16 +++++++++++++--- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/aftermath/Command.swift b/aftermath/Command.swift index e5f1de0..e61df8b 100644 --- a/aftermath/Command.swift +++ b/aftermath/Command.swift @@ -1,9 +1,9 @@ // - // Command.swift - // aftermath - // +// Command.swift +// aftermath +// // Copyright 2022 JAMF Software, LLC - // +// import Foundation @@ -13,6 +13,8 @@ static let deep = Options(rawValue: 1 << 0) static let output = Options(rawValue: 1 << 1) static let analyze = Options(rawValue: 1 << 2) + static let pretty = Options(rawValue: 1 << 3) + } @main @@ -35,6 +37,7 @@ class Command { case "-h", "--help": Self.printHelp() case "--cleanup": Self.cleanup() case "-d", "--deep": Self.options.insert(.deep) + case "--pretty": Self.options.insert(.pretty) case "-o", "--output": if let index = args.firstIndex(of: arg) { Self.options.insert(.output) diff --git a/aftermath/Module.swift b/aftermath/Module.swift index 63dc33e..ea4242f 100644 --- a/aftermath/Module.swift +++ b/aftermath/Module.swift @@ -25,6 +25,7 @@ class AftermathModule { let filemanager = FileManager.default var caseLogSelector: URL var caseDirSelector: URL + var isPretty: Bool = false init() { if Command.options.contains(.analyze) { @@ -34,6 +35,10 @@ class AftermathModule { caseLogSelector = CaseFiles.logFile caseDirSelector = CaseFiles.caseDir } + if Command.options.contains(.pretty) { + isPretty = true + } + users = getUsersOnSystem() } @@ -265,9 +270,14 @@ class AftermathModule { let module = URL(fileURLWithPath: file).lastPathComponent let entry = "\(Date().ISO8601Format()) - \(module) - \(note)" - let colorized = "\(Color.magenta.rawValue)\(Date().ISO8601Format())\(Color.colorstop.rawValue) - \(Color.yellow.rawValue)\(module)\(Color.colorstop.rawValue) - \(Color.cyan.rawValue)\(note)\(Color.colorstop.rawValue)" - print(colorized) - + if isPretty { + let colorized = "\(Color.magenta.rawValue)\(Date().ISO8601Format())\(Color.colorstop.rawValue) - \(Color.yellow.rawValue)\(module)\(Color.colorstop.rawValue) - \(Color.cyan.rawValue)\(note)\(Color.colorstop.rawValue)" + print(colorized) + } else { + let plainText = "\(Date().ISO8601Format()) - \(module) - \(note)" + print(plainText) + } + if displayOnly == false { addTextToFile(atUrl: caseLogSelector, text: entry) } From 0cb3ed3ffb8dcab16d71ec11def50903f03758a7 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Fri, 26 Aug 2022 14:49:02 -0700 Subject: [PATCH 05/31] removed comments --- analysis/LogParser.swift | 8 ++++++++ unifiedlogs/UnifiedLogModule.swift | 3 +-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 analysis/LogParser.swift diff --git a/analysis/LogParser.swift b/analysis/LogParser.swift new file mode 100644 index 0000000..79af5f0 --- /dev/null +++ b/analysis/LogParser.swift @@ -0,0 +1,8 @@ +// +// LogParser.swift +// aftermath +// +// Created by Stuart Ashenbrenner on 8/26/22. +// + +import Foundation diff --git a/unifiedlogs/UnifiedLogModule.swift b/unifiedlogs/UnifiedLogModule.swift index c7365f3..2c3f5bb 100644 --- a/unifiedlogs/UnifiedLogModule.swift +++ b/unifiedlogs/UnifiedLogModule.swift @@ -16,7 +16,7 @@ class UnifiedLogModule: AftermathModule, AMProto { let predicates: [String: String] override init() { - //predicates eventually to be loaded from external file + self.predicates = [ "login": "process == \"logind\"", "tcc": "process == \"tccd\"", @@ -38,7 +38,6 @@ class UnifiedLogModule: AftermathModule, AMProto { if output.components(separatedBy: "\n").count > 2 { let logfile = self.createNewCaseFile(dirUrl: moduleDirRoot, filename: "\(filtername).txt") self.addTextToFile(atUrl: logfile, text: output) - //self.caseHandler.log(module: self.moduleName, "Done filtering for \(filtername) events") } else { continue } } } From 4d94cf8503df63a9b748fb74e97bc7fa6545dea1 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Fri, 26 Aug 2022 14:49:22 -0700 Subject: [PATCH 06/31] removed comments --- systemRecon/SystemReconModule.swift | 36 ++++------------------------- 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/systemRecon/SystemReconModule.swift b/systemRecon/SystemReconModule.swift index cec0ab6..46ea727 100644 --- a/systemRecon/SystemReconModule.swift +++ b/systemRecon/SystemReconModule.swift @@ -59,7 +59,6 @@ class SystemReconModule: AftermathModule, AMProto { let data = filemanager.contents(atPath: installPath) let installDict = try! PropertyListSerialization.propertyList(from: data!, options: [], format: nil) as! Array<[String: Any]> - var installHistoryArray = [String]() var date:String = "" var contentType:String = "" var displayName:String = "" @@ -72,72 +71,47 @@ class SystemReconModule: AftermathModule, AMProto { dateFormatter.locale = Locale(identifier: "en_US") dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" -// -// var procName: String -// var dateString: String -// var type: String -// var name: String -// var version: String -// var package: String + if data["processName"] != nil { processName = data["processName"]! as! String -// procName = processName -// installHistoryArray.append("ProcessName: \(processName)") } else { -// installHistoryArray.append("ProcessName: ") processName = "unknown" } if data["date"] != nil { date = dateFormatter.string(from: data["date"]! as! Date) -// dateString = date -// installHistoryArray.append("Date: \(date)") } else { -// installHistoryArray.append("Date: ") date = "unknown" } if data["contentType"] != nil { contentType = data["contentType"]! as! String -// type = contentType -// installHistoryArray.append("ContentType: \(contentType)") } else { -// installHistoryArray.append("ContentType: ") contentType = "unknown" } if data["displayName"] != nil { displayName = data["displayName"]! as! String -// name = displayName -// installHistoryArray.append("DisplayName: \(displayName)") } else { -// installHistoryArray.append("DisplayName: ") displayName = "unknown" } if data["displayVersion"] != nil { displayVersion = data["displayVersion"]! as! String -// version = displayName -// installHistoryArray.append("DisplayVersion: \(displayVersion)") } else { -// installHistoryArray.append("DisplayVersion: ") displayVersion = "unknown" } if data["packageIdentifiers"] != nil { packageIdentifiers = data["packageIdentifiers"]! as! Array -// package = packageIdentifiers.joined(separator: ",") -// installHistoryArray.append("PackageIdentifiers: \(packageIdentifiers.joined(separator: ", "))\n") } else { -// installHistoryArray.append("PackageIdentifiers: \n") packageIdentifiers = ["unknown"] } self.addTextToFile(atUrl: saveFile, text: "\(processName),\(date),\(contentType),\(displayName),\(displayVersion),\(packageIdentifiers.joined(separator: ","))") } -// self.addTextToFile(atUrl: saveFile, text: installHistoryArray.joined(separator: "\n")) } func runningApps(saveFile: URL) { @@ -204,15 +178,15 @@ class SystemReconModule: AftermathModule, AMProto { let dict = ["Gatekeeper Status": "spctl --status", "SIP Status": "csrutil status", - "Login History": "last", "Screen Sharing": "sudo launchctl list com.apple.screensharing", - "I/O Statistics": "iostat", - "Network Interface Parameters": "ifconfig", "Firewall Status (Enabled = 1, Disabled = 0)": "defaults read /Library/Preferences/com.apple.alf globalstate", "Filevault Status": "sudo fdesetup status", "Airdrop Status": "sudo ifconfig awdl0 | awk '/status/{print $2}'", "Remote Login": "sudo systemsetup -getremotelogin", - "Network File Shares": "nfsd status" + "Network File Shares": "nfsd status", + "I/O Statistics": "iostat", + "Login History": "last", + "Network Interface Parameters": "ifconfig" ] for (heading,command) in dict { From 4bf49a6d292a3a4aeb265acd18362209301ae216 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Fri, 26 Aug 2022 14:50:55 -0700 Subject: [PATCH 07/31] updates to analysis - including the syslog and install log --- aftermath/Command.swift | 1 + analysis/AnalysisModule.swift | 11 ++- analysis/DatabaseParser.swift | 12 ++-- analysis/LogParser.swift | 130 ++++++++++++++++++++++++++++++++++ analysis/Storyline.swift | 18 ++++- analysis/Timeline.swift | 18 ++++- 6 files changed, 173 insertions(+), 17 deletions(-) diff --git a/aftermath/Command.swift b/aftermath/Command.swift index e61df8b..577b100 100644 --- a/aftermath/Command.swift +++ b/aftermath/Command.swift @@ -78,6 +78,7 @@ class Command { mainModule.log("Started analysis on Aftermath directory: \(unzippedDir)") let analysisModule = AnalysisModule(collectionDir: unzippedDir) analysisModule.run() + mainModule.log("Finished analysis module") // Move analysis directory to tmp diff --git a/analysis/AnalysisModule.swift b/analysis/AnalysisModule.swift index 2457509..bc2ba2b 100644 --- a/analysis/AnalysisModule.swift +++ b/analysis/AnalysisModule.swift @@ -14,8 +14,9 @@ class AnalysisModule: AftermathModule, AMProto { let description = "A module for analyzing results of Aftermath" lazy var moduleDirRoot = self.createNewDirInRoot(dirName: dirName) let collectionDir: String - lazy var timelineFile = self.createNewCaseFile(dirUrl: CaseFiles.analysisCaseDir, filename: "timeline.csv") - lazy var storylineFile = self.createNewCaseFile(dirUrl: CaseFiles.analysisCaseDir, filename: "storyline.csv") + lazy var timelineFile = self.createNewCaseFile(dirUrl: CaseFiles.analysisCaseDir, filename: "temp_timeline.csv") + lazy var storylineFile = self.createNewCaseFile(dirUrl: CaseFiles.analysisCaseDir, filename: "temp_storyline.csv") + init(collectionDir: String) { @@ -26,13 +27,17 @@ class AnalysisModule: AftermathModule, AMProto { func run() { self.log("Running analysis on collected aftermath files") - + + let _ = self.copyFileToCase(fileToCopy: URL(fileURLWithPath: "\(collectionDir)/Recon/system_information.txt"), toLocation: CaseFiles.analysisCaseDir, isAnalysis: true) // ex: timestamp, tcc_update, com.jamf.aftermath, addTextToFile(atUrl: storylineFile, text: "timestamp,type,other,path") let dbParser = DatabaseParser(collectionDir: collectionDir, storylineFile: storylineFile) dbParser.run() + let logParser = LogParser(collectionDir: collectionDir, storylineFile: storylineFile) + logParser.run() + let timeline = Timeline(collectionDir: collectionDir, timelineFile: timelineFile, storylineFile: storylineFile) timeline.run() diff --git a/analysis/DatabaseParser.swift b/analysis/DatabaseParser.swift index 13315fe..a45d2d7 100644 --- a/analysis/DatabaseParser.swift +++ b/analysis/DatabaseParser.swift @@ -23,7 +23,7 @@ class DatabaseParser: AftermathModule { func parseTCC() { self.addTextToFile(atUrl: tccWriteFile, text: "name, service, auth_value, auth_reason, last_modified") - + let rawDir = "\(self.collectionDir)/Artifacts/raw/" var tccFiles = [URL]() for f in filemanager.filesInDir(path: rawDir) { @@ -33,7 +33,6 @@ class DatabaseParser: AftermathModule { } for tcc_path in tccFiles { - self.log("Querying TCC database for path \(tcc_path.path)") var db : OpaquePointer? if sqlite3_open(tcc_path.path, &db) == SQLITE_OK { @@ -166,9 +165,6 @@ class DatabaseParser: AftermathModule { } } - - self.log("Finished capturing LSQuarantine data") - } else { self.log("An error occurred when attempting to query the LSQuarantine database...") } @@ -176,11 +172,11 @@ class DatabaseParser: AftermathModule { } func run() { - self.log("Parsing collected database files...") - self.log("Parsing LSQuarantine database") + self.log("Parsing collected database files") + self.log("Parsing LSQuarantine database...") parseLSQuarantine() - self.log("Parsing TCC database") + self.log("Parsing TCC database...") parseTCC() } diff --git a/analysis/LogParser.swift b/analysis/LogParser.swift index 79af5f0..02fb87e 100644 --- a/analysis/LogParser.swift +++ b/analysis/LogParser.swift @@ -6,3 +6,133 @@ // import Foundation + +class LogParser: AftermathModule { + + lazy var logsFile = self.createNewCaseFile(dirUrl: CaseFiles.analysisCaseDir, filename: "logs.csv") +// lazy var systemLogParsedFile = self.createNewCaseFile(dirUrl: CaseFiles.analysisCaseDir, filename: "system_log.csv") +// lazy var logTimeline = self.createNewCaseFile(dirUrl: CaseFiles.analysisCaseDir, filename: "log_timeline.csv") + let collectionDir: String + let storylineFile: URL + + init(collectionDir: String, storylineFile: URL) { + self.collectionDir = collectionDir + self.storylineFile = storylineFile + } + + func parseInstallLog() { + // install.log + + let installLog = "\(collectionDir)/Artifacts/raw/logs/system_logs/install.log" + do { + let contents = try String(contentsOf: URL(fileURLWithPath: installLog)) + let installLogContents = contents.components(separatedBy: "\n") + + for ind in 0...installLogContents.count - 1 { + + let splitLine = installLogContents[ind].components(separatedBy: " ") + + guard let date = splitLine[safe: 0] else { continue } + guard let time = splitLine[safe: 1] else { continue } + let unformattedDate = date + "T" + time // "2022-03-1516:22:55-07" + let dateFormatter = DateFormatter() + dateFormatter.locale = Locale(identifier: "en_US") + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" + dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) + + + + var info = "" + + for i in 0...splitLine.count - 1 { + if i == 0 || i == 1 { continue } + info = info.appending(" " + splitLine[i]) + } + + sanatizeInfo(&info) + + guard let dateZone = dateFormatter.date(from: unformattedDate) else { continue } + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" + let formattedDate = dateFormatter.string(from: dateZone) + let text = "\(formattedDate), INSTALL, \(info)" + self.addTextToFile(atUrl: logsFile, text: text) + self.addTextToFile(atUrl: self.storylineFile, text: text) + + + + + +// if let dateString = dateFormatter.date(from: unformattedDate) { + +// } else { +// continue +// } + } + } catch { + print("Unable to parse contents") + } + } + + fileprivate func sanatizeInfo(_ info: inout String) { + info = info.replacingOccurrences(of: ",", with: "") + info = info.replacingOccurrences(of: "\"", with: "") + } + + func parseSysLog() { + // system.log + + let systemLog = "\(collectionDir)/Artifacts/raw/logs/system_logs/system.log" + + do { + let contents = try String(contentsOf: URL(fileURLWithPath: systemLog)) + let systemLogConetnts = contents.components(separatedBy: "\n") + + for ind in 0...systemLogConetnts.count - 1 { + + let splitLine = systemLogConetnts[ind].components(separatedBy: " ") + + let dateFormatter = DateFormatter() + dateFormatter.locale = Locale(identifier: "en_US") + dateFormatter.dateFormat = "MMM dd yyyy HH:mm:ss" + dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) + + let currentYear = Calendar(identifier: .gregorian).dateComponents([.year], from: .now).year + guard let month = splitLine[safe: 0] else { continue } // Aug + guard let date = splitLine[safe: 1] else { continue } // 26 + guard let time = splitLine[safe: 2] else { continue } // 00:17:38 + + var info = "" + let dateArray = [0,1,2] + + for i in 0...splitLine.count - 1 { + if dateArray.contains(i) { continue } + info = info.appending(" " + splitLine[i]) + } + + sanatizeInfo(&info) + + + let unformattedTimestamp = "\(month) \(date) \(currentYear!) \(time)" + + guard let formatted = dateFormatter.date(from: unformattedTimestamp) else { continue } // 2022-08-26 00:01:40 UTC + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" + let dateString = dateFormatter.string(from: formatted) + + let text = "\(dateString), SYSLOG, \(info)" + self.addTextToFile(atUrl: logsFile, text: text) + self.addTextToFile(atUrl: storylineFile, text: text) + + } + } catch { + print("Unable to parse contentes") + } + } + + func run() { + self.log("Parsing install log...") + parseInstallLog() + + self.log("Parsing system log...") + parseSysLog() + } +} diff --git a/analysis/Storyline.swift b/analysis/Storyline.swift index 16d9b52..1e38cda 100644 --- a/analysis/Storyline.swift +++ b/analysis/Storyline.swift @@ -117,9 +117,9 @@ class Storyline: AftermathModule { func sortStoryline() { - self.log("Sorting the storyline...") + self.log("Creating the storyline...") - let sortedStoryline = self.createNewCaseFile(dirUrl: CaseFiles.analysisCaseDir, filename: "sorted_storyline.csv") + let sortedStoryline = self.createNewCaseFile(dirUrl: CaseFiles.analysisCaseDir, filename: "storyline.csv") do { let csvFile = try EnumeratedCSV(url: self.storylineFile) @@ -129,16 +129,28 @@ class Storyline: AftermathModule { let line = row.joined(separator: ",") self.addTextToFile(atUrl: sortedStoryline, text: "\(line)") } - self.log("Finished sorting the storyline") + self.log("Finished creating the storyline") } catch { print(error) } } + + func removeUnsorted() { + + do { + if filemanager.fileExists(atPath: self.storylineFile.path) { + try filemanager.removeItem(at: self.storylineFile) + } + } catch { + print("Unable to remove unsorted timeline file at \(self.storylineFile.path) due to error\n\(error)") + } + } func run() { addSafariData() addFirefoxData() addChromeData() sortStoryline() + removeUnsorted() } } diff --git a/analysis/Timeline.swift b/analysis/Timeline.swift index 6372457..73bdcae 100644 --- a/analysis/Timeline.swift +++ b/analysis/Timeline.swift @@ -61,9 +61,9 @@ class Timeline: AftermathModule { func sortTimeline() { - self.log("Sorting the timeline...") + self.log("Creating a file timeline...") - let sortedTimeline = self.createNewCaseFile(dirUrl: CaseFiles.analysisCaseDir, filename: "sorted_timeline.csv") + let sortedTimeline = self.createNewCaseFile(dirUrl: CaseFiles.analysisCaseDir, filename: "file_timeline.csv") do { let csvFile = try EnumeratedCSV(url: self.timelineFile) @@ -75,15 +75,27 @@ class Timeline: AftermathModule { self.addTextToFile(atUrl: sortedTimeline, text: "\(line)") } - self.log("Finished sorting the timeline") + self.log("Finished creating the timeline") } catch { print(error) } } + func removeUnsorted() { + + do { + if filemanager.fileExists(atPath: self.timelineFile.path) { + try filemanager.removeItem(at: self.timelineFile) + } + } catch { + print("Unable to remove unsorted timeline file at \(self.timelineFile.path) due to error\n\(error)") + } + } + func run() { organizeMetadata() //timestamp, type(download,birth,access,etc), path sortTimeline() + removeUnsorted() } } From d59ebc1fa6c55d8efcf003b87657274c2f83e1d6 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Fri, 26 Aug 2022 14:51:00 -0700 Subject: [PATCH 08/31] scheme change --- aftermath.xcodeproj/project.pbxproj | 4 ++++ .../xcshareddata/xcschemes/aftermath-analysis.xcscheme | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/aftermath.xcodeproj/project.pbxproj b/aftermath.xcodeproj/project.pbxproj index 5723390..a32f5f7 100644 --- a/aftermath.xcodeproj/project.pbxproj +++ b/aftermath.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ A0E1E3F6275ED2E4008D0DC6 /* NetworkModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E1E3F5275ED2E4008D0DC6 /* NetworkModule.swift */; }; A0E1E3F8275ED35D008D0DC6 /* NetworkConnections.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E1E3F7275ED35D008D0DC6 /* NetworkConnections.swift */; }; A0E22EF2285CD60A003A411A /* CommonDirectories.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E22EF1285CD60A003A411A /* CommonDirectories.swift */; }; + A0FAEEFE28B94B2C00AC655F /* LogParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0FAEEFD28B94B2C00AC655F /* LogParser.swift */; }; A3046F8E27627DAC0069AA21 /* Module.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3046F8D27627DAC0069AA21 /* Module.swift */; }; A3046F902763AE5E0069AA21 /* CaseFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3046F8F2763AE5E0069AA21 /* CaseFiles.swift */; }; A3745358275730870074B65C /* LaunchItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3745357275730870074B65C /* LaunchItems.swift */; }; @@ -107,6 +108,7 @@ A0E1E3F7275ED35D008D0DC6 /* NetworkConnections.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConnections.swift; sourceTree = ""; }; A0E22EF1285CD60A003A411A /* CommonDirectories.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonDirectories.swift; sourceTree = ""; }; A0E46B8A288F55A600975EC8 /* aftermath.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = aftermath.entitlements; sourceTree = ""; }; + A0FAEEFD28B94B2C00AC655F /* LogParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogParser.swift; sourceTree = ""; }; A3046F8D27627DAC0069AA21 /* Module.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Module.swift; sourceTree = ""; }; A3046F8F2763AE5E0069AA21 /* CaseFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaseFiles.swift; sourceTree = ""; }; A3745357275730870074B65C /* LaunchItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchItems.swift; sourceTree = ""; }; @@ -228,6 +230,7 @@ A006B5A02882FBA70091FAA1 /* DatabaseParser.swift */, A0C930D328A4318F0011FB87 /* Timeline.swift */, A02509F028AD93DA0030D6A7 /* Storyline.swift */, + A0FAEEFD28B94B2C00AC655F /* LogParser.swift */, ); path = analysis; sourceTree = ""; @@ -383,6 +386,7 @@ A3CD4E56274434EE00869ECB /* Command.swift in Sources */, A0C2E89728AAAE33008FA597 /* ProcLib.h in Sources */, A3745358275730870074B65C /* LaunchItems.swift in Sources */, + A0FAEEFE28B94B2C00AC655F /* LogParser.swift in Sources */, A05BF3BD284FF8C0009E197B /* FileSystemModule.swift in Sources */, A0E22EF2285CD60A003A411A /* CommonDirectories.swift in Sources */, A3046F902763AE5E0069AA21 /* CaseFiles.swift in Sources */, diff --git a/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-analysis.xcscheme b/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-analysis.xcscheme index f576916..d901152 100644 --- a/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-analysis.xcscheme +++ b/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-analysis.xcscheme @@ -57,7 +57,7 @@ isEnabled = "YES"> From 45308949caa5b52edce181b14d1dbfc44b900efe Mon Sep 17 00:00:00 2001 From: stuartjash Date: Sat, 27 Aug 2022 13:20:27 -0700 Subject: [PATCH 09/31] added version of xprotect remediator to system information --- systemRecon/SystemReconModule.swift | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/systemRecon/SystemReconModule.swift b/systemRecon/SystemReconModule.swift index 46ea727..f1c98fa 100644 --- a/systemRecon/SystemReconModule.swift +++ b/systemRecon/SystemReconModule.swift @@ -30,8 +30,13 @@ class SystemReconModule: AftermathModule, AMProto { self.log("Error has occured, MRT returned nil") return } + + guard let xprotectRemediatorVersion = XProtectRemediator(key: "CFBundleShortVersionString") else { + self.log("Error has occured, XProtect Remediator returned nil") + return + } - self.addTextToFile(atUrl: saveFile, text: "HostName: \(hostName)\nUserName: \(userName)\nFullName: \(fullName)\nSystem Version: \(systemVersion)\nXProtect Version: \(xprotectVersion)\nMRT Version: \(mrtVersion)") + self.addTextToFile(atUrl: saveFile, text: "HostName: \(hostName)\nUserName: \(userName)\nFullName: \(fullName)\nSystem Version: \(systemVersion)\nXProtect Version: \(xprotectVersion)\nXProtect Remediator Version: \(xprotectRemediatorVersion)\nMRT Version: \(mrtVersion)") self.addTextToFile(atUrl: saveFile, text: "\n----------\n") } @@ -171,11 +176,21 @@ class SystemReconModule: AftermathModule, AMProto { return nil } } + + func XProtectRemediator(key: String) -> String? { + let xprotectRemediatorPath = URL(fileURLWithPath: "/Library/Apple/System/Library/CoreServices/XProtect.app/Contents/version.plist") + + let xprotectRemediatorDict = Aftermath.getPlistAsDict(atUrl: xprotectRemediatorPath) + + if let xprotectRemKeyValue = xprotectRemediatorDict[key] { + return String(describing:xprotectRemKeyValue) + } else { + self.log("Error has occured reading xprotect remediator plist") + return nil + } + } func securityAssessment(saveFile: URL) { - - - let dict = ["Gatekeeper Status": "spctl --status", "SIP Status": "csrutil status", "Screen Sharing": "sudo launchctl list com.apple.screensharing", From 94791e5a565f18ab95b43ee1c81c236fbbaaa613 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 07:59:05 -0700 Subject: [PATCH 10/31] added TrueTree license to file comments --- libs/ProcLib/ProcLib.h | 1 + libs/launchdXPC/launchdXPC.h | 1 + libs/launchdXPC/launchdXPC.m | 21 +++++++++------------ processes/Pids.swift | 1 + processes/Tree.swift | 3 +-- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/libs/ProcLib/ProcLib.h b/libs/ProcLib/ProcLib.h index dcd1b8c..0a8af99 100644 --- a/libs/ProcLib/ProcLib.h +++ b/libs/ProcLib/ProcLib.h @@ -1,3 +1,4 @@ +// TrueTree License: https://github.com/themittenmac/TrueTree/blob/master/license.md #ifndef ProcLib_h #define ProcLib_h diff --git a/libs/launchdXPC/launchdXPC.h b/libs/launchdXPC/launchdXPC.h index 26cad99..77a1cdd 100644 --- a/libs/launchdXPC/launchdXPC.h +++ b/libs/launchdXPC/launchdXPC.h @@ -4,6 +4,7 @@ // Created by Patrick Wardle // Ported from code by Jonathan Levin // +// TrueTree License: https://github.com/themittenmac/TrueTree/blob/master/license.md #ifndef launchdXPC_h #define launchdXPC_h diff --git a/libs/launchdXPC/launchdXPC.m b/libs/launchdXPC/launchdXPC.m index 326ef6b..edfcf13 100644 --- a/libs/launchdXPC/launchdXPC.m +++ b/libs/launchdXPC/launchdXPC.m @@ -3,6 +3,7 @@ // Created by Patrick Wardle // Ported from code by Jonathan Levin // +// TrueTree License: https://github.com/themittenmac/TrueTree/blob/master/license.md #include #import @@ -162,22 +163,17 @@ int getSubmittedPid(int pid) { processInfoBuffer = 0; } + // return the path of the process return processInfo[@"path"]; -// return processInfo; } - - - - -///////////////// -/// -/// -/// -/// - -//hit up launchd (via XPC) to get process info +/* + hit up launchd (via XPC) to get process info + this method was updated from the original version of TrueTree to + include collecting the process arguments instead of just the + proc path + */ NSMutableDictionary* getProcessArgs(unsigned long pid) { //proc info @@ -292,6 +288,7 @@ int getSubmittedPid(int pid) { processInfoBuffer = 0; } + // get the arguments return processInfo[@"arguments "]; } diff --git a/processes/Pids.swift b/processes/Pids.swift index 4278c0e..0e31a1b 100644 --- a/processes/Pids.swift +++ b/processes/Pids.swift @@ -7,6 +7,7 @@ // The following code (with minor modifications) is from TrueTree, written by Jaron Bradley. // 2020 TheMittenMac // TrueTree: https://github.com/themittenmac/TrueTree +// TrueTree License: https://github.com/themittenmac/TrueTree/blob/master/license.md import Foundation import Darwin diff --git a/processes/Tree.swift b/processes/Tree.swift index 9c9b4f4..4b7a471 100644 --- a/processes/Tree.swift +++ b/processes/Tree.swift @@ -8,6 +8,7 @@ // 2020 TheMittenMac // TrueTree: https://github.com/themittenmac/TrueTree // Inspired by https://www.journaldev.com/21383/swift-tree-binary-tree-data-structure +// TrueTree License: https://github.com/themittenmac/TrueTree/blob/master/license.md import Foundation @@ -201,7 +202,6 @@ class Tree: ProcessModule { } } - // get the arguments of the process let processArguments = getProcessArgs(UInt(pid)) var allArgs: String = "" @@ -214,7 +214,6 @@ class Tree: ProcessModule { } } - self.addTextToFile(atUrl: processFile, text: "\(node.timestamp) \(node.pid) \(node.ppid) \(node.responsiblePid) \(subNode) \(node.procPath) \(allArgs)") From 296e141a781b624a850243d65ce82a91f51b8fc1 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 11:16:55 -0700 Subject: [PATCH 11/31] updated to take an array of file paths to dump as opposed to doing so by default --- aftermath/Command.swift | 24 ++++++++--- artifacts/ArtifactsModule.swift | 9 ---- filesystem/CommonDirectories.swift | 69 +++++++++++++++++++++--------- filesystem/FileSystemModule.swift | 4 +- filesystem/FileWalker.swift | 2 +- filesystem/Slack.swift | 2 +- 6 files changed, 70 insertions(+), 40 deletions(-) diff --git a/aftermath/Command.swift b/aftermath/Command.swift index 577b100..d0483ff 100644 --- a/aftermath/Command.swift +++ b/aftermath/Command.swift @@ -14,14 +14,16 @@ static let output = Options(rawValue: 1 << 1) static let analyze = Options(rawValue: 1 << 2) static let pretty = Options(rawValue: 1 << 3) + static let collectDirs = Options(rawValue: 1 << 4) } @main class Command { - static var options: Options = [] - static var analysisDir: String? = nil - static var outputDir: String = "/tmp" + static var options: Options = [] + static var analysisDir: String? = nil + static var outputDir: String = "/tmp" + static var collectDirs: [String] = [] static func main() { setup(with: CommandLine.arguments) @@ -48,6 +50,15 @@ class Command { Self.options.insert(.analyze) Self.analysisDir = args[index + 1] } + case "--collect-dirs": + if let index = args.firstIndex(of: arg) { + self.options.insert(.collectDirs) + var i = 1 + while (index + i) < args.count && !args[index + i].starts(with: "-") { + self.collectDirs.append(contentsOf: [args[index + i]]) + i += 1 + } + } default: if !arg.starts(with: "-") { } else { @@ -89,8 +100,9 @@ class Command { } else { CaseFiles.CreateCaseDir() let mainModule = AftermathModule() - mainModule.log("Aftermath Started") + mainModule.log("Aftermath Collection Started") mainModule.addTextToFile(atUrl: CaseFiles.metadataFile, text: "file,birth,modified,accessed,permissions,uid,gid, downloadedFrom") + // System Recon mainModule.log("Started system recon") @@ -119,11 +131,11 @@ class Command { persistenceModule.run() mainModule.log("Finished logging persistence items") - + // FileSystem mainModule.log("Started gathering file system information...") let fileSysModule = FileSystemModule() - fileSysModule.run() + fileSysModule.run(collectDirs: self.collectDirs) mainModule.log("Finished gathering file system information") diff --git a/artifacts/ArtifactsModule.swift b/artifacts/ArtifactsModule.swift index fc4f437..ac19e44 100644 --- a/artifacts/ArtifactsModule.swift +++ b/artifacts/ArtifactsModule.swift @@ -7,15 +7,6 @@ import Foundation - -// tcc -// lsquarantine -// /etc/sudoers -// /etc/host -// /etc/ssh/.* -// ~/.ssh - - class ArtifactsModule: AftermathModule, AMProto { let name = "Artifacts Module" diff --git a/filesystem/CommonDirectories.swift b/filesystem/CommonDirectories.swift index 3ef7d1e..f0eedfe 100644 --- a/filesystem/CommonDirectories.swift +++ b/filesystem/CommonDirectories.swift @@ -11,40 +11,47 @@ class CommonDirectories: FileSystemModule { let writeFile: URL var isAftermath: Bool = false + let collectDirs: [String] - init(writeFile: URL) { + init(writeFile: URL, collectDirs: [String]) { self.writeFile = writeFile + self.collectDirs = collectDirs } - func dumpTmp(tmpDir: String, tmpRawDir: URL) { + func writeTmpPaths(tmpDir: String) { + self.addTextToFile(atUrl: self.writeFile, text: "\n\nContents of \(tmpDir)\n") + for file in filemanager.filesInDirRecursive(path: tmpDir) { if isAftermathDir(directory: file) { continue } - self.copyFileToCase(fileToCopy: file, toLocation: tmpRawDir) + self.addTextToFile(atUrl: self.writeFile, text: "\(file.path)") } } - func dumpTrash(trashRawDir: URL) { - + func writeTrashPaths() { + for user in getBasicUsersOnSystem() { let path = "\(user.homedir)/.Trash" + self.addTextToFile(atUrl: self.writeFile, text: "\n\nContents of \(path)\n") + for file in filemanager.filesInDirRecursive(path: path) { if isAftermathDir(directory: file) { continue } - self.copyFileToCase(fileToCopy: file, toLocation: trashRawDir) + self.addTextToFile(atUrl: self.writeFile, text: "\(file.path)") } } } - func dumpDownloads(downloadsRawDir: URL) { - + func writeDownloadsPaths() { + for user in getBasicUsersOnSystem() { let path = "\(user.homedir)/Downloads" - + self.addTextToFile(atUrl: self.writeFile, text: "\n\nContents of \(path)\n") + for file in filemanager.filesInDirRecursive(path: path) { if isAftermathDir(directory: file) { continue } if file.lastPathComponent == ".DS_Store" { continue } - self.copyFileToCase(fileToCopy: file, toLocation: downloadsRawDir) + self.addTextToFile(atUrl: self.writeFile, text: "\(file.path)") } } } @@ -62,19 +69,39 @@ class CommonDirectories: FileSystemModule { return isAftermath } - override func run() { - self.log("Capturing data from common directories...") + func collectContents(directory: String) { + + let rawDir = self.createNewDir(dir: self.rawDir, dirname: URL(fileURLWithPath: directory).lastPathComponent) + self.addTextToFile(atUrl: self.writeFile, text: "\n\nContents of \(directory)\n") + + for file in filemanager.filesInDirRecursive(path: directory) { + if isAftermathDir(directory: file) { continue } + if file.lastPathComponent == ".DS_Store" { continue } + self.addTextToFile(atUrl: self.writeFile, text: "\(file.path)") + self.copyFileToCase(fileToCopy: file, toLocation: rawDir) + } - self.log("Dumping tmp directory...") - let tmpRawDir = self.createNewDir(dir: self.rawDir, dirname: "tmp_files") - dumpTmp(tmpDir: "/tmp", tmpRawDir: tmpRawDir) + } + + + + func run() { + self.log("Capturing data from common directories...") - self.log("Dumping the Trash...") - let trashRawDir = self.createNewDir(dir: self.rawDir, dirname: "trash") - dumpTrash(trashRawDir: trashRawDir) + if collectDirs != [] { + for dir in collectDirs { + self.log("Dumping the contents from directory \(dir)") + collectContents(directory: dir) + } + } - self.log("Dumping the Downloads directory") - let downloadsRawDir = self.createNewDir(dir: self.rawDir, dirname: "downloads") - dumpDownloads(downloadsRawDir: downloadsRawDir) + self.log("Writing the files in the tmp directory...") + writeTmpPaths(tmpDir: "/tmp") + + self.log("Writing the file names in the Trash...") + writeTrashPaths() + + self.log("Writing the file paths of Downloads directory") + writeDownloadsPaths() } } diff --git a/filesystem/FileSystemModule.swift b/filesystem/FileSystemModule.swift index 19cf6ad..6cad4e1 100644 --- a/filesystem/FileSystemModule.swift +++ b/filesystem/FileSystemModule.swift @@ -22,7 +22,7 @@ class FileSystemModule: AftermathModule, AMProto { // self.deepScan = deepScan // } - func run() { + func run(collectDirs: [String]) { // run browser module let browserModule = BrowserModule() browserModule.run() @@ -34,7 +34,7 @@ class FileSystemModule: AftermathModule, AMProto { // get data from common directories let commonDirFile = self.createNewCaseFile(dirUrl: self.moduleDirRoot, filename: "common_directories.txt") - let common = CommonDirectories(writeFile: commonDirFile) + let common = CommonDirectories(writeFile: commonDirFile, collectDirs: collectDirs) common.run() // get users on system diff --git a/filesystem/FileWalker.swift b/filesystem/FileWalker.swift index f0d70c8..62f4f98 100644 --- a/filesystem/FileWalker.swift +++ b/filesystem/FileWalker.swift @@ -32,7 +32,7 @@ class FileWalker: FileSystemModule { } } - override func run() { + func run() { var directories = ["/tmp", "/opt", "/Library/LaunchDaemons", "/Library/LaunchAgents"] diff --git a/filesystem/Slack.swift b/filesystem/Slack.swift index 75111aa..9a78a6f 100644 --- a/filesystem/Slack.swift +++ b/filesystem/Slack.swift @@ -34,7 +34,7 @@ class Slack: FileSystemModule { } } - override func run() { + func run() { self.log("Collecting Slack information") extractSlackPrefs() } From 2b04228931dbdca959bf5caf34137a87eb147f18 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 11:22:58 -0700 Subject: [PATCH 12/31] made note that browsers need to be closed --- filesystem/browsers/BrowserModule.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/filesystem/browsers/BrowserModule.swift b/filesystem/browsers/BrowserModule.swift index 1dc9e6f..64eb4db 100644 --- a/filesystem/browsers/BrowserModule.swift +++ b/filesystem/browsers/BrowserModule.swift @@ -14,7 +14,6 @@ class BrowserModule: AftermathModule, AMProto { var description = "A module that gathers artifacts from different web browsers" lazy var moduleDirRoot = self.createNewDirInRoot(dirName: dirName) - func run() { let firefoxDir = self.createNewDir(dir: moduleDirRoot, dirname: "Firefox") @@ -22,7 +21,7 @@ class BrowserModule: AftermathModule, AMProto { let safariDir = self.createNewDir(dir: moduleDirRoot, dirname: "Safari") let writeFile = self.createNewCaseFile(dirUrl: moduleDirRoot, filename: "browsers.txt") - self.log("Collecting browser information...") + self.log("Collecting browser information. Make sure browsers are closed to prevent file data from being locked.") // Check if Firefox is installed @@ -40,4 +39,3 @@ class BrowserModule: AftermathModule, AMProto { } } - From 839b03c8e0b5854aa2189f7fe7a78c148a76bb43 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 11:23:05 -0700 Subject: [PATCH 13/31] minor tweak --- filesystem/CommonDirectories.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/filesystem/CommonDirectories.swift b/filesystem/CommonDirectories.swift index f0eedfe..343cc8d 100644 --- a/filesystem/CommonDirectories.swift +++ b/filesystem/CommonDirectories.swift @@ -83,8 +83,6 @@ class CommonDirectories: FileSystemModule { } - - func run() { self.log("Capturing data from common directories...") From 2e5653c8dd99030744638a1b9ead393dca67bf97 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 12:32:47 -0700 Subject: [PATCH 14/31] added log line to warn users of zipping times --- aftermath/CaseFiles.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aftermath/CaseFiles.swift b/aftermath/CaseFiles.swift index b40f71f..de44d9c 100644 --- a/aftermath/CaseFiles.swift +++ b/aftermath/CaseFiles.swift @@ -36,6 +36,8 @@ struct CaseFiles { static func MoveCaseDir(outputDir: String) { + print("Moving the case directory from its temporary location. This may take some time. Please wait...") + var endURL: URL if outputDir == "default" { From 5c47abdf0edcd14cd46627791823634db2ec67ee Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 12:33:18 -0700 Subject: [PATCH 15/31] ignoring dumping raw contents. added arg for user to specify space-delimited array of directories --- aftermath/Command.swift | 7 +++++-- filesystem/CommonDirectories.swift | 12 +++++------- filesystem/FileSystemModule.swift | 9 ++------- filesystem/FileWalker.swift | 2 +- filesystem/Slack.swift | 2 +- 5 files changed, 14 insertions(+), 18 deletions(-) diff --git a/aftermath/Command.swift b/aftermath/Command.swift index d0483ff..df01b93 100644 --- a/aftermath/Command.swift +++ b/aftermath/Command.swift @@ -135,7 +135,7 @@ class Command { // FileSystem mainModule.log("Started gathering file system information...") let fileSysModule = FileSystemModule() - fileSysModule.run(collectDirs: self.collectDirs) + fileSysModule.run() mainModule.log("Finished gathering file system information") @@ -204,7 +204,10 @@ class Command { print(" usage: -o Users/user/Desktop") print("--analyze -> Analyze the results of the Aftermath results") print(" usage: --analyze ") - print("--cleanup -> Remove Aftermath Response Folders") + print("--collect-dirs -> specify locations of (space-separated) directories to dump those raw files") + print(" usage: --collect-dirs /Users//Downloads /tmp") + print("--pretty -> Colorize Terminal output") + print("--cleanup -> Remove Aftermath Folders in default locations") exit(1) } } diff --git a/filesystem/CommonDirectories.swift b/filesystem/CommonDirectories.swift index 343cc8d..85fd560 100644 --- a/filesystem/CommonDirectories.swift +++ b/filesystem/CommonDirectories.swift @@ -11,11 +11,9 @@ class CommonDirectories: FileSystemModule { let writeFile: URL var isAftermath: Bool = false - let collectDirs: [String] - init(writeFile: URL, collectDirs: [String]) { + init(writeFile: URL) { self.writeFile = writeFile - self.collectDirs = collectDirs } func writeTmpPaths(tmpDir: String) { @@ -83,11 +81,11 @@ class CommonDirectories: FileSystemModule { } - func run() { + override func run() { self.log("Capturing data from common directories...") - - if collectDirs != [] { - for dir in collectDirs { + + if Command.options.contains(.collectDirs) { + for dir in Command.collectDirs { self.log("Dumping the contents from directory \(dir)") collectContents(directory: dir) } diff --git a/filesystem/FileSystemModule.swift b/filesystem/FileSystemModule.swift index 6cad4e1..5e1ce21 100644 --- a/filesystem/FileSystemModule.swift +++ b/filesystem/FileSystemModule.swift @@ -16,13 +16,8 @@ class FileSystemModule: AftermathModule, AMProto { lazy var moduleDirRoot = self.createNewDirInRoot(dirName: dirName) lazy var rawDir = self.createNewDir(dir: moduleDirRoot, dirname: "raw") -// let deepScan: Bool -// -// init(deepScan: Bool) { -// self.deepScan = deepScan -// } - func run(collectDirs: [String]) { + func run() { // run browser module let browserModule = BrowserModule() browserModule.run() @@ -34,7 +29,7 @@ class FileSystemModule: AftermathModule, AMProto { // get data from common directories let commonDirFile = self.createNewCaseFile(dirUrl: self.moduleDirRoot, filename: "common_directories.txt") - let common = CommonDirectories(writeFile: commonDirFile, collectDirs: collectDirs) + let common = CommonDirectories(writeFile: commonDirFile) common.run() // get users on system diff --git a/filesystem/FileWalker.swift b/filesystem/FileWalker.swift index 62f4f98..f0d70c8 100644 --- a/filesystem/FileWalker.swift +++ b/filesystem/FileWalker.swift @@ -32,7 +32,7 @@ class FileWalker: FileSystemModule { } } - func run() { + override func run() { var directories = ["/tmp", "/opt", "/Library/LaunchDaemons", "/Library/LaunchAgents"] diff --git a/filesystem/Slack.swift b/filesystem/Slack.swift index 9a78a6f..75111aa 100644 --- a/filesystem/Slack.swift +++ b/filesystem/Slack.swift @@ -34,7 +34,7 @@ class Slack: FileSystemModule { } } - func run() { + override func run() { self.log("Collecting Slack information") extractSlackPrefs() } From 01e1aec25c3a5a59e3716bfa9216a52b5d24176a Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 13:16:37 -0700 Subject: [PATCH 16/31] added comment --- aftermath/CaseFiles.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aftermath/CaseFiles.swift b/aftermath/CaseFiles.swift index de44d9c..b979b44 100644 --- a/aftermath/CaseFiles.swift +++ b/aftermath/CaseFiles.swift @@ -36,7 +36,7 @@ struct CaseFiles { static func MoveCaseDir(outputDir: String) { - print("Moving the case directory from its temporary location. This may take some time. Please wait...") + print("Moving the collection directory from its temporary location. This may take some time. Please wait...") var endURL: URL @@ -63,6 +63,8 @@ struct CaseFiles { let endURL = URL(fileURLWithPath: "/tmp/\(analysisCaseDir.lastPathComponent)") let zippedURL = endURL.appendingPathExtension("zip") + print("Moving the analysis directory from its temporary location. This may take some time. Please wait...") + do { try fm.zipItem(at: analysisCaseDir, to: endURL, shouldKeepParent: true, compressionMethod: .deflate) try fm.moveItem(at: endURL, to: zippedURL) From 533a2593b2061f847fa4b33712b30f9d6e7aebad Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 13:26:37 -0700 Subject: [PATCH 17/31] updated help menu and readme --- Readme.md | 14 ++++++++------ aftermath/Command.swift | 5 +++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Readme.md b/Readme.md index e4c9e95..fb01535 100644 --- a/Readme.md +++ b/Readme.md @@ -1,5 +1,5 @@ # Aftermath - + ## About Aftermath is a Swift-based, open-source incident response framework. @@ -47,13 +47,15 @@ Example ## Help Menu ```bash --o -> specify an output location for Aftermath collection results +-o -> specify an output location for Aftermath results (defaults to /tmp) usage: -o Users/user/Desktop --analyze -> Analyze the results of the Aftermath results - usage: --analyze ---cleanup -> Remove Aftermath Response Folders ---deep -> Perform a deep scan of the file system for modified and accessed timestamped metadata - WARNING: This will be a time-intensive scan. + usage: --analyze +--collect-dirs -> specify locations of (space-separated) directories to dump those raw files + usage: --collect-dirs /Users//Downloads /tmp +--deep -> performs deep scan and captures metadata from Users entire directory (WARNING: this may be time-consuming) +--pretty -> colorize Terminal output +--cleanup -> remove Aftermath Folders in default locations ``` ## Contributors diff --git a/aftermath/Command.swift b/aftermath/Command.swift index df01b93..9cbb313 100644 --- a/aftermath/Command.swift +++ b/aftermath/Command.swift @@ -206,8 +206,9 @@ class Command { print(" usage: --analyze ") print("--collect-dirs -> specify locations of (space-separated) directories to dump those raw files") print(" usage: --collect-dirs /Users//Downloads /tmp") - print("--pretty -> Colorize Terminal output") - print("--cleanup -> Remove Aftermath Folders in default locations") + print("--deep -> performs deep scan and captures metadata from Users entire directory (WARNING: this may be time-consuming)") + print("--pretty -> colorize Terminal output") + print("--cleanup -> remove Aftermath Folders in default locations") exit(1) } } From da9c99b249d746779e96b56acef8e1982744590f Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 13:28:48 -0700 Subject: [PATCH 18/31] new name for readme --- Readme.md => README.md | 0 aftermath.xcodeproj/project.pbxproj | 4 +- .../xcschemes/aftermath-collect-dirs.xcscheme | 97 +++++++++++++++++++ 3 files changed, 99 insertions(+), 2 deletions(-) rename Readme.md => README.md (100%) create mode 100644 aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-collect-dirs.xcscheme diff --git a/Readme.md b/README.md similarity index 100% rename from Readme.md rename to README.md diff --git a/aftermath.xcodeproj/project.pbxproj b/aftermath.xcodeproj/project.pbxproj index a32f5f7..520184a 100644 --- a/aftermath.xcodeproj/project.pbxproj +++ b/aftermath.xcodeproj/project.pbxproj @@ -114,7 +114,7 @@ A3745357275730870074B65C /* LaunchItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchItems.swift; sourceTree = ""; }; A3745359275735B40074B65C /* LoginHooks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginHooks.swift; sourceTree = ""; }; A374535C2757C1300074B65C /* FileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManager.swift; sourceTree = ""; }; - A3A3A3CD274754B400F8F557 /* Readme.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Readme.md; sourceTree = ""; }; + A3A3A3CD274754B400F8F557 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; A3CD4E52274434EE00869ECB /* aftermath */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = aftermath; sourceTree = BUILT_PRODUCTS_DIR; }; A3CD4E55274434EE00869ECB /* Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -290,7 +290,7 @@ A029AB132876A01300649701 /* processes */, 70A44401275707800035F40E /* systemRecon */, 8ABB9E2927568E9000C0ADD7 /* unifiedlogs */, - A3A3A3CD274754B400F8F557 /* Readme.md */, + A3A3A3CD274754B400F8F557 /* README.md */, A3CD4E53274434EE00869ECB /* Products */, ); sourceTree = ""; diff --git a/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-collect-dirs.xcscheme b/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-collect-dirs.xcscheme new file mode 100644 index 0000000..6450a9e --- /dev/null +++ b/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-collect-dirs.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From f3e0216a6d61ec9298f3d1b6e8b99940259ea0c1 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 13:47:34 -0700 Subject: [PATCH 19/31] updated readme --- README.md | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index fb01535..27a769a 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ Aftermath is a Swift-based, open-source incident response framework. Aftermath can be leveraged by defenders in order to collect and subsequently analyze the data from the compromised host. Aftermath can be deployed from an MDM (ideally), but it can also run independently from the infected user's command line. +Aftermath first runs a series of modules for collection. The output of this will either be written to the location of your choice, via the `-o` or `--output` option, or by default, it is written to the `/tmp` directory. + +Once collection is complete, the final zip/archive file can be pulled from the end user's disk. This file can then be analyzed using the `--analyze` argument pointed at the archive file. The results of this will be written to the `/tmp` directory. The administrator can then unzip that analysis directory and see a parsed view of the locally collected databases, a timeline of files with the file creation, last accessed, and last modified dates (if they're available), and a storyline which includes the file metadata, database changes, and browser information to potentially track down the infection vector. + ## Build To build Aftermath locally, clone it from the repository @@ -29,39 +33,53 @@ sudo ./aftermath ``` ## Usage -Aftermath needs to be root, as well as have full disk access (FDA) in order to run. FDA can be granted to the Terminal application in which it is running. If using an MDM to deploy Aftermath, FDA can be granted through PPPC in your MDM solution. +Aftermath needs to be root, as well as have *full disk access (FDA)* in order to run. FDA can be granted to the Terminal application in which it is running. The default usage of Aftermath runs ```bash -./aftermath +sudo ./aftermath ``` To specify certain options ```bash -./aftermath [option1] [option2] +sudo ./aftermath [option1] [option2] +``` +Examples +```bash +sudo ./aftermath -o /Users/user/Desktop --deep ``` -Example ```bash -./aftermath -o /Users/user/Desktop --deep +sudo ./aftermath --analyze ``` -## Help Menu +If deploying from an MDM solution, deploy a PPPC configuration profile with the Terminal given full disk access. You can then push a policy to deploy and trigger aftermath. +## Releases +There is an Aftermath.pkg available under [Releases](https://github.com/jamf/aftermath/releases). This pkg is signed and notarized. It will install the aftermath binary at `/usr/local/bin/`. This would be the ideal way to deploy via MDM. Since this is installed in `bin`, you can then run aftermath like ```bash --o -> specify an output location for Aftermath results (defaults to /tmp) - usage: -o Users/user/Desktop ---analyze -> Analyze the results of the Aftermath results - usage: --analyze +sudo aftermath [option1] [option2] +``` + +## Help Menu + +``` +--analyze -> analyze the results of the Aftermath results + usage: --analyze --collect-dirs -> specify locations of (space-separated) directories to dump those raw files - usage: --collect-dirs /Users//Downloads /tmp ---deep -> performs deep scan and captures metadata from Users entire directory (WARNING: this may be time-consuming) + usage: --collect-dirs +--deep or -d -> perform a deep scan of the file system for modified and accessed timestamped metadata + WARNING: This will be a time-intensive, memory-consuming scan. +-o or --output -> specify an output location for Aftermath collection results (defaults to /tmp) + usage: -o Users/user/Desktop --pretty -> colorize Terminal output ---cleanup -> remove Aftermath Folders in default locations +--cleanup -> remove Aftermath Response Folders + ``` ## Contributors - Stuart Ashenbrenner -- Matt Benyo - Jaron Bradley +- Maggie Zirnhelt +- Matt Benyo - Ferdous Saljooki ## Thank You From 523bde4edba9c517084c341365360c0fec31a91c Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 16:06:34 -0700 Subject: [PATCH 20/31] updated metadata collection to use kernel level instead of spotlight --- .../xcschemes/aftermath-analysis.xcscheme | 2 +- aftermath/Module.swift | 111 ++++++++---------- helpers/CHelpers.swift | 25 +++- 3 files changed, 75 insertions(+), 63 deletions(-) diff --git a/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-analysis.xcscheme b/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-analysis.xcscheme index d901152..cc3f252 100644 --- a/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-analysis.xcscheme +++ b/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-analysis.xcscheme @@ -57,7 +57,7 @@ isEnabled = "YES"> diff --git a/aftermath/Module.swift b/aftermath/Module.swift index ea4242f..6457222 100644 --- a/aftermath/Module.swift +++ b/aftermath/Module.swift @@ -188,64 +188,56 @@ class AftermathModule { var lastModifiedTimestamp: String var lastAccessedTimestamp: String + if fromFile.path.contains(",") { + let sanitized = fromFile.path.replacingOccurrences(of: ",", with: " ") + metadata = "\(sanitized)," + } else { + metadata = "\(fromFile.path)," + } + + if let birth = helpers.getFileBirth(fromFile: fromFile) { + birthTimestamp = Aftermath.dateFromEpochTimestamp(timeStamp: birth) + metadata.append("\(birthTimestamp),") + } else { + metadata.append("unknwon,") + } + + if let lastModified = helpers.getFileLastModified(fromFile: fromFile) { + lastModifiedTimestamp = Aftermath.dateFromEpochTimestamp(timeStamp: lastModified) + metadata.append("\(lastModifiedTimestamp),") + } else { + metadata.append("unknown,") + } + + if let lastAccessed = helpers.getFileLastAccessed(fromFile: fromFile) { + lastAccessedTimestamp = Aftermath.dateFromEpochTimestamp(timeStamp: lastAccessed) + metadata.append("\(lastAccessedTimestamp),") + } else { + metadata.append("unknown,") + } + + if let permissions = helpers.getFilePermissions(fromFile: fromFile) { + metadata.append("\(String(permissions).dropFirst(3)),") + } else { + metadata.append("unknwon,") + } + + if let uid = helpers.getFileUid(fromFile: fromFile) { + metadata.append("\(uid),") + } else { + metadata.append("unknown,") + } + + if let gid = helpers.getFileGid(fromFile: fromFile) { + metadata.append("\(gid),") + } else { + metadata.append("unknown,") + } if let mditem = MDItemCreate(nil, fromFile.path as CFString), let mdnames = MDItemCopyAttributeNames(mditem), let mdattrs = MDItemCopyAttributes(mditem, mdnames) as? [String:Any] { - if fromFile.path.contains(",") { - let sanitized = fromFile.path.replacingOccurrences(of: ",", with: " ") - metadata = "\(sanitized)," - } else { - metadata = "\(fromFile.path)," - } - - if let birth = mdattrs[kMDItemContentCreationDate as String] { - birthTimestamp = Aftermath.standardizeMetadataTimestamp(timeStamp: String(describing: birth)) - metadata.append("\(birthTimestamp),") - } else if let birthFS = mdattrs[kMDItemFSCreationDate as String] { - birthTimestamp = Aftermath.standardizeMetadataTimestamp(timeStamp: String(describing: birthFS)) - metadata.append("\(birthTimestamp),") - } else { - metadata.append("unknown,") - } - - if let lastModified = mdattrs[kMDItemContentModificationDate as String] { - lastModifiedTimestamp = Aftermath.standardizeMetadataTimestamp(timeStamp: String(describing: lastModified)) - metadata.append("\(lastModifiedTimestamp),") - } else if let lastModifiedFS = mdattrs[kMDItemFSContentChangeDate as String] { - lastModifiedTimestamp = Aftermath.standardizeMetadataTimestamp(timeStamp: String(describing: lastModifiedFS)) - metadata.append("\(lastModifiedTimestamp),") - } else { - metadata.append("unknown,") - } - - if let lastAccessed = mdattrs[kMDItemLastUsedDate as String] { - lastAccessedTimestamp = Aftermath.standardizeMetadataTimestamp(timeStamp: String(describing: lastAccessed)) - metadata.append("\(lastAccessedTimestamp),") - } else { - metadata.append("unknown,") - } - - - if let permissions = helpers.getFilePermissions(fromFile: fromFile) { - metadata.append("\(String(permissions).dropFirst(3)),") - } else { - metadata.append("unknwon,") - } - - if let uid = mdattrs[kMDItemFSOwnerUserID as String] { - metadata.append("\(uid),") - } else { - metadata.append("unknwon,") - } - - if let gid = mdattrs[kMDItemFSOwnerGroupID as String] { - metadata.append("\(gid),") - } else { - metadata.append("unknown,") - } - // this is last in case array is longer than 1 or 2 items if let downloadedFrom = mdattrs[kMDItemWhereFroms as String] { if let downloadedArr = downloadedFrom as Any as? [String] { @@ -253,16 +245,13 @@ class AftermathModule { metadata.append("\(downloaded),") } } - } else { - metadata.append("unknown,") } + + } else { + metadata.append("unknown,") + } - - self.addTextToFile(atUrl: CaseFiles.metadataFile, text: metadata) - - } else { - print("Can't get attributes for \(fromFile.path)") - } + self.addTextToFile(atUrl: CaseFiles.metadataFile, text: metadata) } func log(_ note: String, displayOnly: Bool = false, file: String = #file) { diff --git a/helpers/CHelpers.swift b/helpers/CHelpers.swift index d82096c..46115b0 100644 --- a/helpers/CHelpers.swift +++ b/helpers/CHelpers.swift @@ -38,8 +38,31 @@ class CHelpers { return Int(status.st_gid) } } -} + + func getFileBirth(fromFile: URL) -> Double? { + return fromFile.path.withCString { cs in + var status = stat() + stat(cs, &status) + return Double(status.st_birthtimespec.tv_sec) + } + } + func getFileLastAccessed(fromFile: URL) -> Double? { + return fromFile.path.withCString { cs in + var status = stat() + stat(cs, &status) + return Double(status.st_atimespec.tv_sec) + } + } + + func getFileLastModified(fromFile: URL) -> Double? { + return fromFile.path.withCString { cs in + var status = stat() + stat(cs, &status) + return Double(status.st_mtimespec.tv_sec) + } + } +} struct FileMode: OptionSet { let rawValue: mode_t From 0eca48868c0313e66c461af78b8b9ac9426ab4a6 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 20:16:45 -0700 Subject: [PATCH 21/31] removed extra comment --- filesystem/browsers/Safari.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/filesystem/browsers/Safari.swift b/filesystem/browsers/Safari.swift index 1f7e2d8..d149416 100644 --- a/filesystem/browsers/Safari.swift +++ b/filesystem/browsers/Safari.swift @@ -100,7 +100,6 @@ class Safari: BrowserModule { let dateString = dateFormatter.string(from: dateTimestamp as Date) timestamp = dateString -// timestamp = Aftermath.standardizeMetadataTimestamp(timeStamp: value as! String) } if key as! String == "DownloadEntryURL" { url = value as! String From c371473f1ce92e723de31572ad42604bc45b2390 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 20:16:57 -0700 Subject: [PATCH 22/31] include chrome extensions in collection --- filesystem/browsers/Chrome.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/filesystem/browsers/Chrome.swift b/filesystem/browsers/Chrome.swift index 228edc4..5367083 100644 --- a/filesystem/browsers/Chrome.swift +++ b/filesystem/browsers/Chrome.swift @@ -180,11 +180,24 @@ class Chrome: BrowserModule { self.addTextToFile(atUrl: self.writeFile, text: "\n----- End of Chrome Cookies -----\n") } + func captureExtensions() { + let chromeExtensionDir = self.createNewDir(dir: self.chromeDir, dirname: "extensions") + + for user in getBasicUsersOnSystem() { + let path = "\(user.homedir)/Library/Application Support/Google/Chrome/Default/Extensions" + + for file in filemanager.filesInDirRecursive(path: path) { + self.copyFileToCase(fileToCopy: file, toLocation: chromeExtensionDir) + } + } + } + override func run() { self.log("Collecting Chrome browser information...") gatherHistory() dumpDownloads() dumpPreferences() dumpCookies() + captureExtensions() } } From 004e6351b8f15cd6e61d0c54603a4f0aad334310 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 20:28:33 -0700 Subject: [PATCH 23/31] updated to ignore schemes --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c9f30cb..4d01d0e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ DerivedData/ *.perspectivev3 !default.perspectivev3 xcuserdata/ +xcschemes/ ## Other *.moved-aside From db11a10f34924a55ccb55f0aa3b7e3796a34cd44 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 20:31:06 -0700 Subject: [PATCH 24/31] another gitignore add --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4d01d0e..9aef7ef 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ xcschemes/ *.xccheckout *.xcscmblueprint *.profraw +*.xcscheme ## Obj-C/Swift specific *.hmap From 26b7b49622c46a6cdf4a11c4eafaf2ac86f85a73 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 20:33:26 -0700 Subject: [PATCH 25/31] updated --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9aef7ef..d815c71 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,6 @@ DerivedData/ *.perspectivev3 !default.perspectivev3 xcuserdata/ -xcschemes/ ## Other *.moved-aside From 221379f20f35c5552a0764e874c33434e70d5531 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 20:36:01 -0700 Subject: [PATCH 26/31] removed schemes --- .../xcschemes/aftermath-analysis.xcscheme | 89 ----------------- .../xcschemes/aftermath-clean.xcscheme | 84 ---------------- .../xcschemes/aftermath-collect-dirs.xcscheme | 97 ------------------- .../xcshareddata/xcschemes/aftermath.xcscheme | 89 ----------------- 4 files changed, 359 deletions(-) delete mode 100644 aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-analysis.xcscheme delete mode 100644 aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-clean.xcscheme delete mode 100644 aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-collect-dirs.xcscheme delete mode 100644 aftermath.xcodeproj/xcshareddata/xcschemes/aftermath.xcscheme diff --git a/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-analysis.xcscheme b/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-analysis.xcscheme deleted file mode 100644 index cc3f252..0000000 --- a/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-analysis.xcscheme +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-clean.xcscheme b/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-clean.xcscheme deleted file mode 100644 index d061fdf..0000000 --- a/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-clean.xcscheme +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-collect-dirs.xcscheme b/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-collect-dirs.xcscheme deleted file mode 100644 index 6450a9e..0000000 --- a/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath-collect-dirs.xcscheme +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath.xcscheme b/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath.xcscheme deleted file mode 100644 index 062be28..0000000 --- a/aftermath.xcodeproj/xcshareddata/xcschemes/aftermath.xcscheme +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 2be3e83936ed4da080c0642eec26f83ea35d0d5b Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 29 Aug 2022 20:43:08 -0700 Subject: [PATCH 27/31] cleanup --- analysis/LogParser.swift | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/analysis/LogParser.swift b/analysis/LogParser.swift index 02fb87e..8a0df4e 100644 --- a/analysis/LogParser.swift +++ b/analysis/LogParser.swift @@ -10,8 +10,6 @@ import Foundation class LogParser: AftermathModule { lazy var logsFile = self.createNewCaseFile(dirUrl: CaseFiles.analysisCaseDir, filename: "logs.csv") -// lazy var systemLogParsedFile = self.createNewCaseFile(dirUrl: CaseFiles.analysisCaseDir, filename: "system_log.csv") -// lazy var logTimeline = self.createNewCaseFile(dirUrl: CaseFiles.analysisCaseDir, filename: "log_timeline.csv") let collectionDir: String let storylineFile: URL @@ -34,14 +32,12 @@ class LogParser: AftermathModule { guard let date = splitLine[safe: 0] else { continue } guard let time = splitLine[safe: 1] else { continue } - let unformattedDate = date + "T" + time // "2022-03-1516:22:55-07" + let unformattedDate = date + "T" + time // "ex: 2022-03-1516:22:55-07" let dateFormatter = DateFormatter() dateFormatter.locale = Locale(identifier: "en_US") dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) - - var info = "" for i in 0...splitLine.count - 1 { @@ -57,16 +53,6 @@ class LogParser: AftermathModule { let text = "\(formattedDate), INSTALL, \(info)" self.addTextToFile(atUrl: logsFile, text: text) self.addTextToFile(atUrl: self.storylineFile, text: text) - - - - - -// if let dateString = dateFormatter.date(from: unformattedDate) { - -// } else { -// continue -// } } } catch { print("Unable to parse contents") @@ -97,7 +83,7 @@ class LogParser: AftermathModule { dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) let currentYear = Calendar(identifier: .gregorian).dateComponents([.year], from: .now).year - guard let month = splitLine[safe: 0] else { continue } // Aug + guard let month = splitLine[safe: 0] else { continue } // Feb guard let date = splitLine[safe: 1] else { continue } // 26 guard let time = splitLine[safe: 2] else { continue } // 00:17:38 @@ -111,17 +97,15 @@ class LogParser: AftermathModule { sanatizeInfo(&info) - let unformattedTimestamp = "\(month) \(date) \(currentYear!) \(time)" - guard let formatted = dateFormatter.date(from: unformattedTimestamp) else { continue } // 2022-08-26 00:01:40 UTC + guard let formatted = dateFormatter.date(from: unformattedTimestamp) else { continue } //Ex: 2022-08-26 00:01:40 UTC dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" let dateString = dateFormatter.string(from: formatted) let text = "\(dateString), SYSLOG, \(info)" self.addTextToFile(atUrl: logsFile, text: text) self.addTextToFile(atUrl: storylineFile, text: text) - } } catch { print("Unable to parse contentes") From 86a9a1c571eb12fe21047cf1e292df32a35b9552 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Tue, 30 Aug 2022 11:33:17 -0700 Subject: [PATCH 28/31] added aftermath banner --- aftermath/Command.swift | 13 +++++++++++++ aftermath/aftermath.entitlements | 5 ----- aftermath/aftermathRelease.entitlements | 5 ----- 3 files changed, 13 insertions(+), 10 deletions(-) delete mode 100644 aftermath/aftermath.entitlements delete mode 100644 aftermath/aftermathRelease.entitlements diff --git a/aftermath/Command.swift b/aftermath/Command.swift index 9cbb313..802c0cb 100644 --- a/aftermath/Command.swift +++ b/aftermath/Command.swift @@ -69,6 +69,8 @@ class Command { } static func start() { + printBanner() + if Self.options.contains(.analyze) { CaseFiles.CreateAnalysisCaseDir() @@ -211,4 +213,15 @@ class Command { print("--cleanup -> remove Aftermath Folders in default locations") exit(1) } + + static func printBanner() { + print(#""" + ___ ______ __ __ + / | / __/ /____ _________ ___ ____ _/ /_/ /_ + / /| | / /_/ __/ _ \/ ___/ __ `__ \/ __ `/ __/ __ \ + / ___ |/ __/ /_/ __/ / / / / / / / /_/ / /_/ / / / + /_/ |_/_/ \__/\___/_/ /_/ /_/ /_/\__,_/\__/_/ /_/ + + """#) + } } diff --git a/aftermath/aftermath.entitlements b/aftermath/aftermath.entitlements deleted file mode 100644 index 0c67376..0000000 --- a/aftermath/aftermath.entitlements +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/aftermath/aftermathRelease.entitlements b/aftermath/aftermathRelease.entitlements deleted file mode 100644 index 0c67376..0000000 --- a/aftermath/aftermathRelease.entitlements +++ /dev/null @@ -1,5 +0,0 @@ - - - - - From dfcc1ef867ce87229937443b4baaf9f5385d992b Mon Sep 17 00:00:00 2001 From: stuartjash Date: Tue, 30 Aug 2022 11:33:30 -0700 Subject: [PATCH 29/31] removed unnecessary entitlements --- aftermath.entitlements | 5 ----- aftermath.xcodeproj/project.pbxproj | 10 ++++------ aftermathRelease.entitlements | 5 ----- 3 files changed, 4 insertions(+), 16 deletions(-) delete mode 100644 aftermath.entitlements delete mode 100644 aftermathRelease.entitlements diff --git a/aftermath.entitlements b/aftermath.entitlements deleted file mode 100644 index 0c67376..0000000 --- a/aftermath.entitlements +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/aftermath.xcodeproj/project.pbxproj b/aftermath.xcodeproj/project.pbxproj index 520184a..8eefc76 100644 --- a/aftermath.xcodeproj/project.pbxproj +++ b/aftermath.xcodeproj/project.pbxproj @@ -94,7 +94,6 @@ A08342D7284E48FC005E437A /* LogFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogFiles.swift; sourceTree = ""; }; A0879956275AD2DC00E885BC /* SystemConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemConfig.swift; sourceTree = ""; }; A09B239B2848F6050062D592 /* Periodic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Periodic.swift; sourceTree = ""; }; - A0A1AE4B288ADD85004B2BE5 /* aftermathRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = aftermathRelease.entitlements; sourceTree = ""; }; A0C930D328A4318F0011FB87 /* Timeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Timeline.swift; sourceTree = ""; }; A0D6D54227F76C58002BB3C8 /* Cron.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cron.swift; sourceTree = ""; }; A0D6D54627FE147D002BB3C8 /* Overrides.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Overrides.swift; sourceTree = ""; }; @@ -107,7 +106,6 @@ A0E1E3F5275ED2E4008D0DC6 /* NetworkModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkModule.swift; sourceTree = ""; }; A0E1E3F7275ED35D008D0DC6 /* NetworkConnections.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConnections.swift; sourceTree = ""; }; A0E22EF1285CD60A003A411A /* CommonDirectories.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonDirectories.swift; sourceTree = ""; }; - A0E46B8A288F55A600975EC8 /* aftermath.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = aftermath.entitlements; sourceTree = ""; }; A0FAEEFD28B94B2C00AC655F /* LogParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogParser.swift; sourceTree = ""; }; A3046F8D27627DAC0069AA21 /* Module.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Module.swift; sourceTree = ""; }; A3046F8F2763AE5E0069AA21 /* CaseFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaseFiles.swift; sourceTree = ""; }; @@ -306,8 +304,6 @@ A3CD4E54274434EE00869ECB /* aftermath */ = { isa = PBXGroup; children = ( - A0E46B8A288F55A600975EC8 /* aftermath.entitlements */, - A0A1AE4B288ADD85004B2BE5 /* aftermathRelease.entitlements */, A3CD4E55274434EE00869ECB /* Command.swift */, 8ABB9E302756D2B500C0ADD7 /* Aftermath.swift */, A3046F8D27627DAC0069AA21 /* Module.swift */, @@ -465,6 +461,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_HARDENED_RUNTIME = YES; @@ -527,6 +524,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_HARDENED_RUNTIME = YES; @@ -554,8 +552,8 @@ buildSettings = { ARCHS = "$(ARCHS_STANDARD)"; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = aftermath/aftermath.entitlements; CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = 6PV5YF2UES; ENABLE_HARDENED_RUNTIME = YES; @@ -580,8 +578,8 @@ buildSettings = { ARCHS = "$(ARCHS_STANDARD)"; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = aftermath/aftermathRelease.entitlements; CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = 6PV5YF2UES; ENABLE_HARDENED_RUNTIME = YES; diff --git a/aftermathRelease.entitlements b/aftermathRelease.entitlements deleted file mode 100644 index 0c67376..0000000 --- a/aftermathRelease.entitlements +++ /dev/null @@ -1,5 +0,0 @@ - - - - - From 356fa8a56d9ba276c01a33ebc73ec94cd5689e0b Mon Sep 17 00:00:00 2001 From: stuartjash Date: Tue, 30 Aug 2022 11:34:52 -0700 Subject: [PATCH 30/31] added ascii art to readme --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ae18ef1..5e0669c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -# Aftermath + ___ ______ __ __ + / | / __/ /____ _________ ___ ____ _/ /_/ /_ + / /| | / /_/ __/ _ \/ ___/ __ `__ \/ __ `/ __/ __ \ + / ___ |/ __/ /_/ __/ / / / / / / / /_/ / /_/ / / / + /_/ |_/_/ \__/\___/_/ /_/ /_/ /_/\__,_/\__/_/ /_/ ## About Aftermath is a Swift-based, open-source incident response framework. From aebdf8e171d32e04b87feb6b5c8288963d419509 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Tue, 30 Aug 2022 12:24:46 -0700 Subject: [PATCH 31/31] added xprotect remediator to unified log and parser --- aftermath.xcodeproj/project.pbxproj | 8 ++--- analysis/LogParser.swift | 47 +++++++++++++++++++++++++++-- analysis/Storyline.swift | 3 +- unifiedlogs/UnifiedLogModule.swift | 3 +- 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/aftermath.xcodeproj/project.pbxproj b/aftermath.xcodeproj/project.pbxproj index 8eefc76..14f34a6 100644 --- a/aftermath.xcodeproj/project.pbxproj +++ b/aftermath.xcodeproj/project.pbxproj @@ -461,7 +461,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; + CODE_SIGN_INJECT_BASE_ENTITLEMENTS = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_HARDENED_RUNTIME = YES; @@ -524,7 +524,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; + CODE_SIGN_INJECT_BASE_ENTITLEMENTS = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_HARDENED_RUNTIME = YES; @@ -553,7 +553,7 @@ ARCHS = "$(ARCHS_STANDARD)"; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Developer ID Application"; - CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; + CODE_SIGN_INJECT_BASE_ENTITLEMENTS = YES; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = 6PV5YF2UES; ENABLE_HARDENED_RUNTIME = YES; @@ -579,7 +579,7 @@ ARCHS = "$(ARCHS_STANDARD)"; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Developer ID Application"; - CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; + CODE_SIGN_INJECT_BASE_ENTITLEMENTS = YES; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = 6PV5YF2UES; ENABLE_HARDENED_RUNTIME = YES; diff --git a/analysis/LogParser.swift b/analysis/LogParser.swift index 8a0df4e..f8d2f09 100644 --- a/analysis/LogParser.swift +++ b/analysis/LogParser.swift @@ -32,7 +32,7 @@ class LogParser: AftermathModule { guard let date = splitLine[safe: 0] else { continue } guard let time = splitLine[safe: 1] else { continue } - let unformattedDate = date + "T" + time // "ex: 2022-03-1516:22:55-07" + let unformattedDate = date + "T" + time // ex: 2022-03-15T16:22:55-07 let dateFormatter = DateFormatter() dateFormatter.locale = Locale(identifier: "en_US") dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" @@ -108,7 +108,47 @@ class LogParser: AftermathModule { self.addTextToFile(atUrl: storylineFile, text: text) } } catch { - print("Unable to parse contentes") + print("Unable to parse contents") + } + } + + func parseXProtectRemediatorLog() { + + let xprotectremLog = "\(collectionDir)/UnifiedLog/xprotect_remediator.txt" + + do { + let contents = try String(contentsOf: URL(fileURLWithPath: xprotectremLog)) + let remediatorLogContents = contents.components(separatedBy: "\n") + + for ind in 1...remediatorLogContents.count - 1 { + let splitLine = remediatorLogContents[ind].components(separatedBy: " ") + + guard let date = splitLine[safe: 0] else { continue } + guard let time = splitLine[safe: 1] else { continue } + let unformattedDate = date + "T" + time // ex: 2022-08-30T06:51:47.381439-0700 + let dateFormatter = DateFormatter() + dateFormatter.locale = Locale(identifier: "en_US") + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ" + dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) + + var info = "" + + for i in 0...splitLine.count - 1 { + if i == 0 || i == 1 { continue } + info = info.appending(" " + splitLine[i]) + } + + sanatizeInfo(&info) + + guard let dateZome = dateFormatter.date(from: unformattedDate) else { continue } + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" + let formattedDate = dateFormatter.string(from: dateZome) + let text = "\(formattedDate), XPROTECT_REMEDIATOR, \(info)" + self.addTextToFile(atUrl: logsFile, text: text) + self.addTextToFile(atUrl: self.storylineFile, text: text) + } + } catch { + print("Unable to parse contents") } } @@ -118,5 +158,8 @@ class LogParser: AftermathModule { self.log("Parsing system log...") parseSysLog() + + self.log("Parsing XProtect Remediator log...") + parseXProtectRemediatorLog() } } diff --git a/analysis/Storyline.swift b/analysis/Storyline.swift index 1e38cda..af4d599 100644 --- a/analysis/Storyline.swift +++ b/analysis/Storyline.swift @@ -117,10 +117,9 @@ class Storyline: AftermathModule { func sortStoryline() { - self.log("Creating the storyline...") + self.log("Creating the storyline...Please wait...") let sortedStoryline = self.createNewCaseFile(dirUrl: CaseFiles.analysisCaseDir, filename: "storyline.csv") - do { let csvFile = try EnumeratedCSV(url: self.storylineFile) let sortedArr = try Aftermath.sortCSV(unsortedArr: csvFile.rows) diff --git a/unifiedlogs/UnifiedLogModule.swift b/unifiedlogs/UnifiedLogModule.swift index 2c3f5bb..b805254 100644 --- a/unifiedlogs/UnifiedLogModule.swift +++ b/unifiedlogs/UnifiedLogModule.swift @@ -23,7 +23,8 @@ class UnifiedLogModule: AftermathModule, AMProto { "ssh": "process == \"sshd\"", "failed_sudo": "process == \"sudo\" and eventMessage CONTAINS \"TTY\" AND eventMessage CONTAINS \"3 incorrect password attempts\"", "manual_configuration_profile_install": "subsystem == \"com.apple.ManagedClient\" AND process == \"mdmclient\" AND category == \"MDMDaemon\" and eventMessage CONTAINS \"Installed configuration profile:\" AND eventMessage CONTAINS \"Source: Manual\"", - "screensharing": "(process == \"screensharingd\" || process == \"ScreensharingAgent\")" + "screensharing": "(process == \"screensharingd\" || process == \"ScreensharingAgent\")", + "xprotect_remediator": "subsystem == \"com.apple.XProtectFramework.PluginAPI\"" ] }