Malware traffic

AMOS stealer traffic: one server, three uploads and a task loop

Updated

File
2026-06-09-AMOS-infection-traffic.pcap
Format
pcap, ether
Size
11.64 MB
Packets
12956
Duration
47 min 20 s
Captured (UTC)
2026-06-09 19:28:20
SHA-256
7768ebce412a147fd4154c843ad8b85018913659e899b1e34f25fa8560a9405f

Four and a half seconds after a pasted Terminal command starts fetching it, the malware in this AMOS stealer PCAP sends its first stolen archive to 178.62.250.99, and 47 minutes later the same server is still handing the Mac tasks. Malware-traffic-analysis.net published the capture on 9 June 2026: a malicious ad led to a fake Homebrew page whose ClickFix instructions had the user paste a command into Terminal. Atomic macOS Stealer, AMOS, is a commodity infostealer sold to many operators.

Unlike the two-server macOS stealer, AMOS runs almost its whole operation through that single command server over plain HTTP, so A-Packets reconstructs the check-in, the staged collection, the upload and the follow-on task loop from one conversation. The infected host is 10.6.9.237, and because the C2 is unencrypted every request and its headers are visible, which makes this a clean reference for what AMOS traffic looks like on the wire.

HTTP view of the AMOS stealer PCAP in A-Packets. The HTTP session shows the stage=credentials report sent with curl and the server's 204 No Content acknowledgement.
The HTTP session shows the stage=credentials report sent with curl and the server's 204 No Content acknowledgement.

One server, four jobs, one curl user agent

Every cleartext request in the capture goes to 178.62.250.99 with User-Agent: curl/8.7.1, and that one host carries all four jobs: the stage telemetry, the data uploads, the bot registration and the task polling. Two custom headers, user (a base64 bot id) and BuildID, appear on every request and tie them together. Developers and administrators run curl too, so the signal worth alerting on is the combination: stage metrics, multipart uploads and task polling to one bare address.

One more address appears only inside a task. A command the server issues tells the host to report device details to 165.227.153.3, but that host only ever receives SYN packets and never completes a handshake, so the callback fails at this capture point. Put it on the blocklist anyway: a list built only from completed flows would miss an address the command server explicitly asked for.

HostRequestStatusType
178.62.250.99POST /api/metrics/run?event=started&stage=bootUA: curl/8.7.1204 No Content
178.62.250.99POST /api/metrics/run?event=stage&stage=init_sessionUA: curl/8.7.1204 No Content
178.62.250.99POST /api/metrics/run?event=stage&stage=messengersUA: curl/8.7.1204 No Content
178.62.250.99POST /api/metrics/run?event=stage&stage=credentialsUA: curl/8.7.1204 No Content
178.62.250.99POST /api/metrics/run?event=stage&stage=browsersUA: curl/8.7.1204 No Content
178.62.250.99POST /api/metrics/run?event=stage&stage=walletsUA: curl/8.7.1204 No Content
178.62.250.99POST /contactUA: curl/8.7.1200 OKtext/plain 3 Bytes
178.62.250.99POST /api/metrics/run?event=stage&stage=resolve_authUA: curl/8.7.1204 No Content
178.62.250.99POST /contactUA: curl/8.7.1200 OKtext/plain 3 Bytes
178.62.250.99POST /api/metrics/run?event=stage&stage=local_dataUA: curl/8.7.1204 No Content
178.62.250.99POST /contactUA: curl/8.7.1100 Continue
178.62.250.99POST /api/join/4fdvDwBqQ0MnJXyIbL31YLmNu9YYK1g7z5TaPU4…=UA: curl/8.7.1200 OKtext/plain 22 Bytes

49 more requests in the interactive report.

Open the HTTP view →

The stage reports name what AMOS takes

AMOS announces its collection the same way its macOS cousins do. A run of POST /api/metrics/run?event=stage&stage=… requests names each module in turn: boot, init_session, messengers, credentials, browsers, wallets, then resolve_auth and local_data. Each carries a 68-byte JSON body, {"os":"macOS","os_version":"26.5.1","arch":"arm64","locale":"en_US"}, and draws a 204 No Content. These are progress pings the operator watches live; the names are the stealer's own inventory of targets: messaging apps, saved credentials, browser profiles and cryptocurrency wallets.

The first collection pass takes less than a second: the boot report leaves at 3.9 seconds and the first upload at 4.5. No analyst reacts that fast, so only automated egress controls and endpoint detections can act before this part of the theft is over. A stage report says a module ran, not what it found, which is why the uploads that follow matter more than the labels.

The AMOS infection as a timeline

Laid out against the capture clock, the run has five phases, and the gaps between them say as much as the requests. The times below come from the packets; the right-hand column is what each step means for the person responding.

AMOS timeline: boot at 3.9 seconds, first upload at 4.5 seconds, local-data collection, then registration at 91 seconds.
AMOS timeline: boot at 3.9 seconds, first upload at 4.5 seconds, local-data collection, then registration at 91 seconds.
TimeOn the wireWhat it tells the responder
0.0 sDNS for lyricopal1.com, then chiselvibe.com; 4.3 MB arrive from lyricopal1.comThe pasted command fetching the stealer
3.9 sstage=boot reportThe stealer is running
4.5 sFirst out.zip upload, 3.1 KBThe first pass is finished in 0.6 s
60.4 sstage=resolve_auth, second upload, 3.8 KBA 56-second pause before the password stage
68.9 to 87.7 sstage=local_data, then a 3.3 MB uploadThe largest archive leaves in 19 s
88.5 sAbout 800 KB from ballad-20.comA second download just before registration
91.0 sPOST /api/join/, first task fetchedThe resident component checks in
91.5 to 101.7 sUnanswered SYNs to 165.227.153.3, then the task acknowledgementThe first task fails after a ten-second wait
2,260.7 sA larger collection task arrivesA second collection round, 37 minutes in
2,840.5 sCapture ends during pollingThe host is still under control

The longest pause in the theft is the 56 seconds between the first upload and resolve_auth, and nothing crosses the network in it. AMOS is known for asking for the account password through a dialog that imitates a system prompt, and the gap has the length of someone reading it and typing; the capture cannot show the dialog, but the stage named after it comes next. That makes the awareness message for this family specific rather than generic: a Terminal command pasted from a web page, followed by a password request that no installer explained, is the pattern to refuse.

For response priority the timeline sorts the work. The first archive leaves at 4.5 seconds, after the credentials, browsers and wallets stages and before the password step, so rotation starts with what those stages name. The 3.3 MB upload after local_data extends the scope to the user's own files, which matters for any data-protection assessment. And the registration at 91 seconds turns a theft into an intrusion: from then on the operator could act on the host at will, which is exactly what the task at 37 minutes shows.

Three uploads, then a resident task loop

Interleaved with the stage reports are three POST /contact uploads, each a multipart/form-data body with one file named out.zip. The first two are small, 3.1 KB and 3.8 KB, and A-Packets carves both: each waits under its session in the HTTP view as a .bin. The third is announced with Expect: 100-continue and a declared length of 3,439,626 bytes on TCP stream 16. The server's interim 100 Continue is frame 4047 at 68.86 seconds, the reassembled request completes in frame 10026 at 87.69 seconds, and the final 200 follows in frame 10079 at 88.47. A-Packets carves that body as well, but files it under the server's interim reply: the HTTP view shows the 100 Continue with the start of the multipart body below it and a Download as .bin button, and the table below lists it as a download named contact, with no type or size. In the Connections view the uploads make 178.62.250.99 the busiest destination in the capture, 4.9 MB sent to it, so an egress-volume rule would flag the exfiltration even without reading the paths.

DirectionNameTypeSizeHost
uploadPOST contactmultipart/form-data3.06 KB178.62.250.99
uploadPOST contactmultipart/form-data3.79 KB178.62.250.99
downloadcontact-178.62.250.99
downloadmg9WrPzlnvOkUZtiY0oEjwtext/plain-178.62.250.99

Open the files in the HTTP view →

After the theft, at 91 seconds, the host registers with POST /api/join/ and begins polling GET /api/tasks/mg9WrPzlnvOkUZtiY0oEjw?v=2.0 every 59 to 98 seconds, forty polls across the capture. Most replies are four bytes, pong; each real task is a short shell script tagged with a task id, and the host confirms it with POST /api/tasks/ack carrying that id. The larger task at 2,260.7 seconds is a second collection script: it asks for browser cookie stores, the login Keychain and wallet data and names 165.227.153.3 as the upload destination, which the host never reaches. The register-poll-ack loop is an ordinary C2 pattern, and its recurring request structure is a useful detection signal even though the intervals vary.

Nine of the task replies declare 634-byte bodies, scattered between the pong answers before the larger task arrives; the HTTP view shows each body, so it is quick to check whether they repeat one command. Every callback toward 165.227.153.3 stops at the SYN, so the device report and the second collection upload that the tasks ask for never complete in this file.

The lure and payload domains hide behind Cloudflare

Before any of the C2 traffic, the infection chain ran through a set of throwaway domains, all proxied by Cloudflare so their real hosting is hidden. The SSL/TLS view lists them among the Apple background sessions: lyricopal1.com, chiselvibe.com, ballad-20.com and wuess.com. These are the delivery and staging names a defender would pull from the capture to block and to hunt for in DNS logs, since the host resolved each one just before fetching from it.

Server nameServerVersion and cipherCertificate
amp.shazam.com34.149.250.49:443TLS 1.2TLS_AES_256_GCM_SHA384ALPN h2, http/1.1
api.apple-cloudkit.com17.248.195.65:443TLS 1.2TLS_AES_256_GCM_SHA384ALPN h2, http/1.1
ballad-20.com x2104.21.76.201:443TLS 1.2TLS_CHACHA20_POLY1305_SHA256ALPN h2, http/1.1
chiselvibe.com104.21.29.243:443TLS 1.2TLS_CHACHA20_POLY1305_SHA256ALPN h2, http/1.1
cloudflare-dns.com104.16.249.249:443TLS 1.2TLS_CHACHA20_POLY1305_SHA256ALPN h2, http/1.1
configuration.apple.com17.253.127.139:443TLS 1.2TLS_AES_256_GCM_SHA384ALPN http/1.1
configuration.ls.apple.com17.253.127.145:443TLS 1.2TLS_AES_256_GCM_SHA384ALPN h2, http/1.1
dns.google x28.8.8.8:443TLS 1.2TLS_AES_256_GCM_SHA384ALPN h2
dns.google x28.8.8.8:853TLS 1.2TLS_AES_256_GCM_SHA384ALPN dot
fpinit.itunes.apple.com17.253.127.138:443TLS 1.2TLS_AES_256_GCM_SHA384ALPN h2, http/1.1
gsp57-ssl-background.ls.apple.com17.253.127.147:443TLS 1.2TLS_AES_256_GCM_SHA384ALPN http/1.1
lyricopal1.com104.21.22.105:443TLS 1.2TLS_CHACHA20_POLY1305_SHA256ALPN h2, http/1.1
lyricopal1.com172.67.204.95:443TLS 1.2TLS_CHACHA20_POLY1305_SHA256ALPN h2, http/1.1
ocsp2.apple.com17.253.127.150:443TLS 1.2TLS_AES_256_GCM_SHA384ALPN h2, http/1.1
sf-api-token-service.itunes.apple.com184.29.90.32:443TLS 1.2TLS_AES_256_GCM_SHA384ALPN h2, http/1.1
shazam-events.cdn-apple.com17.253.127.142:443TLS 1.2TLS_AES_256_GCM_SHA384ALPN h2, http/1.1
wuess.com104.21.1.30:443TLS 1.2TLS_CHACHA20_POLY1305_SHA256ALPN h2, http/1.1
wuess.com172.67.151.230:443TLS 1.2TLS_CHACHA20_POLY1305_SHA256ALPN h2, http/1.1

Open the SSL/TLS view →

A-Packets does not decrypt TLS, so what these sessions reveal is their server names and certificates, not their contents. For this family that is enough, because the names themselves are the indicators; the stolen data left over the plain-HTTP channel, where it is fully visible, rather than through these encrypted sessions.

The same table holds a warning for anyone who hunts through resolver logs alone. The host also opened encrypted DNS sessions: to dns.google on port 853, which is DNS over TLS, and on 443, plus a lookup of cloudflare-dns.com at 90.7 seconds, a DNS-over-HTTPS endpoint. Lookups sent there bypass an organisation's resolver logs, and the capture shows that the sessions happened, not what they asked. Two practical consequences follow: block or route outbound 853 and the well-known DoH endpoints through a resolver you log, and keep passive captures or flow records in the evidence set, because they still show the server name in the TLS handshake even when the DNS itself was encrypted, unless the client also uses Encrypted Client Hello, which nothing in this capture does.

Why AMOS traffic is easy to confirm after the fact

This sample shares its HTTP protocol with the May macOS capture: the /api/metrics/run telemetry, the /contact upload path, the out.zip multipart filename and the user and BuildID headers appear in both. A signature that combines several of them finds related infections in proxy logs and stored captures alike, and it outlives any address list. Treat the paths and headers as the long-lived signature, and the server addresses and lure domains as indicators with a short shelf life.

The capture also shows the limits of blocking by connection alone. The working C2 at 178.62.250.99 is obvious from its traffic, but 165.227.153.3, named in the task script and never reached, would be absent from any blocklist derived from successful flows. Pulling indicators from issued commands, not just from completed connections, is the difference between blocking where the malware went and blocking where it was told to go.

Scoping the loss and shutting the door

Treat the Mac as compromised and the announced stages as successful: rotate the passwords saved in its browsers and Keychain and revoke browser sessions from a clean device, starting with financial and administrative accounts, and move any wallet funds to new keys. The task loop shows the operator could still act on the host when the capture ended, so isolate the Mac, check LaunchAgents and LaunchDaemons for whatever keeps the loop alive, image it if the incident will be documented, and rebuild it rather than clean it.

Map the behaviour to MITRE ATT&CK for the report: the paste that started it is T1204.004 (Malicious Copy and Paste); grabbing the Keychain and browser logins is T1555 with T1539 for the cookies; the uploads to the command server are T1041, Exfiltration Over C2 Channel; and the register-poll loop that follows is a T1071.001 web-protocol C2 channel. For hunting, the durable strings are the request paths and the two custom headers, not the addresses.

The macOS ClickFix write-up covers the browser side of this lure in depth, and the two-server macOS stealer report shows a related stealer splitting theft and tasking across two hosts. To analyze a capture from your own network, upload a PCAP, or keep incident traffic private with a paid plan.

More sample reports