<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel J</title>
    <description>The latest articles on DEV Community by Daniel J (@tuxxin).</description>
    <link>https://dev.to/tuxxin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4055668%2F2923f58e-fe2b-49e8-a07e-3aeb8c4bf8b2.gif</url>
      <title>DEV Community: Daniel J</title>
      <link>https://dev.to/tuxxin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tuxxin"/>
    <language>en</language>
    <item>
      <title>Referer-gated cloaking: why our scanner arrives with a source</title>
      <dc:creator>Daniel J</dc:creator>
      <pubDate>Tue, 22 Sep 2026 13:25:04 +0000</pubDate>
      <link>https://dev.to/tuxxin/referer-gated-cloaking-why-our-scanner-arrives-with-a-source-2bh</link>
      <guid>https://dev.to/tuxxin/referer-gated-cloaking-why-our-scanner-arrives-with-a-source-2bh</guid>
      <description>&lt;p&gt;A page can serve one thing to a visitor who arrived from an ad and something else to a visitor who arrived from nowhere. Traffic source picks the branch.&lt;/p&gt;

&lt;p&gt;A scanner that opens the link cold states no source. It lands in the branch built for everyone the campaign did not pay for.&lt;/p&gt;

&lt;p&gt;Cloaked delivery itself is settled ground, and our writeup on &lt;a href="https://whack.sh/news/malware-that-hides-from-scanners" rel="noopener noreferrer"&gt;a cloaked link that showed scanners a clean page&lt;/a&gt; covers the foundation. That post listed a referer gate among the limits of comparing vantage points.&lt;/p&gt;

&lt;p&gt;Our scanner can request a URL with a specified referring source and carry it through the redirect chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a cold scan lands on the clean page
&lt;/h2&gt;

&lt;p&gt;A browser normally tells a site which page sent the visitor. The &lt;code&gt;Referer&lt;/code&gt; field is an ordinary part of a web request. Whatever client makes the request supplies the value itself, and nothing authenticates it.&lt;/p&gt;

&lt;p&gt;A source-gated page reads that self-reported value and branches on it. A visitor whose stated source matches the campaign reaches the payload. Everyone else reaches a clean page.&lt;/p&gt;

&lt;p&gt;No page sent the automated request, so the field arrives empty. To the page's rule, an empty source is the wrong source, so the clean page is the correct output of the operator's own logic.&lt;/p&gt;

&lt;p&gt;The verdict is accurate about that one fetch. It says nothing about what the URL serves the intended audience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gate works by requirement instead of detection
&lt;/h2&gt;

&lt;p&gt;Fingerprinting a scanner is work. The operator has to model automation tells, and that model needs maintenance as clients change.&lt;/p&gt;

&lt;p&gt;A source gate skips all of it. The operator requires something a checker has no reason to send, and every client that fails to send it sorts itself into the harmless branch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The page never has to recognize a scanner to defeat one.&lt;/strong&gt; A better client profile or a cleaner egress changes nothing about the value the page is keyed on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cold checkers agree with each other by design
&lt;/h2&gt;

&lt;p&gt;Most public checkers fetch cold, and a cold fetch states no source. Pointed at a source-gated page, they receive the clean branch.&lt;/p&gt;

&lt;p&gt;Their results then agree. Agreement across tools reads like confirmation while it repeats one observation several times.&lt;/p&gt;

&lt;p&gt;That shared habit correlates the answers, so agreement can grow while the evidence behind it does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source gating is delivery discipline
&lt;/h2&gt;

&lt;p&gt;Campaigns buy specific traffic. An operator who pays for clicks from one channel wants the payload spent on that channel's visitors, and serving anyone else is cost with no return.&lt;/p&gt;

&lt;p&gt;Every copy handed out is also a copy that can be collected, submitted and written into a signature. The clean branch costs nothing to give away, so the operator hands it to everyone outside the audience it paid for.&lt;/p&gt;

&lt;p&gt;The rule lives in the campaign rather than in any one host, so replacing a domain does not require rewriting it.&lt;/p&gt;

&lt;p&gt;Reading these gates as budget control explains behavior that looks strange otherwise. A page can ignore your client fingerprint and still serve you nothing, because it is checking where you say you came from.&lt;/p&gt;

&lt;h2&gt;
  
  
  What survives the redirect chain
&lt;/h2&gt;

&lt;p&gt;A link rarely answers on the first hop. Clicks pass through tracking links, shorteners and intermediate pages before the destination replies.&lt;/p&gt;

&lt;p&gt;What the destination sees depends on what survived those hops. A stated source can be carried forward, rewritten or dropped along the way, and the hop that decides what to serve is the last one.&lt;/p&gt;

&lt;p&gt;Two checkers can disagree about the same link without either one malfunctioning. One held the context across the chain and the other lost it, so they asked the destination different questions.&lt;/p&gt;

&lt;p&gt;Our scanner can specify a source and hold it through those hops. Where that source is the one the gate is keyed on, the destination answers the way it answers the intended visitor.&lt;/p&gt;

&lt;p&gt;An analyst sets the source inside the engine, and choosing which source to try is a judgment call.&lt;/p&gt;

&lt;h2&gt;
  
  
  The verdict comes from the captured artifact
&lt;/h2&gt;

&lt;p&gt;We fetch the URL from datacenter, residential and mobile networks and keep the real response body. Classification reads what that artifact is and what it does.&lt;/p&gt;

&lt;p&gt;Counting how many engines have voted on a domain answers a different question. One domain that reputation engines rated clean served a large obfuscated payload when we fetched it, and the capture is what showed it.&lt;/p&gt;

&lt;p&gt;Keeping the body is what makes comparison possible. The same URL fetched two ways produces two artifacts, and the pair is the finding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Client-type cloaking inverts the usual assumption
&lt;/h2&gt;

&lt;p&gt;Client-type cloaking is an evasion class we see and track, and reading it out of a capture is analyst work today.&lt;/p&gt;

&lt;p&gt;The familiar cloak feeds real browsers the payload and automated clients a harmless page. Some hosts run it the other way around.&lt;/p&gt;

&lt;p&gt;The file goes to an automated application client. An ordinary web browser receives a firewall block page, and a browser-based check reads that block as a dead end.&lt;/p&gt;

&lt;p&gt;The block page is part of the delivery. It answers the observer most likely to be a checker, and it answers with something that reads as safety.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where source control stops
&lt;/h2&gt;

&lt;p&gt;Source control does not tell you which source to try. A gate keyed to a value nobody guessed returns the clean page, and the space of plausible sources is large.&lt;/p&gt;

&lt;p&gt;Any client can state any source, which is what makes the gate cheap to build and cheap to stack. An operator who adds a second condition on top of the source check reopens the gap. The gain is that &lt;strong&gt;conditional delivery costs the operator more to hide&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gates a fetch cannot open
&lt;/h3&gt;

&lt;p&gt;Some branches demand what no fetch can produce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A valid session established by a real login.&lt;/li&gt;
&lt;li&gt;A one-time click token that has already been spent.&lt;/li&gt;
&lt;li&gt;An authenticated context tied to a specific account.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source control does not touch that class. A payload served once per address also defeats the confirming second look, which our earlier post covers alongside the limits around session cookies and the time of day.&lt;/p&gt;

&lt;h3&gt;
  
  
  Divergence starts a human read
&lt;/h3&gt;

&lt;p&gt;Branching on traffic source is common outside abuse. Hotlink protection, anti-scraping rules, paywalls and partner licensing all decide on the same signal, so a split tells you the page branches and stays quiet about why.&lt;/p&gt;

&lt;p&gt;That limit was published with the first post on this subject and it holds unchanged on the source axis. A clean result tells you which sources we tried and when. The sources we did not try stay open.&lt;/p&gt;

&lt;p&gt;One line of that published list changes. A checker can now arrive with a source stated, and the rest of the list stands as written.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking a link against a source gate
&lt;/h2&gt;

&lt;p&gt;Against a source-gated page, a single cold fetch returns the branch the operator built for checkers. Two fetches of one URL that differ only in the source stated is the cheapest test that gate fails.&lt;/p&gt;

&lt;p&gt;A link that answers those two fetches two different ways has told you something a verdict cannot. A multi-egress scan at &lt;a href="https://whack.sh/" rel="noopener noreferrer"&gt;whack.sh&lt;/a&gt; records each fetch and the body it returned.&lt;/p&gt;

</description>
      <category>cloaking</category>
      <category>referergating</category>
      <category>conditionaldelivery</category>
      <category>trafficsource</category>
    </item>
    <item>
      <title>Mapping a whole domain fleet from one reverse IP lookup</title>
      <dc:creator>Daniel J</dc:creator>
      <pubDate>Mon, 21 Sep 2026 13:00:02 +0000</pubDate>
      <link>https://dev.to/tuxxin/mapping-a-whole-domain-fleet-from-one-reverse-ip-lookup-1enj</link>
      <guid>https://dev.to/tuxxin/mapping-a-whole-domain-fleet-from-one-reverse-ip-lookup-1enj</guid>
      <description>&lt;p&gt;One reverse IP lookup on issued.live returned 532 hostnames, effectively all first observed inside the same four-day window. The endpoint answers what else lives at an address: every hostname the resolvers have recorded answering there, each with the date that pairing was first observed. One address goes in, and a list with structure in it comes back.&lt;/p&gt;

&lt;p&gt;Addresses and hostnames below are placeholders. The calls are real, and they run against the &lt;a href="https://issued.live/developers/pro" rel="noopener noreferrer"&gt;Pro API reference&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The inverse lookup is one request
&lt;/h2&gt;

&lt;p&gt;Passive DNS tooling answers what a domain resolves to. The inverse question turns a single observation into a set, which is what moves a thread forward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"https://issued.live/api/v1/ip/198.51.100.24?limit=500"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$ISSUED_LIVE_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key travels in the header. There is no &lt;code&gt;?key=&lt;/code&gt; parameter, because query strings land in access logs, proxy logs and &lt;code&gt;Referer&lt;/code&gt; headers. A key in the URL therefore leaves the request unauthenticated and answers 401.&lt;/p&gt;

&lt;p&gt;Each row in &lt;code&gt;hosts[]&lt;/code&gt; carries &lt;code&gt;hostname&lt;/code&gt;, &lt;code&gt;domain&lt;/code&gt; for the registrable domain, &lt;code&gt;ip&lt;/code&gt;, &lt;code&gt;first_seen&lt;/code&gt;, &lt;code&gt;last_seen&lt;/code&gt; and &lt;code&gt;observed&lt;/code&gt;. The envelope carries &lt;code&gt;query&lt;/code&gt;, &lt;code&gt;addresses&lt;/code&gt;, &lt;code&gt;count&lt;/code&gt;, &lt;code&gt;truncated&lt;/code&gt;, &lt;code&gt;limit&lt;/code&gt;, &lt;code&gt;order&lt;/code&gt;, and &lt;code&gt;next_cursor&lt;/code&gt; when another page exists.&lt;/p&gt;

&lt;p&gt;Read &lt;code&gt;truncated&lt;/code&gt; first. A limit returns up to that many rows and promises nothing about completeness. A true there means the picture in front of you is partial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enumerating an address completely is a cursor walk
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;limit&lt;/code&gt; defaults to 500 and tops out at 50000. Out-of-range values are clamped rather than refused, so a request for a million quietly returns 50000. Busy hosting addresses hold far more names than that.&lt;/p&gt;

&lt;p&gt;Past the cap, the cursor is the only correct route. Send &lt;code&gt;?after=&lt;/code&gt; with an empty value to begin, then pass back each response's &lt;code&gt;next_cursor&lt;/code&gt; until one arrives without it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"https://issued.live/api/v1/ip/198.51.100.24?after=&amp;amp;limit=50000"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$ISSUED_LIVE_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; page1.ndjson
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ordering changes when the cursor appears. Without &lt;code&gt;after&lt;/code&gt; the page sorts by &lt;code&gt;last_seen&lt;/code&gt; descending and cannot be resumed, since a re-observed name moves between pages. With &lt;code&gt;after&lt;/code&gt; present the sort is the table's primary key, which never changes for a given observation.&lt;/p&gt;

&lt;p&gt;Four parameter names people reach for answer 400: &lt;code&gt;page&lt;/code&gt;, &lt;code&gt;offset&lt;/code&gt;, &lt;code&gt;skip&lt;/code&gt; and &lt;code&gt;cursor&lt;/code&gt;. All four were once accepted and ignored, which returned page one with a 200 however many times you asked for page four. Code written against that behavior read the same page in a loop.&lt;/p&gt;

&lt;p&gt;At a &lt;code&gt;limit&lt;/code&gt; of 5000 or above the answer streams as newline-delimited JSON, one object per line. Records always carry &lt;code&gt;hostname&lt;/code&gt; and the trailer carries &lt;code&gt;end&lt;/code&gt;, so the two are unambiguous. A stream that dies mid-flight has no trailer and no cursor, and that absence is the error signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  first_seen dates the association between a name and an address
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;first_seen&lt;/code&gt; is the earliest time the resolvers saw that hostname answer with that address. It records when the name arrived at its current host. When the name was created is a separate fact, and RDAP is where that one lives.&lt;/p&gt;

&lt;p&gt;Take a domain registered years ago whose &lt;code&gt;first_seen&lt;/code&gt; at its current address is three weeks old. That is an aged name that moved recently. It behaves like a new one while carrying the reputation of an old one.&lt;/p&gt;

&lt;p&gt;The gap between the two dates is a reading, and it stays a reading. Treat a change of hands as an interpretation, and never assert who holds a name.&lt;/p&gt;

&lt;p&gt;The same field takes a server-side window, on either bound alone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"https://issued.live/api/v1/ip/198.51.100.24?first_seen_from=2026-09-01&amp;amp;first_seen_to=2026-09-05&amp;amp;limit=5000"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$ISSUED_LIVE_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RFC3339, &lt;code&gt;YYYY-MM-DD HH:MM:SS&lt;/code&gt;, &lt;code&gt;YYYY-MM-DD&lt;/code&gt; and unix seconds all parse, and a missing zone means UTC. A malformed or reversed window answers 400.&lt;/p&gt;

&lt;p&gt;Copy the published reasoning into your own API: "A limit is a preference, so we clamp it and answer the question you meant. A timestamp asserts which rows you want, and substituting a different window is how you end up with a gap you cannot see."&lt;/p&gt;

&lt;p&gt;These observations begin in August 2026. A window earlier than that comes back empty because nobody was looking yet. Check the window before concluding an address sat unused.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hostname list has a grammar
&lt;/h2&gt;

&lt;p&gt;Co-location on shared hosting proves little by itself. The timing carried the weight in that case: names that arrive together were deployed together.&lt;/p&gt;

&lt;p&gt;The shape of the names is the second measurement. Three greps classified most of that list in a few seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'select(.hostname) | .hostname'&lt;/span&gt; page1.ndjson | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; names.txt

&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-cE&lt;/span&gt; &lt;span class="s1"&gt;'(clk|clks|trk|trks|track)'&lt;/span&gt; names.txt
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-cE&lt;/span&gt; &lt;span class="s1"&gt;'[a-z]+[0-9]{2,}\.'&lt;/span&gt; names.txt
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-cE&lt;/span&gt; &lt;span class="s1"&gt;'(scrty|sec-|-secure|verify)'&lt;/span&gt; names.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of the 532, the click and redirect abbreviations came to 163, the leetspeak variants to 15 and the security-themed lures to 37. A fourth pass over insurance and quote wording caught 27 more. One query enumerated a disposable redirect fleet, and a grep fingerprinted how it names itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A grammar returns a candidate set rather than a finding.&lt;/strong&gt; Cheap names bought in bulk look alike because one script generated them, and unrelated operators land on the same conventions.&lt;/p&gt;

&lt;p&gt;What lifts a candidate above coincidence is a second independent link. Co-tenancy on one certificate, a shared public key, an account-specific nameserver pair or a registration inside the same hour each qualify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern search reaches the members on other hosts
&lt;/h2&gt;

&lt;p&gt;An address holds the members parked there on the day you looked. The rest of the family sits elsewhere, and the grammar is what reaches them.&lt;/p&gt;

&lt;p&gt;Pattern search matches a naming hypothesis against the corpus, next to the data. That is the only place it can run, because there is no wordlist and the space is combinatorial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://issued.live/api/v1/search?name=[a-z]{3,8}clk.com&amp;amp;ns=ns.example-dns.net&amp;amp;limit=500"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$ISSUED_LIVE_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-g&lt;/code&gt; earns its place. Braces and brackets are curl's own globbing syntax, so without &lt;code&gt;--globoff&lt;/code&gt; the pattern never reaches the API intact.&lt;/p&gt;

&lt;p&gt;Patterns are written forwards, the way you say the name, with &lt;code&gt;*&lt;/code&gt; for a run of characters, &lt;code&gt;?&lt;/code&gt; for exactly one, and a counted class such as &lt;code&gt;[a-z]{3,8}&lt;/code&gt;. The count is required and bounded, with a ceiling of 64. An open class like &lt;code&gt;[a-z]+&lt;/code&gt; is refused, because an open bound is how a pattern silently becomes a full scan.&lt;/p&gt;

&lt;p&gt;Every pattern must name a TLD, so &lt;code&gt;*agent&lt;/code&gt; alone is refused and &lt;code&gt;*agent.*&lt;/code&gt; means every TLD. Names are stored suffix-first, so pinning one turns the pattern into a key range. The reference measures anchored searches at 0.03 to 0.7 seconds, against 1 to 3.5 seconds for one that reads the whole key space.&lt;/p&gt;

&lt;p&gt;The response reports &lt;code&gt;anchored&lt;/code&gt; and &lt;code&gt;key_prefix&lt;/code&gt;, so you can see which case you landed in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Window mode reads the registration feed by date
&lt;/h3&gt;

&lt;p&gt;Sending a time bound switches the search into window mode, which reads the registration feed by date. That is the right mode for "which names matching this grammar appeared last week". The reference measures that query at 0.094 seconds.&lt;/p&gt;

&lt;p&gt;Cursors carry no meaning between the two modes, and the wrong one answers 400 instead of silently restarting the walk.&lt;/p&gt;

&lt;p&gt;Every hit carries &lt;code&gt;ns&lt;/code&gt; and &lt;code&gt;cert_id&lt;/code&gt;, and the &lt;code&gt;ns&lt;/code&gt; filter above is what keeps a grammar from collecting coincidences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Batch lookup tests 500 candidates in one call
&lt;/h2&gt;

&lt;p&gt;A search plus a key pivot produces a few hundred names. Looking them up one at a time is a few hundred requests against a concurrency gate. Batch lookup takes up to 500 in a single POST.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"https://issued.live/api/v1/domains?include=timing"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$ISSUED_LIVE_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"domains":["name-one.example","name-two.example"]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every input gets a row back in the order you sent it, duplicates included. The reply zips against your list without normalizing anything. Rows come back &lt;code&gt;found&lt;/code&gt;, &lt;code&gt;not_found&lt;/code&gt;, or &lt;code&gt;rejected&lt;/code&gt; where the input never parsed as a registrable domain.&lt;/p&gt;

&lt;p&gt;Those last two stay separate on purpose. A batch that dropped unparseable input would let you record a domain as absent when it was never queried.&lt;/p&gt;

&lt;p&gt;Any &lt;code&gt;include&lt;/code&gt; value drops the per-request cap to 100 names, each carrying up to 10 sub-entries. Sending more names than the cap truncates to it and sets &lt;code&gt;truncated&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pivot on the certificate, then on the key
&lt;/h2&gt;

&lt;p&gt;A domain record publishes &lt;code&gt;ssl_cert&lt;/code&gt;, the 32-hex &lt;code&gt;cert_id&lt;/code&gt;, and that value is the bridge into the certificate endpoints.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"https://issued.live/api/v1/cert/&lt;/span&gt;&lt;span class="nv"&gt;$CERT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$ISSUED_LIVE_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"https://issued.live/api/v1/spki/&lt;/span&gt;&lt;span class="nv"&gt;$SPKI_SHA256&lt;/span&gt;&lt;span class="s2"&gt;?limit=500"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$ISSUED_LIVE_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;sans&lt;/code&gt; array lists the names a certificate covers, which is a link that survives a reverse proxy. &lt;code&gt;spki_sha256&lt;/code&gt; is a hash over the SubjectPublicKeyInfo, so two certificates carrying the same value were issued for one key pair. Often that is a renewal, and sometimes it is one operator across names that share nothing else.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;th&gt;Strength&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
&lt;td&gt;Shared address&lt;/td&gt;
&lt;td&gt;Weak alone&lt;/td&gt;
&lt;td&gt;A CDN address fronts unrelated tenants at scale, so co-location proves shared hosting.&lt;/td&gt;
&lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;Shared nameservers&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;td&gt;Strong when the pair is account-specific, weak when it is a large provider's default.&lt;/td&gt;
&lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;Shared certificate&lt;/td&gt;
&lt;td&gt;Strong&lt;/td&gt;
&lt;td&gt;Names packed onto one certificate were issued together, by one party, for one deployment.&lt;/td&gt;
&lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;Shared key&lt;/td&gt;
&lt;td&gt;Strongest&lt;/td&gt;
&lt;td&gt;One key pair across certificates means one holder of one private key.&lt;/td&gt;
&lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read the &lt;code&gt;coverage&lt;/code&gt; field on every key pivot before drawing a conclusion. Key hashes were first recorded on September 7, 2026, and certificates ingested earlier cannot be matched on one. A thin result from an older pivot therefore reads as unknown.&lt;/p&gt;

&lt;p&gt;Certificate detail stays queryable for 14 days and then lives on in the archived daily files. A 404 for an older id reports that horizon and says nothing about whether the certificate existed.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ja4x&lt;/code&gt; fingerprints the issuance software a certificate authority runs. issued.live reports the value and offers no pivot on it. The value space is small enough that a match would return a large share of one CA's output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the range sweep is the wrong instrument
&lt;/h2&gt;

&lt;p&gt;Reverse IP has a sibling that takes a CIDR. Write the prefix slash as a hyphen, since a literal slash is a path separator and will not route.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"https://issued.live/api/v1/range/198.51.100.0-24"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$ISSUED_LIVE_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A range covers at most 256 addresses, an IPv4 /24, and anything wider answers 400. On shared cloud space the surrounding /24 belongs to unrelated tenants. A sweep there returns hundreds of neighbors with nothing to do with your thread.&lt;/p&gt;

&lt;p&gt;On dedicated hosting that trade-off inverts, and the /24 becomes the fastest way to see a whole rented block. Sweeping anything larger means iterating a /24 at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concurrency is the limit that bites
&lt;/h2&gt;

&lt;p&gt;The per-minute ceiling rarely stops this work. Advanced queries are bounded by how many run at once on one key: one on Plus, two on Pro.&lt;/p&gt;

&lt;p&gt;Reverse IP, range, pattern search, batch lookup and the provisioning feed each take a slot. Certificate, key, domain and timeline lookups take none.&lt;/p&gt;

&lt;p&gt;Over the gate a request waits up to five seconds for a slot, then returns &lt;code&gt;503 timeout&lt;/code&gt;. It never queues, so a pile-up of slow queries cannot form. Branch on the &lt;code&gt;retryable&lt;/code&gt; field and &lt;code&gt;Retry-After&lt;/code&gt; rather than on the status number.&lt;/p&gt;

&lt;p&gt;The timeout status stays 503 by design. This origin sits behind a CDN that replaces a 504 with its own error page, stripped of the JSON body.&lt;/p&gt;

&lt;p&gt;Serialize a multi-block walk. The gate admits two at a time on Pro anyway, and concurrent cold range queries make each other slower.&lt;/p&gt;

&lt;h2&gt;
  
  
  The feeds keep the map current
&lt;/h2&gt;

&lt;p&gt;A fleet enumerated on Tuesday is a snapshot. Two cursor-paged feeds turn a finished investigation into a standing one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"https://issued.live/api/v1/provisioning?max_hours_to_cert=6&amp;amp;ns=ns.example-dns.net"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$ISSUED_LIVE_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"https://issued.live/api/v1/nrd?certified=1&amp;amp;max_hours_to_cert=1&amp;amp;limit=500"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$ISSUED_LIVE_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The provisioning feed returns domains registered, certificated and pointed at a host inside one window. Any one of those facts is ordinary. All three inside an afternoon is a domain being stood up to be used.&lt;/p&gt;

&lt;p&gt;The hours-to-certificate bound is what keeps that query affordable. On the reference's own measurements, widening it from one hour to six multiplies the candidates in a 72-hour window by roughly 19. Widening it again to 24 hours multiplies them by another 10.&lt;/p&gt;

&lt;p&gt;Recent windows are thin by construction, and the response says so. Published coverage figures: 36.9% of candidates carry a DNS observation inside 24 hours, 83.9% inside 72 hours and 96.1% inside a week.&lt;/p&gt;

&lt;p&gt;Rows without one are marked &lt;code&gt;not_yet_observed&lt;/code&gt;, which records an observation nobody has made yet. It says nothing about whether the name resolves.&lt;/p&gt;

&lt;p&gt;The newly registered domains feed runs in ascending time order from a cursor. A consumer resumes exactly where it stopped, so a scheduled job can miss a run without losing an event. Store &lt;code&gt;next_cursor&lt;/code&gt; on every page, including the ones where &lt;code&gt;caught_up&lt;/code&gt; comes back true.&lt;/p&gt;

&lt;p&gt;Sub-hour certification runs at about 1.1% of certified registrations, against 8.4% taking over a week. The tight end of that filter is a small set. &lt;code&gt;max_hours_to_cert&lt;/code&gt; requires &lt;code&gt;certified=1&lt;/code&gt;, and sending it alone answers 400 instead of quietly doing nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the plans cover
&lt;/h2&gt;

&lt;p&gt;issued.live opened paid plans on September 19, 2026, three of them. Pattern search, batch lookup and both feeds belong to Pro at $199 a month. Pro also carries 1,200 requests a minute, two advanced queries in flight, and daily files for DNS by vantage point, certificates and address history.&lt;/p&gt;

&lt;p&gt;The earlier steps sit lower. Reverse IP, the CIDR sweep, the pivots and the domain timeline by API run on Plus at $99 a month, with one query in flight. Basic at $39 takes the daily domain and DNS files plus the extended record and the domain timeline on the website.&lt;/p&gt;

&lt;p&gt;Unkeyed access stays free at 1,000 requests a day per address. Billing is monthly through PayPal, and sign-in is a code sent to your email. Full &lt;a href="https://issued.live/pricing" rel="noopener noreferrer"&gt;plan and limit detail sits on the pricing page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Methodology
&lt;/h2&gt;

&lt;p&gt;Every endpoint, parameter, cap and error code above was checked on September 18, 2026 against the issued.live Pro API reference, last updated September 15, 2026. Plan boundaries come from the pricing page on the same date, and the performance and coverage percentages are that site's published measurements. The 532-hostname classification comes from Tuxxin investigation work, and &lt;a href="https://tuxxin.com/founder" rel="noopener noreferrer"&gt;Daniel Jones&lt;/a&gt; reviewed the post before publication.&lt;/p&gt;

</description>
      <category>reverseiplookup</category>
      <category>passivedns</category>
      <category>certificatetransparency</category>
      <category>threatintelligence</category>
    </item>
    <item>
      <title>issued.live paid plans are open: Basic $39, Plus $99, Pro $199</title>
      <dc:creator>Daniel J</dc:creator>
      <pubDate>Sat, 19 Sep 2026 13:00:02 +0000</pubDate>
      <link>https://dev.to/tuxxin/issuedlive-paid-plans-are-open-basic-39-plus-99-pro-199-3al0</link>
      <guid>https://dev.to/tuxxin/issuedlive-paid-plans-are-open-basic-39-plus-99-pro-199-3al0</guid>
      <description>&lt;p&gt;Paid plans are open on issued.live today: Basic at $39 a month, Plus at $99 and Pro at $199. Basic reads the extended record on the website and takes the daily files. Plus opens the keyed lookups, and Pro adds the sweeps that run across the corpus.&lt;/p&gt;

&lt;p&gt;issued.live reads the public Certificate Transparency logs, resolves DNS from seven vantage points and loads ICANN zone files. It observes third-party infrastructure and operates none of it.&lt;/p&gt;

&lt;p&gt;Free covers the public record for any domain with no account and no key: certificate, issuer, expiry, hosting address and current DNS. The cap is 1,000 requests a day per client address, resetting at 00:00 UTC.&lt;/p&gt;

&lt;p&gt;Two boundaries separate the three paid plans. One runs between the website and the API. The other is how many heavy queries you may hold open at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Website access against API access
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Basic, at $39 a month
&lt;/h3&gt;

&lt;p&gt;Basic runs at 60 requests a minute with no daily cap, and carries the domains and dns daily files in &lt;code&gt;csv.zst&lt;/code&gt; and &lt;code&gt;json.zst&lt;/code&gt;. Every dataset has a sample row on the &lt;a href="https://issued.live/downloads" rel="noopener noreferrer"&gt;download catalog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Basic also opens the extended record on the website for any domain you look up. That is certificate internals, DNS per vantage point and the full address history on the page, plus registration and expiry dates and the registrar. The domain timeline sits there too, every dated event in order.&lt;/p&gt;

&lt;p&gt;Its keyed surface ends there. The advanced queries start at Plus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Plus, at $99 a month
&lt;/h3&gt;

&lt;p&gt;Plus runs at 300 requests a minute and moves those same answers onto your key. It suits a hunter working one lead at a time: an address, a certificate, a name.&lt;/p&gt;

&lt;p&gt;Reverse IP and CIDR return every hostname our resolvers have recorded answering at an address or a range. Each row carries &lt;code&gt;first_seen&lt;/code&gt;, &lt;code&gt;last_seen&lt;/code&gt; and an observed count.&lt;/p&gt;

&lt;p&gt;Pivots by certificate, SPKI or JA4X return the other names on a certificate and everything sharing a public key. The extended record and the domain timeline come by API as well, the timeline carrying &lt;code&gt;gap_seconds&lt;/code&gt; on each event.&lt;/p&gt;

&lt;p&gt;Plus takes the same two daily files as Basic, and runs one advanced query at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concurrency: how many sweeps run at once
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pro, at $199 a month
&lt;/h3&gt;

&lt;p&gt;Pro runs at 1,200 requests a minute and adds the sweeps. Pattern search runs a name grammar across the corpus, and batch lookup answers up to 500 domains in one call. The provisioning feed and the newly registered domains feed are both cursor-paged.&lt;/p&gt;

&lt;p&gt;Pro also takes the dns_vantage, certificates and address_history datasets as daily files, and runs two advanced queries at once.&lt;/p&gt;

&lt;h3&gt;
  
  
  What takes a concurrency slot
&lt;/h3&gt;

&lt;p&gt;Advanced queries are bounded by how many run at once on your key: &lt;strong&gt;one on Plus, two on Pro&lt;/strong&gt;. The per-minute ceiling meters the public record API instead.&lt;/p&gt;

&lt;p&gt;Five endpoints take a slot: reverse IP, range, pattern search, batch lookup and the provisioning feed. The certificate, key, domain and newly-registered-domain endpoints are point lookups and take none. The slot is yours alone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the gate sits there
&lt;/h3&gt;

&lt;p&gt;The cost of a range query is the reason. A cold reverse-IP range can read hundreds of millions of rows and hold its database connection for the better part of 18 seconds. Enough of those at once would exhaust the connection pool and stall every other request on the site.&lt;/p&gt;

&lt;p&gt;A request over the gate waits five seconds for a slot and then returns &lt;code&gt;503 timeout&lt;/code&gt; rather than queueing. An unbounded queue would turn one slow query into a pile-up, with every request holding a connection until they all expire together.&lt;/p&gt;

&lt;p&gt;A range covers at most 256 addresses, an IPv4 /24, for the same reason. A CIDR expands into its individual addresses, so the address count is the query's cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four plans side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Free&lt;/th&gt;
&lt;th&gt;Basic&lt;/th&gt;
&lt;th&gt;Plus&lt;/th&gt;
&lt;th&gt;Pro&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Price a month&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;$39&lt;/td&gt;
&lt;td&gt;$99&lt;/td&gt;
&lt;td&gt;$199&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate&lt;/td&gt;
&lt;td&gt;1,000 a day per address&lt;/td&gt;
&lt;td&gt;60 a minute&lt;/td&gt;
&lt;td&gt;300 a minute&lt;/td&gt;
&lt;td&gt;1,200 a minute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public record for one domain&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extended record and timeline on the website&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extended record and timeline by API&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reverse IP, CIDR and the key pivots&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pattern search, batch lookup and the feeds&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daily files&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;domains, dns&lt;/td&gt;
&lt;td&gt;domains, dns&lt;/td&gt;
&lt;td&gt;all five datasets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full weekly snapshot&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Its datasets&lt;/td&gt;
&lt;td&gt;Its datasets&lt;/td&gt;
&lt;td&gt;All five&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Advanced queries in flight&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;One&lt;/td&gt;
&lt;td&gt;Two&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Published SHA-256 checksums&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ad-free pages, sign-in and billing&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every paid plan includes a full weekly snapshot of the datasets it covers, for seeding a new system before the daily deltas take over. The checksums stay unkeyed on every plan, Free included, so a file you already hold can be verified without an account.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a keyed call looks like
&lt;/h2&gt;

&lt;p&gt;Authentication is a bearer token in an &lt;code&gt;Authorization&lt;/code&gt; header. There is no key parameter on the query string, because query strings land in access logs, proxy logs and browser history.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"https://issued.live/api/v1/range/157.240.17.0-24?limit=5000&amp;amp;after="&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Write the CIDR prefix slash as a hyphen. A literal slash is a path separator and will not route.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reading the response
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;limit&lt;/code&gt; parameter defaults to 500 and tops out at 50000, and an out-of-range value is clamped. &lt;code&gt;truncated:true&lt;/code&gt; is what tells you rows remain.&lt;/p&gt;

&lt;p&gt;Pagination is the &lt;code&gt;after&lt;/code&gt; cursor. Send &lt;code&gt;?after=&lt;/code&gt; empty to begin, then pass back each response's &lt;code&gt;next_cursor&lt;/code&gt;, whose absence ends the walk. &lt;code&gt;page&lt;/code&gt;, &lt;code&gt;offset&lt;/code&gt;, &lt;code&gt;skip&lt;/code&gt; and &lt;code&gt;cursor&lt;/code&gt; each answer 400, after a spell when all four were accepted and ignored.&lt;/p&gt;

&lt;p&gt;Each &lt;code&gt;hosts[]&lt;/code&gt; row carries the hostname, its registrable domain, the address, &lt;code&gt;first_seen&lt;/code&gt;, &lt;code&gt;last_seen&lt;/code&gt; and &lt;code&gt;observed&lt;/code&gt;. Set &lt;code&gt;format=ndjson&lt;/code&gt; to stream one object per line, which also happens automatically at a limit of 5000 or more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bounding the window server-side
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;first_seen_from&lt;/code&gt; and &lt;code&gt;first_seen_to&lt;/code&gt; filter on first-observation time at the server, and either bound may be sent alone. They take RFC3339, "YYYY-MM-DD HH:MM:SS", "YYYY-MM-DD" or unix seconds, and no zone means UTC. A malformed or reversed window answers 400.&lt;/p&gt;

&lt;p&gt;The reasoning for treating those two parameters differently is published in the &lt;a href="https://issued.live/developers/pro" rel="noopener noreferrer"&gt;Pro API reference&lt;/a&gt;: "A limit is a preference, so we clamp it and answer the question you meant. A timestamp asserts which rows you want, and substituting a different window is how you end up with a gap you cannot see."&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the other limits sit where they do
&lt;/h2&gt;

&lt;p&gt;The free daily cap is sized for an interactive visitor or a small script. It stops one scraper monopolizing a backend everyone shares.&lt;/p&gt;

&lt;p&gt;The per-minute ceilings smooth bursts, and they point at the files. Paging an API for the whole corpus is slower for you and more expensive for us than one download.&lt;/p&gt;

&lt;p&gt;File downloads run one per file per day, shared between the download page and your key, and that allowance resets at 00:00 UTC. Each file is generated once a day, and re-downloading the same one is the main way bulk access gets expensive.&lt;/p&gt;

&lt;p&gt;If a limit blocks legitimate work, tell us. These are sized to protect the service, and we would rather adjust one than lose the use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who should stay on Free
&lt;/h2&gt;

&lt;p&gt;Anyone reading a handful of domains a day from a shell is served by Free. The daily cap covers an interactive visitor and most small scripts, and there is no key to manage.&lt;/p&gt;

&lt;p&gt;Anyone asking what a certificate looked like in 2019 should use &lt;a href="https://crt.sh" rel="noopener noreferrer"&gt;crt.sh&lt;/a&gt;, which answers that for nothing. Our queryable certificate window is 14 days.&lt;/p&gt;

&lt;p&gt;Registrant contact details and long whois histories sit elsewhere too, and we collect neither. The &lt;a href="https://issued.live/compare" rel="noopener noreferrer"&gt;comparison page&lt;/a&gt; weighs this service against AllZonefiles.io, Domains-Monitor, WhoisXML API and crt.sh, and says where each of them wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  How long each dataset is kept
&lt;/h2&gt;

&lt;p&gt;Certificate detail is queryable for 14 days and then ages out of the database. The daily certificates file that recorded it is archived permanently, and that archive starts with the files for September 17, 2026.&lt;/p&gt;

&lt;p&gt;The 14-day window is a decision about interaction. It covers the moment a hunter first finds a name and wants SANs, SPKI, JA4X and the hashes on screen. A month-old certificate is archival research, and a file serves that better.&lt;/p&gt;

&lt;p&gt;The window is short because of what certificates cost to store. Three 32-byte hashes come to 42% of that table at roughly 10 GiB each, and hashes resist compression, so the bill grows in a straight line with the window. A 13-month hot window was costed at about 3.6 TB and declined.&lt;/p&gt;

&lt;p&gt;DNS change history runs 18 months, because age and value move in opposite directions in a change log. Registration data, address history and the reverse-IP index have no expiry. The &lt;a href="https://issued.live/about" rel="noopener noreferrer"&gt;about page&lt;/a&gt; carries the full retention table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Billing and sign-in
&lt;/h2&gt;

&lt;p&gt;Billing is monthly through PayPal. There is no password: you sign in with a code sent to your email, then manage your key and billing from the same page.&lt;/p&gt;

&lt;p&gt;Cancel any time in PayPal and the billing page follows within minutes. Access runs to the end of the period you have paid for. The feature matrix and the subscribe buttons are on &lt;a href="https://issued.live/pricing" rel="noopener noreferrer"&gt;pricing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Daniel Jones checked every price, rate, limit and capability above against issued.live's own pricing, about, compare and downloads pages on September 18, 2026. The API mechanics come from the Pro API reference, last updated September 15, 2026, and he reviewed the post before publication. &lt;a href="https://tuxxin.com/founder" rel="noopener noreferrer"&gt;More about the author&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>issuedlive</category>
      <category>certificatetransparency</category>
      <category>passivedns</category>
      <category>reverseip</category>
    </item>
    <item>
      <title>Brevo delivered KongTuke ClickFix to 51 customer sites</title>
      <dc:creator>Daniel J</dc:creator>
      <pubDate>Thu, 17 Sep 2026 12:12:30 +0000</pubDate>
      <link>https://dev.to/tuxxin/brevo-delivered-kongtuke-clickfix-to-51-customer-sites-k05</link>
      <guid>https://dev.to/tuxxin/brevo-delivered-kongtuke-clickfix-to-51-customer-sites-k05</guid>
      <description>&lt;p&gt;Brevo's own delivery infrastructure served malicious JavaScript to 51 customer page domains on September 14, 2026. The injected code drew a fake Cloudflare verification panel and walked visitors through a ClickFix sequence ending in a command shell. The window ran from 16:05 to 20:12 UTC, and by the following day every loader hostname it used had stopped resolving.&lt;/p&gt;

&lt;p&gt;Kirk of ADAMnetworks published the technical analysis. Whack.sh took part in the shared investigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the attack works
&lt;/h2&gt;

&lt;p&gt;Two files Brevo serves to its customers carried appended code. The SDK loader at &lt;code&gt;cdn.brevo[.]com/js/sdk-loader.js&lt;/code&gt; went from a clean 3,442 bytes to 3,615 or 3,616. The Sibforms bundle at &lt;code&gt;sibforms[.]com/forms/end-form/build/main.js&lt;/code&gt; went from a clean 461,411 bytes to 461,584 or 461,585. The one-byte spread is the loader hostname: a page served by &lt;code&gt;cdn11&lt;/code&gt; carries a longer label than one served by &lt;code&gt;cdn2&lt;/code&gt;. If you are checking a capture against these numbers, both values are modified.&lt;/p&gt;

&lt;p&gt;The appended code is a script tag pointing at a loader host under &lt;code&gt;sendibt1[.]com&lt;/code&gt;. That domain is the reason this reached so far: Brevo lists it among its own technical domains, so it is a name a customer following Brevo's own integration guidance would already have allowed. This was delivery through a vendor's genuine infrastructure, and a site owner reviewing their network log would have seen nothing out of place.&lt;/p&gt;

&lt;p&gt;The panel the code drew imitated a Cloudflare verification step. It instructed visitors to press Win+R, then Ctrl+V, then Enter. The clipboard carried a command built around &lt;code&gt;conhost.exe --headless&lt;/code&gt; and &lt;code&gt;curl&lt;/code&gt;, piped into &lt;code&gt;cmd.exe&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What the command retrieved is unknown. Every captured request to the four download servers returned an empty HTTP 444, and a Joe Sandbox run that executed the command on Windows 10 at 18:34:33 recorded the same thing. The tokens are single use: the servers answered the victim and nobody else. Anyone who completed the paste got a payload, and it was not captured.&lt;/p&gt;

&lt;p&gt;The panel was served in an iframe requesting &lt;code&gt;fullscreen&lt;/code&gt; and &lt;code&gt;clipboard-write&lt;/code&gt; permissions, and carried a proof-of-work challenge before it would render. It reported telemetry events named &lt;code&gt;vclick&lt;/code&gt; and &lt;code&gt;copy_fail&lt;/code&gt;, so the operators were measuring how many visitors completed the paste and how many gave up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built ahead of time, and torn down fast
&lt;/h2&gt;

&lt;p&gt;The certificate record dates the buildout well before the attack. Kirk's analysis records a certificate for &lt;code&gt;cdn.sendibt1[.]com&lt;/code&gt; issued on August 25, twenty days ahead, and a wildcard for &lt;code&gt;*.sendibt1[.]com&lt;/code&gt; on September 8. Certificates for &lt;code&gt;cdn9&lt;/code&gt;, &lt;code&gt;cdn10&lt;/code&gt; and &lt;code&gt;cdn11&lt;/code&gt; were issued on September 14 between 16:09 and 16:12 UTC, four minutes after the window opened: capacity added while the campaign was already running.&lt;/p&gt;

&lt;p&gt;Seven loader hosts served the campaign. By September 15 all seven returned NXDOMAIN. The whole operation lasted about four hours.&lt;/p&gt;

&lt;p&gt;A separate thread in the same research connects the operator to MintsLoader, and it is worth being precise about the distance. The four download servers share a naming scheme with hosts that a MintsLoader sample from September 9 queried. That earlier sample reached a staging host, &lt;code&gt;jg2n36pxkqjfqp7[.]top&lt;/code&gt;, and DNS and hosting records tie that host to 97 domains across three addresses on BL Networks' AS399629, all using 15-character lowercase labels under &lt;code&gt;.top&lt;/code&gt;. That estate hangs off the September 9 staging host, not off the &lt;code&gt;sendibt1&lt;/code&gt; loaders: it is the same operator by inference, on different infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to stay safe
&lt;/h2&gt;

&lt;p&gt;Treat any instruction to copy a command and paste it into the Run box, PowerShell, or Terminal as the attack itself. A verification step that asks you to run something is a lure. Cloudflare's real challenge never asks you to paste a command.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Close the tab. Nothing is repaired by completing the step.&lt;/li&gt;
&lt;li&gt;If you already ran it, treat the machine as compromised and change passwords from a different device.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For teams running a site that loads third-party JavaScript:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subresource Integrity on vendor scripts turns a modified file into a blocked file. It is the direct control for this attack, and it costs an attribute. A Content-Security-Policy would not have helped here: the malicious host was a domain the vendor documents, so a correctly written policy already trusted it.&lt;/li&gt;
&lt;li&gt;Compare your vendor bundles against known-clean byte counts. Both modified sizes above differ from clean by fewer than 200 bytes in a 461 KB file.&lt;/li&gt;
&lt;li&gt;Watch endpoints for a shell spawned shortly after browser activity, and specifically for &lt;code&gt;conhost.exe --headless&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Certificate transparency carried the first record of this infrastructure, three weeks early. Watching CT for new names under your vendors' domains is cheap, though be honest about what it buys: a new &lt;code&gt;cdn&lt;/code&gt; subdomain on a domain the vendor already documents is routine, so August 25 would have been a row in a log rather than an alarm. Its value here is retrospective, which is how the timeline above was built.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Credit and sources
&lt;/h2&gt;

&lt;p&gt;The research and the writeup are Kirk's, at ADAMnetworks, published September 16, 2026. The report thanks &lt;a href="https://tuxxin.com/founder" rel="noopener noreferrer"&gt;Daniel Jones&lt;/a&gt; of Tuxxin LLC, who took part using Whack.sh, and Emiliano of The Matrix Project, for their work on the infrastructure records.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ADAMnetworks report: &lt;a href="https://adamnet.works/blog/brevo-delivers-kongtuke-clickfix-to-customer-sites/" rel="noopener noreferrer"&gt;Brevo delivers KongTuke ClickFix to customer sites&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Earlier collaboration: &lt;a href="https://whack.sh/news/pasteswitch-clickfix-operation" rel="noopener noreferrer"&gt;PasteSwitch, 108 crypto-theft ads on a hijacked HBO Max Reddit account&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>clickfix</category>
      <category>kongtuke</category>
      <category>mintsloader</category>
      <category>brevo</category>
    </item>
    <item>
      <title>PasteSwitch: 108 crypto-theft ads on a hijacked HBO Max Reddit account</title>
      <dc:creator>Daniel J</dc:creator>
      <pubDate>Mon, 14 Sep 2026 17:55:05 +0000</pubDate>
      <link>https://dev.to/tuxxin/pasteswitch-108-crypto-theft-ads-on-a-hijacked-hbo-max-reddit-account-2a45</link>
      <guid>https://dev.to/tuxxin/pasteswitch-108-crypto-theft-ads-on-a-hijacked-hbo-max-reddit-account-2a45</guid>
      <description>&lt;p&gt;Attackers hijacked the official verified HBO Max account on Reddit, &lt;code&gt;u/hbomax&lt;/code&gt;, and used it to run 108 distinct malicious advertisements over roughly 48 hours. The ads promoted an "HBO Max for macOS" application that does not exist. Reddit administrators paused the affected ads and opened an internal investigation with their Security and Safety teams to secure the account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft6ny9c9hh8tjwkjua3u0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft6ny9c9hh8tjwkjua3u0.png" alt="The malicious HBO Max advertisement as it ran on the hijacked u/hbomax Reddit account, offering an HBO Max application for macOS." width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One of the 108 advertisements run from the hijacked account. Screenshot via Hudson Rock.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hudson Rock published the technical analysis, conducted jointly with Kirk of ADAMnetworks. Whack.sh took part in the shared investigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the attack works
&lt;/h2&gt;

&lt;p&gt;The campaign uses a technique called ClickFix. The lure page presents an installation or verification step and gives the visitor a command to copy. The visitor pastes that command into Terminal, PowerShell, or the Run box and presses enter. The command downloads and runs the payload under the visitor's own account, so the operating-system and browser warnings that gate a downloaded file never appear.&lt;/p&gt;

&lt;p&gt;Hudson Rock and ADAMnetworks named the operation PasteSwitch after the two halves of that mechanism. The victim pastes an attacker-supplied command, and the delivery system switches among platform, campaign, payload, and monetization branches based on checks that qualify the visitor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three delivery branches
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS&lt;/strong&gt; received Swift-based counterfeit Ledger, Trezor Suite, and Exodus applications, built to harvest 12- and 24-word BIP39 recovery phrases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows&lt;/strong&gt; received a separate payload path using &lt;code&gt;mshta&lt;/code&gt; and PowerShell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clipboard hijacking&lt;/strong&gt; arrived as AnimateClipper and ZigClipper, which replace a copied cryptocurrency address with an address the attacker controls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The clippers read their command-and-control location from Binance Smart Chain contracts used as mutable dead drops. Researchers recorded 36 mainnet changes from a single controller address between March and July 2026. Hosting the pointer on a public blockchain gives the operator a channel that survives ordinary takedowns, because the contract stays reachable and the destination can be rewritten at will.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to stay safe
&lt;/h2&gt;

&lt;p&gt;Treat any instruction to copy a command and paste it into Terminal, PowerShell, or the Run box as the attack itself. A real installer is a file you download from the vendor's own site or an official app store.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type a vendor's address yourself rather than following an advertisement.&lt;/li&gt;
&lt;li&gt;Enter a hardware-wallet recovery phrase only into the device. An application that asks for your 12 or 24 words is stealing them.&lt;/li&gt;
&lt;li&gt;A verified badge carries no guarantee. This account belonged to the brand and was under attacker control for the duration of the campaign.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For teams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name the copy-paste-into-terminal pattern directly in security awareness material. Guidance written around email attachments leaves it uncovered.&lt;/li&gt;
&lt;li&gt;Watch endpoints for a shell spawned shortly after browser activity, and for processes that write to the clipboard.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Credit and sources
&lt;/h2&gt;

&lt;p&gt;Hudson Rock conducted this research jointly with Kirk of ADAMnetworks. Hudson Rock's report also thanks Daniel Jones of Tuxxin LLC, who took part using Whack.sh, and Emiliano of The Matrix Project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hudson Rock report: &lt;a href="https://www.hudsonrock.com/blog/hbo-max-ads-on-a-compromised-reddit-account-exposed-a-massive-pasteswitch-clickfix-operation" rel="noopener noreferrer"&gt;HBO Max ads on a compromised Reddit account exposed a massive PasteSwitch ClickFix operation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Companion report from ADAMnetworks: &lt;a href="https://adamnet.works/blog/hbo-max-ads-exposed-the-pasteswitch-clickfix-operation/" rel="noopener noreferrer"&gt;HBO Max ads exposed the PasteSwitch ClickFix operation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both reports carry the complete indicator list.&lt;/p&gt;

&lt;p&gt;Whack.sh opens a suspicious URL and reports what it serves, including cloaked multi-stage delivery like this one. &lt;a href="https://whack.sh" rel="noopener noreferrer"&gt;Run a scan&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>clickfix</category>
      <category>threatintel</category>
      <category>macos</category>
      <category>windows</category>
    </item>
    <item>
      <title>The malware that shows scanners a clean page</title>
      <dc:creator>Daniel J</dc:creator>
      <pubDate>Fri, 11 Sep 2026 01:07:59 +0000</pubDate>
      <link>https://dev.to/tuxxin/the-malware-that-shows-scanners-a-clean-page-359</link>
      <guid>https://dev.to/tuxxin/the-malware-that-shows-scanners-a-clean-page-359</guid>
      <description>&lt;p&gt;A cloaked link serves a clean page to scanners and malware to real visitors. Every reputation engine that checked one recent case rated it safe for months, because each engine looked from a single datacenter IP. Seeing the real behavior takes loading the same URL from several vantage points at once.&lt;/p&gt;

&lt;p&gt;By &lt;a href="https://tuxxin.com/founder" rel="noopener noreferrer"&gt;Daniel Jones&lt;/a&gt;, Founder, Tuxxin LLC.&lt;/p&gt;

&lt;p&gt;Most link checks ask one question from one place, then report what came back.&lt;/p&gt;

&lt;p&gt;That worked when a bad link was bad for everyone. It stopped working when attackers started deciding what to show based on who appears to be asking.&lt;/p&gt;

&lt;p&gt;I ran into a textbook case earlier in 2026. A fully authenticated Carnival Cruise Line email, booking number and all, routed real customers into malware installers and fullscreen lockers. The scanners that guard inboxes and browsers saw nothing wrong.&lt;/p&gt;

&lt;p&gt;The investigation, the payloads and the takedown are written up in full on the research site: &lt;a href="https://tuxxin.com/blog/carnival-cclpromos-malvertising" rel="noopener noreferrer"&gt;A real Carnival Cruise Line email was serving customers malware&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This piece covers the detection problem underneath it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the cloak does
&lt;/h2&gt;

&lt;p&gt;The malicious landing page fingerprints the visitor and makes a per-visit decision.&lt;/p&gt;

&lt;p&gt;Scanners, command-line tools and headless browsers fail the check and receive a harmless skeleton. Real browsers on real networks get routed to the live payload.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tuxxin.com/assets/images/carnival/attack-flow-light.svg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffq3ahl1p8df7tgzxsvl6.png" alt="Attack-flow diagram. One authenticated email link fans out through a device-aware cloaker into a clean page for scanners and platform-specific malware for real desktop, mobile and Windows visitors." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One link, one cloaker, four outcomes. The datacenter branch on the left is the only page any scanner reached.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The gate is cheap to run and it is templated. urlscan.io flagged the page as one of "10,000+ similar pages."&lt;/p&gt;

&lt;p&gt;The reputation services I checked returned clean verdicts, consistent with those services having received the skeleton rather than the payload. The link then inherited full brand trust, arriving through the sender's real click-tracker from a domain that passed every email authentication check.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tuxxin.com/assets/images/carnival/cloaker-im-human-gate.webp" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F28cqgg8am3uainzb9pmb.webp" alt="An interstitial page reading Did You Mean Ccl Promos, with a large I'm Human button and a claim that it protects browsing with a free mitigate connection utility." width="799" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The sorting gate itself. A visitor who clears it and a scanner that fails it leave with different pages. Screenshot captured June 13th, 2026.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  This machinery already has a name
&lt;/h2&gt;

&lt;p&gt;Trinity Cyber documented the redirection layer and the payload family in November 2025, in &lt;a href="https://www.trinitycyber.com/blog/blurred-lines-adtech-abuse-delivers-browser-hijackers-through-the-microsoft-store" rel="noopener noreferrer"&gt;Blurred Lines: AdTech Abuse Delivers Browser Hijackers Through the Microsoft Store&lt;/a&gt;. Tanner Piliego and Jared Grumbein named the redirection layer PseudoTDS and the browser-hijacker family PhantomJack.&lt;/p&gt;

&lt;p&gt;Their victims arrived by mistyping a domain. Ours arrived through an authenticated marketing email, which is a different door into the same building.&lt;/p&gt;

&lt;p&gt;Both names in this piece are theirs. The reason it is worth revisiting is that the machinery is still running.&lt;/p&gt;

&lt;h2&gt;
  
  
  A clean verdict describes one observation
&lt;/h2&gt;

&lt;p&gt;A scan result is a record of what one client saw at one moment from one network. Treating it as a property of the URL is where the reasoning breaks.&lt;/p&gt;

&lt;p&gt;That distinction stays invisible while most bad links behave the same way for everyone. It becomes the whole problem once the page starts choosing.&lt;/p&gt;

&lt;p&gt;Under a cloak, "clean" is a true statement about the observation and a false statement about the URL. Both halves are worth holding at once, because it means a defender who checked in good faith and found nothing was reading an accurate result.&lt;/p&gt;

&lt;p&gt;The same applies to a verdict that ages. These pages rotate what they serve between visits, so a result from last week describes a page that no longer exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the scanner missed
&lt;/h2&gt;

&lt;p&gt;A visitor who cleared the fingerprint check landed somewhere quite different. The Windows branch served PhantomJack as forced installers of about 148 MB, signed with three-day certificates that had already expired.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tuxxin.com/assets/images/carnival/fake-windows-security-alert.webp" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcn8dztpsfsuvcy067onb.webp" alt="A fake Windows security alert filling the browser, claiming a security risk was detected and prompting the user to act, served from the cloaked domain." width="800" height="661"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One of the payloads behind the clean page. Screenshot captured June 25th, 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Automated checks rated that domain safe throughout. The divergence between the two results is the entire finding.&lt;/p&gt;

&lt;p&gt;Two of the packages went further. They shipped a forced auto-update channel that checks on every launch, runs in the background, and accepts any version the operator offers. The manifest is quoted in full in the research writeup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why one vantage point cannot catch it
&lt;/h2&gt;

&lt;p&gt;A cloak only has to satisfy the observer it can see.&lt;/p&gt;

&lt;p&gt;If your check comes from a single datacenter IP running a headless browser, that is the exact profile the cloak is tuned to feed a clean page. You are the input it was built for.&lt;/p&gt;

&lt;p&gt;The tell appears when you load the same URL from several profiles at once and compare. Datacenter, residential and mobile.&lt;/p&gt;

&lt;p&gt;In this case the split was stark. Blank and benign from the scanner, a forced malware download from the phone. That disagreement is the signal, and no single vantage point produces it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The page fights the analyst too
&lt;/h2&gt;

&lt;p&gt;Cloaking sorts the traffic. A second layer works on whoever gets through and starts looking closely.&lt;/p&gt;

&lt;p&gt;Opening developer tools froze the page on a debugger trap assembled at runtime, with no literal &lt;code&gt;debugger&lt;/code&gt; token in the source for a scanner to match on. The same layer watches for console evaluation and injected extensions, and reports back to the operator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tuxxin.com/assets/images/carnival/anti-analysis-debugger-trap.webp" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F21o970rrkwlmol5mtyoy.webp" alt="Firefox developer tools halted at a debugger statement injected by the malicious page, an anti-analysis technique that interrupts inspection." width="800" height="766"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The anti-analysis layer firing on inspection. Screenshot from Firefox developer tools, June 24th, 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Both layers point the same direction. Automated collection captures the skeleton, and manual analysis is slowed down enough to make the sample expensive to pin down before it rotates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why an emailed link is the harder case
&lt;/h2&gt;

&lt;p&gt;The prior reporting on this machinery describes victims who mistyped a domain. That victim has one thing going for them: they know they made a mistake, and the page they land on is unfamiliar.&lt;/p&gt;

&lt;p&gt;An authenticated brand email removes every one of those cues. The sender is real, the message is expected, the booking number is correct, and the link opens from a domain the recipient has trusted for years.&lt;/p&gt;

&lt;p&gt;That inverts where the detection burden sits. A typosquat has a suspicious human in the loop who might close the tab. Authenticated brand mail hands the whole job to whatever automated check runs before the click.&lt;/p&gt;

&lt;p&gt;The check in this case was the one thing the cloak was built to defeat. Both halves of the pipeline were working correctly and the outcome was still malware, because each half was answering a question the other one assumed had been covered.&lt;/p&gt;

&lt;p&gt;Email authentication proves the sender. It says nothing about where the links inside go, and a link is only as trustworthy as whoever currently owns the domain at the other end of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The blocklist is chasing the wrong layer
&lt;/h2&gt;

&lt;p&gt;I rescanned the chain on September 10th, 2026. The hosts named in the 2025 reporting have moved on, and the build behind the Carnival link has not.&lt;/p&gt;

&lt;p&gt;Of 12 redirect-hop domains I checked, 11 came back as dead parked pages: six lowercase letters plus &lt;code&gt;.com&lt;/code&gt;, bulk-registered, first seen the same month, all sitting on one parking IP.&lt;/p&gt;

&lt;p&gt;Blocking that layer buys little when a replacement costs a registration fee. The command-and-control naming carried across both reporting sets on rotated apexes, which is consistent with one operation continuing while its address list turned over.&lt;/p&gt;

&lt;p&gt;Most of that layer is still unmapped, and I am staying on it. What survived rotation was the delivery kit itself. The request paths, the event names and a fixed parameter prefix stayed identical across unrelated brand domains, which makes that grammar the more durable thing to detect on.&lt;/p&gt;

&lt;p&gt;Domain reputation answers where a request went. Behavior answers what it did. Only the second one survives the operator buying new domains.&lt;/p&gt;

&lt;p&gt;This report covers what the requests did and how the chain responded to abuse reports. The commercial terms behind it are outside what I can observe, so nothing here characterizes intent.&lt;/p&gt;

&lt;h2&gt;
  
  
  What multi-vantage checking does not solve
&lt;/h2&gt;

&lt;p&gt;Comparing several vantage points catches a page that discriminates by network or device. Naming the limits matters as much as claiming the win.&lt;/p&gt;

&lt;p&gt;A page that gates on a session cookie, a referrer, a one-time click token or the time of day can still show every one of your profiles the same thing. Plenty of these chains do exactly that, which is why the tokenized links in the Carnival case expired into an error page rather than replaying.&lt;/p&gt;

&lt;p&gt;Single-serving behavior defeats a retry in the same way. Where a chain burns the payload once per address, the confirming second look comes back clean and the first result starts to read like a fluke.&lt;/p&gt;

&lt;p&gt;It also proves divergence rather than intent. A page serving different content by geography may be a cloak or may be a licensing rule.&lt;/p&gt;

&lt;p&gt;The result is a signal that needs a human read. It narrows the field from every link to the handful behaving inconsistently, and that is the useful part.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do about it
&lt;/h2&gt;

&lt;p&gt;Treat a scan verdict as evidence with a timestamp and an observer attached, rather than a durable property of a URL.&lt;/p&gt;

&lt;p&gt;Check from more than one egress when the answer matters, and compare the results instead of accepting the first one. A single agreement across profiles is worth more than any individual verdict.&lt;/p&gt;

&lt;p&gt;Detect on behavior that survives rotation: request-path grammar, event names, parameter shapes and download prompts. Domain lists go stale by design.&lt;/p&gt;

&lt;p&gt;Recheck anything reached from mail you did not send yourself. The FBI's IC3 published &lt;a href="https://www.ic3.gov/PSA/2026/PSA260618" rel="noopener noreferrer"&gt;PSA I-061826-PSA&lt;/a&gt; on June 18th, 2026, on criminals routing victims through traffic distribution systems to malicious sites, and this threat class remains invisible to a check that looks from one place.&lt;/p&gt;

&lt;h2&gt;
  
  
  What surfaces it
&lt;/h2&gt;

&lt;p&gt;A single-vantage check gave every defender in that chain a false all-clear for months. A multi-vantage check shows the split the first time anyone looks.&lt;/p&gt;

&lt;p&gt;That is what &lt;a href="https://whack.sh" rel="noopener noreferrer"&gt;whack.sh&lt;/a&gt; does: load a URL from several real vantage points at once and surface the divergence.&lt;/p&gt;

&lt;p&gt;When a link behaves one way for a scanner and another way for a phone, you want to see both.&lt;/p&gt;

&lt;p&gt;If you check links, check them from more than one place. If you would rather not build that yourself, that is what &lt;a href="https://whack.sh" rel="noopener noreferrer"&gt;whack.sh&lt;/a&gt; is for.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Full technical writeup, indicators of compromise and the downloadable whitepaper are on the research site: &lt;a href="https://tuxxin.com/blog/carnival-cclpromos-malvertising" rel="noopener noreferrer"&gt;tuxxin.com&lt;/a&gt;. For coordinated disclosure or the full evidence package: &lt;a href="mailto:contact@tuxxin.com"&gt;contact@tuxxin.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cloaking</category>
      <category>urlscanning</category>
      <category>multivantage</category>
      <category>malvertising</category>
    </item>
    <item>
      <title>A real Carnival Cruise Line email was serving customers malware</title>
      <dc:creator>Daniel J</dc:creator>
      <pubDate>Fri, 11 Sep 2026 01:07:57 +0000</pubDate>
      <link>https://dev.to/tuxxin/a-real-carnival-cruise-line-email-was-serving-customers-malware-4jle</link>
      <guid>https://dev.to/tuxxin/a-real-carnival-cruise-line-email-was-serving-customers-malware-4jle</guid>
      <description>&lt;p&gt;A genuine Carnival Cruise Line booking confirmation routed customers to malware. The mail was authentic and passed SPF, DKIM and DMARC. The failure was a promotional domain Carnival had let lapse, still linked from live marketing mail, re-registered by someone else and wired into a redirection network.&lt;/p&gt;

&lt;p&gt;Carnival re-acquired the domain on August 26th, 2026, and I verified the vector dead the next day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tuxxin.com/assets/images/carnival/attack-flow-light.svg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffq3ahl1p8df7tgzxsvl6.png" alt="Attack-flow diagram. An authenticated Carnival email passes through a legitimate click-tracker to the lapsed cclpromos.com domain and its device-aware cloaker, which serves a clean page to datacenter scanners and platform-specific malware to real desktop, mobile and Windows visitors." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The full path. A datacenter scanner only ever reached the clean parking page on the left. Real visitors reached the malware on the right.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Credit where this work starts
&lt;/h2&gt;

&lt;p&gt;Trinity Cyber documented the redirection technique and the payload family in November 2025, in &lt;a href="https://www.trinitycyber.com/blog/blurred-lines-adtech-abuse-delivers-browser-hijackers-through-the-microsoft-store" rel="noopener noreferrer"&gt;Blurred Lines: AdTech Abuse Delivers Browser Hijackers Through the Microsoft Store&lt;/a&gt;. Tanner Piliego and Jared Grumbein named the redirection layer PseudoTDS and the browser-hijacker family PhantomJack, and traced the initial redirects through the Trillion ad-tech network, formerly Trellian.&lt;/p&gt;

&lt;p&gt;Their report describes victims reaching that machinery by mistyping a domain. This report describes the same machinery reached through an authenticated marketing email, which is the part that is new.&lt;/p&gt;

&lt;p&gt;Both names in this article are theirs. What I add is the delivery path, a current set of indicators, and one payload behavior their report does not cover.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I did to confirm it
&lt;/h2&gt;

&lt;p&gt;On June 13th, 2026, I received a Players Club casino email from Carnival Cruise Line. It was a real booking confirmation carrying my own booking number, and all three authentication checks passed.&lt;/p&gt;

&lt;p&gt;I clicked one of its links on my own PC, the way any recipient would, and landed on a fake-security page pushing a download. I reread the address bar and ran it again. Same result.&lt;/p&gt;

&lt;p&gt;Then I checked it from a second computer and a different inbox belonging to another booked guest who received the same email. Same behavior, so nothing about it was specific to my account.&lt;/p&gt;

&lt;p&gt;From there I stopped clicking blind and started capturing, from public scanners, from my own servers in different parts of the world, and from my phone. The same link behaved differently depending on where I loaded it from.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cloak decides what you see
&lt;/h2&gt;

&lt;p&gt;The landing page fingerprints the visitor and routes on the result, per visit.&lt;/p&gt;

&lt;p&gt;Scanners, command-line tools and headless browsers fail that check and receive a benign skeleton. Real browsers on real networks reach a live payload.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tuxxin.com/assets/images/carnival/cloaker-im-human-gate.webp" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F28cqgg8am3uainzb9pmb.webp" alt="A cclpromos.com interstitial reading Did You Mean Ccl Promos, with a large I'm Human button and a claim that it protects browsing with a free mitigate connection utility." width="799" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The gate that sorts visitors. Screenshot from cclpromos.com, June 13th, 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Point a scanner or a datacenter IP at that link and you get a clean, empty page. Load it on a phone or a home connection and it drops you into malware installers, scareware and fullscreen lockers.&lt;/p&gt;

&lt;p&gt;The public reputation services I checked returned clean verdicts throughout. Those results are consistent with the services having received the same skeleton page my automated checks did.&lt;/p&gt;

&lt;p&gt;When I ran the page through urlscan.io it came back flagged as one of "10,000+ similar pages," which points at a templated network rather than a one-off.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the click bought
&lt;/h2&gt;

&lt;p&gt;Here is what I observed. The domain resolved into a parked-domain monetization service. The content it returned changed with the visitor: automated clients received a compliant parking page, real browsers were handed to an advertiser, and which advertiser changed between visits.&lt;/p&gt;

&lt;p&gt;An advertiser network sits behind that domain, and the disclosure record establishes it. I reported individual advertisers to the monetization service and those advertisers came down. Something in that chain decides which advertiser a visitor reaches.&lt;/p&gt;

&lt;p&gt;Each removal had to be earned. The service acted on an advertiser once I had captured and handed over proof for that specific advertiser, which leaves the collection work with whoever happened to notice. Reporting a cloaked page is slow work, and doing it per advertiser is the slowest version of it.&lt;/p&gt;

&lt;p&gt;The removals did not fix anything. Each one was replaced, the entry domain stayed live, and the email link kept routing into it until Carnival re-acquired the domain on August 26th. Removing an advertiser treats the symptom, and the timeline in the disclosure section shows it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the split happens, as far as I can tell
&lt;/h3&gt;

&lt;p&gt;Why the traffic splits the way it does is my reading rather than a documented fact. The behavior is consistent with parked traffic being sorted by what it is worth: traffic that passes strict quality checks goes to the buyers with the strictest policies, and traffic that fails those checks, by geography, device or automation signals, moves elsewhere.&lt;/p&gt;

&lt;p&gt;On that reading, an automated checker resembles traffic the strict buyers accept and receives the compliant page, while a person on a phone is routed further down. It would explain why these domains test clean, and why anyone who checked one and found nothing was reading an accurate result.&lt;/p&gt;

&lt;p&gt;The commercial terms are the part I cannot see. I have no contract, no revenue figure and no visibility into what any party knew about a specific buyer, so this report describes what the infrastructure did rather than what anyone intended by it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What real visitors got
&lt;/h2&gt;

&lt;p&gt;The page fingerprints the device and serves whatever fits it, rotating downstream domains as they get taken down. Every visit could return something different.&lt;/p&gt;

&lt;p&gt;Between June 13th and 24th, 2026, this is what came through.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fake-security installers for Windows
&lt;/h3&gt;

&lt;p&gt;Forced &lt;code&gt;.msix&lt;/code&gt; downloads dressed as privacy and antivirus apps, about 148 MB each. Four names rotated: SafeWatch, NetGuard, LeakGuard and PrivacyKeeper.&lt;/p&gt;

&lt;p&gt;Each was signed with a certificate naming a bare GUID as the organization, issued by a Microsoft Marketplace CA and valid for three days. Both certificates had already expired before the packages were served.&lt;/p&gt;

&lt;p&gt;These are PhantomJack, the browser-hijacker family Trinity Cyber named. SafeWatch, NetGuard and LeakGuard all appear in their report. PrivacyKeeper, SecuriGuard, QuickBrowse, MyConverterHub and SecuredWeb came off the same machinery and do not.&lt;/p&gt;

&lt;p&gt;ReversingLabs classifies my SafeWatch sample as &lt;code&gt;Win32.Trojan.PhantomJack&lt;/code&gt;, which is a vendor's read on the family rather than mine.&lt;/p&gt;

&lt;p&gt;The package size is consistent with padding to exceed the size ceilings that antivirus engines and sandboxes apply before they will scan a file. SafeWatch is 147,724,942 bytes and PrivacyKeeper is 148,652,394 bytes, which a browser rounds to 141 MB in its download bar. I did not test any specific scanner's limit, so treat that as a reading of the size rather than a measurement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tuxxin.com/assets/images/carnival/msix-payloads-download-history.webp" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F86zu883q1ofeo16g45kd.webp" alt="Chrome recent download history listing SafeWatch.msix at 141 MB, LeakGuard.msix at 141 MB and PrivacyKeeper.msix at 142 MB, downloaded minutes apart." width="309" height="193"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Three payloads in 13 minutes from one link. Screenshot from Chrome download history, June 13th, 2026.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Scareware and fullscreen lockers
&lt;/h3&gt;

&lt;p&gt;Several fake-antivirus landers rotated through the chain, including cloned McAfee "Run Quick Scan" pages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tuxxin.com/assets/images/carnival/fake-mcafee-scareware.webp" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fekhxf176g15nshwoengi.webp" alt="A cloned McAfee safety warning page claiming the computer might be at risk, with a prominent scan button, served from a domain unrelated to McAfee." width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A cloned McAfee lure served through the same link. Screenshot captured June 25th, 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The lockers worry me more, and both of the ones I caught were absent from prior public reporting. There is no download and no install.&lt;/p&gt;

&lt;p&gt;The page goes fullscreen, hides the cursor through the Pointer Lock API, loops a synthesized "your PC is locked" voice over a beeping alarm, and runs a fake scrolling hacker terminal. Each pushed its own scam phone number.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tuxxin.com/assets/images/carnival/browser-locker-fake-microsoft.webp" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm5lx2wtjbww5rwy5xpu7.webp" alt="A fullscreen browser locker imitating Microsoft support, displaying a fake security warning and a toll-free phone number for the victim to call." width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A browser locker imitating Windows Defender Security Center, pushing +1 (866) 315-0945. Screenshot captured June 23rd, 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For a non-technical relative, that is harder to escape than an installer and likelier to work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Different device, different attack
&lt;/h3&gt;

&lt;p&gt;A Firefox search hijacker, an iOS "your iPhone has been hacked" scam, and browser push-notification hijacking all appeared, depending on the device.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tuxxin.com/assets/images/carnival/ios-fake-hacked-alert.webp" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbjusso9jjm1ocg1e1hnl.webp" alt="An iPhone browser page claiming the iPhone connection was hacked and someone is tracking the user, urging installation of a protection app." width="738" height="1600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The iOS variant of the same link. Screenshot captured June 13th, 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Three unrelated advertisers came off that one domain inside 48 hours. The slot simply gets refilled.&lt;/p&gt;

&lt;h2&gt;
  
  
  PseudoJack: PhantomJack builds that keep a door open
&lt;/h2&gt;

&lt;p&gt;Two of the installers arrived as &lt;code&gt;.appinstaller&lt;/code&gt; loaders rather than plain packages. I captured both on June 20th, 2026: &lt;code&gt;BelezaAI.PrivacyKeeper&lt;/code&gt; version 1.0.6.0 and &lt;code&gt;BonitoApp.SecuredWeb&lt;/code&gt; version 9.0.3.0.&lt;/p&gt;

&lt;p&gt;Both manifests carry an identical update block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;UpdateSettings&amp;gt;
  &amp;lt;OnLaunch HoursBetweenUpdateChecks="0" /&amp;gt;
  &amp;lt;AutomaticBackgroundTask /&amp;gt;
  &amp;lt;ForceUpdateFromAnyVersion&amp;gt;true&amp;lt;/ForceUpdateFromAnyVersion&amp;gt;
&amp;lt;/UpdateSettings&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three settings matter here. &lt;code&gt;HoursBetweenUpdateChecks="0"&lt;/code&gt; checks on every launch with no delay, &lt;code&gt;&amp;lt;AutomaticBackgroundTask /&amp;gt;&lt;/code&gt; checks even when the app is closed, and &lt;code&gt;ForceUpdateFromAnyVersion&lt;/code&gt; accepts any version the server offers, including a downgrade.&lt;/p&gt;

&lt;p&gt;Together they are a standing channel for the operator to push an arbitrary package at any time. A standard PhantomJack install is a one-time event. A build carrying this block keeps a door open behind it.&lt;/p&gt;

&lt;p&gt;I track these builds as PseudoJack: PhantomJack packages that ship a forced auto-update channel. The name records lineage rather than a discovery, because the apps are already PhantomJack and the machinery is already PseudoTDS.&lt;/p&gt;

&lt;p&gt;I proved the channel exists. I did not observe it delivering a second stage, and I did not detonate the samples, so every claim about what the installed app does comes from Trinity Cyber's analysis rather than mine.&lt;/p&gt;

&lt;p&gt;Both publisher identities are bare GUIDs, &lt;code&gt;CN=31E36F3F-1F26-4209-AA73-9C18172AA2E6&lt;/code&gt; and &lt;code&gt;CN=11069677-9767-4ED4-9C5B-619B79B9FE6B&lt;/code&gt;, which are throwaway signing identities rather than vendor names.&lt;/p&gt;

&lt;p&gt;There is precedent for the format itself. Microsoft &lt;a href="https://www.microsoft.com/en-us/security/blog/2023/12/28/financially-motivated-threat-actors-misusing-app-installer/" rel="noopener noreferrer"&gt;disabled the ms-appinstaller protocol handler by default in December 2023&lt;/a&gt; after tracking abuse by Storm-1113, Storm-0569 and Sangria Tempest, and reported that multiple criminals were selling a malware kit as a service built on the MSIX format and that handler. My captures deliver the file by direct download rather than through an &lt;code&gt;ms-appinstaller:&lt;/code&gt; URI, which is consistent with that handler having been off by default since 2023.&lt;/p&gt;

&lt;h2&gt;
  
  
  It fought back against inspection
&lt;/h2&gt;

&lt;p&gt;The landers ship an anti-analysis layer. Opening developer tools froze the page on a debugger trap built at runtime, with no literal &lt;code&gt;debugger&lt;/code&gt; token in the source for a scanner to find.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tuxxin.com/assets/images/carnival/anti-analysis-debugger-trap.webp" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F21o970rrkwlmol5mtyoy.webp" alt="Firefox developer tools halted at a debugger statement injected by the malicious page, an anti-analysis technique that interrupts inspection." width="800" height="766"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The anti-analysis layer firing on inspection. Screenshot from Firefox developer tools, June 24th, 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The same layer hooks DOM query methods and inspects the caller's stack to spot console evaluation or injected extensions. It reports back to the operator's own telemetry endpoint.&lt;/p&gt;

&lt;p&gt;That combination explains both halves of the problem. Payloads are hard to reproduce under analysis, and automated scanners only ever capture the skeleton.&lt;/p&gt;

&lt;h2&gt;
  
  
  The infrastructure is still live
&lt;/h2&gt;

&lt;p&gt;I rescanned the chain on September 10th, 2026. The hosts Trinity Cyber reported have moved on: &lt;code&gt;results.streamio[.]site&lt;/code&gt; now returns clean and &lt;code&gt;cint[.]browsingit[.]online&lt;/code&gt; is unreachable.&lt;/p&gt;

&lt;p&gt;The build I captured is still running. &lt;code&gt;get.privacykeepersite[.]com&lt;/code&gt; returns a phishing verdict, &lt;code&gt;securi-guard-browser[.]com&lt;/code&gt; is live, and &lt;code&gt;cablegaurdian[.]online&lt;/code&gt;, &lt;code&gt;guardianrole[.]online&lt;/code&gt;, &lt;code&gt;euob.northwavepoint[.]com&lt;/code&gt;, &lt;code&gt;tratobid[.]com&lt;/code&gt; and &lt;code&gt;kineticharbor[.]com&lt;/code&gt; are all still cloaking.&lt;/p&gt;

&lt;p&gt;The naming carries across. Their command-and-control host was &lt;code&gt;cint[.]browsingit[.]online&lt;/code&gt;; mine are &lt;code&gt;cint2.scrtgrd[.]online&lt;/code&gt; and &lt;code&gt;cinga.ngapp[.]online&lt;/code&gt;. Same subdomain grammar, different apexes, rotated infrastructure.&lt;/p&gt;

&lt;p&gt;The hop layer is disposable by design. Of 12 redirect-hop domains I scanned, 11 came back as dead parked pages: six lowercase letters plus &lt;code&gt;.com&lt;/code&gt;, bulk-registered, all first seen in the same month, all on one parking IP.&lt;/p&gt;

&lt;p&gt;All four of my samples were unknown to MalwareBazaar and ThreatFox before I filed them on September 10th, 2026.&lt;/p&gt;

&lt;p&gt;The rotation is why this stays open. I mapped 12 of the 33 hop domains I collected, which leaves most of that layer unexamined, and the selection logic inside the redirect itself is still unseen.&lt;/p&gt;

&lt;p&gt;I am continuing to track this kit and the domains it moves through, and I will publish what that turns up. Researchers working the same kit, or anyone holding telemetry on the hosts listed below, can reach me at &lt;a href="mailto:contact@tuxxin.com"&gt;contact@tuxxin.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I could and could not confirm
&lt;/h2&gt;

&lt;p&gt;I resolved the recipient's full 16-year email archive server-side, at a deliberately gentle rate, and validated the resolver against the known-bad link first.&lt;/p&gt;

&lt;p&gt;The booking confirmation from June 13th is confirmed to route live to the lapsed domain.&lt;/p&gt;

&lt;p&gt;The broader mailstream is clean. Across 541 deduplicated casino tracker links from 2018 to 2026, and 241 emails sent after the domain takeover, none routed to it. Every archived link still live resolves to &lt;code&gt;www.carnival.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Older booking emails from 2024 and 2025 are unverifiable. Their tracker tokens have expired and now return an error page, so their original destinations cannot be recovered either way.&lt;/p&gt;

&lt;p&gt;The exposure window runs from November 22nd, 2024, when the domain left Carnival's control, to August 26th, 2026. The cloaker is confirmed present by March 12th, 2026, the earliest archived snapshot containing it, and the previous snapshot from July 23rd, 2025, was still benign.&lt;/p&gt;

&lt;p&gt;Several things stay out of this report for lack of evidence. I have no victim counts, no dwell time and no install numbers.&lt;/p&gt;

&lt;p&gt;The commercial arrangements are also outside what I can observe. I documented what the infrastructure served and how it responded to reports, and I have no contract, no revenue figure and no basis to characterize intent.&lt;/p&gt;

&lt;h2&gt;
  
  
  How a Carnival link ends up serving malware
&lt;/h2&gt;

&lt;p&gt;The root cause is mundane, which is what makes it worth writing about.&lt;/p&gt;

&lt;p&gt;The domain was Carnival's own Players Club casino site. The 2021 Wayback snapshot shows Carnival branding, links to carnival.com, and the same &lt;code&gt;/carnivalplayersclub/&lt;/code&gt; path the malicious email still pointed at.&lt;/p&gt;

&lt;p&gt;Content ceased around August 2022 and the domain lapsed. A third party caught it in November 2024.&lt;/p&gt;

&lt;p&gt;The rest of the sending setup checks out from outside. The sending domain sits on an enterprise registrar with locks in place and years left on it, and the authentication passed because the mail genuinely was Carnival's.&lt;/p&gt;

&lt;p&gt;One brand domain lapsed while live marketing mail still linked to it. That is the root cause visible from the outside, and the internal reason it lapsed is Carnival's record rather than mine.&lt;/p&gt;

&lt;p&gt;Whoever holds a domain in that position has no need to build an attack. Pointing it at a monetization service turns leftover traffic into revenue, and the visitor is handed onward from there.&lt;/p&gt;

&lt;p&gt;Chasing advertisers one at a time achieves nothing. Every one I reported was removed, and every removal was followed by a replacement, because the domain is the asset and the advertiser is inventory.&lt;/p&gt;

&lt;h2&gt;
  
  
  The breach question, answered directly
&lt;/h2&gt;

&lt;p&gt;Carnival disclosed a data breach in April 2026, and this infrastructure predates it. Anyone reading the timeline will notice that, so I checked rather than leave it hanging.&lt;/p&gt;

&lt;p&gt;I found no evidence connecting the two and I assert no connection. The domain takeover in November 2024 and the cloaker in March 2026 both predate the April breach detection.&lt;/p&gt;

&lt;p&gt;What I documented is generic, mass-scale ad-monetization abuse. A targeted intrusion has a different profile. These are two separate events.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reporting it, and what worked
&lt;/h2&gt;

&lt;p&gt;I reported it the ordinary way first, to everyone who could act on a piece of it. The email provider, the registrars and hosts of the payload domains, Cloudflare, the browser blocklists, IC3 and the FTC.&lt;/p&gt;

&lt;p&gt;Reaching Carnival took longer. The abuse and security inboxes I wrote to on June 15th went unanswered. A staff member confirmed the issue had been flagged internally, and no further response followed.&lt;/p&gt;

&lt;p&gt;Phone routes ended at general customer service. The hotline stood up for Carnival's earlier breach did not cover this report either.&lt;/p&gt;

&lt;p&gt;Meanwhile the parking service removed individual advertisers, each one after I supplied evidence for it. The entry domain stayed live throughout and the email link kept routing into it.&lt;/p&gt;

&lt;p&gt;A certified letter is what worked. On August 5th I sent a formal notice with return receipt to Carnival's General Counsel, Global CISO and registered agent.&lt;/p&gt;

&lt;p&gt;The Deputy General Counsel engaged on August 13th and moved it to Global Cybersecurity Services. I delivered the full technical package on August 25th, holding the live malware samples back until they asked for them.&lt;/p&gt;

&lt;p&gt;Carnival re-acquired the domain on August 26th, moved it onto their own DNS and pointed it at a real Carnival page. I ran a final multi-vantage check on August 27th and confirmed the routing was dead everywhere.&lt;/p&gt;

&lt;p&gt;From certified letter to resolved was 22 days. Reaching the letter took seven weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I take from it
&lt;/h2&gt;

&lt;p&gt;Domain hygiene is a security control. A single expired promo domain, still linked from live authenticated mail, turned a trusted brand channel into a malware delivery system.&lt;/p&gt;

&lt;p&gt;Audit the domains your live mail links to against the domains you still own. That is a lifecycle control rather than a mail control, and no amount of email authentication substitutes for it.&lt;/p&gt;

&lt;p&gt;Put every brand and marketing domain on auto-renew with a registry lock. Then go looking for the ones that quietly lapsed years ago.&lt;/p&gt;

&lt;p&gt;One vantage point tells you what that vantage point sees. Anything built to cloak is built to pass exactly that test.&lt;/p&gt;

&lt;p&gt;One last note on the reporting path, for researchers rather than for Carnival. The technical work took days. Reaching a desk that owned the problem took seven weeks and a certified letter, and that is the part worth planning for at any large organization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Indicators of compromise
&lt;/h2&gt;

&lt;p&gt;Hashes are SHA-256. The two &lt;code&gt;.msix&lt;/code&gt; samples are on MalwareBazaar and all four are on ThreatFox.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8a9006cfaee227415eeef0d645183ca423c80b9a8181e35a57bec71468e72daa  PrivacyKeeper.msix  (148,652,394 bytes)
d66895d8da6d5eb1d8658647c80f66dce40236c06bb600f1c62a44a657f923b3  SafeWatch.msix      (147,724,942 bytes)
b8f0c82894032766293b7f6e6feaf287a366edd4886b8e665c41bc5103d512ce  PrivacyKeeper.appinstaller
8de1357454488bd0702f0f22e912b412af41d814dffdc7c76f416d75d7dc1dc6  SecuredWeb.appinstaller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MalwareBazaar holds the two packages: &lt;a href="https://bazaar.abuse.ch/sample/8a9006cfaee227415eeef0d645183ca423c80b9a8181e35a57bec71468e72daa/" rel="noopener noreferrer"&gt;PrivacyKeeper.msix&lt;/a&gt; and &lt;a href="https://bazaar.abuse.ch/sample/d66895d8da6d5eb1d8658647c80f66dce40236c06bb600f1c62a44a657f923b3/" rel="noopener noreferrer"&gt;SafeWatch.msix&lt;/a&gt;. The two &lt;code&gt;.appinstaller&lt;/code&gt; manifests are XML rather than binaries, so MalwareBazaar dropped them in processing and only their ThreatFox entries remain. That is why the manifest block is quoted in full above: for those two, the quoted XML is the evidence.&lt;/p&gt;

&lt;p&gt;Delivery and update hosts, defanged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get.privacykeepersite[.]com    get.gosecuredweb[.]com     file.ngapp[.]online
file.swatchapp[.]online        cinga.ngapp[.]online       spot.swatchapp[.]online
cint2.scrtgrd[.]online         show.ngrapp[.]online       spons.lkguard[.]online
cinnabon.pkeeper[.]net         lpic.prvbrws[.]com         guardianrole[.]online
securi-guard-browser[.]com     extensionsnewtab[.]com     pkeepers[.]online
pbrowsingapp[.]online          cablegaurdian[.]online     safe-guard[.]online
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Redirect layer: &lt;code&gt;tratobid[.]com&lt;/code&gt; and &lt;code&gt;jubaaa[.]com&lt;/code&gt;. Anti-bot and beacon layer: &lt;code&gt;ob.sd559908.js.brandsmat[.]com&lt;/code&gt;, &lt;code&gt;euob.northwavepoint[.]com&lt;/code&gt;, &lt;code&gt;ob.buzzfighter[.]com&lt;/code&gt;, &lt;code&gt;assets.webfervor[.]com&lt;/code&gt; and &lt;code&gt;kineticharbor[.]com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Domains rotate. The delivery-kit path grammar has not, which makes it the more durable signature.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/lps/security-check/
/impression?c=&amp;lt;campaign&amp;gt;&amp;amp;ext_name=&amp;lt;Brand&amp;gt;&amp;amp;cid=&amp;lt;id&amp;gt;
/downloadproxy/&amp;lt;campaign&amp;gt;/&amp;lt;clickid&amp;gt;/?ext_name=&amp;lt;Brand&amp;gt;&amp;amp;cid=&amp;lt;id&amp;gt;&amp;amp;tag=&amp;lt;cid&amp;gt;_&amp;lt;YYYY-MM-DD&amp;gt;&amp;amp;file=true
/event/download   /event/download_button_clicked   /event/download_xhr_completed
/event/pageload   /event/incognito_status
appData=fr2TvjWVEr+MejYst84xw...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;appData&lt;/code&gt; prefix is a fixed 21 characters and appears unchanged across unrelated brand domains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evidence and technical detail
&lt;/h2&gt;

&lt;p&gt;The full findings report documents the de-cloaked redirect chain, every payload variant, the cloaking and anti-analysis techniques, the complete indicators of compromise with hashes, and the disclosure record.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whitepaper: &lt;a href="https://tuxxin.com/dl/Carnival-cclpromos-Security-Findings.pdf" rel="noopener noreferrer"&gt;Carnival-cclpromos-Security-Findings.pdf&lt;/a&gt; (2.9 MB) &lt;code&gt;SHA-256 BED672E46273F7AFF10E75310600AA4B1C7F28A795A16DC15CDB356669BF3D24&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Evidence package, screenshots and sanitized HAR captures: &lt;a href="https://tuxxin.com/dl/Carnival-cclpromos-Findings-Evidence.zip" rel="noopener noreferrer"&gt;Carnival-cclpromos-Findings-Evidence.zip&lt;/a&gt; (6.9 MB) &lt;code&gt;SHA-256 F995FCCC77E40EA63E3A842E22796C0FEC07D548E8F05A471EEE884C3C53C3C6&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Checksums, including the sealed priority hash from June 25th, 2026: &lt;a href="https://tuxxin.com/dl/Carnival-cclpromos-Findings-Evidence.sha256.txt" rel="noopener noreferrer"&gt;Carnival-cclpromos-Findings-Evidence.sha256.txt&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check a download against the hash above before opening it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sha256sum Carnival-cclpromos-Security-Findings.pdf
Get-FileHash Carnival-cclpromos-Security-Findings.pdf -Algorithm SHA256
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line is Linux and macOS, the second is PowerShell.&lt;/p&gt;

&lt;p&gt;For context on the technique, the FBI's IC3 published &lt;a href="https://www.ic3.gov/PSA/2026/PSA260618" rel="noopener noreferrer"&gt;PSA I-061826-PSA&lt;/a&gt; on June 18th, 2026, warning about criminals redirecting users through malicious traffic distribution systems. I make no claim that my report prompted it. A federal advisory takes far longer than a few days to prepare, so I note it only as independent confirmation that this attack class was active and recognized in the same window.&lt;/p&gt;

&lt;p&gt;All malicious domains in this report are defanged. The original findings were documented and cryptographically hashed on June 25th, 2026, and that timestamp hash is published with the evidence package for verification.&lt;/p&gt;

&lt;p&gt;This report is the original work of Tuxxin LLC, published under &lt;a href="https://creativecommons.org/licenses/by-nd/4.0/" rel="noopener noreferrer"&gt;Creative Commons Attribution-NoDerivatives 4.0&lt;/a&gt;. Media and researchers may quote and republish it with attribution to Daniel Jones, Tuxxin LLC. For the full evidence package or coordinated disclosure: &lt;a href="mailto:contact@tuxxin.com"&gt;contact@tuxxin.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>malvertising</category>
      <category>carnivalcruiseline</category>
      <category>cloaking</category>
      <category>expireddomains</category>
    </item>
    <item>
      <title>Capture any site in light or dark theme</title>
      <dc:creator>Daniel J</dc:creator>
      <pubDate>Wed, 09 Sep 2026 19:19:46 +0000</pubDate>
      <link>https://dev.to/tuxxin/capture-any-site-in-light-or-dark-theme-1im7</link>
      <guid>https://dev.to/tuxxin/capture-any-site-in-light-or-dark-theme-1im7</guid>
      <description>&lt;p&gt;Webshot now sets the color scheme the target page renders in. Pick Auto, Light or Dark in the capture form, or send a &lt;code&gt;theme&lt;/code&gt; parameter to the API.&lt;/p&gt;

&lt;p&gt;Chrome reports &lt;code&gt;prefers-color-scheme: light&lt;/code&gt; by default. A site that follows the visitor's system setting could only be captured in its light form, whatever it looks like to the people using it. overwatch.earth, issued.live and whack.sh all work that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it from the form
&lt;/h2&gt;

&lt;p&gt;A Theme control sits next to Format. Auto is the default and sends no override, so captures you make today match the ones you made last week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it from the API
&lt;/h2&gt;

&lt;p&gt;Add &lt;code&gt;theme&lt;/code&gt; to the request body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://webshot.site/api/capture &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"url":"https://overwatch.earth","mode":"desktop_viewport","theme":"dark"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; shot.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accepted values are &lt;code&gt;system&lt;/code&gt;, &lt;code&gt;light&lt;/code&gt; and &lt;code&gt;dark&lt;/code&gt;. Anything else returns HTTP 400 with the accepted list, so a typo fails loudly rather than returning the wrong theme.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the override is applied
&lt;/h2&gt;

&lt;p&gt;The scheme is set before the page loads. A theme script that reads the preference once during startup, which is how a site avoids a flash of the wrong colors, sees the requested scheme on its first paint.&lt;/p&gt;

&lt;p&gt;Credit cost is unchanged.&lt;/p&gt;

</description>
      <category>webshot</category>
      <category>screenshots</category>
      <category>api</category>
      <category>darkmode</category>
    </item>
    <item>
      <title>overwatch.earth rebuild adds feed pages, discussions, and passwordless comments</title>
      <dc:creator>Daniel J</dc:creator>
      <pubDate>Wed, 09 Sep 2026 13:00:03 +0000</pubDate>
      <link>https://dev.to/tuxxin/overwatchearth-rebuild-adds-feed-pages-discussions-and-passwordless-comments-574l</link>
      <guid>https://dev.to/tuxxin/overwatchearth-rebuild-adds-feed-pages-discussions-and-passwordless-comments-574l</guid>
      <description>&lt;p&gt;&lt;a href="https://overwatch.earth" rel="noopener noreferrer"&gt;overwatch.earth&lt;/a&gt; now opens on a website. Earlier versions loaded straight into the 3D globe. The front page lists all 34 live feeds grouped by theme, and every feed has its own page covering the data behind it, where it comes from, and a globe focused on that feed alone.&lt;/p&gt;

&lt;p&gt;The globe is one click away and still needs no account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every feed has its own page
&lt;/h2&gt;

&lt;p&gt;The 34 feeds are grouped into four themes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transit&lt;/strong&gt; (6): aircraft, ships, Amtrak trains, military aircraft, public transit, airports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environmental&lt;/strong&gt; (14): earthquakes, wildfires, live clouds, satellite imagery, lightning, disaster alerts, natural events, aurora, ocean buoys, air quality, weather alerts, EMSC seismic, fireballs, volcanoes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Digital&lt;/strong&gt; (5): submarine cables, internet outages, data centers, IODA outages, internet events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Curiosity&lt;/strong&gt; (9): Wikipedia pulse, global news, satellites and the ISS, rocket launches, biodiversity, webcams, Starlink, the Deep Space Network, rare birds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each feed page names its upstream provider, and every layer is attributed on the data sources page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussions on every feed
&lt;/h2&gt;

&lt;p&gt;Each map carries a comments drawer, so a conversation attaches to the feed it belongs to. The discussion page collects what people post across all 34 feeds and ranks it by recent activity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Commenting takes an email and a username
&lt;/h2&gt;

&lt;p&gt;Enter an email address and pick a username. overwatch.earth emails a pin code from &lt;code&gt;noreply@tuxxin.com&lt;/code&gt;, and entering that code signs you in. You choose whether to stay signed in for 30 days or keep the session temporary.&lt;/p&gt;

&lt;p&gt;Every sign-in works this way, so the site stores no passwords. There is nothing to reuse across sites, nothing to leak in a dump, and no reset flow to phish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Suggest a feed
&lt;/h2&gt;

&lt;p&gt;The contact form accepts data source submissions. It asks for the source name, a URL or link to its API docs, and a note about what it covers. Send one through &lt;a href="https://overwatch.earth/contact?type=source" rel="noopener noreferrer"&gt;Suggest a data source&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;More feeds are queued for release. Work has also started on a multi-tenant system that will let people build their own globes from their own data.&lt;/p&gt;

&lt;p&gt;overwatch.earth is free to read, runs on open public data sources, and requires no login.&lt;/p&gt;

</description>
      <category>overwatchearth</category>
      <category>livedata</category>
      <category>opendata</category>
      <category>webgl</category>
    </item>
    <item>
      <title>Rebuilding Houston's Courier: New Site, Driver Portal, and a Dispatch System in Progress</title>
      <dc:creator>Daniel J</dc:creator>
      <pubDate>Thu, 03 Sep 2026 15:38:30 +0000</pubDate>
      <link>https://dev.to/tuxxin/rebuilding-houstons-courier-new-site-driver-portal-and-a-dispatch-system-in-progress-18oj</link>
      <guid>https://dev.to/tuxxin/rebuilding-houstons-courier-new-site-driver-portal-and-a-dispatch-system-in-progress-18oj</guid>
      <description>&lt;p&gt;Houston's Courier has been moving freight and time-critical deliveries around the Gulf Coast for decades. Their website had not kept up. This is what we rebuilt, what we surfaced, what we added, and what is still on the bench.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting with the part customers see
&lt;/h2&gt;

&lt;p&gt;The old site was nearly a decade old. It &lt;em&gt;had&lt;/em&gt; a mobile layout — it just did not work properly, with errors throughout that got worse the smaller the screen got. Underneath that, the site was thin where it counted: very little SEO foundation and effectively no structured data, so search engines had to guess at what the business did, where it operated, and what services it offered.&lt;/p&gt;

&lt;p&gt;The service list had also fallen behind. Not because the company changed direction — Houston's Courier does what it has always done — but because it kept adding capabilities and the website never caught up. Work they were actively selling simply was not on the site.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://houstonscourier.com/" rel="noopener noreferrer"&gt;new site&lt;/a&gt; is a ground-up replacement: same-day delivery leads, the catalog reflects everything the company actually offers in 2026, structured data describes the business properly, and the client login sits on the homepage instead of behind a hostname nobody could remember.&lt;/p&gt;

&lt;h2&gt;
  
  
  One service the site was hiding, and one that is coming
&lt;/h2&gt;

&lt;p&gt;A courier already sends a bonded, uniformed, background-checked person to a customer's door with a package. That position turns out to be worth more than a single delivery.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mobile notary — already offered, never advertised.&lt;/strong&gt; This is not a new service line; Houston's Courier has been doing it. It was nowhere on the old website. Documents that need a notary are frequently the same documents that need a courier, so one visit covers both — and the company was earning none of the search traffic for it, because as far as Google was concerned the service did not exist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Electronic ID verification for age-restricted deliveries (18+ and 21+) — built, not yet launched.&lt;/strong&gt; Alcohol, tobacco, vape, and certain pharmacy items carry a legal obligation to verify age at handoff. This is a genuinely new capability rather than an unadvertised one, and it ships with the new dispatch system described below — it is not orderable today.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those two are worth separating clearly, because they are different kinds of work. One was a marketing failure with an easy fix — the service existed, the website just never said so. The other required building something that did not exist, and it launches when the new system does.&lt;/p&gt;

&lt;h2&gt;
  
  
  The driver portal, where the compliance actually happens
&lt;/h2&gt;

&lt;p&gt;"We verify ID" is a claim. The driver portal is the evidence. Drivers work from a portal built for a phone in one hand and a package in the other:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Barcode scanning off the air bill.&lt;/strong&gt; The driver scans rather than types. That kills transposed tracking numbers, which are the single most common cause of a package that is physically fine and administratively lost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Photo delivery confirmation.&lt;/strong&gt; A timestamped photo at the drop point, attached to the stop. "It was left at the side door" stops being a memory and becomes a record.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Electronic ID verification at handoff, over a secure network.&lt;/strong&gt; Launching with the new system: the age check is performed and captured electronically at the point of delivery and transmitted over an encrypted connection — not written on a signature sheet, not photographed and emailed, and not reconstructed from memory weeks later when someone asks for proof.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The through-line is that each of these replaces something a driver used to remember with something the system holds, securely. That matters most on the day a client disputes a delivery: the answer is a record retrieved in seconds, rather than a phone call to a driver who has made two hundred stops since.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dispatch: replacing the system, not skinning it
&lt;/h2&gt;

&lt;p&gt;The largest piece of this engagement is roughly two-thirds built, and it is worth being precise about what it replaces.&lt;/p&gt;

&lt;p&gt;Dispatch, driver management, and the customer portal all lived inside one integrated legacy application on &lt;code&gt;manage.houstonscourier.com&lt;/code&gt;. That coupling is the problem. Every change to how dispatchers work risked the customer-facing side, and every change a customer noticed had to be regression-tested against dispatch. Nothing could be improved in isolation.&lt;/p&gt;

&lt;p&gt;The replacement is a standalone system with three separate surfaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Admin and dispatcher&lt;/strong&gt; — order intake, assignment, and the live board dispatchers work from all day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Driver&lt;/strong&gt; — the portal above: scanning, photo confirmation, electronic ID capture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client&lt;/strong&gt; — and this one goes &lt;em&gt;into the new website&lt;/em&gt; rather than beside it. Customers place and track orders on the same domain they landed on, not on a subdomain that looks and behaves like different software, because it was.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three things ship with it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A suite of new security controls&lt;/strong&gt; across all three surfaces. A system holding delivery addresses, signed proof-of-delivery, and age-verification records is holding sensitive data, and the legacy application was not designed with that in mind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A far easier experience end to end&lt;/strong&gt; — from submitting an order to paying the invoice. The old flow worked if you already knew it. The new one does not require that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous route optimization&lt;/strong&gt; using the current Google Maps platform APIs, re-optimizing as the day changes rather than planning once in the morning and hoping. Traffic in Houston does not hold still, and neither should the route.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Splitting one application into three surfaces on a shared core costs more up front than restyling the old one. It buys the ability to change a dispatcher's workflow on a Tuesday without touching anything a customer can see.&lt;/p&gt;

&lt;h2&gt;
  
  
  No AI in the workflow. That is a decision, not an omission.
&lt;/h2&gt;

&lt;p&gt;It is worth stating plainly, because in 2026 the default assumption runs the other way: &lt;strong&gt;no AI touches the day-to-day operation of Houston's Courier.&lt;/strong&gt; Every delivery ticket, every email, every phone call, and every delivery is handled by a real person.&lt;/p&gt;

&lt;p&gt;The software's job here is to remove clerical work and to remember things accurately — scan instead of type, capture instead of recall, re-optimize a route as conditions change. The judgment calls stay with the dispatchers and drivers who know the accounts, the buildings, and which receiving dock actually opens at 7am.&lt;/p&gt;

&lt;p&gt;For a same-day courier that is not nostalgia, it is the product. When a delivery is time-critical and something goes wrong, the customer wants a person who can fix it — not a queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the search numbers did
&lt;/h2&gt;

&lt;p&gt;A rebuild is a genuine risk to organic search. New URLs, new structure, and a decade of accumulated signals to carry across — sites lose traffic doing this. Six months on, compared against the six months before:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Impressions: 153K → 273K&lt;/strong&gt;, up 78%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clicks: 3.05K → 3.84K&lt;/strong&gt;, up 26%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Worth reading those two together rather than separately. Impressions grew faster than clicks, which means the site now surfaces for a far wider set of queries — including broader, higher-funnel searches that were previously out of reach entirely and that convert at a lower rate by nature. Average click-through moved from about 2.0% to about 1.4% as a result. That is the arithmetic of a site appearing in a great many more places, not a site getting worse at earning the click, and the number that pays the bills — actual visits from search — is up by roughly 790 a month.&lt;/p&gt;

&lt;p&gt;A good share of that is the unglamorous work: real structured data, a service catalog that lists the services, and pages for offerings like mobile notary that were previously invisible to search entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Still to come
&lt;/h2&gt;

&lt;p&gt;The dispatch and portal build is about two-thirds done. The remaining third is the part that has to be right rather than fast: cutting live operations off the legacy system without dropping an order mid-flight, and moving the client portal into the new site so customers stop seeing two different pieces of software.&lt;/p&gt;

&lt;p&gt;Age-restricted delivery goes live at the same time, with its own service page — there is no point advertising a capability before the system that performs it is carrying real traffic.&lt;/p&gt;

&lt;p&gt;We will write that up when it ships, including whatever goes wrong on the way.&lt;/p&gt;




&lt;p&gt;Houston's Courier is a Tuxxin LLC client. If you are running a business on software a decade past its service life — and you can usually tell, because everyone has a workaround for it — &lt;a href="https://tuxxin.com/contact" rel="noopener noreferrer"&gt;get in touch&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>casestudy</category>
      <category>clientwork</category>
      <category>logistics</category>
      <category>dispatch</category>
    </item>
    <item>
      <title>Is curl | bash Safe? What qsa.sh Does Instead</title>
      <dc:creator>Daniel J</dc:creator>
      <pubDate>Thu, 03 Sep 2026 12:52:57 +0000</pubDate>
      <link>https://dev.to/tuxxin/is-curl-bash-safe-what-qsash-does-instead-2in2</link>
      <guid>https://dev.to/tuxxin/is-curl-bash-safe-what-qsash-does-instead-2in2</guid>
      <description>&lt;p&gt;You have seen the pattern in a hundred install docs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://example.com/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is convenient. It is also the single most-argued line in DevOps. Before you paste it into a root shell, it is worth being precise about what it actually does — and about what &lt;code&gt;curl qsa.sh&lt;/code&gt; does &lt;em&gt;not&lt;/em&gt; do, because the two look similar and are not the same thing at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;curl &amp;lt;url&amp;gt; | bash&lt;/code&gt; actually does
&lt;/h2&gt;

&lt;p&gt;The pipe does exactly what it says. &lt;code&gt;curl&lt;/code&gt; fetches whatever bytes the server sends, and &lt;code&gt;bash&lt;/code&gt; executes them as a shell script — on your machine, with your privileges, right now. There is no preview, no diff, no confirmation step. Whatever the server returns &lt;em&gt;is&lt;/em&gt; the program that runs.&lt;/p&gt;

&lt;p&gt;That is not a hypothetical. It is arbitrary local code execution by design. The risk is not that the pattern is broken; it is that it works perfectly and hands full trust to a remote party. Specifically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You cannot see what you ran.&lt;/strong&gt; By the time the script executes, you have already run it. Auditing after the fact is not auditing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trust extends to the whole chain.&lt;/strong&gt; You are trusting the domain, its TLS, its DNS, the CDN in front of it, and anyone who can compromise any of those. A server can also detect a piped shell (no TTY) and serve different bytes than it shows in a browser — this has been demonstrated publicly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure modes get worse.&lt;/strong&gt; A truncated download can leave a half-run script. &lt;code&gt;sudo curl ... | bash&lt;/code&gt; runs remote code as root.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To be fair — and this matters — &lt;code&gt;curl | bash&lt;/code&gt; is not automatically malicious. Downloading a release binary, an &lt;code&gt;apt&lt;/code&gt; package, or a container image is also "run code you didn't read." The pattern's real sins are that it skips the checkpoint where you &lt;em&gt;could&lt;/em&gt; have looked, and that it re-fetches live every time, so a good script today can become a bad script tomorrow with nothing pinned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The safer way to run an install script
&lt;/h2&gt;

&lt;p&gt;If you are going to run one, split fetch from execute so there is a moment to look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://example.com/install.sh &lt;span class="nt"&gt;-o&lt;/span&gt; install.sh
less install.sh          &lt;span class="c"&gt;# actually read it&lt;/span&gt;
&lt;span class="nb"&gt;sha256sum &lt;/span&gt;install.sh     &lt;span class="c"&gt;# compare to a published checksum, if any&lt;/span&gt;
bash install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not glamorous. But now there is a checkpoint, and you are running a known artifact instead of a live stream. Prefer signed packages or checksummed binaries when the project offers them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;curl qsa.sh&lt;/code&gt; is a different animal
&lt;/h2&gt;

&lt;p&gt;Here is the part people conflate. &lt;code&gt;curl qsa.sh&lt;/code&gt; &lt;strong&gt;is not&lt;/strong&gt; &lt;code&gt;curl qsa.sh | bash&lt;/code&gt;. There is no pipe into a shell, and there is nothing on our end that expects one.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl qsa.sh&lt;/code&gt; makes an HTTP request and &lt;strong&gt;streams text back to your terminal.&lt;/strong&gt; That text is a scan report. Your shell prints it, the same way it would print a README. No script is downloaded to be executed, no code runs on your machine, and adding &lt;code&gt;| bash&lt;/code&gt; would accomplish nothing useful because the output is a human-readable report, not a program.&lt;/p&gt;

&lt;p&gt;The work happens on &lt;strong&gt;our&lt;/strong&gt; side, not yours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The scan runs from qsa.sh's own real, attributable scanner IP (reverse-DNS &lt;code&gt;scanner.qsa.sh&lt;/code&gt;) — an outside-in view, exactly what an external attacker sees.&lt;/li&gt;
&lt;li&gt;It scans &lt;strong&gt;only the public IP you are connecting from.&lt;/strong&gt; There is no target field; you cannot aim it at anyone else. Your IP is shown with a countdown before anything starts.&lt;/li&gt;
&lt;li&gt;The engine is &lt;a href="https://qsa.sh/how-it-works" rel="noopener noreferrer"&gt;the same open-source tooling you could run yourself&lt;/a&gt;: naabu for port discovery, &lt;code&gt;nmap -sV --script vulners&lt;/code&gt; for service/version and CVE matching, then nuclei for templated checks (&lt;a href="https://qsa.sh/news/engine-update-nmap-7-99-nuclei-3-11-origin-intel" rel="noopener noreferrer"&gt;current engine versions&lt;/a&gt;). No black box.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://qsa.sh/privacy" rel="noopener noreferrer"&gt;Zero retention&lt;/a&gt;: results stream to your terminal and are gone. No stored scan output, no stored IPs, no user-agents, no history.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the trust question inverts. With &lt;code&gt;curl &amp;lt;url&amp;gt; | bash&lt;/code&gt;, the remote server runs code on your machine. With &lt;code&gt;curl qsa.sh&lt;/code&gt;, your machine runs nothing — it just displays what our scanner found looking at your public IP from the outside.&lt;/p&gt;

&lt;h2&gt;
  
  
  Side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;curl &amp;lt;url&amp;gt; | bash&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;curl qsa.sh&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What comes back&lt;/td&gt;
&lt;td&gt;A shell script&lt;/td&gt;
&lt;td&gt;A text report&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What runs it&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Your shell, on your host&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nothing — it is printed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Where code executes&lt;/td&gt;
&lt;td&gt;Your machine, your privileges&lt;/td&gt;
&lt;td&gt;Our scanner, server-side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What it can touch&lt;/td&gt;
&lt;td&gt;Your filesystem, your creds&lt;/td&gt;
&lt;td&gt;Only your public IP, from outside&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retained&lt;/td&gt;
&lt;td&gt;Whatever the script writes&lt;/td&gt;
&lt;td&gt;Nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  A fair caveat, because this is a security site
&lt;/h2&gt;

&lt;p&gt;You should still know who you are talking to. &lt;code&gt;curl qsa.sh&lt;/code&gt; streams text and runs nothing locally — that is true whether you trust us or not, because the output never touches your shell as code. But the &lt;em&gt;report itself&lt;/em&gt; comes from us, so treat qsa.sh like any external scanner: it is a starting point, an attacker's-eye view of your exposed surface, not a full audit.&lt;/p&gt;

&lt;p&gt;One practical note: plain &lt;code&gt;curl qsa.sh&lt;/code&gt; is HTTP. To encrypt the request and response in transit, use &lt;code&gt;curl https://qsa.sh&lt;/code&gt;. Same scan, same report — just protected on the wire. On a network you do not control, prefer the &lt;code&gt;https://&lt;/code&gt; form.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;curl &amp;lt;url&amp;gt; | bash&lt;/code&gt; is not evil, but it is arbitrary local code execution with no checkpoint — read it, checksum it, or use a signed package first. &lt;code&gt;curl qsa.sh&lt;/code&gt; is not that pattern: it prints a report, runs no local code, and does its scanning from our own IP against only your public IP.&lt;/p&gt;

&lt;p&gt;Run it and read the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://qsa.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See &lt;a href="https://qsa.sh/how-it-works" rel="noopener noreferrer"&gt;how it works&lt;/a&gt;, what we &lt;a href="https://qsa.sh/privacy" rel="noopener noreferrer"&gt;do and don't retain&lt;/a&gt;, or what the &lt;a href="https://qsa.sh/pricing" rel="noopener noreferrer"&gt;paid tiers&lt;/a&gt; add.&lt;/p&gt;

</description>
      <category>curl</category>
      <category>bash</category>
      <category>shell</category>
      <category>security</category>
    </item>
    <item>
      <title>OAuth Consent Phishing: The Attack a Password Change Does Not Fix</title>
      <dc:creator>Daniel J</dc:creator>
      <pubDate>Thu, 03 Sep 2026 08:07:27 +0000</pubDate>
      <link>https://dev.to/tuxxin/oauth-consent-phishing-the-attack-a-password-change-does-not-fix-1dle</link>
      <guid>https://dev.to/tuxxin/oauth-consent-phishing-the-attack-a-password-change-does-not-fix-1dle</guid>
      <description>&lt;p&gt;The FBI's Internet Crime Complaint Center published &lt;a href="https://www.ic3.gov/PSA/2026/PSA260901" rel="noopener noreferrer"&gt;PSA I-090126&lt;/a&gt; on September 1, 2026, and it describes an attack that quietly defeats the two things most people rely on: changing your password, and having multi-factor authentication turned on.&lt;/p&gt;

&lt;p&gt;Neither one helps here. That is not a flaw in either — it is that this attack never touches your password at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  What consent phishing actually is
&lt;/h2&gt;

&lt;p&gt;You have almost certainly done the legitimate version of this. You sign into some service with “Continue with Google,” a permission screen appears listing what the app wants — read your email, see your files, post on your behalf — and you click &lt;strong&gt;Allow&lt;/strong&gt;. That grant is an OAuth token. It is how a scheduling tool reads your calendar without ever knowing your password.&lt;/p&gt;

&lt;p&gt;Consent phishing is the same flow with a hostile app at the end of it. The attacker registers a real application with a real provider, requests broad permissions, and sends you a link. You land on a genuine login page — the actual Google or Microsoft one, correct domain, valid certificate — and authenticate normally. Then the permission screen appears, and if you approve it, the attacker holds a token that reads your mail and files whenever it likes.&lt;/p&gt;

&lt;p&gt;The IC3 is blunt about the consequence. The access &lt;em&gt;“can only be revoked by the victim invalidating the token in their application security settings; not by changing the password.”&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why your usual response does nothing
&lt;/h2&gt;

&lt;p&gt;Think about what you would do if you suspected an account compromise. Change the password. Check MFA is on. Sign out other sessions, maybe.&lt;/p&gt;

&lt;p&gt;None of that revokes an OAuth grant.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Changing your password&lt;/strong&gt; does not invalidate the token. The token was never derived from your password.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MFA does not stop it.&lt;/strong&gt; You completed MFA yourself, correctly, on the real login page. The attacker never needed to defeat it — you satisfied it and then handed over a key on the next screen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There is no suspicious login to spot.&lt;/strong&gt; The app is calling an API with a valid token, not signing in from a strange country. Your login history looks clean.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nothing expires on a useful timescale.&lt;/strong&gt; A refresh token can stay valid for months or longer, renewing itself quietly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is the one worth sitting with. This is not a break-in you notice and clean up. It is a key you handed out, filed under a permission screen you closed and forgot — genuinely out of sight, out of mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is being targeted
&lt;/h2&gt;

&lt;p&gt;The PSA names &lt;strong&gt;prominent individuals, their family members, and personal acquaintances&lt;/strong&gt;, approached through ordinary commercial messaging apps rather than email. The pretexts impersonate government officials, media figures, and event coordinators, and the ask is mundane: review this document, verify your identity, confirm attendance.&lt;/p&gt;

&lt;p&gt;Family and acquaintances are in that list deliberately. If you are hard to reach directly, the people around you are the way in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go audit your grants — this is the useful part
&lt;/h2&gt;

&lt;p&gt;Whether or not you have been targeted, this is worth twenty minutes. Most people have not looked at these lists in years, and find things they no longer recognize: a trial from four jobs ago, a plugin that asked for far more than it needed, a “free analyzer” that still reads everything.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google&lt;/strong&gt; — &lt;a href="https://myaccount.google.com/connections" rel="noopener noreferrer"&gt;myaccount.google.com/connections&lt;/a&gt;. Sort by access level. Anything holding Gmail or Drive read access deserves a hard second look.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microsoft / Entra&lt;/strong&gt; — &lt;a href="https://myapps.microsoft.com" rel="noopener noreferrer"&gt;myapps.microsoft.com&lt;/a&gt;, then &lt;em&gt;My Account → Privacy → Apps and services&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn&lt;/strong&gt; — Settings → Data Privacy → &lt;em&gt;Permitted services&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt; — &lt;a href="https://github.com/settings/applications" rel="noopener noreferrer"&gt;Authorized OAuth Apps&lt;/a&gt;, and check &lt;em&gt;Installed GitHub Apps&lt;/em&gt; separately — they are two different lists and people routinely check only one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dropbox, Slack, Zoom, Meta&lt;/strong&gt; — all keep an equivalent connected-apps page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Revoke anything you do not actively use. Revocation is instant and free; if you break something you still need, you reauthorize it in a click. That asymmetry is the whole argument for being aggressive here.&lt;/p&gt;

&lt;h2&gt;
  
  
  What good scrutiny looks like
&lt;/h2&gt;

&lt;p&gt;The moment that matters is the permission screen, and it is over in about two seconds. Slow it down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read the scopes, not the app name.&lt;/strong&gt; The name is chosen by whoever registered the app and can be anything. The permissions are the truth. A PDF viewer does not need to send mail as you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask why this needs an account at all.&lt;/strong&gt; Reviewing a document rarely requires read/write access to your entire mailbox.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify the sender out of band.&lt;/strong&gt; Not by replying to the message — through a number or address you already had.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat urgency as the tell.&lt;/strong&gt; A deadline attached to a permission request is doing a job.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  If you think you clicked Allow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Revoke the grant first&lt;/strong&gt;, on the provider's connected-apps page. Do this before anything else — until the token is gone, the access continues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Then&lt;/strong&gt; change the password and re-check MFA. In that order; the reverse accomplishes nothing on its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check mailbox rules and forwarding.&lt;/strong&gt; A common follow-up is a quiet auto-forward or a rule that files security alerts straight into the trash.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review sent mail.&lt;/strong&gt; Access is frequently used to phish your contacts from your real address.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Report it&lt;/strong&gt; — your local FBI field office, or &lt;a href="https://www.ic3.gov" rel="noopener noreferrer"&gt;ic3.gov&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The honest tension
&lt;/h2&gt;

&lt;p&gt;Worth saying plainly: OAuth consent is not the problem. It is a genuine improvement over the thing it replaced, which was typing your actual password into third-party sites and hoping. Every grant that reads your calendar without knowing your password is that design working.&lt;/p&gt;

&lt;p&gt;The weakness is that one screen carries an enormous amount of meaning and gets about two seconds of attention, and that what it hands over is durable in a way people do not expect. A password is something you can change. A consent grant is something you have to &lt;em&gt;remember you gave&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So put it on the calendar. Once or twice a year, open those pages and prune. It takes twenty minutes and it is the only maintenance that actually closes this class of access.&lt;/p&gt;

</description>
      <category>security</category>
      <category>oauth</category>
      <category>phishing</category>
      <category>fbi</category>
    </item>
  </channel>
</rss>
