{"id":4094,"date":"2026-10-12T23:00:41","date_gmt":"2026-10-13T03:00:41","guid":{"rendered":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/"},"modified":"2026-10-13T20:31:48","modified_gmt":"2026-10-14T00:31:48","slug":"waski-downloader-cloudflare-payload-silent-callbacks","status":"publish","type":"post","link":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/","title":{"rendered":"Waski Downloader \u2014 Cloudflare Payload, Silent Callbacks"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">A 19 KB C++ trojan downloader drops a child process, wraps its payload fetch in a valid Cloudflare-fronted TLS session, and produces exactly zero certificate errors in 150 seconds of traffic. Then it requests <code>\/images\/full\/wav.exe<\/code> \u2014 an executable named like an audio file, filed under images \u2014 and gets a 404. Sixty-eight kilobytes of beautifully themed error page. Forty-two seconds later it asks for the same URL again and gets the same answer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Join me as I walk through the PCAP and Zeek logs \u2014 the 22 DNS queries of which only three belong to the malware, the two 404s and how we know they came from the origin rather than Cloudflare&#8217;s cache, and eleven sockets to a bare Indian IP where nine complete the TCP handshake, six get as far as a Client Hello, and every one of those six is acknowledged and then hung up on after exactly five seconds of silence. That silence is what sets the retry cadence, which makes the &#8220;beacon interval&#8221; somebody else&#8217;s idle timeout. We finish on two Zeek detection scripts, both actually run against the capture, and on the reason certificate-error hunting fires five times here without once touching the malware.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Full slides: <a href=\"https:\/\/www.UnpackThePCAP.com\">https:\/\/www.UnpackThePCAP.com<\/a><\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Perfect TLS, Valid Cert, and a 404: Unpacking the Waski Downloader\" width=\"500\" height=\"281\" src=\"https:\/\/www.youtube.com\/embed\/9L2jrtzSX6Q?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Transcript:<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">00:00:00:00 &#8211; 00:00:04:38<br>Keith<br>Welcome back to Unpack the PCAP. I&#8217;m, Keith. Let&#8217;s crack open another PCAP.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:00:04:53 &#8211; 00:00:13:32<br>Keith<br>Today we&#8217;re looking at the Waski Trojan Downloader. It&#8217;s a C++ sample that pulls its payload through Cloudflare&#8217;s TLS infrastructure.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:00:13:37 &#8211; 00:00:25:51<br>Keith<br>So the download, it looks like ordinary web browsing. And this sample&#8217;s from September 3rd, 2026, and it&#8217;s interesting because the malware piggybacks on Cloudflare IPs.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:00:25:55 &#8211; 00:00:30:16<br>Keith<br>And that makes the malicious TLS sessions look like just routine web traffic.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:00:30:21 &#8211; 00:00:40:21<br>Keith<br>It&#8217;s kind of like stashing a body at the county landfill. Technically, it&#8217;s findable, but nobody&#8217;s going to excavate 12 million tons of garbage just on a hunch.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:00:40:26 &#8211; 00:00:49:12<br>Keith<br>These slides are live at UnpackThePCAP.com. Every query on this deck is there, and you can just copy and paste it and follow along.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:00:49:17 &#8211; 00:00:56:56<br>Keith<br>Yes, I&#8217;m, asking you to paste code off the internet into a terminal, and I&#8217;m, aware of what this episode is about. Do it anyways.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:00:57:01 &#8211; 00:01:21:21<br>Keith<br>Before we get started, everything here is from publicly available sources, so all the data is from Triage sandbox. You can get the PCAP there, you can get the behavioral reports and the network reports all from Triage sandbox. Every IP, domain, hash and time stamp is backed by the PCAP itself or the sandbox report that you can just download.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:01:21:26 &#8211; 00:01:41:40<br>Keith<br>We ran Zeek against the PCAP to generate our own logs and some HTTP details like get requests, user agents, 301, 404 responses. They come from the Triage sandbox because Zeek, if you just run it on the PCAP, will see the outside encrypted portion.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:01:41:45 &#8211; 00:01:57:53<br>Keith<br>But Triage sandbox is able to decrypt the inside portion and give you what those HTTP requests were so you can actually see what happens. And therefore, when you get a PCAP to the service, you get a little more information than you would if you were just to see it on the network.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:01:57:57 &#8211; 00:02:03:05<br>Keith<br>And it is useful for showing how some of this detection can work and so forth.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:02:03:05 &#8211; 00:02:14:51<br>Keith<br>Everything here is public. I&#8217;m, the true crime podcast of cybersecurity. Somebody else did the crime. I show up afterward with a nice microphone and too much confidence.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:02:14:56 &#8211; 00:02:30:20<br>Keith<br>Okay, we have 10 sections covering the Waski Downloader&#8217;s network behavior from the first DNS lookup all the way through the last failed callback. And you&#8217;ll probably say, why are we looking at this sample if there&#8217;s failed callbacks?<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:02:30:25 &#8211; 00:02:42:45<br>Keith<br>Well, it&#8217;s the Cloudflare portion that is actually interesting, and we&#8217;ll talk about some of the implications if you were to try to block this sample of actual benign traffic you would accidentally block.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:02:42:52 &#8211; 00:02:47:35<br>Keith<br>So stay tuned, and I&#8217;ll describe all that throughout this episode.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:02:47:40 &#8211; 00:03:01:57<br>Keith<br>So 10 sections for a file smaller than a selfie. And to be fair, your selfie never retried shaf.in website 11 times in 78 seconds. so let&#8217;s fire up Zeek and see what it says.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:03:02:02 &#8211; 00:03:16:57<br>Keith<br>So here&#8217;s a flow diagram of the malware activity and you can see two destinations and in those two destinations there&#8217;s two completely different hiding strategies but behind it all it&#8217;s a It&#8217;s the same TLS client trying to talk to both of them.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:03:16:58 &#8211; 00:03:23:00<br>Keith<br>And you&#8217;ll see we&#8217;re able to prove that and look at the fingerprint a little later in a future slide.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:03:23:05 &#8211; 00:03:36:03<br>Keith<br>The domain packaging-labeling.com sits behind Cloudflare. And the sandbox, the Triage sandbox, decrypted the TLS. And it found a get request for slash images slash full slash wav.exe.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:03:36:03 &#8211; 00:04:13:01<br>Keith<br>Now, when that request was made, the payload is already gone. It got a 404 but we see five TLS sessions all carrying one identical ja3, ja4 fingerprint and that&#8217;s the fingerprint that I was talking about a little earlier where we can fingerprint these TLS sessions and try to group them for detection purposes. Now we know that shaf.in it resolves to a straight to a bare Indian server. There&#8217;s no CDN, no proxy. There&#8217;s 11 TCP connections but only six got as far as a TLS client hello and every one of<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:04:13:01 &#8211; 00:04:23:07<br>Keith<br>those failed. So there was zero response bytes and this is where the two fingerprints between the JA3 and the JA4 they start to alternate.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:04:23:12 &#8211; 00:04:50:04<br>Keith<br>Now I&#8217;m, calling shaf.in the callbacks because I&#8217;m, not 100 % sure if it&#8217;s command and control because in these connections no application data was actually transferred. So I don&#8217;t have enough evidence to say hey this is definitely a command and control or hey this is definitely a failover for the other hosts that it couldn&#8217;t reach um so for now I&#8217;m, just going to call it a callback or maybe a failover throughout because we don&#8217;t really know what it is.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:04:50:04 &#8211; 00:05:28:07<br>Keith<br>All right so the same malware uses the same TLS client profiles for both domains. Both the packaging labeling and the shaf.in. Now these fingerprints alone aren&#8217;t proof of anything, they&#8217;re just fingerprints of you know TLS connections and other applications could order things just perfectly and also fit what this malware is using for its fingerprint. So if you look at just fingerprints you could get some false positives but if you start looking at the fingerprint plus these domains that we<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:05:28:07 &#8211; 00:05:47:52<br>Keith<br>highlighted and retrying every few seconds that could be your signal in this case. So this malware seemed like it ordered from a restaurant that shut down six months ago and then it called a friend and that friend let it ring 11 times and this was the saddest 78 seconds in this PCAP.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:05:47:52 &#8211; 00:06:21:46<br>Keith<br>Per the Triage analysis this Waski downloader it&#8217;s a c++ downloader that scores a perfect 10 out of 10 on their threat scale and at 19 kilobytes it&#8217;s tiny. You can think of it as just the opening act. It doesn&#8217;t do the actual damage to your computer itself it just sets the stage for whatever comes next. And I have the hashes on the screen if you&#8217;re playing along at home. Now I&#8217;m, just mentioning you&#8217;re going to see these IOCs throughout my slides and I&#8217;ll list them fully one time and then later on<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:06:21:46 &#8211; 00:06:37:42<br>Keith<br>I&#8217;ll probably truncate them because I only have so much real estate per slide so I&#8217;ve got to make things fit so in this case you can see uh the full hash but later on you&#8217;re going to see some dots in here and if you ever need any of them just go back to the first time I referenced them and you&#8217;ll find the full value.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:06:37:42 &#8211; 00:07:14:09<br>Keith<br>So this MD5 and SHA256, these are straight from the Triage report. And one thing that will confuse you if you follow the Triage link, the name of the file, the sample that they uploaded was the date and then an MD5 hash and then the phrase e-l-e-x underscore Wannacry dot e-x-e. And that&#8217;s just naming noise from whoever or whatever collected that sample and it&#8217;s not a verdict, okay? Triage&#8217;s own tags on this sample say family Waski downloader and discovery it doesn&#8217;t say a ransomware<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:07:14:09 &#8211; 00:07:25:13<br>Keith<br>anywhere on the tag. And nothing in the behavior looks like encryption so it&#8217;s a downloader, okay? Not ransomware even though it has Wannacry in its name on Triage.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:07:25:13 &#8211; 00:07:28:14<br>Keith<br>The file name on the sample says Wannacry.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:07:28:18 &#8211; 00:07:41:54<br>Keith<br>It&#8217;s not. And somewhere upstream, a collection pipeline took one look at this thing and just guessed. And now that guess is going to follow this sample around the rest of its life like a bad high school nickname.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:07:41:59 &#8211; 00:07:48:08<br>Keith<br>All right, so next, let&#8217;s look at what the sandbox actually saw this sample do.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:07:48:13 &#8211; 00:08:01:22<br>Keith<br>So here&#8217;s some behaviors the sandbox observed the sample doing. First thing is it reads control panel, international, geo nation for the country code, and then it reads control NLS language for the system language.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:08:01:27 &#8211; 00:08:20:23<br>Keith<br>Triage&#8217;s own words in the report are likely geofence. So it&#8217;s saying the malware wants to confirm it&#8217;s on the right target before downloading anything. Because sometimes these malware, they&#8217;ll, for instance, if you&#8217;re hostile with Iran, might check to see if the computer&#8217;s in Iran before it actually does its dirty deeds.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:08:20:30 &#8211; 00:08:26:00<br>Keith<br>And then if it&#8217;s anywhere else in the world, it may just be benign. That&#8217;s an example of geofencing.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:08:26:05 &#8211; 00:08:40:47<br>Keith<br>So second, this malware enumerates physical storage devices. And the Triage report describes this as attempting to interact with connected storage and optical drives. And it tags it system information discovery.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:08:40:51 &#8211; 00:08:55:37<br>Keith<br>Okay. So it&#8217;s saying this sample performs system information discovery. Now, it&#8217;s tempting to call this as an anti-virtual machine check. But nothing in the sandbox data actually says that. So I&#8217;m, not going to go that far and label that.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:08:55:37 &#8211; 00:09:14:29<br>Keith<br>Okay, third, and this one kind of reframes this episode, the parent wav.exe writes a codecsupdater.exe into the user&#8217;s temp directory, and it runs it. The 19 kilobyte sample we just hashed is done at 766 milliseconds and terminates.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:09:14:34 &#8211; 00:09:35:10<br>Keith<br>The child starts at 750 milliseconds and never exits. So by the time any malicious traffic leaves this box, the only malware still running is this codecsupdater.exe PID 3932. And this is the child of the wav.exe I talked about earlier.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:09:35:15 &#8211; 00:09:49:52<br>Keith<br>So to be precise about it, not every packet in this capture is the malware. Okay. Most of them are just Windows being Windows. There&#8217;s telemetry, there&#8217;s updates, there&#8217;s OSCP, and we&#8217;ll spend a slide on that noise later.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:09:49:57 &#8211; 00:10:24:47<br>Keith<br>What the child owns is the malicious half, the package labeling.com sessions and every callback to shaf.in. So when I say the malware connects to shaf.in I mean the dropped child the codecsupdater.exe not the file you&#8217;d get from Triage&#8217;s download button which is the wav.exe. That distinction matters if you&#8217;re hunting. Okay both files are about 19 kilobytes but the MD5s tell you that the content is completely different so the parent and the child are not the same thing.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:10:24:52 &#8211; 00:10:29:04<br>Keith<br>These hashes again are on the IOC list that we talked about throughout this episode.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:10:29:09 &#8211; 00:10:45:30<br>Keith<br>Now this also explains the user agent that we&#8217;ll see later, and the user agent is updates downloader and it&#8217;s just codecsupdater.exe basically ratting out on itself that is not a legitimate application.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:10:45:35 &#8211; 00:10:54:08<br>Keith<br>So this malware enumerates your drives like a burglar counting the TVs before whether the job is even worth doing.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:10:54:13 &#8211; 00:11:06:15<br>Keith<br>codecupdater, the name is the technique. It&#8217;s engineered so your eye just slides right off it in task manager, right? It&#8217;s like the beige sedan of executables.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:11:06:20 &#8211; 00:11:26:43<br>Keith<br>All right, so now we&#8217;re going to get Zeek involved. We generate the Zeek logs by running Zeek dash c dash r against this PCAP and I like to use this this variable where it&#8217;s default underscore logdir equals output and what that does is it takes all your logs that Zeek creates and puts it into a subdirectory and just kind of keeps things tidy for you.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:11:26:43 &#8211; 00:11:39:27<br>Keith<br>Now Zeek won&#8217;t create that directory automatically for you. It will&#8230; you you could just do like a make directory or in your Windows explorer just right click and create a directory before you run that Zeek command.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:11:39:27 &#8211; 00:11:52:37<br>Keith<br>Now Zeek will not create that output directory for you but it will however error once per log writer so you fully appreciate the scale of what you&#8217;ve done if you don&#8217;t make the directory.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:11:52:42 &#8211; 00:11:59:35<br>Keith<br>So everything else in this deck it runs from the same directory so the paths throughout my slides will stay consistent all the way through.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:11:59:40 &#8211; 00:12:15:38<br>Keith<br>Now if you&#8217;re wondering what the dash C flag is that just tells Zeek to ignore check sums and that&#8217;s kind of standard for offline analysis. I&#8217;ve been using Zeek for close to a decade now and I&#8217;ve always used dash C when I&#8217;ve imported PCAPs because it makes the errors go away.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:12:15:38 &#8211; 00:12:27:26<br>Keith<br>Now the JA3 and JA4 arguments load the two ZKG packages that add the TLS fingerprints. Zeek can&#8217;t just automatically load this itself otherwise we have to say that we want it.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:12:27:26 &#8211; 00:12:37:06<br>Keith<br>And once we load it those new fields will start to appear in our SSL log and we&#8217;ll come back to what they actually compute to later.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:12:37:11 &#8211; 00:12:53:34<br>Keith<br>Now, when you run Zeek, there are eight log files that come out of this capture that the capture is only two and a half minutes long. There&#8217;s about 1 ,492 packets over about 150 seconds, and it&#8217;s about 140 kilobytes all in.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:12:53:39 &#8211; 00:13:12:17<br>Keith<br>The victim in this PCAP is 10.127.0.5. It&#8217;s a private IP address. So this was captured on an internal network, which is exactly what we should expect for a malware sandbox. You can imagine they have probably virtual machines running on an internal network that pushes these samples to.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:13:12:22 &#8211; 00:13:18:38<br>Keith<br>They execute them on Windows, record all the PCAPs, and then shut it down and restore it back to its original state.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:13:18:38 &#8211; 00:13:40:22<br>Keith<br>Now, this capture, this PCAP capture is noisy. There&#8217;s a lot of Microsoft telemetry, and that&#8217;s pretty much normal for Microsoft. Okay, it&#8217;s our job to find the needle in this haystack. So in this PCAP, there&#8217;s actually two ICMP packets in the entire capture, and both are the gateway complaining that someone&#8217;s packets are too big.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:13:40:27 &#8211; 00:13:47:02<br>Keith<br>So the router&#8217;s whole contribution to this PCAP in this incident is just filing a noise complaint.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:13:47:02 &#8211; 00:13:56:34<br>Keith<br>There&#8217;s 19 ARP frames, and nobody has voluntarily read an ARP frame since 2004, and we&#8217;re not going to start in this episode today.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:13:56:39 &#8211; 00:14:09:10<br>Keith<br>And eight log files from 150 seconds of traffic. And Zeek is like the coworker who minutes every meeting. It&#8217;s insufferable in stand-up, but indispensable later on during an incident.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:14:09:10 &#8211; 00:14:22:53<br>Keith<br>Now on this slide, let&#8217;s confirm the victim. So every outbound connection originates from 10.127.0.5, and that&#8217;s our infected host. That&#8217;s the malware sandbox. There&#8217;s another host we see at 10.127.0.1.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:14:22:58 &#8211; 00:14:33:41<br>Keith<br>That&#8217;s the gateway of this internal network. It&#8217;s not a second victim. It&#8217;s just the other side of the communication of this ICMP flow we talked about earlier.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:14:33:46 &#8211; 00:14:35:19<br>Keith<br>Nothing suspicious.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:14:35:24 &#8211; 00:14:51:34<br>Keith<br>So here you have the DNS query breakdown, and there&#8217;s 13 unique names, and it&#8217;s basically a Microsoft family reunion. Three entries don&#8217;t belong, the package labeling.com, its www variant, and the shaf.in callback host we&#8217;ll visit later.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:14:51:34 &#8211; 00:15:09:26<br>Keith<br>There&#8217;s one DNS query each, and it&#8217;s just hiding in plain sight like they&#8217;re just avoiding a group photo. Package-Labeling.com is a name tag that reads, definitely not a predator. No legitimate business has ever had to work that hard at sounding boring.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:15:09:31 &#8211; 00:15:19:20<br>Keith<br>So before we start running Zeek queries, we need to install the DuckDB Zeek extension written by Yacin Nadji.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:15:19:25 &#8211; 00:15:23:59<br>Keith<br>This extension provides the read underscore Zeek function, and that reads Zeek TSV files natively.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:15:23:59 &#8211; 00:15:32:44<br>Keith<br>And then you have this INET module extension that handles the IP address column type. So we can do searches within subnets, for instance.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:15:32:49 &#8211; 00:15:45:54<br>Keith<br>And force install, because after 30 years in cybersecurity, I&#8217;ve learned that everything works better when it&#8217;s a little scared of you. The malware executed for 150 seconds, and I&#8217;ve spent longer than that explaining DuckDB.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:15:45:59 &#8211; 00:15:51:08<br>Keith<br>So one of us is the real threat here, and it&#8217;s not the malware.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:15:51:13 &#8211; 00:16:12:07<br>Keith<br>So let&#8217;s break down the traffic by protocol. We have 66 TCP rows, 20 UDPs, and 1 ICMP. And be very careful how you read that TCP number. Those are con.log rows, not necessarily connections. The hosts only used 45 source ports, and it picked a fresh one for every new connection.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:16:12:11 &#8211; 00:16:26:38<br>Keith<br>So 45 is closer to the real count. The extra 21 rows are just Zeek closing out unanswered SYN attempts after five seconds, which means a client retransmitting into silence. That gets logged several times over in Zeek.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:16:26:38 &#8211; 00:16:30:32<br>Keith<br>We watched that happen in the shaf.in slides coming up.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:16:30:32 &#8211; 00:16:48:39<br>Keith<br>The UDP is entirely DNS. We have 22 queries riding on those 20 rows, and all are going to 8.8.8.8, which is Google&#8217;s DNS server. And the TCP is where the action is. You got TLS, HTTP, and the suspicious connections.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:16:48:44 &#8211; 00:16:59:49<br>Keith<br>So 45 connections in 150 seconds. 11 of them got back nothing, not one byte. I have sat in sprint plannings with exactly that throughput.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:16:59:54 &#8211; 00:17:09:26<br>Keith<br>And you have one ICMP flow, and it&#8217;s just the gateway telling the host that its packets are too big. And it&#8217;s not a ping. Sorry, Captain Ramius.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:17:09:31 &#8211; 00:17:33:22<br>Keith<br>Let&#8217;s isolate the malicious DNS queries. We have three domains: the packaging labeling.com it resolved first at 11:22:20. The capture starts at 11:22:17 so that&#8217;s about 2.8 seconds in and by then, the parent had already exited and dropped the child, and that was the only malware process running.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:17:33:27 &#8211; 00:17:39:41<br>Keith<br>Then the package labeling.com and the shaf.in happened roughly 67 seconds later.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:17:39:41 &#8211; 00:17:59:01<br>Keith<br>The package labeling.com resolves to 104.21.27.45, and 172.67.168.231. These are classic Cloudflare Anycast, and we&#8217;ll confirm that with a Whois lookup here in a few minutes.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:17:59:06 &#8211; 00:18:22:04<br>Keith<br>The Package-Labeling.com resolves to the same two IPs, and note that the malware didn&#8217;t decide to try the WWW host on its own. This happened about a second later after the first apex session that actually completed the handshake because the server answered that one with a 301 and it pointed to its www host.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:18:22:09 &#8211; 00:18:27:23<br>Keith<br>So we&#8217;ll see that redirect here in a slide or two in the future.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:18:27:28 &#8211; 00:18:41:43<br>Keith<br>The shaf.in resolves to a single IP address of 103.212.120.166. And whois shows this is MilesWeb, not a known CDN provider, and it&#8217;s also confirmed on this next slide.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:18:41:48 &#8211; 00:18:54:26<br>Keith<br>So the malware honors HTTP redirects. It reads the location header. It respects the standard. It&#8217;s going to die having done absolutely everything correctly except the crime.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:18:54:31 &#8211; 00:19:08:56<br>Keith<br>Before we look at TLS, let&#8217;s confirm who owns these IP addresses. WhoIs lookups against ARIN and APNIC. Both the packaging-labeling.com IPs come back to Cloudflare, Inc., and that&#8217;s a confirmed Anycast proxy.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:19:09:00 &#8211; 00:19:28:48<br>Keith<br>The Shaf.in, MilesWeb Internet Services, is an Indian hosting provider. And as far as I can tell, no CDN, no CloudFlare, no hiding. It&#8217;s just a normal IP address. And this matters because the CloudFlare IP addresses that we talked about earlier are shared across millions of sites.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:19:28:53 &#8211; 00:19:49:20<br>Keith<br>Blocking by IP address not only would block the maliciousness, but it would block many, many, many, many innocent customers. So when we detect and block, it has to be done by domain or TLS fingerprint or something else that&#8217;s not just an IP address.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:19:49:25 &#8211; 00:20:04:59<br>Keith<br>So sure, go ahead, block these Cloudflare IPs. You&#8217;ll take out a respectable fraction of the Internet to stop a 19-kilobyte downloader. It&#8217;s like having a wasp problem in your house and you just decide to burn down your whole house.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:20:05:04 &#8211; 00:20:13:03<br>Keith<br>Here you can see five TLS sessions to packaging-labeling.com. They&#8217;re all TLS 1.2, and you can tell by the version column.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:20:13:08 &#8211; 00:20:20:16<br>Keith<br>So the first session fails. We see that with established equals false, and the next two succeed with new handshakes.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:20:20:21 &#8211; 00:20:38:36<br>Keith<br>And we see established equals true but resumed equals false and then the last two resume the connections because you see resumed equals true. So we see five TLS connections to a server that had already told it no so this is a downloader that just hears a 404 and it hears not yet.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:20:38:41 &#8211; 00:20:49:51<br>Keith<br>Resumed TLS sessions skip the cert exchange and they use a previously negotiated session so So the whole cert chain will be null in the logs for those connections.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:20:49:56 &#8211; 00:21:00:10<br>Keith<br>So if we filter on the chains that are not null in the logs, that gives us three connections that actually completed the full handshake and received the chain.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:21:00:15 &#8211; 00:21:08:54<br>Keith<br>These are the only connections where we can pull the full cert chain. And in the next slide, we&#8217;re going to prove that the three shared all the same cert chain.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:21:08:59 &#8211; 00:21:15:50<br>Keith<br>Resumed sessions skip the cert exchange, so it seems that even threat actors have their own loyalty programs.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:21:15:55 &#8211; 00:21:28:01<br>Keith<br>To prove all three share the same cert chain, we&#8217;re going to hash the cert chain array and look at the count. And then we&#8217;ll use some SQL to group them. And you see here, there&#8217;s one hash, the count equals three, so it&#8217;s confirmed.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:21:28:06 &#8211; 00:21:29:21<br>Keith<br>They all use the same one.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:21:29:21 &#8211; 00:21:41:17<br>Keith<br>So any three of these connections will work for pulling the search chain. And we&#8217;re just going to pick the one that ends in LPI because it&#8217;s established and not resumed, and it&#8217;s probably the cleanest example.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:21:41:22 &#8211; 00:21:56:35<br>Keith<br>There are three full handshakes and one identical cert chain. This attacker bought exactly one certificate it and is going to get every penny of value out of it. On the next slide we&#8217;re going to pull the full cert chain for this connection.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:21:56:40 &#8211; 00:22:13:14<br>Keith<br>There you can see the complex SQL to pull the full cert chain for one connection and we see the three levels: the leaf, the intermediate, and the root. And we picked LPI the connection LPI because it&#8217;s a non-resumed connection and resume connections don&#8217;t resend the cert chain.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:22:13:28 &#8211; 00:22:20:11<br>Keith<br>So the cert chain is null in those instances and is not null in this instance.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:22:20:16 &#8211; 00:22:30:34<br>Keith<br>The leaf is signed by WE1, a Google Trust Services Intermediate. And WE1 is signed by GTS root R4, a Google root CA.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:22:30:45 &#8211; 00:22:40:28<br>Keith<br>So GTS root R4 is cross-signed by global sign root CA, a well-known public root. So this is a legitimate publicly trusted certificate chain.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:22:40:33 &#8211; 00:22:45:40<br>Keith<br>The attacker didn\u2019t forge it. They obtained a real cert from Google Trust Services.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:22:45:44 &#8211; 00:23:03:12<br>Keith<br>And this means the attacker controls the domain and passed Google&#8217;s domain validation checks. The cert is valid, publicly trusted, and chaining to a known root. So the malware has a certificate from Google Trust Services, and my self-signed certs get rejected by Chrome.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:23:03:17 &#8211; 00:23:08:01<br>Keith<br>Apparently malware has better CA relationships than I do.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:23:08:06 &#8211; 00:23:25:51<br>Keith<br>Now here&#8217;s where the sandbox earns its keep. Zeek only sees TLS connections to packaging-labeling.com from the outside, and it only sees the encryption portion. Now there&#8217;s HTTP inside the encrypted portion that the sandbox is able to tell us about.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:23:25:56 &#8211; 00:23:43:58<br>Keith<br>The Triage sandbox decrypted the TLS and revealed that the actual HTTP request is get slash images slash full slash wav.exe with a hard-coded user agent of updates downloader. So it&#8217;s trying to download a Windows executable disguise as a WAV audio file.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:23:44:03 &#8211; 00:23:47:55<br>Keith<br>Subtle.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:23:48:00 &#8211; 00:23:56:34<br>Keith<br>The server returned a 301 redirect to the www subdomain. The location header points at the exact same path. And then, when the connection happens, there&#8217;s a<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:23:56:34 &#8211; 00:24:04:11<br>Keith<br>404. So the malware follows a redirect and asks again, which is why you&#8217;ll see the two connections per attempt.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:24:04:11 &#8211; 00:24:29:19<br>Keith<br>And it does the whole thing twice a 301 then a 404 at 11:23 then a 301 and a 404 again at 11:24. Four requests for a wav.exe in total. That file that wav.exe was not on the web server but the site itself is still serving traffic normally so this is a live host missing one payload and probably not abandoned infrastructure.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:24:29:24 &#8211; 00:24:45:50<br>Keith<br>The server didn&#8217;t just say no, it sent a 301 to the www subdomain, the exact same path, so the malware could go ask again somewhere else and get told no there instead. That&#8217;s not infrastructure failure, that&#8217;s just customer service.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:24:45:55 &#8211; 00:24:59:48<br>Keith<br>Slash images slash full slash wav.exe. Three lies in one URL. The directory says images, the name says audio, and the extension says executable. And that&#8217;s just commitment to the bit.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:24:59:53 &#8211; 00:25:14:37<br>Keith<br>So we started with the Cloudflare IPs from the DNS slide. Filtered conn.log to these IPs and sorted by response bytes. And our top two responses are 68 kilobyte and 65 kilobyte. And they are the 404 pages.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:25:14:42 &#8211; 00:25:36:27<br>Keith<br>The third one, which is 4.8 kilobyte, is the 301 redirect. So these 404s, they came from the real website, not Cloudflare. And we know this because of the CF-cache-status says dynamic. And if you do some research online, Cloudflare&#8217;s cache docs define that asset as being ineligible for cache.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:25:36:32 &#8211; 00:25:46:25<br>Keith<br>So the request went all the way to the origin web server without a cache lookup. So that&#8217;s the site&#8217;s own themed 404 page, all 68 kilobytes of it.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:25:46:25 &#8211; 00:25:51:32<br>Keith<br>So the site is live and well, it just doesn&#8217;t have WAV.exe anymore.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:25:51:37 &#8211; 00:25:56:57<br>Keith<br>68 kilobytes of 404, three times larger than the malware itself.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:25:57:02 &#8211; 00:26:01:28<br>Keith<br>That error page has more content than the malware&#8217;s entire career.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:26:01:33 &#8211; 00:26:13:50<br>Keith<br>So this connection ID that ends in CF is about 500 bytes sent and 68k bytes received. The Triage sandbox decrypted the TLS on this session and we see it&#8217;s the 404.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:26:13:55 &#8211; 00:26:30:05<br>Keith<br>500 bytes of question, 68 kilobytes of no. That is the reply to substance ratio of a legal department. The S1 flag means nobody ever hung up. The capture ends with both sides still politely pretending the conversation is going somewhere.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:26:30:10 &#8211; 00:26:48:58<br>Keith<br>shaf.in is a second destination, and the Whois shows MilesWeb, not a known CDN, just a domain pointing to a single IP address. The timing is the interesting part. The first bytes of the 404 hit the wire at 70.7882 seconds into this capture.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:26:49:03 &#8211; 00:27:11:10<br>Keith<br>And the DNS query goes out at 70.7906, two and a half milliseconds later, while the 68 kilobyte error page is still streaming in and won&#8217;t finish arriving until 70.87 seconds. So it read the status line, wrote that host off, and moved on without even waiting for that error body to come back.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:27:11:10 &#8211; 00:27:43:57<br>Keith<br>Then it alternates. It hammers shaf.in it goes back and re-requests the wav.exe from the other domain and then it collects another 404 and it goes back to shaf.in and that&#8217;s part of the reason why it&#8217;s difficult to tell what that shaf.in is without more context. So the interleaving reads more like a backup download source than a command and control channel but no handshake to shaf.in ever completed so no application data was ever exchanged. And I&#8217;m, not going to tell you which one it was<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:27:43:57 &#8211; 00:27:57:00<br>Keith<br>because the capture also cannot tell you which one it was. shaf.in that&#8217;s the four letters and a country code and that&#8217;s the domain equivalent of a burner phone bought with exact change.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:27:57:05 &#8211; 00:28:06:20<br>Keith<br>It read the 404 status line and was already dialing shaf.in two and a half milliseconds later while the error page was still arriving.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:28:06:25 &#8211; 00:28:11:45<br>Keith<br>That&#8217;s not efficiency. That&#8217;s a downloader that&#8217;s probably been let down before.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:28:11:50 &#8211; 00:28:23:07<br>Keith<br>So six TLS sessions to shaf.in and out of the 11 TCP connections and all six of them failed. All six of them ran exactly 5.077 seconds before closing.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:28:23:11 &#8211; 00:28:29:46<br>Keith<br>The history on every one of these connections show that the server&#8217;s FIN was sent first.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:28:29:51 &#8211; 00:28:42:57<br>Keith<br>The server completes the handshake TCP half, acknowledges the client hello, sits on it for five seconds, and then just hangs up. And that&#8217;s the server side idle timeout. It&#8217;s what sets the malware&#8217;s retry cadence.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:28:43:01 &#8211; 00:28:48:57<br>Keith<br>The gap between the attempts are 5.08 seconds because the malware reconnects the instant it gets dropped.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:28:48:57 &#8211; 00:29:02:04<br>Keith<br>And we&#8217;re going to talk about the TLS fingerprinting using JA3 and JA4 on the next slide. So you have six attempts, zero responses. The server didn&#8217;t even have the courtesy to say, new phone, who dis?<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:29:02:09 &#8211; 00:29:36:35<br>Keith<br>JA3 and JA4 are TLS client fingerprinting methods, and these are the two packages we installed way back in the log generation slide. They add the JA3, JA3S, JA4, JA4S fields into SSL.log. And without those these packages those columns don&#8217;t exist which is why we had to install it and we run it when we run Zeek so that those fields show up. The JA3 gives you a 32 character MD5 hash and it&#8217;s simple and widely supported because it&#8217;s been out there for a long time and the newer JA4 gives<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:29:36:35 &#8211; 00:29:54:35<br>Keith<br>you a human readable string with more detail. It gives you a version, a cipher count, an extension count. It also gives you this thing called ALPN, which is Application Layer Protocol Negotiation. And this is when the client lists the protocols it can speak right in the client hello.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:29:54:39 &#8211; 00:30:02:51<br>Keith<br>And this gets agreed to during the handshake instead of costing a round trip later on. Now, none of the malware&#8217;s 11 connections do this.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:30:02:51 &#8211; 00:30:04:12<br>Keith<br>That&#8217;s important, okay?<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:30:04:12 &#8211; 00:30:17:54<br>Keith<br>These are things that survive domain rotation. So the TLS stack itself stays the same even when the infrastructure changes. So we should expect these fingerprints also to stay the same.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:30:17:54 &#8211; 00:30:24:10<br>Keith<br>You know, the attacker can burn the domain, rotate the IP, rename the binary, and rehost on a different continent.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:30:24:15 &#8211; 00:30:35:45<br>Keith<br>And the handshake still comes out byte identical every time because nobody recompiles a TLS library to hide. It&#8217;s the DNA evidence left by someone who took the trouble to wear gloves.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:30:35:50 &#8211; 00:30:40:39<br>Keith<br>You see here the two JA3 and JA4 client fingerprints from the malicious connections.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:30:40:39 &#8211; 00:31:17:29<br>Keith<br>And one caveat, there&#8217;s no global registry that says these hashes belongs to Waski Downloader. So any app built on the same TLS stack with the same config is going to produce identical hashes. That&#8217;s just how it is. Okay. But the proof is right in this capture. The benign Windows OS traffic carries a JA4 that has some identical portions: the b and the c sections to the malware&#8217;s b and c sections of their fingerprints. And only the extension count and the ALPN that I talked about earlier change so<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:31:17:29 &#8211; 00:31:44:49<br>Keith<br>you can see that it&#8217;s the same TLS library but in this case it&#8217;s a different caller. So we&#8217;re able to differentiate it. So what you want to do is you want to use these hash matches as kind of like a lead. Not necessarily a verdict. You know we&#8217;re going to write a JA4 rule later on and it&#8217;s going to fire on that fingerprint by itself and that&#8217;s deliberate because it&#8217;s a hunting rule where we want to cast a wide net to see what&#8217;s out there on our network.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:31:44:54 &#8211; 00:31:56:08<br>Keith<br>What makes it actionable is the destination and the retry behavior around it. That&#8217;s what&#8217;s going to make a human look at that and say, okay, well, this is probably the Waski downloader versus something that may be benign.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:31:56:08 &#8211; 00:32:09:35<br>Keith<br>So this is perfect camouflage accidentally achieved and then ruined by dropping ALPN. And it&#8217;s like a ghillie suit with a giant name tag on it. So next slide, let&#8217;s go ahead and decode the JA4 strings.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:32:09:40 &#8211; 00:32:49:01<br>Keith<br>Let&#8217;s go ahead and decode these JA4 strings using the FoxIO spec. Now both fingerprints end in 00 so it means no ALPN and most modern browsers typically negotiate ALPN and this is a strong signal that the malware&#8217;s TLS stack doesn&#8217;t transmit the ALPN. And it&#8217;s one of those things you can look for and say okay well this might be a malware connection versus a likely benign browser connection. And then you can look and see the TLS 1.2 with 19 ciphers and 8 extensions and it&#8217;s the same TLS stack that<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:32:49:01 &#8211; 00:32:53:26<br>Keith<br>the operating itself uses. And I can prove that rather than assume it.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:32:53:31 &#8211; 00:32:58:36<br>Keith<br>Pull the raw client hello&#8217;s out of this PCAP and the malware offers the same 19 cipher suites<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:32:58:36 &#8211; 00:33:04:44<br>Keith<br>in the same order with the same extensions as the Windows telemetry client hello&#8217;s from the same box.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:33:04:49 &#8211; 00:33:16:26<br>Keith<br>The only difference is that the malware emits ALPN extension 16 and nothing else changes. That&#8217;s why B and C halves of the JA4 match the benign traffic exactly.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:33:16:26 &#8211; 00:33:38:37<br>Keith<br>The second one is the TLS with 1.0 with seven ciphers and seven extensions and that&#8217;s very unusual in 2026 and it doesn&#8217;t even send signature algorithms. Parts B and C are truncated hashes and they&#8217;re useful for grouping but the A section is what makes these connections in this sample rare.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:33:38:42 &#8211; 00:33:47:37<br>Keith<br>So TLS 1.0 in 2026 is such a loud signal that the encryption is actively helping you find it. It brought a disguise that glows.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:33:47:37 &#8211; 00:34:08:04<br>Keith<br>You see here that all five sessions use the same JA4 fingerprint there&#8217;s no variation. JA4 is the modern standard. JA3 is dated and a little less granular but JA4 captures at least in this sample. This is what matters. The alternation we saw earlier only happens on shaf.in.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:34:08:09 &#8211; 00:34:19:33<br>Keith<br>On the next slide we&#8217;re going to take a look at shaf.in. So five sessions, one fingerprint&#8230; not one attempt to vary it. This malware packed a single outfit for a five-day conference.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:34:19:38 &#8211; 00:34:35:32<br>Keith<br>So you see strict alternation here. You got the TLS 1.2, TLS 1.0, TLS 1.2, TLS 1.0, never two in a row. You see the payload sizes track the fingerprint exactly. You see the TLS 1.2 client hello is 174 bytes.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:34:35:37 &#8211; 00:34:51:14<br>Keith<br>The TLS 1.0 one is 120. And these are two hard-coded handshakes, It&#8217;s not necessarily a negotiation. The gaps aren&#8217;t a clean beacon interval. You have 26 seconds, then 5, then 5, then 32, then 5.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:34:51:14 &#8211; 00:34:58:11<br>Keith<br>It&#8217;s a retry loop, not a timer, and we don&#8217;t know why it swaps. The PCAP only shows the pattern and not the reason.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:34:58:11 &#8211; 00:35:04:43<br>Keith<br>The fallback logic works perfectly. It correctly and repeatedly falls back to a second protocol that also does not work.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:35:04:43 &#8211; 00:35:22:13<br>Keith<br>13 conn.log entries for shaf.in over 78 seconds, but the host only opened 11 sockets. That gap is the most useful thing on this slide. Look at the pattern first. 5 second durations with 174, 120 byte payloads alternating.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:35:22:18 &#8211; 00:35:58:35<br>Keith<br>Then a short.075 second connections that closed cleanly with FINs and there&#8217;s no data transferred. The responding bytes column is the punchline. Not one byte ever came back. Every row that counted bytes logs a literal zero and the two single SYN rows never counted anything so they log null. Now the source port column rows two three and four are all source port four nine nine four five. That&#8217;s one socket not three. It sent five SYNs over 15 seconds backing off one, two, four, then eight, and never got a<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:35:58:35 &#8211; 00:36:13:23<br>Keith<br>single reply. Here&#8217;s why Zeek split it. There is a TCP attempt, delay variable that expires an unanswered connection attempt after five seconds. The first record covers the three SYNs inside that window.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:36:13:27 &#8211; 00:36:46:11<br>Keith<br>Every retransmit that arrives after the record has been flushed, and it opens a brand new one, one packet, nothing to count, so orig bytes, responding bytes, and duration all come back as null. So don&#8217;t read the null rows as the malware gave up after one SYN, they&#8217;re the opposite. They are the retries. And same for the Cloudflare side where 11 entries are really seven sockets. Two of them split into three rows each exactly like port<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:36:46:16 &#8211; 00:36:46:53<br>Keith<br>49945.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:36:46:53 &#8211; 00:37:05:35<br>Keith<br>And this is why you check source ports before you quote a connection count off of conn.log. Log lines are not connections. Conn state SF means the connection completed normally from Zeek&#8217;s perspective, but every SF connection to shaf.in has zero response bytes.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:37:05:40 &#8211; 00:37:19:17<br>Keith<br>The malware is sending its callback and getting nothing back. So it&#8217;s shouting into the void. 11 attempts in 78 seconds. That&#8217;s more persistent than an ex who just discovered you have a new Netflix password.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:37:19:22 &#8211; 00:37:25:10<br>Keith<br>Are you there? Hello? I just want to talk. Please respond. I&#8217;ll try again in five seconds.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:37:25:15 &#8211; 00:37:39:47<br>Keith<br>So here&#8217;s the problem for the SOC analyst. 19 out of the 22 DNS queries are legitimate Microsoft and certificate validation traffic. Windows telemetry, update checks, OCSP, CRL, all normal, all noisy.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:37:39:52 &#8211; 00:37:50:26<br>Keith<br>The malicious domains, the packaging labeling, and shaf.in are only 3 queries out of 22, and that&#8217;s only 13.6 % of the DNS traffic.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:37:50:31 &#8211; 00:37:57:05<br>Keith<br>So this malware hides in the benign noise. It&#8217;s like shoplifting at Walmart on Black Friday.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:37:57:10 &#8211; 00:38:03:27<br>Keith<br>Everyone&#8217;s carrying suspicious bags. You need a really good reason to stop one specific person.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:38:03:32 &#8211; 00:38:11:01<br>Keith<br>Three malicious queries buried in 19 Microsoft ones. The malware didn&#8217;t need encryption. It had Windows doing the noise for free.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:38:11:01 &#8211; 00:38:29:08<br>Keith<br>We established MilesWeb back on the ownership slide and I&#8217;m, not going to be running that whois again. But what GEOIP adds to the detail is a city and a country and a network number. We see that&#8217;s commodity infrastructure, not APT grade, not sophisticated it&#8217;s just a cheap VPS.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:38:29:13 &#8211; 00:38:32:50<br>Keith<br>And it&#8217;s worth noticing that the GeoIP agrees with the whois here.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:38:32:55 &#8211; 00:38:37:22<br>Keith<br>Hold on to that because that&#8217;s on the very next slide we&#8217;ll see that it doesn&#8217;t agree.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:38:37:27 &#8211; 00:38:50:02<br>Keith<br>City, state, ASN, GeoIP hands over the whole address without being asked. And for once it&#8217;s telling the truth. That&#8217;s what you get when the target is one server sitting in the one place it&#8217;s always ever been.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:38:50:07 &#8211; 00:38:53:17<br>Keith<br>So enjoy it. It does not last past this slide.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:38:53:17 &#8211; 00:39:10:14<br>Keith<br>Now cf-ray is a header cloudflare adds the responses and cloudflare&#8217;s docs call it a hashed value in coding information about the data center and the visitor&#8217;s requests. The ID is unique per request. The three letters after the dash are the data center.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:39:10:19 &#8211; 00:39:15:44<br>Keith<br>We only have it because a sandbox decrypted this TLS, and otherwise Zeek would have never have seen it.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:39:15:44 &#8211; 00:39:31:59<br>Keith<br>Now, don&#8217;t trust that city. Both IPs geolocate to Toronto. But all four decrypted responses, both 301s and both 404s, and their CF ray in LHR, each with a different ID.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:39:32:04 &#8211; 00:39:37:57<br>Keith<br>Toronto is in Cloudflare&#8217;s own list as YYZ. Both links are on the slide so you can<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:39:37:57 &#8211; 00:39:49:40<br>Keith<br>GeoIP on anycast address is a database picking a favorite out of hundreds of announcement points. The header names an actual facility. Neither one gives you the origin. That&#8217;s Cloudflare&#8217;s whole product.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:39:49:45 &#8211; 00:40:03:04<br>Keith<br>Cloudflare is the hide-behind-the-crowd strategy of the Internet. When your traffic looks like everyone else&#8217;s, nobody gets stopped at the door. It&#8217;s the security equivalent of I&#8217;m, with the band. GeoIP says Toronto.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:40:03:09 &#8211; 00:40:07:44<br>Keith<br>The header says London, and its origin is somewhere neither of them will admit to.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:40:07:49 &#8211; 00:40:13:59<br>Keith<br>Three sources, zero actual addresses. That&#8217;s Cloudflare working exactly as advertised.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:40:14:04 &#8211; 00:40:15:44<br>Keith<br>Here are some IOCs.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:40:15:49 &#8211; 00:40:27:56<br>Keith<br>File hashes first, domains next, then the network indicators. Yep, I know. I abbreviated on this slide. Hey, I only have so much real estate. But like I said earlier, you can actually get to these slides through Unpack the PCAP.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:40:28:05 &#8211; 00:40:35:43<br>Keith<br>And I have all the unabridged versions of the hashes in these slides that you can copy out. So hope to see you over there.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:40:35:48 &#8211; 00:40:55:25<br>Keith<br>The two file hashes in here are not one. So if you only block list the parent, the wav.exe, you&#8217;re going to miss the codecsupdater.exe, and the child is the process that actually touches the network. So you&#8217;re going to want to block on both of those hashes.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:40:55:30 &#8211; 00:41:01:19<br>Keith<br>The attacker left more fingerprints than your average toddler at a sliding glass door.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:41:01:24 &#8211; 00:41:18:36<br>Keith<br>Here we have three domains and one URL path. Both the bare domain and the www host are worth blocking, and it&#8217;s because the campaign used both, one to deliver and one to redirect. The URL is encrypted inside TLS, so Zeek actually never saw it.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:41:18:41 &#8211; 00:41:37:50<br>Keith<br>It came from the Triage sandbox TLS decryption. If you have decryption on your network, you could then look for and block against this URL. If you don&#8217;t, you&#8217;re out of luck. I&#8217;m, sorry. So the parent process was codecsupdater.exe, and the payload was wav.exe.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:41:37:56 &#8211; 00:41:41:38<br>Keith<br>And you&#8217;ve got to give them this much. They committed to the whole audio bit.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:41:41:38 &#8211; 00:41:45:22<br>Keith<br>But the funny part is, then they parked it in the images folder.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:41:45:27 &#8211; 00:41:50:49<br>Keith<br>Here we have IPs and the JA3 and JA4 fingerprints and the user agent.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:41:50:54 &#8211; 00:42:02:25<br>Keith<br>The Cloudflare pair is context, not a block list entry, because if you block those IP addresses, you&#8217;re going to be blocking thousands or millions of legitimate sites.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:42:02:30 &#8211; 00:42:16:59<br>Keith<br>So we need to look at things like the domain plus the IP address plus the user agent and things like that. The user agent is another TLS decrypted artifact from the sandbox and not something that Zeek pulled off the wire.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:42:17:05 &#8211; 00:42:22:09<br>Keith<br>So like I said, if you&#8217;re already decrypting it, that is something you can look for on your network.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:42:22:14 &#8211; 00:42:25:27<br>Keith<br>And updates downloader as the user agent. I mean,<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:42:25:27 &#8211; 00:42:28:13<br>Keith<br>at least the attacker could have made it sound like a browser, right?<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:42:28:18 &#8211; 00:42:43:16<br>Keith<br>Here&#8217;s our first detection rule. And this is SNI based. simple, effective, but also noisy. This fires whenever a TLS client hello contains one of the three malicious domains as the server name indication.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:42:43:21 &#8211; 00:42:51:29<br>Keith<br>It sits at the domain names tier of the pyramid of pain. Easy for the attacker to rotate, but useful for immediate blocking.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:42:51:34 &#8211; 00:43:01:42<br>Keith<br>Domain-based detection is like putting someone&#8217;s photo on a wanted poster. Effective until they shave and put on glasses but for now it works.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:43:01:47 &#8211; 00:43:14:32<br>Keith<br>And now we&#8217;ll continue on with our detection. Note that the event connection state remove is not SSL established. I did this because every shaf.in handshake failed so if we were to handle SSL established,<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:43:14:37 &#8211; 00:43:20:07<br>Keith<br>it&#8217;s never going to fire for shaft.in and we miss the most important half of this infection in this sample.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:43:20:12 &#8211; 00:43:40:04<br>Keith<br>Now packaging labeling.com uses a valid cert. Okay we saw that it was Google Trust Services earlier so we can&#8217;t be looking for invalid server cert notices. It\u2019s not going to alert us to this attack and it&#8217;s worse than that it&#8217;s worse than just having no signal.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:43:40:09 &#8211; 00:43:55:39<br>Keith<br>If you were to load Zeek&#8217;s protocols, SSL validate dash certs against this PCAP, you&#8217;re going to get some invalid cert notices, but it&#8217;s going to be for the benign Microsoft connections and not the malicious connections that we&#8217;re actually looking for.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:43:55:44 &#8211; 00:44:15:17<br>Keith<br>So the signal here is actually inverted. All right. Just be aware of that. And so like I talked about earlier, blocking IP addresses, that&#8217;s would be a bad way of trying to stop this because we would be blocking benign websites with this Anycast on Cloudflare. We&#8217;ve got to be looking at SNI.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:44:15:22 &#8211; 00:44:22:46<br>Keith<br>That SNI is going to tell us where the client is going, and if we&#8217;re going to block on it, that&#8217;s the point where we should be considering a block.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:44:22:51 &#8211; 00:44:35:12<br>Keith<br>You know, regarding the invalid certs, too, It&#8217;s like we have five suspects, and all of them are our own operating system. The one guy who doesn&#8217;t have an invalid cert has clean paperwork, and that&#8217;s the person that did it.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:44:35:12 &#8211; 00:45:06:29<br>Keith<br>This is the JA4 base detection using the two known Waski fingerprints JA4 is preferred over JA3. It&#8217;s human readable. There is an a section that makes it easier to understand what&#8217;s unusual about the fingerprint. We&#8217;re able to pick out things easily like TLS 1.0 with seven ciphers and no ALPN easily just by looking at the fingerprint. And these fingerprints ,they survive domain rotations. They can be deployed as a persistent hunting rule.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:45:06:32 &#8211; 00:45:13:31<br>Keith<br>So if shaft.in disappears and goes to a different domain, we should still be able to catch it if we&#8217;re looking at fingerprints.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:45:13:36 &#8211; 00:45:27:30<br>Keith<br>And notice that this rule does not do some things. It does not look at a destination. It does not look at a SNI condition. So any process that has the same TLS stack with the same config will trip it.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:45:27:30 &#8211; 00:45:37:59<br>Keith<br>Now in this capture, it fires twice and both are on the malware. But if you&#8217;re going to push this out into your real network, you&#8217;re going to want to tune this one before you hook it up to someone&#8217;s pager.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:45:38:04 &#8211; 00:45:44:06<br>Keith<br>Two fingerprints, no destination check. So anything running the same TLS stack sets this off.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:45:44:11 &#8211; 00:45:50:26<br>Keith<br>This rule is like a smoke detector mounted directly above your stove. Genuinely useful, but you&#8217;ll still come to resent it.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:45:50:26 &#8211; 00:45:57:55<br>Keith<br>On our next slide, we&#8217;re going to talk about the event handler that catches all the connections, including failed handshakes.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:45:58:00 &#8211; 00:46:11:37<br>Keith<br>So you can see in our detection logic here, we use connection state remove, and that catches all connections, including the failed TLS handshakes. And it generates a notice with the JA4 hash, a source, destination IP, the SNI, and whether the handshake completed.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:46:11:42 &#8211; 00:46:19:34<br>Keith<br>The Zeek script that catches malware by its TLS fingerprint. The bouncer doesn&#8217;t care about your fake ID. He remembers the shape of your ears.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:46:19:39 &#8211; 00:46:22:41<br>Keith<br>So theory is great, but let&#8217;s prove that these scripts actually work.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:46:22:46 &#8211; 00:46:26:51<br>Keith<br>We run both of them against the PCAP and we check notice.log for detections.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:46:26:56 &#8211; 00:46:42:06<br>Keith<br>The scariest rule in any SIEM is the one that has never fired, because you can&#8217;t tell nothing happened from this has been broken since 2019. And writing the rule is the fun part. Running it is the part where you find out you typed the SNI wrong.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:46:42:11 &#8211; 00:46:47:13<br>Keith<br>Every detection rule works perfectly right up until the first time you run it.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:46:47:18 &#8211; 00:47:08:45<br>Keith<br>Luckily, in our case here, you see, our notices actually fired. There&#8217;s 11 TLS sessions that carried a malicious SNI, 5 to packaging, 6 to shaf.in. We get five notices, not 11, because identifier plus Zeek&#8217;s default one-hour suppression window collapses repeats of the same source, destination, and SNI.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:47:08:50 &#8211; 00:47:15:49<br>Keith<br>That&#8217;s a feature. You want one alert per infection, not one alert every single time it happens.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:47:15:54 &#8211; 00:47:30:57<br>Keith<br>So two things in the output that look like mistakes, and they&#8217;re not. First, www.packaging-labeling.com is listed twice. My identifier includes the responder IP, and those two sessions went to different Cloudflare addresses.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:47:31:02 &#8211; 00:47:44:15<br>Keith<br>There&#8217;s the one that starts with the 104, and then there&#8217;s the one that starts with 172. So Zeek counts them separately. Drop resp underscore h from the identifier if you want one alert per domain instead.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:47:44:20 &#8211; 00:48:01:44<br>Keith<br>Second, three notices share the same second, 11:24:47. Those three connections are all connection state S1, which means established but never closed. So conn state remove doesn&#8217;t fire until Zeek flushes the remaining state at the end of the capture.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:48:01:48 &#8211; 00:48:06:40<br>Keith<br>The notice time stamp is when Zeek retired the connection, not when the client hello went out.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:48:06:48 &#8211; 00:48:16:54<br>Keith<br>The important line is the shaft.in. conn state removed catches the SNI from the client hello, even when the handshake fails and the server never responds.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:48:16:59 &#8211; 00:48:23:10<br>Keith<br>This works without SSL inspection. The SNI is visible in the client hello.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:48:23:22 &#8211; 00:48:37:59<br>Keith<br>Five alerts is a suspiciously pleasant number. On most days, I&#8217;d assume the sensor was unplugged. Suppression is the one feature in your entire stack designed around the fact that humans read the top of a list and nothing else.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:48:38:04 &#8211; 00:48:50:16<br>Keith<br>You can see that JA4 caught both fingerprints, even shaf.in, where the TLS handshake failed. The SNI is good for known domains. JA4 is good for known client behavior, regardless of a domain.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:48:50:16 &#8211; 00:48:54:04<br>Keith<br>Caught by the handwriting on a ransom note that never got mailed.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:48:54:09 &#8211; 00:49:08:12<br>Keith<br>They can change the domain, the IP, and the certificate. They cannot change the way they walk into a room. You can rotate infrastructure all day. You&#8217;re still the guy who orders the same weird thing at the restaurant.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:49:08:17 &#8211; 00:49:11:14<br>Keith<br>All right, we&#8217;re now down to our takeaways.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:49:11:19 &#8211; 00:49:24:07<br>Keith<br>So Cloudflare IPs are shared and GeoIP on them is fiction. Okay. You are going to want to block by domain, not IP address. So that way you&#8217;re not blocking a bunch of benign websites accidentally.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:49:24:12 &#8211; 00:49:37:56<br>Keith<br>If you only block the samples hash that you get from Triage, you miss the process to actually touch the network. There are two hashes to this and the child is the one that matters.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:49:38:01 &#8211; 00:49:43:59<br>Keith<br>And before you quote the number of lines in a conn.log and say that&#8217;s the number of connections, be sure to count by the four tuple first.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:49:44:04 &#8211; 00:49:54:04<br>Keith<br>Zeek&#8217;s five-second attempt timeout inflated this capture by two rows in shaf.in and four more in Cloudflare&#8217;s slide, if you were to just count by rows.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:49:54:09 &#8211; 00:50:22:12<br>Keith<br>The JA3 and the JA4 fingerprints survived domain rotation. The most durable detection we&#8217;ve got this whole thing, but the benign Windows traffic in the same PCAP differs by exactly one extension so tune this before you page somebody. The malware analysis is 90% filtering noise and 10% finding the one suspicious domain and 90% explaining to somebody why 150 seconds of traffic took you six hours.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:50:22:20 &#8211; 00:50:23:53<br>Keith<br>The math just doesn&#8217;t work.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:50:23:53 &#8211; 00:50:36:46<br>Keith<br>Okay, so that&#8217;s a wrap on the Waski Downloader. Short, sneaky, and persistent. Remember, the PCAP and all analysis artifacts are publicly available. You can download them, run your own queries, and build your own detection rules.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:50:36:51 &#8211; 00:50:46:15<br>Keith<br>You just sat through a full autopsy of a 19-kilobyte file that got a 404 from its payload server and the silent treatment from its callback host. It<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:50:46:15 &#8211; 00:50:50:31<br>Keith<br>It failed at everything it tried, but I still got some useful content out of it.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:50:50:36 &#8211; 00:50:56:13<br>Keith<br>Hey, if you found this useful, please hit like and subscribe. It&#8217;s how videos like this find the people who need them.<\/p>\n\n\n<p class=\"wp-block-paragraph\">00:50:56:25 &#8211; 00:51:02:04<br>Keith<br>Please comment with what you&#8217;d like to see me unpack next. See you next time on Unpack the PCAP.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A 19 KB C++ trojan downloader drops a child process, wraps its payload fetch in a valid Cloudflare-fronted TLS session, and produces exactly zero certificate errors in 150 seconds of traffic. Then it requests \/images\/full\/wav.exe \u2014 an executable named like an audio file, filed under images \u2014 and gets a 404. Sixty-eight kilobytes of beautifully [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[144,436,140,91,24,435,4],"tags":[],"class_list":["post-4094","post","type-post","status-publish","format-standard","hentry","category-detection","category-duckdb","category-malware","category-open-source","category-pcaps","category-unpackthepcap","category-zeek"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.3 - aioseo.com -->\n\t<meta name=\"description\" content=\"A 19 KB C++ trojan downloader drops a child process, wraps its payload fetch in a valid Cloudflare-fronted TLS session, and produces exactly zero certificate errors in 150 seconds of traffic. Then it requests \/images\/full\/wav.exe \u2014 an executable named like an audio file, filed under images \u2014 and gets a 404. Sixty-eight kilobytes of beautifully\" \/>\n\t<meta name=\"robots\" content=\"max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n\t<meta name=\"author\" content=\"drkeithjones\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"DrKeithJones.com - A cybersecurity researcher&#039;s journey.\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Waski Downloader \u2014 Cloudflare Payload, Silent Callbacks - DrKeithJones.com\" \/>\n\t\t<meta property=\"og:description\" content=\"A 19 KB C++ trojan downloader drops a child process, wraps its payload fetch in a valid Cloudflare-fronted TLS session, and produces exactly zero certificate errors in 150 seconds of traffic. Then it requests \/images\/full\/wav.exe \u2014 an executable named like an audio file, filed under images \u2014 and gets a 404. Sixty-eight kilobytes of beautifully\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/drkeithjones.com\/wp-content\/uploads\/2023\/02\/ecb.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/drkeithjones.com\/wp-content\/uploads\/2023\/02\/ecb.jpg\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-10-13T03:00:41+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-10-14T00:31:48+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/keithjjones\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\t\t<meta name=\"twitter:site\" content=\"@keithjjones\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Waski Downloader \u2014 Cloudflare Payload, Silent Callbacks - DrKeithJones.com\" \/>\n\t\t<meta name=\"twitter:description\" content=\"A 19 KB C++ trojan downloader drops a child process, wraps its payload fetch in a valid Cloudflare-fronted TLS session, and produces exactly zero certificate errors in 150 seconds of traffic. Then it requests \/images\/full\/wav.exe \u2014 an executable named like an audio file, filed under images \u2014 and gets a 404. Sixty-eight kilobytes of beautifully\" \/>\n\t\t<meta name=\"twitter:creator\" content=\"@keithjjones\" \/>\n\t\t<meta name=\"twitter:image\" content=\"https:\/\/drkeithjones.com\/wp-content\/uploads\/2023\/02\/ecb.jpg\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BlogPosting\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/2026\\\/10\\\/12\\\/waski-downloader-cloudflare-payload-silent-callbacks\\\/#blogposting\",\"name\":\"Waski Downloader \\u2014 Cloudflare Payload, Silent Callbacks - DrKeithJones.com\",\"headline\":\"Waski Downloader \\u2014 Cloudflare Payload, Silent Callbacks\",\"author\":{\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/author\\\/drkeithjones_iitpux\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/#person\"},\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/2026\\\/10\\\/12\\\/waski-downloader-cloudflare-payload-silent-callbacks\\\/#articleImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/dee83de34730f3a340cb0cdf15ab1de7f04e96f129f78c426da03098df1912fb?s=96&d=retro&r=g\",\"width\":96,\"height\":96,\"caption\":\"drkeithjones\"},\"datePublished\":\"2026-10-12T23:00:41-04:00\",\"dateModified\":\"2026-10-13T20:31:48-04:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/2026\\\/10\\\/12\\\/waski-downloader-cloudflare-payload-silent-callbacks\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/2026\\\/10\\\/12\\\/waski-downloader-cloudflare-payload-silent-callbacks\\\/#webpage\"},\"articleSection\":\"Detection, DuckDB, Malware, Open Source, PCAPs, Unpack the PCAP, Zeek\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/2026\\\/10\\\/12\\\/waski-downloader-cloudflare-payload-silent-callbacks\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/drkeithjones.com\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/category\\\/zeek\\\/#listItem\",\"name\":\"Zeek\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/category\\\/zeek\\\/#listItem\",\"position\":2,\"name\":\"Zeek\",\"item\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/category\\\/zeek\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/2026\\\/10\\\/12\\\/waski-downloader-cloudflare-payload-silent-callbacks\\\/#listItem\",\"name\":\"Waski Downloader \\u2014 Cloudflare Payload, Silent Callbacks\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/2026\\\/10\\\/12\\\/waski-downloader-cloudflare-payload-silent-callbacks\\\/#listItem\",\"position\":3,\"name\":\"Waski Downloader \\u2014 Cloudflare Payload, Silent Callbacks\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/category\\\/zeek\\\/#listItem\",\"name\":\"Zeek\"}}]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/#person\",\"name\":\"drkeithjones\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/2026\\\/10\\\/12\\\/waski-downloader-cloudflare-payload-silent-callbacks\\\/#personImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/dee83de34730f3a340cb0cdf15ab1de7f04e96f129f78c426da03098df1912fb?s=96&d=retro&r=g\",\"width\":96,\"height\":96,\"caption\":\"drkeithjones\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/author\\\/drkeithjones_iitpux\\\/#author\",\"url\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/author\\\/drkeithjones_iitpux\\\/\",\"name\":\"drkeithjones\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/2026\\\/10\\\/12\\\/waski-downloader-cloudflare-payload-silent-callbacks\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/dee83de34730f3a340cb0cdf15ab1de7f04e96f129f78c426da03098df1912fb?s=96&d=retro&r=g\",\"width\":96,\"height\":96,\"caption\":\"drkeithjones\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/2026\\\/10\\\/12\\\/waski-downloader-cloudflare-payload-silent-callbacks\\\/#webpage\",\"url\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/2026\\\/10\\\/12\\\/waski-downloader-cloudflare-payload-silent-callbacks\\\/\",\"name\":\"Waski Downloader \\u2014 Cloudflare Payload, Silent Callbacks - DrKeithJones.com\",\"description\":\"A 19 KB C++ trojan downloader drops a child process, wraps its payload fetch in a valid Cloudflare-fronted TLS session, and produces exactly zero certificate errors in 150 seconds of traffic. Then it requests \\\/images\\\/full\\\/wav.exe \\u2014 an executable named like an audio file, filed under images \\u2014 and gets a 404. Sixty-eight kilobytes of beautifully\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/2026\\\/10\\\/12\\\/waski-downloader-cloudflare-payload-silent-callbacks\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/author\\\/drkeithjones_iitpux\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/index.php\\\/author\\\/drkeithjones_iitpux\\\/#author\"},\"datePublished\":\"2026-10-12T23:00:41-04:00\",\"dateModified\":\"2026-10-13T20:31:48-04:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/#website\",\"url\":\"https:\\\/\\\/drkeithjones.com\\\/\",\"name\":\"DrKeithJones.com\",\"description\":\"A cybersecurity researcher's journey.\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/drkeithjones.com\\\/#person\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Waski Downloader \u2014 Cloudflare Payload, Silent Callbacks - DrKeithJones.com","description":"A 19 KB C++ trojan downloader drops a child process, wraps its payload fetch in a valid Cloudflare-fronted TLS session, and produces exactly zero certificate errors in 150 seconds of traffic. Then it requests \/images\/full\/wav.exe \u2014 an executable named like an audio file, filed under images \u2014 and gets a 404. Sixty-eight kilobytes of beautifully","canonical_url":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/","robots":"max-snippet:-1, max-image-preview:large, max-video-preview:-1","keywords":"","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BlogPosting","@id":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/#blogposting","name":"Waski Downloader \u2014 Cloudflare Payload, Silent Callbacks - DrKeithJones.com","headline":"Waski Downloader \u2014 Cloudflare Payload, Silent Callbacks","author":{"@id":"https:\/\/drkeithjones.com\/index.php\/author\/drkeithjones_iitpux\/#author"},"publisher":{"@id":"https:\/\/drkeithjones.com\/#person"},"image":{"@type":"ImageObject","@id":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/#articleImage","url":"https:\/\/secure.gravatar.com\/avatar\/dee83de34730f3a340cb0cdf15ab1de7f04e96f129f78c426da03098df1912fb?s=96&d=retro&r=g","width":96,"height":96,"caption":"drkeithjones"},"datePublished":"2026-10-12T23:00:41-04:00","dateModified":"2026-10-13T20:31:48-04:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/#webpage"},"isPartOf":{"@id":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/#webpage"},"articleSection":"Detection, DuckDB, Malware, Open Source, PCAPs, Unpack the PCAP, Zeek"},{"@type":"BreadcrumbList","@id":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/drkeithjones.com\/#listItem","position":1,"name":"Home","item":"https:\/\/drkeithjones.com\/","nextItem":{"@type":"ListItem","@id":"https:\/\/drkeithjones.com\/index.php\/category\/zeek\/#listItem","name":"Zeek"}},{"@type":"ListItem","@id":"https:\/\/drkeithjones.com\/index.php\/category\/zeek\/#listItem","position":2,"name":"Zeek","item":"https:\/\/drkeithjones.com\/index.php\/category\/zeek\/","nextItem":{"@type":"ListItem","@id":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/#listItem","name":"Waski Downloader \u2014 Cloudflare Payload, Silent Callbacks"},"previousItem":{"@type":"ListItem","@id":"https:\/\/drkeithjones.com\/#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/#listItem","position":3,"name":"Waski Downloader \u2014 Cloudflare Payload, Silent Callbacks","previousItem":{"@type":"ListItem","@id":"https:\/\/drkeithjones.com\/index.php\/category\/zeek\/#listItem","name":"Zeek"}}]},{"@type":"Person","@id":"https:\/\/drkeithjones.com\/#person","name":"drkeithjones","image":{"@type":"ImageObject","@id":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/#personImage","url":"https:\/\/secure.gravatar.com\/avatar\/dee83de34730f3a340cb0cdf15ab1de7f04e96f129f78c426da03098df1912fb?s=96&d=retro&r=g","width":96,"height":96,"caption":"drkeithjones"}},{"@type":"Person","@id":"https:\/\/drkeithjones.com\/index.php\/author\/drkeithjones_iitpux\/#author","url":"https:\/\/drkeithjones.com\/index.php\/author\/drkeithjones_iitpux\/","name":"drkeithjones","image":{"@type":"ImageObject","@id":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/dee83de34730f3a340cb0cdf15ab1de7f04e96f129f78c426da03098df1912fb?s=96&d=retro&r=g","width":96,"height":96,"caption":"drkeithjones"}},{"@type":"WebPage","@id":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/#webpage","url":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/","name":"Waski Downloader \u2014 Cloudflare Payload, Silent Callbacks - DrKeithJones.com","description":"A 19 KB C++ trojan downloader drops a child process, wraps its payload fetch in a valid Cloudflare-fronted TLS session, and produces exactly zero certificate errors in 150 seconds of traffic. Then it requests \/images\/full\/wav.exe \u2014 an executable named like an audio file, filed under images \u2014 and gets a 404. Sixty-eight kilobytes of beautifully","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/drkeithjones.com\/#website"},"breadcrumb":{"@id":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/#breadcrumblist"},"author":{"@id":"https:\/\/drkeithjones.com\/index.php\/author\/drkeithjones_iitpux\/#author"},"creator":{"@id":"https:\/\/drkeithjones.com\/index.php\/author\/drkeithjones_iitpux\/#author"},"datePublished":"2026-10-12T23:00:41-04:00","dateModified":"2026-10-13T20:31:48-04:00"},{"@type":"WebSite","@id":"https:\/\/drkeithjones.com\/#website","url":"https:\/\/drkeithjones.com\/","name":"DrKeithJones.com","description":"A cybersecurity researcher's journey.","inLanguage":"en-US","publisher":{"@id":"https:\/\/drkeithjones.com\/#person"}}]},"og:locale":"en_US","og:site_name":"DrKeithJones.com - A cybersecurity researcher's journey.","og:type":"article","og:title":"Waski Downloader \u2014 Cloudflare Payload, Silent Callbacks - DrKeithJones.com","og:description":"A 19 KB C++ trojan downloader drops a child process, wraps its payload fetch in a valid Cloudflare-fronted TLS session, and produces exactly zero certificate errors in 150 seconds of traffic. Then it requests \/images\/full\/wav.exe \u2014 an executable named like an audio file, filed under images \u2014 and gets a 404. Sixty-eight kilobytes of beautifully","og:url":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/","og:image":"https:\/\/drkeithjones.com\/wp-content\/uploads\/2023\/02\/ecb.jpg","og:image:secure_url":"https:\/\/drkeithjones.com\/wp-content\/uploads\/2023\/02\/ecb.jpg","article:published_time":"2026-10-13T03:00:41+00:00","article:modified_time":"2026-10-14T00:31:48+00:00","article:publisher":"https:\/\/www.facebook.com\/keithjjones","twitter:card":"summary_large_image","twitter:site":"@keithjjones","twitter:title":"Waski Downloader \u2014 Cloudflare Payload, Silent Callbacks - DrKeithJones.com","twitter:description":"A 19 KB C++ trojan downloader drops a child process, wraps its payload fetch in a valid Cloudflare-fronted TLS session, and produces exactly zero certificate errors in 150 seconds of traffic. Then it requests \/images\/full\/wav.exe \u2014 an executable named like an audio file, filed under images \u2014 and gets a 404. Sixty-eight kilobytes of beautifully","twitter:creator":"@keithjjones","twitter:image":"https:\/\/drkeithjones.com\/wp-content\/uploads\/2023\/02\/ecb.jpg"},"aioseo_meta_data":{"post_id":"4094","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":null,"created":"2026-10-13 17:28:26","updated":"2026-10-14 01:27:25","seo_analyzer_scan_date":null,"focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/drkeithjones.com\/\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/drkeithjones.com\/index.php\/category\/zeek\/\" title=\"Zeek\">Zeek<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tWaski Downloader \u2014 Cloudflare Payload, Silent Callbacks\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/drkeithjones.com\/"},{"label":"Zeek","link":"https:\/\/drkeithjones.com\/index.php\/category\/zeek\/"},{"label":"Waski Downloader \u2014 Cloudflare Payload, Silent Callbacks","link":"https:\/\/drkeithjones.com\/index.php\/2026\/10\/12\/waski-downloader-cloudflare-payload-silent-callbacks\/"}],"jetpack-related-posts":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/drkeithjones.com\/index.php\/wp-json\/wp\/v2\/posts\/4094","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/drkeithjones.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/drkeithjones.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/drkeithjones.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/drkeithjones.com\/index.php\/wp-json\/wp\/v2\/comments?post=4094"}],"version-history":[{"count":1,"href":"https:\/\/drkeithjones.com\/index.php\/wp-json\/wp\/v2\/posts\/4094\/revisions"}],"predecessor-version":[{"id":4122,"href":"https:\/\/drkeithjones.com\/index.php\/wp-json\/wp\/v2\/posts\/4094\/revisions\/4122"}],"wp:attachment":[{"href":"https:\/\/drkeithjones.com\/index.php\/wp-json\/wp\/v2\/media?parent=4094"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/drkeithjones.com\/index.php\/wp-json\/wp\/v2\/categories?post=4094"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/drkeithjones.com\/index.php\/wp-json\/wp\/v2\/tags?post=4094"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}