From 4dd3937cba3fb17867bbcdd3169d2e1130248a62 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Thu, 8 Dec 2022 07:34:31 -0800 Subject: [PATCH 01/13] collecting the name of the installed config profiles --- artifacts/ArtifactsModule.swift | 3 +++ artifacts/ConfigurationProfiles.swift | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 artifacts/ConfigurationProfiles.swift diff --git a/artifacts/ArtifactsModule.swift b/artifacts/ArtifactsModule.swift index ac19e44..720a8dd 100644 --- a/artifacts/ArtifactsModule.swift +++ b/artifacts/ArtifactsModule.swift @@ -34,5 +34,8 @@ class ArtifactsModule: AftermathModule, AMProto { let logFiles = LogFiles(logFilesDir: logFilesDir) logFiles.run() + + let configProfiles = ConfigurationProfiles() + configProfiles.run() } } diff --git a/artifacts/ConfigurationProfiles.swift b/artifacts/ConfigurationProfiles.swift new file mode 100644 index 0000000..80864c3 --- /dev/null +++ b/artifacts/ConfigurationProfiles.swift @@ -0,0 +1,23 @@ +// +// ConfigurationProfiles.swift +// aftermath +// +// Copyright 2022 JAMF Software, LLC +// + +import Foundation + +class ConfigurationProfiles: ArtifactsModule { + + override func run() { + + self.log("Writing installed configuration profiles...") + + let outputFile = self.createNewCaseFile(dirUrl: moduleDirRoot, filename: "config_profiles.txt") + + let command = "sudo profiles list -all" + let output = Aftermath.shell("\(command)") + + self.addTextToFile(atUrl: outputFile, text: output) + } +} From 4b1448354fd9b4eae28340c50700f92aadd5926c Mon Sep 17 00:00:00 2001 From: stuartjash Date: Thu, 8 Dec 2022 07:35:57 -0800 Subject: [PATCH 02/13] gather extended attributes if they exist during metadata collection --- aftermath/Module.swift | 15 ++++++++++++-- analysis/Timeline.swift | 6 ++++-- extensions/URL.swift | 44 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 extensions/URL.swift diff --git a/aftermath/Module.swift b/aftermath/Module.swift index c8f9c4f..787950d 100644 --- a/aftermath/Module.swift +++ b/aftermath/Module.swift @@ -187,6 +187,7 @@ class AftermathModule { var birthTimestamp: String var lastModifiedTimestamp: String var lastAccessedTimestamp: String + var xattr: String = "" if fromFile.path.contains(",") { let sanitized = fromFile.path.replacingOccurrences(of: ",", with: " ") @@ -203,8 +204,6 @@ class AftermathModule { metadata.append("unknwon,") } - - if let lastModified = helpers.getFileLastModified(fromFile: fromFile) { lastModifiedTimestamp = Aftermath.dateFromEpochTimestamp(timeStamp: lastModified) metadata.append("\(lastModifiedTimestamp),") @@ -237,6 +236,18 @@ class AftermathModule { metadata.append("unknown,") } + do { + let xattrs = try fromFile.listExtendedAttributes() + if xattrs.isEmpty { + xattr.append("none,") + } else { xattrs.forEach { xattr.append("\($0) ") } } + + metadata.append("\(xattr),") + } catch { + xattr.append("unknown,") + self.log("Unable to capture extended attributes for \(fromFile.path) due to error: \(error)") + } + if let mditem = MDItemCreate(nil, fromFile.path as CFString), let mdnames = MDItemCopyAttributeNames(mditem), let mdattrs = MDItemCopyAttributes(mditem, mdnames) as? [String:Any] { diff --git a/analysis/Timeline.swift b/analysis/Timeline.swift index 7a94d42..632e0e3 100644 --- a/analysis/Timeline.swift +++ b/analysis/Timeline.swift @@ -122,9 +122,10 @@ class Timeline: AftermathModule { let permissions = columns[4] let uid = columns[5] let gid = columns[6] - let downloadedFrom = columns[7] + let xattr = columns[7] + let downloadedFrom = columns[8] - let singleEntry = Metadata(file: filePath, birth: birth, modified: modified, accessed: accessed, permissions: permissions, uid: uid, gid: gid, downloadedFrom: downloadedFrom) + let singleEntry = Metadata(file: filePath, birth: birth, modified: modified, accessed: accessed, permissions: permissions, uid: uid, gid: gid, xattr: xattr, downloadedFrom: downloadedFrom) metadata.append(singleEntry) } } @@ -173,6 +174,7 @@ struct Metadata { var permissions: String var uid: String var gid: String + var xattr: String var downloadedFrom: String } diff --git a/extensions/URL.swift b/extensions/URL.swift new file mode 100644 index 0000000..6a496c3 --- /dev/null +++ b/extensions/URL.swift @@ -0,0 +1,44 @@ +// +// URL.swift +// aftermath +// +// Copyright 2022 JAMF Software, LLC +// + +import Foundation + +extension URL { + + // Get list of all extended attributes. + func listExtendedAttributes() throws -> [String] { + + let list = try self.withUnsafeFileSystemRepresentation { fileSystemPath -> [String] in + let length = listxattr(fileSystemPath, nil, 0, 0) + guard length >= 0 else { throw URL.posixError(errno) } + + // Create buffer with required size: + var namebuf = Array(repeating: 0, count: length) + + // Retrieve attribute list: + let result = listxattr(fileSystemPath, &namebuf, namebuf.count, 0) + guard result >= 0 else { throw URL.posixError(errno) } + + // Extract attribute names: + let list = namebuf.split(separator: 0).compactMap { + $0.withUnsafeBufferPointer { + $0.withMemoryRebound(to: UInt8.self) { + String(bytes: $0, encoding: .utf8) + } + } + } + return list + } + return list + } + + // Helper function to create an NSError from a Unix errno. + private static func posixError(_ err: Int32) -> NSError { + return NSError(domain: NSPOSIXErrorDomain, code: Int(err), + userInfo: [NSLocalizedDescriptionKey: String(cString: strerror(err))]) + } +} From d0145475e56c89e076aa17bbfc2f230d90f00fed Mon Sep 17 00:00:00 2001 From: stuartjash Date: Thu, 8 Dec 2022 07:37:24 -0800 Subject: [PATCH 03/13] allow for an externally loaded unified logs file --- aftermath/Command.swift | 12 ++++++-- unifiedlogs/UnifiedLogModule.swift | 48 ++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/aftermath/Command.swift b/aftermath/Command.swift index de3508c..42f4484 100644 --- a/aftermath/Command.swift +++ b/aftermath/Command.swift @@ -15,6 +15,7 @@ static let analyze = Options(rawValue: 1 << 2) static let pretty = Options(rawValue: 1 << 3) static let collectDirs = Options(rawValue: 1 << 4) + static let unifiedLogs = Options(rawValue: 1 << 5) } @@ -24,6 +25,7 @@ class Command { static var analysisDir: String? = nil static var outputDir: String = "/tmp" static var collectDirs: [String] = [] + static var unifiedLogsFile: String? = nil static let version: String = "1.2.0" static func main() { @@ -66,6 +68,11 @@ class Command { i += 1 } } + case "-l", "--logs": + if let index = args.firstIndex(of: arg) { + Self.options.insert(.unifiedLogs) + Self.unifiedLogsFile = args[index + 1] + } case "-v", "--version": print(version) exit(1) @@ -130,7 +137,7 @@ class Command { mainModule.log("Running Aftermath Version \(version)") mainModule.log("Aftermath Collection Started") mainModule.log("Collection started at \(mainModule.getCurrentTimeStandardized())") - mainModule.addTextToFile(atUrl: CaseFiles.metadataFile, text: "file,birth,modified,accessed,permissions,uid,gid, downloadedFrom") + mainModule.addTextToFile(atUrl: CaseFiles.metadataFile, text: "file,birth,modified,accessed,permissions,uid,gid,xattr,downloadedFrom") // System Recon @@ -174,10 +181,9 @@ class Command { artifactModule.run() mainModule.log("Finished gathering artifacts") - // Logs mainModule.log("Started logging unified logs") - let unifiedLogModule = UnifiedLogModule() + let unifiedLogModule = UnifiedLogModule(logFile: unifiedLogsFile) unifiedLogModule.run() mainModule.log("Finished logging unified logs") diff --git a/unifiedlogs/UnifiedLogModule.swift b/unifiedlogs/UnifiedLogModule.swift index 0d2f878..ac87748 100644 --- a/unifiedlogs/UnifiedLogModule.swift +++ b/unifiedlogs/UnifiedLogModule.swift @@ -13,11 +13,13 @@ class UnifiedLogModule: AftermathModule, AMProto { var description = "A module that maintains and runs a list of unified log queries and saves the results" lazy var moduleDirRoot = self.createNewDirInRoot(dirName: dirName) - let predicates: [String: String] + let defaultPredicates: [String: String] + let logFile: String? - override init() { - - self.predicates = [ + init(logFile: String?) { + + self.logFile = logFile + self.defaultPredicates = [ "login": "process == \"logind\"", "tcc": "process == \"tccd\"", "ssh": "process == \"sshd\"", @@ -29,7 +31,7 @@ class UnifiedLogModule: AftermathModule, AMProto { } - func filterPredicates(filter: [String: String]) { + func filterPredicates(predicates: [String: String]) { for (filtername, filter) in predicates { self.log("Filtering for \(filtername) events...") @@ -43,9 +45,43 @@ class UnifiedLogModule: AftermathModule, AMProto { } } + func parsePredicateFile(path: String) -> [String: String] { + self.log("Processing supplied unified log filters...") + var data = "" + var inputPredicates: [String : String] = [:] + + do { + data = try String(contentsOfFile: path) + } catch { + print(error) + } + + let rows = data.components(separatedBy: "\n") + + for row in rows { + let splitRow = row.split(separator: ":") + inputPredicates[String(splitRow[0])] = String(splitRow[1]) + } + + return inputPredicates + } + func run() { self.log("Filtering Unified Log. Hang Tight!") - filterPredicates(filter: predicates) + + // run the external input file of predicates + if let externalLogFile = self.logFile { + if !filemanager.fileExists(atPath: externalLogFile) { + self.log("No external predicate file found at \(externalLogFile)") + } else { + let externalParsedPredicates = parsePredicateFile(path: externalLogFile) + print(externalParsedPredicates) + filterPredicates(predicates: externalParsedPredicates) + } + } + + // run default predicates + filterPredicates(predicates: self.defaultPredicates) self.log("Unified Log filtering complete.") } } From 7b3cac1bd9775ee6583664c04e7a5b18631a30a7 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Thu, 8 Dec 2022 07:49:04 -0800 Subject: [PATCH 04/13] updated readme to add unified logging --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 733fe5c..d8be99c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![](https://github.com/jamf/aftermath/blob/main/AftermathLogo.png) -![](https://img.shields.io/badge/release-1.1.0-bright%20green) ![](https://img.shields.io/badge/macOS-12.0%2B-blue) ![](https://img.shields.io/badge/license-MIT-orange) +![](https://img.shields.io/badge/release-1.3.0-bright%20green) ![](https://img.shields.io/badge/macOS-12.0%2B-blue) ![](https://img.shields.io/badge/license-MIT-orange) ## About @@ -56,6 +56,13 @@ sudo ./aftermath -o /Users/user/Desktop --deep sudo ./aftermath --analyze ``` +### External Unified Log Predicates +As of v1.3.0, users have the ability to pass Aftermath a text file of unified log predicates using the `--logs` or `-l` arguments. The file being passed to Aftermath is required to be a text file and each predicate needs to be newline-separated. In addition, each line item will be a dictionary object. The key in the dictionary will whatever the user desires to call this predicate. For example, if you want to see all login events, we will create a predicate and title it `login_events`. +``` +login_events: processImagePath contains "loginwindow" and eventMessage contains "com.apple.sessionDidLogin +tcc: process == "tccd" +``` + ## 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 @@ -74,6 +81,8 @@ To uninstall the aftermath binary, run the `AftermathUninstaller.pkg` from the [ 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. +--logs -> specify an external text file with unified log predicates (as dictionary objects) to parse + usage: --logs /Users//Desktop/myPredicates.txt -o or --output -> specify an output location for Aftermath collection results (defaults to /tmp) usage: -o Users/user/Desktop --pretty -> colorize Terminal output From a1d0dca7643ac26a7f61333e7ff21935c1a22e2c Mon Sep 17 00:00:00 2001 From: stuartjash Date: Thu, 8 Dec 2022 07:49:19 -0800 Subject: [PATCH 05/13] updated readme to add unified logging --- aftermath/Command.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aftermath/Command.swift b/aftermath/Command.swift index 42f4484..152c918 100644 --- a/aftermath/Command.swift +++ b/aftermath/Command.swift @@ -243,6 +243,8 @@ class Command { print("--collect-dirs -> specify locations of (space-separated) directories to dump those raw files") print(" usage: --collect-dirs /Users//Downloads /tmp") print("--deep -> performs deep scan and captures metadata from Users entire directory (WARNING: this may be time-consuming)") + print("--logs -> specify an external text file with unified log predicates to parse") + print(" usage: --logs /Users//Desktop/myPredicates.txt") print("--pretty -> colorize Terminal output") print("--cleanup -> remove Aftermath Folders in default locations") exit(1) From 9755ca891b7e0fc9b5439ad70938ca9ae0dd8145 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Thu, 8 Dec 2022 11:44:09 -0800 Subject: [PATCH 06/13] added error handling for if a zip file already exists at that location --- aftermath/CaseFiles.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/aftermath/CaseFiles.swift b/aftermath/CaseFiles.swift index 0958bf5..18f2893 100644 --- a/aftermath/CaseFiles.swift +++ b/aftermath/CaseFiles.swift @@ -64,10 +64,19 @@ struct CaseFiles { } else { localCaseDir = caseDir } + do { let endURL = URL(fileURLWithPath: "\(outputDir)/\(localCaseDir.lastPathComponent)") let zippedURL = endURL.appendingPathExtension("zip") + if FileManager.default.fileExists(atPath: zippedURL.relativePath) { + do { + try FileManager.default.removeItem(at: zippedURL) + } catch { + print("Unable to save archive. Error: \(error)") + } + } + try fm.zipItem(at: localCaseDir, to: endURL, shouldKeepParent: true, compressionMethod: .deflate) try fm.moveItem(at: endURL, to: zippedURL) print("Aftermath archive moved to \(zippedURL.path)") From 7a9bb5abe53029367601f39879fe990ca01f4f8f Mon Sep 17 00:00:00 2001 From: stuartjash Date: Thu, 8 Dec 2022 11:44:47 -0800 Subject: [PATCH 07/13] capture the sha256 of binaries being pointed at from LaunchAgents and LaunchDaemons --- extensions/Data.swift | 34 ++++++++++++++++++++++++++++++++++ extensions/String.swift | 17 +++++++++++++++++ persistence/LaunchItems.swift | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 extensions/Data.swift create mode 100644 extensions/String.swift diff --git a/extensions/Data.swift b/extensions/Data.swift new file mode 100644 index 0000000..a74e16d --- /dev/null +++ b/extensions/Data.swift @@ -0,0 +1,34 @@ +// +// Data.swift +// aftermath +// +// Copyright 2022 JAMF Software, LLC +// + +import Foundation +import CommonCrypto + +extension Data{ + public func sha256() -> String{ + return hexStringFromData(input: digest(input: self as NSData)) + } + + private func digest(input : NSData) -> NSData { + let digestLength = Int(CC_SHA256_DIGEST_LENGTH) + var hash = [UInt8](repeating: 0, count: digestLength) + CC_SHA256(input.bytes, UInt32(input.length), &hash) + return NSData(bytes: hash, length: digestLength) + } + + private func hexStringFromData(input: NSData) -> String { + var bytes = [UInt8](repeating: 0, count: input.length) + input.getBytes(&bytes, length: input.length) + + var hexString = "" + for byte in bytes { + hexString += String(format:"%02x", UInt8(byte)) + } + + return hexString + } +} diff --git a/extensions/String.swift b/extensions/String.swift new file mode 100644 index 0000000..aeda58e --- /dev/null +++ b/extensions/String.swift @@ -0,0 +1,17 @@ +// +// String.swift +// aftermath +// +// Copyright 2022 JAMF Software, LLC +// + +import Foundation + +public extension String { + func sha256() -> String{ + if let stringData = self.data(using: String.Encoding.utf8) { + return stringData.sha256() + } + return "" + } +} diff --git a/persistence/LaunchItems.swift b/persistence/LaunchItems.swift index 757456e..84dc834 100644 --- a/persistence/LaunchItems.swift +++ b/persistence/LaunchItems.swift @@ -18,17 +18,49 @@ class LaunchItems: PersistenceModule { func captureLaunchData(urlLocations: [URL], capturedLaunchFile: URL) { for url in urlLocations { let plistDict = Aftermath.getPlistAsDict(atUrl: url) + var binaryName: String? = nil + var binarySHA256: String? = nil + + // get the progarm key and pass value to parser + if let binaryLocation = plistDict["Program"] { + binaryName = binaryLocation as? String + binarySHA256 = collectBinaryHashInformation(binaryLocation: binaryLocation as! String) + } else if let binaryLocation = plistDict["ProgramArguments"] { + // grab first element of ProgramArguments + binaryName = binaryLocation as? String + let arr = binaryLocation as! [String] + binarySHA256 = collectBinaryHashInformation(binaryLocation: arr[0]) + } else { + self.log("Could not get plist information for \(url.relativeString)") + } // copy the plists to the persistence directory self.copyFileToCase(fileToCopy: url, toLocation: self.saveToRawDir) // write the plists to one file self.addTextToFile(atUrl: capturedLaunchFile, text: "\n----- \(url.path) -----\n") + self.addTextToFile(atUrl: capturedLaunchFile, text: "Binary Name: \(binaryName ?? "unknown")\n") + self.addTextToFile(atUrl: capturedLaunchFile, text: "Binary SHA256: \(binarySHA256 ?? "unknown")\n") self.addTextToFile(atUrl: capturedLaunchFile, text: plistDict.description) } } - override func run() { + private func collectBinaryHashInformation(binaryLocation: String) -> String? { //throws + if filemanager.fileExists(atPath: binaryLocation) { + // convert to data + if let data = filemanager.contents(atPath: binaryLocation) { + // use extension to get sha256 + let fileHash = data.sha256() + return fileHash + } + } else { + self.log("Binary does not exist at \(binaryLocation)") + return nil + } + return nil + } + + override func run() { let launchDaemonsPath = "/Library/LaunchDaemons/" let launchAgentsPath = "/Library/LaunchAgents/" let capturedLaunchFile = self.createNewCaseFile(dirUrl: moduleDirRoot, filename: "launchItems.txt") From c587318cae4bc9be75b9d8b24011eaddebd38ee5 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Thu, 8 Dec 2022 14:34:01 -0800 Subject: [PATCH 08/13] get sha256 from binary pointed at by plist --- aftermath.xcodeproj/project.pbxproj | 16 ++++++++++++++++ aftermath/Command.swift | 7 ++++--- extensions/Data.swift | 1 + extensions/String.swift | 1 + 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/aftermath.xcodeproj/project.pbxproj b/aftermath.xcodeproj/project.pbxproj index 1f7f26a..f56e7c1 100644 --- a/aftermath.xcodeproj/project.pbxproj +++ b/aftermath.xcodeproj/project.pbxproj @@ -7,7 +7,11 @@ objects = { /* Begin PBXBuildFile section */ + 5E494473293AC914007FFBDD /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E494472293AC914007FFBDD /* URL.swift */; }; + 5E494475293D50FE007FFBDD /* ConfigurationProfiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E494474293D50FE007FFBDD /* ConfigurationProfiles.swift */; }; 5E6780F22922E7E800BAF04B /* Edge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E6780F12922E7E800BAF04B /* Edge.swift */; }; + 5E93B0AE2941608D009D2AB5 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E93B0AD2941608D009D2AB5 /* Data.swift */; }; + 5E93B0B0294160B6009D2AB5 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E93B0AF294160B6009D2AB5 /* String.swift */; }; 70A44403275707A90035F40E /* SystemReconModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70A44402275707A90035F40E /* SystemReconModule.swift */; }; 70A44405275A76990035F40E /* LSQuarantine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70A44404275A76990035F40E /* LSQuarantine.swift */; }; 70CF9E3A27611C6100FD884B /* ShellHistoryAndProfiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70CF9E3927611C6100FD884B /* ShellHistoryAndProfiles.swift */; }; @@ -73,7 +77,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 5E494472293AC914007FFBDD /* URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = ""; }; + 5E494474293D50FE007FFBDD /* ConfigurationProfiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationProfiles.swift; sourceTree = ""; }; 5E6780F12922E7E800BAF04B /* Edge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Edge.swift; sourceTree = ""; }; + 5E93B0AD2941608D009D2AB5 /* Data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = ""; }; + 5E93B0AF294160B6009D2AB5 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; 70A44402275707A90035F40E /* SystemReconModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemReconModule.swift; sourceTree = ""; }; 70A44404275A76990035F40E /* LSQuarantine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LSQuarantine.swift; sourceTree = ""; }; 70CF9E3927611C6100FD884B /* ShellHistoryAndProfiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellHistoryAndProfiles.swift; sourceTree = ""; }; @@ -221,6 +229,7 @@ 70A44404275A76990035F40E /* LSQuarantine.swift */, 70CF9E3927611C6100FD884B /* ShellHistoryAndProfiles.swift */, A08342D7284E48FC005E437A /* LogFiles.swift */, + 5E494474293D50FE007FFBDD /* ConfigurationProfiles.swift */, ); path = artifacts; sourceTree = ""; @@ -333,6 +342,9 @@ isa = PBXGroup; children = ( A374535C2757C1300074B65C /* FileManager.swift */, + 5E494472293AC914007FFBDD /* URL.swift */, + 5E93B0AD2941608D009D2AB5 /* Data.swift */, + 5E93B0AF294160B6009D2AB5 /* String.swift */, ); path = extensions; sourceTree = ""; @@ -489,6 +501,7 @@ buildActionMask = 2147483647; files = ( A3CD4E56274434EE00869ECB /* Command.swift in Sources */, + 5E494475293D50FE007FFBDD /* ConfigurationProfiles.swift in Sources */, A0C2E89728AAAE33008FA597 /* ProcLib.h in Sources */, A3745358275730870074B65C /* LaunchItems.swift in Sources */, A0FAEEFE28B94B2C00AC655F /* LogParser.swift in Sources */, @@ -505,6 +518,7 @@ A0E1E3EB275EC800008D0DC6 /* Firefox.swift in Sources */, 8ABB9E312756D2B500C0ADD7 /* Aftermath.swift in Sources */, A0E1E3F8275ED35D008D0DC6 /* NetworkConnections.swift in Sources */, + 5E93B0B0294160B6009D2AB5 /* String.swift in Sources */, A0E1E3E9275EC736008D0DC6 /* BrowserModule.swift in Sources */, A02509F428ADB1A80030D6A7 /* CHelpers.swift in Sources */, 70A44403275707A90035F40E /* SystemReconModule.swift in Sources */, @@ -518,9 +532,11 @@ A0E1E3ED275EC809008D0DC6 /* Chrome.swift in Sources */, A3046F8E27627DAC0069AA21 /* Module.swift in Sources */, 8ABB9E2B27568EB700C0ADD7 /* UnifiedLogModule.swift in Sources */, + 5E93B0AE2941608D009D2AB5 /* Data.swift in Sources */, A0879957275AD2DC00E885BC /* SystemConfig.swift in Sources */, A0FD80F628C7F82400E91584 /* ProcessParser.swift in Sources */, A05BF3BF284FF8CF009E197B /* Slack.swift in Sources */, + 5E494473293AC914007FFBDD /* URL.swift in Sources */, A007834E28947D71008489EA /* Emond.swift in Sources */, A076742F2755798F00ED7066 /* ArtifactsModule.swift in Sources */, A0759135275985170006766F /* TCC.swift in Sources */, diff --git a/aftermath/Command.swift b/aftermath/Command.swift index 152c918..9438f4a 100644 --- a/aftermath/Command.swift +++ b/aftermath/Command.swift @@ -115,12 +115,13 @@ class Command { if #available(macOS 12, *) { let analysisModule = AnalysisModule(collectionDir: unzippedDir) analysisModule.run() + + mainModule.log("Finished analysis module") } else { - // Fallback on earlier versions + mainModule.log("Aftermath requires macOS 12 or later in order to analyze collection data.") + print("Aftermath requires macOS 12 or later in order to analyze collection data.") } - mainModule.log("Finished analysis module") - guard isDirectoryThatExists(path: Self.outputDir) else { mainModule.log("Output directory is not a valid directory that exists") return diff --git a/extensions/Data.swift b/extensions/Data.swift index a74e16d..781cde8 100644 --- a/extensions/Data.swift +++ b/extensions/Data.swift @@ -9,6 +9,7 @@ import Foundation import CommonCrypto extension Data{ + public func sha256() -> String{ return hexStringFromData(input: digest(input: self as NSData)) } diff --git a/extensions/String.swift b/extensions/String.swift index aeda58e..0ea9fe0 100644 --- a/extensions/String.swift +++ b/extensions/String.swift @@ -8,6 +8,7 @@ import Foundation public extension String { + func sha256() -> String{ if let stringData = self.data(using: String.Encoding.utf8) { return stringData.sha256() From 4c7ecf89da625201f11c9c30f76547f365728cac Mon Sep 17 00:00:00 2001 From: stuartjash Date: Thu, 8 Dec 2022 14:44:26 -0800 Subject: [PATCH 09/13] fixed error --- aftermath/CaseFiles.swift | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/aftermath/CaseFiles.swift b/aftermath/CaseFiles.swift index 98d649c..7cb6e55 100644 --- a/aftermath/CaseFiles.swift +++ b/aftermath/CaseFiles.swift @@ -22,16 +22,16 @@ struct CaseFiles { } else { return nil } - + guard platformExpert > 0 else { return nil } guard let serialNumber = (IORegistryEntryCreateCFProperty(platformExpert, kIOPlatformSerialNumberKey as CFString, kCFAllocatorDefault, 0).takeUnretainedValue() as? String)?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) else { return nil } - + IOObjectRelease(platformExpert) - + return serialNumber } @@ -56,19 +56,19 @@ struct CaseFiles { static func MoveTemporaryCaseDir(outputLocation: String, isAnalysis: Bool) { print("Checking for existence of output location") - + let fm = FileManager.default let isDir = fm.isDirectoryThatExists(path: outputLocation) guard isDir || fm.fileExists(atPath: outputLocation) else { print("Output path is not a valid file or directory that exists") return } - + print("Moving the aftermath directory from its temporary location. This may take some time. Please wait...") - + // Determine if we should look in /tmp or in the Aftermath case directory within /tmp let localCaseDir = isAnalysis ? analysisCaseDir : caseDir - + let endPath: String if isDir { endPath = "\(outputLocation)/\(localCaseDir.lastPathComponent)" @@ -76,7 +76,7 @@ struct CaseFiles { // Ensure that we end up with the correct (.zip) path extension endPath = fm.deletingPathExtension(path: outputLocation) } - + // The zipped case directory should end up in the specified output location do { let endURL = URL(fileURLWithPath: endPath) @@ -89,14 +89,15 @@ struct CaseFiles { print("Unable to save archive. Error: \(error)") } } - - do { - try fm.zipItem(at: localCaseDir, to: endURL, shouldKeepParent: true, compressionMethod: .deflate) - try fm.moveItem(at: endURL, to: zippedURL) - print("Aftermath archive moved to \(zippedURL.path)") - - } catch { - print("Unable to create archive. Error: \(error)") + + do { + try fm.zipItem(at: localCaseDir, to: endURL, shouldKeepParent: true, compressionMethod: .deflate) + try fm.moveItem(at: endURL, to: zippedURL) + print("Aftermath archive moved to \(zippedURL.path)") + + } catch { + print("Unable to create archive. Error: \(error)") + } } } } From c2ec72968f00fc433342e05f93c4362b56c2f9cc Mon Sep 17 00:00:00 2001 From: stuartjash Date: Thu, 8 Dec 2022 14:53:03 -0800 Subject: [PATCH 10/13] merged in new changes and minor updates --- README.md | 4 ++-- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- aftermath/Command.swift | 2 ++ persistence/LaunchItems.swift | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d8be99c..697585d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![](https://github.com/jamf/aftermath/blob/main/AftermathLogo.png) -![](https://img.shields.io/badge/release-1.3.0-bright%20green) ![](https://img.shields.io/badge/macOS-12.0%2B-blue) ![](https://img.shields.io/badge/license-MIT-orange) +![](https://img.shields.io/badge/release-1.2.0-bright%20green) ![](https://img.shields.io/badge/macOS-12.0%2B-blue) ![](https://img.shields.io/badge/license-MIT-orange) ## About @@ -57,7 +57,7 @@ sudo ./aftermath --analyze ``` ### External Unified Log Predicates -As of v1.3.0, users have the ability to pass Aftermath a text file of unified log predicates using the `--logs` or `-l` arguments. The file being passed to Aftermath is required to be a text file and each predicate needs to be newline-separated. In addition, each line item will be a dictionary object. The key in the dictionary will whatever the user desires to call this predicate. For example, if you want to see all login events, we will create a predicate and title it `login_events`. +As of v1.2.0, users have the ability to pass Aftermath a text file of unified log predicates using the `--logs` or `-l` arguments. The file being passed to Aftermath is required to be a text file and each predicate needs to be newline-separated. In addition, each line item will be a dictionary object. The key in the dictionary will whatever the user desires to call this predicate. For example, if you want to see all login events, we will create a predicate and title it `login_events`. ``` login_events: processImagePath contains "loginwindow" and eventMessage contains "com.apple.sessionDidLogin tcc: process == "tccd" diff --git a/aftermath.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/aftermath.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index f45d205..b5550be 100644 --- a/aftermath.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/aftermath.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,7 +5,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/weichsel/ZIPFoundation", "state" : { - "revision" : "7254c74b49cec2cb81520523ba993c671f71b066", + "revision" : "1b662e2e7a091710ad8a963263939984e2ebf527", "version" : "0.9.14" } } diff --git a/aftermath/Command.swift b/aftermath/Command.swift index b1fb30c..094f095 100644 --- a/aftermath/Command.swift +++ b/aftermath/Command.swift @@ -129,6 +129,7 @@ class Command { // End Aftermath mainModule.log("Aftermath Finished") + exit(1) } else { CaseFiles.CreateCaseDir() let mainModule = AftermathModule() @@ -192,6 +193,7 @@ class Command { // End Aftermath mainModule.log("Aftermath Finished") + exit(1) } } diff --git a/persistence/LaunchItems.swift b/persistence/LaunchItems.swift index 84dc834..e7684dd 100644 --- a/persistence/LaunchItems.swift +++ b/persistence/LaunchItems.swift @@ -31,7 +31,7 @@ class LaunchItems: PersistenceModule { let arr = binaryLocation as! [String] binarySHA256 = collectBinaryHashInformation(binaryLocation: arr[0]) } else { - self.log("Could not get plist information for \(url.relativeString)") + self.log("Could not get plist information for \(url.relativePath)") } // copy the plists to the persistence directory From 00bef8e498e7f69d7c72de037ed2ea015b3d0048 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Thu, 8 Dec 2022 14:54:58 -0800 Subject: [PATCH 11/13] merged in new changes and minor updates --- aftermath/Command.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/aftermath/Command.swift b/aftermath/Command.swift index 094f095..b1fb30c 100644 --- a/aftermath/Command.swift +++ b/aftermath/Command.swift @@ -129,7 +129,6 @@ class Command { // End Aftermath mainModule.log("Aftermath Finished") - exit(1) } else { CaseFiles.CreateCaseDir() let mainModule = AftermathModule() @@ -193,7 +192,6 @@ class Command { // End Aftermath mainModule.log("Aftermath Finished") - exit(1) } } From 7098770681eb74acb3d4b34b104be8866c5c8c12 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Mon, 13 Feb 2023 15:35:31 -0800 Subject: [PATCH 12/13] fixed parser issue where downloadedFrom would contain comma --- aftermath.xcodeproj/project.pbxproj | 15 ++++++++------- aftermath/Command.swift | 2 +- aftermath/Module.swift | 2 +- analysis/Timeline.swift | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/aftermath.xcodeproj/project.pbxproj b/aftermath.xcodeproj/project.pbxproj index f56e7c1..1eb3dd8 100644 --- a/aftermath.xcodeproj/project.pbxproj +++ b/aftermath.xcodeproj/project.pbxproj @@ -715,12 +715,12 @@ buildSettings = { ARCHS = "$(ARCHS_STANDARD)"; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Developer ID Application"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = YES; CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = 6PV5YF2UES; - "DEVELOPMENT_TEAM[sdk=macosx*]" = ""; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=macosx*]" = 6PV5YF2UES; ENABLE_HARDENED_RUNTIME = YES; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -730,6 +730,7 @@ MACH_O_TYPE = mh_execute; NEW_SETTING = ""; ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.jamf.aftermath; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_INCLUDE_PATHS = "$(SRCROOT) $(SRCROOT)/libs/ProcLib $(SRCROOT)/libs/launchdXPC"; @@ -744,11 +745,11 @@ ARCHS = "$(ARCHS_STANDARD)"; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Developer ID Application"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = YES; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = 6PV5YF2UES; - "DEVELOPMENT_TEAM[sdk=macosx*]" = ""; + "DEVELOPMENT_TEAM[sdk=macosx*]" = 6PV5YF2UES; ENABLE_HARDENED_RUNTIME = YES; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -758,7 +759,7 @@ MACH_O_TYPE = mh_execute; NEW_SETTING = ""; ONLY_ACTIVE_ARCH = NO; - PRODUCT_BUNDLE_IDENTIFIER = com.crashsecurity.aftermath; + PRODUCT_BUNDLE_IDENTIFIER = com.jamf.aftermath; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_INCLUDE_PATHS = "$(SRCROOT) $(SRCROOT)/libs/ProcLib $(SRCROOT)/libs/launchdXPC"; diff --git a/aftermath/Command.swift b/aftermath/Command.swift index b1fb30c..182ae49 100644 --- a/aftermath/Command.swift +++ b/aftermath/Command.swift @@ -26,7 +26,7 @@ class Command { static var outputLocation: String = "/tmp" static var collectDirs: [String] = [] static var unifiedLogsFile: String? = nil - static let version: String = "1.2.0" + static let version: String = "1.2.1" static func main() { setup(with: CommandLine.arguments) diff --git a/aftermath/Module.swift b/aftermath/Module.swift index 787950d..6b371ab 100644 --- a/aftermath/Module.swift +++ b/aftermath/Module.swift @@ -256,7 +256,7 @@ class AftermathModule { if let downloadedFrom = mdattrs[kMDItemWhereFroms as String] { if let downloadedArr = downloadedFrom as Any as? [String] { for downloaded in downloadedArr { - metadata.append("\(downloaded),") + metadata.append("\(downloaded) ") } } } diff --git a/analysis/Timeline.swift b/analysis/Timeline.swift index 632e0e3..3482020 100644 --- a/analysis/Timeline.swift +++ b/analysis/Timeline.swift @@ -114,7 +114,7 @@ class Timeline: AftermathModule { for row in rows { let columns = row.components(separatedBy: ",") - if columns.count == 8 { + if columns.count >= 8 { let filePath = columns[0] let birth = columns[1] let modified = columns[2] From a81bb7075dfe514fd7bd7735e3527ec905c3a563 Mon Sep 17 00:00:00 2001 From: stuartjash Date: Tue, 14 Feb 2023 08:03:06 -0800 Subject: [PATCH 13/13] updated README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 697585d..88ee910 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ cd ``` Build using Xcode ```bash -xcodebuild +xcodebuild -scheme "aftermath" ``` `cd` into the Release folder ```bash