On macOS, certain application state is saved in /Users/*/Library/Saved Application State/
.
We can check these files to determine the last time an application was opened, the title of the application window, and when the application/window was later restored, such as after login or reboot.
In general, the following has been observed:
SavedState
directory - Btime
- Last time the application was opened by the user.SavedState
directory - ModTime
- When the application state was last restored (such as after login/reboot).data.data
files - the actual data within the app, such as the scrollback for a Terminal
window. The data within can be an (AES-128-CBC) encrypted blob. This data can be decrypted using the appropriate NSDataKey
value found in windows.plist
.data.data
- ModTime
- changes when new data is added to the state, for example, when interacting with the Terminal application.windows.plist
– contains the name of application windows (NSTitle, as well as other information such as:NSDataKey
NSDockMenu.name
– names respective to the user’s dock/etc.NSWindowID
– can be used to link the NSDataKey
to the PersistentUIRecord
value in the data.data
file.windows.plist
- BTime
- last time application was restoredwindows.plist
- ModTime
- changes when new data is added to the state, for example, when interacting with the Terminal application.
name: MacOS.Applications.SavedState
description: |
On macOS, certain application state is saved in `/Users/*/Library/Saved Application State/`.
We can check these files to determine the last time an application was opened, the title of the application window, and when the application/window was later restored, such as after login or reboot.
In general, the following has been observed:
- The 'SavedState' files are created when the application is started.
- `SavedState` directory - `Btime` - Last time the application was opened by the user.
- `SavedState` directory - `ModTime` - When the application state was last restored (such as after login/reboot).
- `data.data` files - the actual data within the app, such as the scrollback for a `Terminal` window. The data within can be an (AES-128-CBC) encrypted blob. This data can be decrypted using the appropriate `NSDataKey` value found in `windows.plist`.
- `data.data` - `ModTime` - changes when new data is added to the state, for example, when interacting with the Terminal application.
- `windows.plist` -- contains the name of application windows (NSTitle, as well as other information such as:
- `NSDataKey`
- `NSDockMenu.name` -- names respective to the user's dock/etc.
- `NSWindowID` -- can be used to link the `NSDataKey` to the `PersistentUIRecord` value in the `data.data` file.
- `windows.plist` - `BTime` - last time application was restored
- `windows.plist` - `ModTime` - changes when new data is added to the state, for example, when interacting with the Terminal application.
reference:
- https://www.sans.org/blog/osx-lion-user-interface-preservation-analysis/
- https://www.crowdstrike.com/blog/reconstructing-command-line-activity-on-macos/
type: CLIENT
author: Wes Lambert - @therealwlambert|@weslambert@infosec.exchange
parameters:
- name: SavedStateGlob
default: /Users/*/Library/Saved Application State/com.apple.**
- name: NameFilter
default: .
description: Filter used for targeting results by application name
- name: UserFilter
default: .
description: Filter used for targeting results by user name
precondition:
SELECT OS From info() where OS = 'darwin'
sources:
- query: |
LET SavedStateList = SELECT ModTime,
Btime,
OSPath,
regex_replace(source=OSPath[4], replace="", re=".savedState") AS Name,
OSPath[1] AS _User
FROM glob(globs=split(string=SavedStateGlob, sep=","))
SELECT *,
if(condition = OSPath =~ "windows.plist", then=items(item=plist(file=OSPath))._value.NSDockMenu.name)[0][0] AS DockMenuName,
if(condition = OSPath =~ "windows.plist", then=items(item=plist(file=OSPath))._value.NSTitle)[0] AS WindowTitle,
if(condition = OSPath =~ "windows.plist", then=plist(file=OSPath)) AS _WindowDetails
FROM foreach(row=SavedStateList)
WHERE Name =~ NameFilter
AND _User =~ UserFilter