The Counter in the Fragment: Fingerprinting a Reward-Phishing Kit
The Counter in the Fragment: Fingerprinting a Reward-Phishing Kit
Between March and May 2026, one operator ran Costco, Prime Video and UnitedHealthcare reward lures from 60 coined domains onto Amazon S3. Thousands of messages went out before it went quiet. The landing pages arrived in two shapes that looked like separate operations: verbose buckets that spelled the lure out in the hostname, and single-use buckets named with ten random letters. Nothing in the sender addresses tied them together, and most apexes sent once or twice before going dark. The link was in the part of the URL that never reaches a server. Every lander URL carried a fragment, and inside that fragment was a backend send counter that advanced across both rails in strict time order, which collapsed two apparent operators into one.
Key Takeaways
- One operator ran two visually distinct S3 lander rails; a sequential counter embedded in the URL fragment ties them to a single sending backend.
- Per-recipient routing lives entirely after the
#, so the stored S3 object is a brand-static page with no victim data and no destination in it. - Sender apexes came from two acquisition strategies at once: a stash bought 6 to 7 months ahead, and register-and-burn domains minted 2 days before use.
- Message bodies pad the lure with text scraped verbatim from public government and municipal forms, drawn from 38 identifiable source documents.
- Every send passed SPF, DKIM and DMARC, because the operator owned the sending domains and published its own records.
Background
Reward and giveaway phishing is a commodity lure family. Costco publishes a running list of survey-and-reward scams that abuse its brand. The pretext is not what makes an operator worth mapping. The plumbing underneath it is.
This operator's plumbing rests on Amazon S3. Any S3 bucket configured for public read is reachable at <bucket>[.]s3[.]<region>[.]amazonaws[.]com and will serve HTML, CSS and JavaScript straight to a browser. That gives an attacker a static web host with nothing to configure and no domain to buy. The URL is under the amazonaws[.]com parent domain behind AWS's own valid TLS, so a reputation engine or TLS-inspecting proxy sees a mature, correctly certified host rather than a fresh phishing domain. Bucket creation is free and scriptable, which makes burn-and-rotate cheap. The technique class is mature and vendor-acknowledged across all three major clouds: Cofense has documented AWS abuse for credential theft, Microsoft has published on Azure Blob Storage attack chains, and CYFIRMA has covered the cloud-native phishing pattern generally, noting that provider wildcard certificates leave web proxies with nothing actionable.
The other mechanic is the URL fragment. Everything after the # is the fragment identifier, and per RFC 3986 the browser strips it before issuing the HTTP request. It never reaches the origin, and it never lands in a web-server, CDN, proxy or S3 access log. Page JavaScript reads it through location.hash. A kit can therefore carry per-recipient state in the fragment and act on it entirely client-side. The primitive has public precedent: Heimdal Security reported a phishing kit using URI fragmentation against US and Canadian users, where JavaScript reconstructed the redirect target from an anchor and token.
Discovery and Infrastructure
We first mapped this operator through its most legible artifact. A run of S3 buckets spelled the lure into the hostname, in strings like wwwcostcoyetibeachwagongiveawaycostcofgeh and wwwcostcobeachloungewagonyetigfgrg. Bucket names are globally unique and appear verbatim in the URL, so a keyword sweep for a brand pair returns them cleanly. That first pass produced a coherent operator: coined sender apexes, two MTA pools, a giveaway pretext.
It also produced an incomplete one. A later sweep keyed on the sender-address shape rather than the bucket name surfaced a second rail of equal size that the bucket sweep could not see, because its buckets carried no keywords at all. Those buckets were ten random lowercase letters, one per send, discarded afterward.
Two rails, no shared hostname, no shared apex, nothing in the sender addresses to connect them. What connected them was the fragment.
| Indicator | Role | Notes |
|---|---|---|
<bucket>[.]s3[.]us-east-1[.]amazonaws[.]com |
Lander host | Primary region, 21 single-use random buckets plus verbose brand buckets |
<bucket>[.]s3[.]eu-north-1[.]amazonaws[.]com |
Lander host | Secondary region, verbose buckets only |
rozalevo[.]shop, zoltriq[.]shop, fagk[.]shop |
Sender apex | Coined, throwaway, WHOIS privacy |
healthyecigs[.]com, calystia[.]com |
Sender apex | Service rail, plausible-business English labels |
mdicare[.]org |
Sender apex | Deliberate misspelling of a government program name |
A compromised legitimate UK business site also appeared once as a landing host. We have withheld its name: it is a victim of the campaign, not a participant, and publishing it would push takedown pressure onto the wrong party.
How It Works
A recipient gets a message whose display name carries a retail brand and whose subject often carries no brand at all. The body opens with a twelve-character random token, states one short imperative about completing a submission, and embeds a link to an S3 object. Everything after that is padding.
Clicking loads a static brand page from the bucket. The page reads the fragment from the address bar, decodes the positional token string inside it, and redirects onward. Because that decode happens in the browser, the object sitting in the bucket is the same bytes for every recipient. An analyst who downloads it in isolation sees an inert brand page with no victim identifier and no next hop.
Alongside Costco, Prime Video and UnitedHealthcare, the operator also ran T-Mobile and Walmart reward variants on the same infrastructure.
Sample Lures
All samples below are redacted. Recipient identifiers have been removed and every attacker URL is defanged. The trailing text in each sample is scraped verbatim from unrelated public documents, and the organizations that published those documents have no connection to this campaign.
Costco giveaway pretext, verbose bucket, benign subject line:
From: "Costco Member Surprise Reward" <jamie.franklin@pdlnto[.]rozalevo[.]shop>
Subject: We'd love to have you back
jvk4gj0oyebv Please respond soon - your rewards expire in the coming days
(hxxps[:]//wwwcostcoyetiupdaterewardszatydu[.]s3[.]us-east-1[.]amazonaws[.]com/
mycbrggbnu[.]html#4btZmE12351569QgHQ1200jmsvviscbt2368PRQPMLIDUJGWVET53175MSKW8647984k21)
Please print clearly For office only LICENCE # [scraped public form text continues]
Amazon Prime Video subscription-cancellation pretext, single-use random bucket:
From: "Prime Video Membership" <stephanie.lindsey2@fagk[.]shop>
Subject: Your Prime Stream service has been discontinued
2sozog43tuaf Please authenticate the information included in your submission
(hxxps[:]//hfrfrzmxgs[.]s3[.]us-east-1[.]amazonaws[.]com/vdjqozazws[.]html
#4xUmXP12351742iOrL1204owwwdcneqg1088VYKGXOLPXCXWNLU756337FRPL8860131N32)
555 Wright Way Carson City, NV 89711 [scraped public form text continues]
UnitedHealthcare benefit-reward pretext, verbose bucket in the secondary region:
From: "UnitedHealthcare Preventive Care Reward" <kevin.simmons@cloudver[.]online>
Subject: Final notice: Don't forget to collect your rewards
9vfr61dhnvhs Please review your submission! your request is still awaiting completion
(hxxps[:]//wwwpralbdentalkitconfirmationdayezgrhe[.]s3[.]eu-north-1[.]amazonaws[.]com/
wwworalsfgergrgegergrrhthtfezezg[.]html#4LOXyM27gTCw2ddjzcrpyor2WZUATJXLYLUHYVJ31665PCKL14937D9)
City of Madera - Pl[anning Department, scraped public form text continues]
The dental-kit wave uses a shorter fragment sub-format than the grammar described below, which may indicate a second offer feed. Note also the split visible in all three: the brand lives in the display name and in the lander, never in the subject. Two of the three subjects would pass a brand-keyword sweep untouched.
Technical Analysis
Two Bucket Rails, One Backend
The operator ran 37 distinct buckets serving 37 distinct objects. They fall into three classes:
| Bucket class | Buckets | Reuse | Object naming |
|---|---|---|---|
| Random 10 lowercase letters | 21 | One send each, never reused | Random 10-letter .html throughout |
Verbose www plus lure text |
14 | 1 to 5 senders each | Random 10-letter on 8, verbose on 6 |
| Opaque alphanumeric | 2 | One send each | Random 10-letter .html |
Read as separate inventories, the random rail looks like throwaway infrastructure and the verbose rail looks like a branded campaign. They are the same kit. The verbose bucket names terminate in a distinctive keyboard-mash alphabet drawn from e, f, g, h, r, z and t: ...costcofgeh, ...yetigfgrg, ...dayezgrhe, ...closingergeh. That same mash alphabet generates a set of object names outright, in strings like cezfgghthcvehthrgzefzefzgfrgerehtht[.]html and ggerhvdfvzefzefgerzedfzefzgrgr[.]html. Bucket minting and object minting run off one generator.
The Fragment Grammar and the Send Counter
The dominant fragment follows a fixed positional grammar:
#4 <5 mixed-case> <send counter, 6-8 digits> <4 mixed-case> <offer id, 4 digits>
<10 lowercase> <4 digits> <15 uppercase> <5-7 digits> <4 uppercase> <7 digits> <2-3 char tail>
Decomposing one send from 2026-05-31:
#4 xUmXP 12351742 iOrL 1204 owwwdcneqg 1088 VYKGXOLPXCXWNLU 756337 FRPL 8860131 N32
The second field is a backend send counter, and it advances monotonically with time across both bucket rails:
| Date | Sender | Bucket rail | Counter | Offer |
|---|---|---|---|---|
| 05-08 | oakjldsk[.]kavalonix[.]shop |
random | 189867 | 1198 |
| 05-08 | uotzak[.]hbrt[.]site |
random | 189882 | 1198 |
| 05-11 | zormax[.]awya[.]site |
random | 12351566 | 1200 |
| 05-11 | pdlnto[.]rozalevo[.]shop |
verbose (Costco/YETI) | 12351569 | 1200 |
| 05-11 | urappd[.]mrid[.]site |
random | 12351576 | 1200 |
| 05-19 | zoltriq[.]shop |
verbose (Costco/YETI) | 12351675 | 1200 |
| 05-20 | hpeqfi[.]<withheld>[.]pro |
random | 12351676 | 1200 |
| 05-31 | fagk[.]shop |
random | 12351742 | 1204 |
A verbose-bucket send falls three counter ticks between two random-bucket sends on the same day, and counter 12351675 is followed immediately by 12351676 on the other rail. Two independent operators do not produce an interleaved sequence. The counter space was renumbered from a six-digit range to an eight-digit one between 2026-05-08 and 2026-05-11, which reads as a backend migration mid-campaign. The offer field held 1194 and 1198 in the early cohort, then a near-constant 1200, then 1204 at the end, consistent with a single affiliate offer rotated occasionally.
Two sends on 2026-05-19 carry the identical counter and the identical object name, so one generated lander URL was blasted from several burner senders at once.
A rarer path-style fragment variant appeared on a handful of sends, using &-delimited fields and a trailing .home.php query string rather than the positional grammar.
Registration Cohorts: Stash and Burn
Of the 60 apexes, 29 carry a retrievable WHOIS record. Namecheap accounts for 26 and Spaceship for 3. Seventeen use the privacy service Withheld for Privacy ehf. The creation dates cluster hard:
| Cohort | Created | Apexes | Lag to first send | Class |
|---|---|---|---|---|
| Aged single | 2024-07-24 | 1 | 643 days | Stash |
| Autumn stash | 2025-09-22 to 2025-10-30 | 18 | 175 to 223 days | Stash |
| Service arm A | 2026-02-28 to 2026-03-01 | 6 | 56 to 58 days | Stash |
| Service arm B | 2026-03-16 | 2 | 43 days | Stash |
| Register-and-burn | 2026-05-13, 2026-05-19 | 2 | 2 days | Burn |
The distribution is strictly bimodal. Twenty-seven apexes were bought between 43 and 643 days before use; two were bought 2 days before use; nothing falls in the 3-to-42-day band. Stashing defeats the newly-registered-domain penalties that many reputation and filtering rules apply inside a 7, 14 or 30-day window. Register-and-burn takes the opposite bet: get one wave out before any feed catches up. Running both suggests a maintained reserve pool topped up with same-week throwaways when a wave needed a clean apex, and both burn domains appear in the campaign's final week.
Subdomain Grammar and Domain Generation
Apex labels come in two styles. One is 4 to 5 characters of consonant-dense nonsense (fagk, dsfi, hbrt, chfr, vsds, dbrw, mrid, awya). The other is coined pronounceable brandables of 6 to 10 characters (kavalonix, qarnatrixa, sorvynex, nexvotra, rozalevo alongside rozalos, cleartrust alongside quicktrust). The service rail instead used plausible-business English, in labels like healthyecigs and healthquestfitnesscenter, plus one deliberate misspelling of a government program name.
Sending FQDNs put a random label in front of the apex. Across the throwaway-rail traffic we counted 54 distinct labels:
| Label length | Frequency | Template | Examples |
|---|---|---|---|
| 3 to 4 | Occasional | Service word | dep, prod |
| 6 | Dominant | Random [a-z]{6} |
pxatjk, ezolox, hpeqfi, bmhydf |
| 7 | Common | Random [a-z]{7} and service words |
pkaprpz, qvzlptm, notices |
| 8 | Common | Random [a-z]{8} and service words |
sahiixrw, oakjldsk, bouncest |
| 9 | Occasional | Random [a-z]{9} |
ixwvgardu, prjcuzkcj |
| 11 | Occasional | Brand word plus digit, or alphanumeric | nerdwallet1, 20mgase5hdi |
| 13 | Occasional | Literal support plus [a-z]{6} |
supportdornik, supportvelqax |
The alphabet is lowercase only, with a single alphanumeric exception. Random label length drifts upward across the campaign, from 6 and 7 characters in late April to 8 and 9 from 2026-05-08. The support plus six-character template runs as one same-day block. The TLD mix shifts just as sharply: every .com and .org apex sends inside a single week and never again, after which the footprint is entirely cheap TLDs, with .shop alone carrying 25 of the 60 apexes.
Two apexes used generic English compound labels that collide with the names of unrelated real businesses. We verified in both cases that the genuine companies trade on different domains and have no connection to this campaign, and we have withheld those two labels throughout this post, because a reader blocking on the word rather than the domain would hit the wrong party.
Scraped Public-Records Filler
The most distinctive content behavior is below the fold. Every throwaway-rail body follows a three-part template: a twelve-character random token, one short imperative sentence carrying the link, then a long run of text lifted verbatim from an unrelated public document.
The leading token appears on every throwaway-rail send and on none of the service-rail sends. It is exactly twelve characters over a lowercase alphanumeric alphabet, every value distinct, and nearly all contain at least one digit, which matches a uniform random draw over 36 symbols rather than a word list. Values run like 2sozog43tuaf, jvk4gj0oyebv, zfi99aimemhu.
The filler comes from 38 identifiable source documents, overwhelmingly public-sector paperwork. Municipal business-licensing and permit forms dominate, roughly 20 of them, including building-and-safety applications, home-occupation bylaw forms and business tax receipts from cities across the US and Canada. Another nine are state, federal or national government documents: a US state motor-vehicle registration form, an Australian identity declaration form, a US workplace-safety citation, a state alcohol-licensing form. Two are legal filings, including a supreme court petition caption and a flood-protection board enforcement agenda. Around six are scraped website navigation chrome from school, university and public-agency sites, arriving as runs of skip-to-content links.
To be unambiguous: those organizations are not connected to this campaign in any way. Their published documents were scraped for text because that text is free, plentiful, grammatical and utterly unlike scam copy.
The rotation budget is revealing. The filler is per-wave, not per-message. Every send sharing a bucket on a given day carries byte-identical filler, in blocks of two to five sends, while the twelve-character token changes on every single message. The operator spends its randomness where per-message uniqueness matters and reuses the expensive-to-source component.
The CTA sentences rotate too: 37 distinct variants, all short, all imperative, built from a submission-and-confirmation vocabulary. Only 3 of the 37 name a brand. The rest read like Please finalize your submission before deadline or One more step: complete your submission verification, which is the same brand-splitting discipline visible in the subject lines.
Detection Observations
The signal in this campaign is structural rather than lexical, and it is in combinations rather than in any single field.
Brand impersonation is split across fields deliberately. The display name carries the retailer, the lander carries the retailer, and the subject frequently carries nothing at all, in strings like Great seeing you again or Your receipt is ready. A sweep keyed on subject-line brand tokens sees a fraction of this operator. Pairing display-name brand tokens with subject-line neutrality is a stronger signal than either alone. The display names themselves break brand words with punctuation, in forms like Costco-Member.SurpriseReward and Prime-Video Support-Center, so exact-match brand regexes need punctuation-tolerant variants.
Email authentication is inverted here as a trust signal. Every send passed SPF, DKIM and DMARC with correct alignment, which costs an operator who owns the apex nothing beyond publishing a few DNS records. An authentication pass tells you the mail is not spoofing a third party; it says nothing about whether the domain deserves trust. The useful pairing is a perfect authentication result against a domain with no sending history and no business reason to exist.
The most rotation-resistant artifact is the fragment grammar. Bucket names, subjects, display names, apexes and TLDs all rotated during the campaign. The positional token structure after the #, and the counter inside it, did not. A pattern keyed on machine-emitted structure survives rotations that a pattern keyed on human-authored content cannot, and the counter additionally supports clustering across infrastructure that shares nothing else.
Body composition offers a further angle. A message whose visible content is one imperative sentence and a link, followed by several hundred characters of municipal permit-application text, is structurally unlike either legitimate retail mail or ordinary bulk marketing. Where the filler is reused across a wave, the shared block is itself a cluster key.
Finally, cloud-object-storage landers deserve treatment as a category. The bucket host is high-reputation infrastructure that cannot be penalized at the apex, so the actionable unit is the bucket and the object path, not the domain.
Indicators of Compromise
All indicators are defanged. Victim data has been removed. The lists below are a representative subset of the verified export set, not the full inventory.
Senders
| Value | Role | Notes |
|---|---|---|
no-reply@dep[.]healthyecigs[.]com |
Sender | Service rail |
no-reply@prod[.]midhundevasia[.]com |
Sender | Service rail |
no-reply@nerdwallet1[.]calystia[.]com |
Sender | Sub-label impersonates a finance brand |
noreply@swedelk[.]com |
Sender | Service rail, bare apex |
no-reply@product[.]healthlifecoverage[.]com |
Sender | Service rail |
no-reply@mailer[.]healthquestfitnesscenter[.]com |
Sender | Service rail |
no-reply@xpressus[.]healthyfamilytips[.]com |
Sender | Service rail |
support@notices[.]ccmlo[.]org |
Sender | Service rail |
no-reply@bouncest[.]mdicare[.]org |
Sender | Coined misspelling of a government program |
jamie.franklin@pdlnto[.]rozalevo[.]shop |
Sender | Throwaway rail, counter 12351569 |
heather.ward2@pxatjk[.]sudh[.]site |
Sender | Throwaway rail, secondary region wave |
victor.moreno@zoltriq[.]shop |
Sender | Throwaway rail, counter 12351675 |
stacy.oconnor@qarnatrixa[.]shop |
Sender | Throwaway rail, shared lander URL |
michelle.martinez@quicktrust[.]shop |
Sender | Throwaway rail |
john.scott@cleartrust[.]shop |
Sender | Throwaway rail |
| … | representative subset; 60+ malicious senders in the full set |
Domains
| Value | Role | Notes |
|---|---|---|
healthyecigs[.]com |
Sender apex | Namecheap, created 2026-03-01 |
midhundevasia[.]com |
Sender apex | Namecheap, created 2026-03-01 |
calystia[.]com |
Sender apex | Namecheap, created 2026-03-16 |
swedelk[.]com |
Sender apex | Namecheap, created 2025-10-24 |
healthlifecoverage[.]com |
Sender apex | Namecheap, created 2026-03-01 |
healthquestfitnesscenter[.]com |
Sender apex | Namecheap, created 2026-03-01 |
healthyfamilytips[.]com |
Sender apex | Namecheap, created 2026-03-01 |
ccmlo[.]org |
Sender apex | Namecheap, created 2026-03-16 |
mdicare[.]org |
Sender apex | Namecheap, created 2026-02-28 |
rozalevo[.]shop |
Sender apex | Throwaway rail |
sudh[.]site |
Sender apex | Throwaway rail |
zoltriq[.]shop |
Sender apex | Throwaway rail |
qarnatrixa[.]shop |
Sender apex | Throwaway rail |
quicktrust[.]shop |
Sender apex | Throwaway rail |
cleartrust[.]shop |
Sender apex | Throwaway rail, sibling of quicktrust[.]shop |
| … | representative subset; 60+ malicious sender domains in the full set |
Amazon S3 bucket hosts are deliberately excluded from the domain table. amazonaws[.]com is shared hosting infrastructure and must never be blocked at the apex; the actionable unit is the individual bucket and object path.
Conclusion
This operator stopped sending on 2026-05-31, but the stash cohort is the thing to watch. Domains bought 200 days before use imply a reserve that outlives any single wave, and the two-day burn domains show the operator will top that reserve up on demand. If the same backend resumes, the counter should pick up near where it stopped, which makes it a continuity test as well as a clustering key. When an operator rotates every human-authored field, the machine-generated ones are what stay constant, and they are usually the fields nobody thought to look at.
Related research
The Supplement SMS Kit That Borrows Real Business Identities
Since April 2026, one operation has run 26 supplement storefront domains by text message, most carrying the legal identity of a real registered business.
How a Government-Impersonation Smishing Kit Signs Its Own Registration Batches
A smishing kit impersonating toll agencies, tax offices and national police stamped the registration date into its own WHOIS records.
One Lure, Four Operators: Ad Tracking Strings as Fingerprints
Four separate operators ran the same fake TradingView desktop-app ads on Facebook for nine months, and one query-string key was all that told them apart.