Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

external UL file, xattr, config profiles, hash info #45

Merged
merged 14 commits into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.2.0-bright%20green) ![](https://img.shields.io/badge/macOS-12.0%2B-blue) ![](https://img.shields.io/badge/license-MIT-orange)


## About
Expand All @@ -26,7 +26,7 @@ cd <path_to_aftermath_directory>
```
Build using Xcode
```bash
xcodebuild
xcodebuild -scheme "aftermath"
```
`cd` into the Release folder
```bash
Expand Down Expand Up @@ -56,6 +56,13 @@ sudo ./aftermath -o /Users/user/Desktop --deep
sudo ./aftermath --analyze <path_to_collection_zip>
```

### External Unified Log Predicates
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"
```

## 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
Expand All @@ -74,6 +81,8 @@ To uninstall the aftermath binary, run the `AftermathUninstaller.pkg` from the [
usage: --collect-dirs <path_to_dir> <path_to_another_dir>
--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/<USER>/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
Expand Down
31 changes: 24 additions & 7 deletions aftermath.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -73,7 +77,11 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
5E494472293AC914007FFBDD /* URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = "<group>"; };
5E494474293D50FE007FFBDD /* ConfigurationProfiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationProfiles.swift; sourceTree = "<group>"; };
5E6780F12922E7E800BAF04B /* Edge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Edge.swift; sourceTree = "<group>"; };
5E93B0AD2941608D009D2AB5 /* Data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = "<group>"; };
5E93B0AF294160B6009D2AB5 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
70A44402275707A90035F40E /* SystemReconModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemReconModule.swift; sourceTree = "<group>"; };
70A44404275A76990035F40E /* LSQuarantine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LSQuarantine.swift; sourceTree = "<group>"; };
70CF9E3927611C6100FD884B /* ShellHistoryAndProfiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellHistoryAndProfiles.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -221,6 +229,7 @@
70A44404275A76990035F40E /* LSQuarantine.swift */,
70CF9E3927611C6100FD884B /* ShellHistoryAndProfiles.swift */,
A08342D7284E48FC005E437A /* LogFiles.swift */,
5E494474293D50FE007FFBDD /* ConfigurationProfiles.swift */,
);
path = artifacts;
sourceTree = "<group>";
Expand Down Expand Up @@ -333,6 +342,9 @@
isa = PBXGroup;
children = (
A374535C2757C1300074B65C /* FileManager.swift */,
5E494472293AC914007FFBDD /* URL.swift */,
5E93B0AD2941608D009D2AB5 /* Data.swift */,
5E93B0AF294160B6009D2AB5 /* String.swift */,
);
path = extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -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 */,
Expand All @@ -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 */,
Expand All @@ -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 */,
Expand Down Expand Up @@ -699,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)",
Expand All @@ -714,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";
Expand All @@ -728,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)",
Expand All @@ -742,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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/weichsel/ZIPFoundation",
"state" : {
"revision" : "7254c74b49cec2cb81520523ba993c671f71b066",
"revision" : "1b662e2e7a091710ad8a963263939984e2ebf527",
"version" : "0.9.14"
}
}
Expand Down
44 changes: 27 additions & 17 deletions aftermath/CaseFiles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -56,38 +56,48 @@ 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)"
} else {
// 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
let endURL = URL(fileURLWithPath: endPath)
let zippedURL = endURL.appendingPathExtension("zip")

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)")
let endURL = URL(fileURLWithPath: endPath)
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)")
}
}

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)")
}
}
}
}
21 changes: 16 additions & 5 deletions aftermath/Command.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)

}

Expand All @@ -24,7 +25,8 @@ class Command {
static var analysisDir: String? = nil
static var outputLocation: String = "/tmp"
static var collectDirs: [String] = []
static let version: String = "1.2.0"
static var unifiedLogsFile: String? = nil
static let version: String = "1.2.1"

static func main() {
setup(with: CommandLine.arguments)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -108,8 +115,11 @@ 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")
Expand All @@ -125,7 +135,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
Expand Down Expand Up @@ -169,10 +179,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")

Expand Down Expand Up @@ -216,6 +225,8 @@ class Command {
print("--collect-dirs -> specify locations of (space-separated) directories to dump those raw files")
print(" usage: --collect-dirs /Users/<USER>/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/<USER>/Desktop/myPredicates.txt")
print("--pretty -> colorize Terminal output")
print("--cleanup -> remove Aftermath Folders in default locations")
exit(1)
Expand Down
17 changes: 14 additions & 3 deletions aftermath/Module.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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: " ")
Expand All @@ -203,8 +204,6 @@ class AftermathModule {
metadata.append("unknwon,")
}



if let lastModified = helpers.getFileLastModified(fromFile: fromFile) {
lastModifiedTimestamp = Aftermath.dateFromEpochTimestamp(timeStamp: lastModified)
metadata.append("\(lastModifiedTimestamp),")
Expand Down Expand Up @@ -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] {
Expand All @@ -245,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) ")
}
}
}
Expand Down
8 changes: 5 additions & 3 deletions analysis/Timeline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,18 @@ 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]
let accessed = columns[3]
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)
}
}
Expand Down Expand Up @@ -173,6 +174,7 @@ struct Metadata {
var permissions: String
var uid: String
var gid: String
var xattr: String
var downloadedFrom: String
}

Expand Down
Loading