blog.ganska.latRSS

A Revolut smishing campaign, from SMS to live account takeover

Revolut smishing campaign: infrastructure, cloaking and a live-relay phishing kit

A browsable version of this report, split by section with every cited evidence record one click away, is at /revolut-dossier/.

Prepared 2026-09-30 from evidence collected 2026-09-29 and 2026-09-30.

Intended readers: Swedish police, CERT-SE and Revolut’s security team.

1. Summary

A single SMS impersonating Revolut reached a Swedish mobile subscriber on 2026-09-29 at 09:38:31Z, from the alphanumeric sender ID Revolut [OBSERVED], which is not proof of origin, since alphanumeric sender IDs are set by the sending gateway [INFERRED]. The message warned that identity verification was required to avoid the account being locked. [OBSERVED] (2.1) The link in it, https://revolut.com.3984.app/?TRJ1, is not a Revolut domain: revolut.com is a subdomain label inside the unrelated registration 3984.app, registered about six hours before the SMS and fronted, about 80 minutes after registration, by a Let’s Encrypt certificate. [OBSERVED] (2.1, 4.1, 4.3)

A mobile User-Agent on the Swedish mobile network that received the SMS was served a single XOR-obfuscated phishing kit; the same handset, minutes later with a desktop User-Agent, was refused with a 9-byte “Not Found” body. [OBSERVED] (5.1) A separate 28-cell Tor probe grid was refused in every cell by the DDoS-Guard front end in front of the landing host; a healthy control request showed the refusal was DDoS-Guard’s own IP filter, not a dead circuit, and shows nothing about what the kit’s own gate would do to a Tor exit. [OBSERVED], [INFERRED] (5.2, 5.3)

The kit walks a visitor through a phone number, an hCaptcha challenge, a Revolut passcode, a one-time code and a selfie-video liveness check, then a review-poll screen; a static reading of the decoded kit found no local pass/fail logic, and the most likely reading is a human operator relaying the same data into the real Revolut app in real time, an inference from the kit’s design, not a direct observation. [INFERRED] (3.1, 3.3, 6.3) The kit hosts a face-quality model byte-identical to Revolut’s own production model, and embeds an hCaptcha site key whose configuration has a custom theme enabled. [OBSERVED] (6.2, 6.4) On 2026-09-30, against the investigator’s advice and outside the investigation’s own rules, the recipient entered fabricated data in three sessions; the kit advanced past an invalid phone number in both sessions that used +46 22222 and rejected every fabricated passcode entered, one-off observations, not general behavior. [OBSERVED], [REPORTED], [INFERRED] (3.4, 6.6)

Fifty domain entries, 25 sibling subdomains plus their bare parents under the naming scheme revolut.com.<digits>.<app|com>, are confirmed campaign infrastructure by DNS resolution, sharing the 186.2.175.0/24 hosting range, AS59692, and ns1/ns2.ddos-guard.net nameservers; a further 16 names matching the pattern are unconfirmed leads only. [OBSERVED] (4.2, 7.1, 7.2) An earlier hosting range, 185.178.208.x (AS57724), is associated only with unconfirmed leads. urlscan shows it in use from 2026-07-19 to at least 2026-09-08, overlapping with the current range, which confirmed domains used from 2026-08-22. [OBSERVED] (7.3) The earliest evidence tied to confirmed infrastructure is 2026-08-22 [OBSERVED] (4.5), so the campaign has been active since at least that day [INFERRED]. By 2026-09-30, Quad9’s filtered resolver had begun answering NXDOMAIN for the lure domain while Cloudflare and Google still resolved it, an early, partial blocklisting signal collected off the project’s standard Tor pipeline and disclosed as a deviation. [OBSERVED] (8.1, 8.2) The handset captures and a 2026-09-30 Waydroid/mitmproxy capture were also made off Tor, and are disclosed with the method. [OBSERVED] (11.1)

No indicator, IP, ASN, nameserver, registrar, certificate, kit file, kit endpoint or SMS sender, is shared with the separate EasyPark smishing case; the similarities that exist (Swedish-targeted SMS, a spoofed sender, a brand placed as a subdomain of a throwaway domain, User-Agent gating, Let’s Encrypt certificates) are generic to unrelated phishing operations and are not evidence of a shared operator. [OBSERVED], [INFERRED] (9)

Not established: RDAP registration for 18 confirmed .app parents and 6 hosting IPs, persistent after a retry; certificate records for 14 confirmed parents; whether the SMS link’s TRJ1 token is per-recipient or campaign-wide; and the identity, location or infrastructure of any operator. [INFERRED] (12.1, 12.2, 12.3a, 12.6, 12.7) Recommended recipients and actions, traced to the facts above, are set out in section 14: Swedish police, CERT-SE, the four recorded domain registrars, the 186.2.175.0/24 netblock’s abuse contact, the .app registry, DDoS-Guard and Revolut’s own security team.

How to read this report

Every factual sentence carries one of three evidence labels. [OBSERVED] marks a claim backed by an envelope, artifact or capture cited by path or hash. [REPORTED] marks a statement made by the SMS recipient, or in one place (11.4) a note from the investigation log, with no artifact behind it. [INFERRED] marks a conclusion reached by reasoning over other labelled claims, with that reasoning stated alongside it. A claim with no label does not appear in this report.

A cryptographic hash is given in full, 64 hexadecimal characters, the first time it is cited in a section; later citations of the same hash in that section may shorten it to its first 12 hex characters followed by .... Every hash in this report is checked against evidence/MANIFEST.sha256.

2. The lure

2.1 The message

The investigation’s recipient held an SMS from sender ID “Revolut” with the following body, read verbatim from the phone’s inbox record. [OBSERVED] (evidence/raw/sms/20260929T144242Z-Revolut.json, payload_sha256 ba18e44bc5454abd88332f634ce21a109a2c8590a3daede8fcb3d9e9b2e80e2d)

Verifiering av identitet kr{vs: https://revolut.com.3984.app/?TRJ1 Uppdatera
dina uppgifter for att undvika att ditt konto låses.

English gloss: “Identity verification is required: [link] Update your details to avoid your account being locked.” The record is the exact raw_output field of a query made directly against the device (adb shell content query), and it supersedes an earlier envelope that carried only an estimated collection time; the earlier record was kept, not deleted, per the evidence store’s append-only rule. [OBSERVED] (same envelope, supersedes field referencing evidence/raw/sms/20260929T142900Z-Revolut.json)

The message’s internal date_ms field, 1790674711322, converts to 2026-09-29T09:38:31.322Z, which is the actual receipt time. [OBSERVED] (evidence/raw/sms/20260929T142900Z-Revolut.json, date_ms field, payload_sha256 6c05f414819886c2f8953d96da6a53aa65a30aebe4a773a7915a25b2e6ce5aa6) The conversion was independently repeated (1790674711322 / 1000 as a Unix timestamp) and matches. [OBSERVED]

One character in the body is anomalous: kr{vs, where Swedish has krävs (“is required”). In the GSM 03.38 default alphabet ä is code 0x7B, the code of { in ASCII, so the most likely explanation is that the GSM 7-bit text was read as ASCII somewhere between the sender and the inbox, not a typing error by the sender. [INFERRED] The body also renders för as for. [OBSERVED] (same envelope) No artifact in this evidence store proves the encoding pipeline that produced the substitution; the claim rests on the observed character and the known shape of the GSM alphabet, not on a decoded PDU.

Two further facts about the sender and link require the same care:

2.2 The TRJ1 tag

The query-string fragment ?TRJ1 in the SMS link is not inert. The landing server echoes it back as a cookie, _tag=TRJ1, with a two-hour lifetime and the HttpOnly flag, on the very first response served to the recipient’s phone. [OBSERVED] (evidence/raw/cloak-phone/20260929T143943Z-https___revolut.com.3984.app__TRJ1.json, headers_text field: Set-Cookie: _tag=TRJ1; Path=/; Expires=...; Max-Age=7200; HttpOnly) The same cookie, with the same value, reappears the next day in the HAR capture of a Waydroid session against the same URL. [OBSERVED] (evidence/raw/har/20260930T092422Z-*)

Whether TRJ1 identifies this particular recipient, this particular send batch, or is a fixed value shared by every message in the campaign is not established. Only one recipient’s SMS was captured in this investigation, so there is nothing to compare TRJ1 against. [INFERRED] This is recorded as an acknowledged evidence gap, not a finding about how the campaign tracks its targets.

2.3 Visits to the landing URL

The recipient (and, once informed, the investigation) returned to the landing URL several times across 2026-09-29 and 2026-09-30, using different devices, networks and DNS paths. [OBSERVED] (evidence/raw/cloak-phone/*.json, evidence/raw/reporter/*.json, evidence/raw/screenshot/20260930T082443Z-*.json) The individual visits, what each one returned, and what that shows about the gating logic in front of the kit, are laid out in section 5. In outline:

The 2026-09-30 DNS failure over the VPN path is explained in section 8 as a resolver-side blocklist effect, not a change on the operators’ side: the domain kept resolving and kept serving the kit to an unfiltered resolver and network at the same time. [INFERRED]

3. How the operation works, step by step

This section follows the flow a victim who clicks the link would experience, then states what data the kit takes and where it goes, and explains why the strongest claim about the backend, that a human relays the session into the real Revolut app in real time, is an inference rather than a direct observation.

3.1 What the victim sees

A mobile User-Agent on an accepted network is served a single HTML file, 458553 bytes, obfuscated with a repeating XOR key (key byte 31). [OBSERVED] (evidence/raw/kit/20260929T152304Z-https___revolut.com.3984.app__TRJ1.json, xor_key: 31, body_artifact 8b06133d2213db3361034c72b089b16ff432c76fef2e87dd1521f886cecbdc03, decoded_artifact 198a85ec74af744853b523b8469a480eae161131f451f1e4186632ed8a2c8a08) The decoded artifact was read statically for this investigation and never executed. [OBSERVED] Its page title, decoded, is “Inicio de sesion unico | Revolut”, Spanish for “Single sign-on”. [OBSERVED] (same envelope, indicators.title) This minor detail is included because it suggests the same build is reused across languages rather than written fresh per target. [INFERRED]

The page’s own strings are localized into eight languages: German, Greek, English, Spanish, Estonian, Italian, Portuguese and Swedish. [OBSERVED] (same envelope, indicators.locales) The client-side code calls two third-party geolocation services, api.country.is and ipapi.co. [OBSERVED] (same envelope, indicators.external_urls) This is consistent with the visitor’s own IP address choosing which of those eight languages is shown. [INFERRED]

For the face-capture step, the kit loads TensorFlow.js and TFLite (BlazeFace) for on-device face detection, mp4-muxer and webrtc-adapter for recording and muxing video from the phone’s camera, and bodymovin (Lottie) for the animated UI around it. [OBSERVED] (same envelope, indicators.libraries; js/tf.min.js and js/blazeface.min.js: evidence/raw/kit-resource/20260930T082134Z-, 20260930T082136Z-)

A visitor who reaches this page is asked to type their phone number, an hCaptcha challenge, their Revolut passcode, a one-time code, and finally to record a short selfie video for a liveness check; a final screen polls while the page waits. [INFERRED] (static reading of the decoded kit, not executed; the phone-number and passcode screens are also seen in the recipient’s screenshots, evidence/raw/reporter/20260930T082507Z-https___revolut.com.3984.app__TRJ1.json, [OBSERVED]) The named endpoints observed in the static kit are /auth/start, /auth/submit, /auth/send-otp, /auth/face/*, /auth/log and /verify/. [OBSERVED] (same envelope, indicators.endpoints, payload_sha256 7de6b8a29a6f62fbd4bf6df90b44b237567ba8ec71a256a76d90408d66ff7881) This report does not reproduce the request or response formats behind those endpoints, or any other detail of how the flow is built or defended; what follows is the outcome each step produces, not the mechanism.

3.2 Data taken and where it goes

A victim who completes the flow gives the operators a phone number, their Revolut passcode, a one-time code, and a selfie video. [INFERRED] (from the endpoint list in 3.1 and the decoded kit’s static logic, read but not executed) All of it is sent to the same scam domain the victim landed on, which sits behind the DDoS-Guard front described in section 4; nothing in this evidence shows a separate exfiltration destination.

Two further behaviors were read from the static kit and are also inferred, not observed in live traffic: the decoded artifact was never executed, and the one live browser session (11.1) did not reach those steps:

Both are stronger claims about the kit’s internal logic than the endpoint list itself, and both rest on reading code that was never run, rather than on a captured request; they are carried at the same INFERRED weight as the relay conclusion below, not as directly observed backend behavior.

3.3 The relay conclusion, and why it is inferred

The sequence of steps, phone number and hCaptcha, then passcode, then a one-time code, then a selfie-video liveness check, then a review-poll screen that holds the victim waiting, is the shape of a kit that does not decide pass or fail by itself. No local logic reachable from outside the kit determines whether an entered passcode or one-time code is accepted; the flow instead waits on the poll step for an answer. [INFERRED] (static reading of the decoded kit, not executed) The natural reading of that shape is that a human operator, sitting on the other end of the review-poll endpoint, is entering the same phone number, passcode, one-time code and liveness data into the real Revolut app as the victim types it, and the poll step is telling the victim’s browser what the real app’s response was. [INFERRED]

This is stated as an inference, not an observation, because no operator, no second device, and no connection to Revolut’s own service was captured anywhere in this evidence. The only thing observed is the kit’s own design: a poll step with no local pass/fail path. Everything about who or what answers that poll, on what schedule, from where, is outside what this evidence store holds. [INFERRED]

3.4 The recipient’s own fabricated-data entries

Outside the investigation’s own no-data-entry rule, and against the investigator’s advice, the recipient entered fabricated data into the kit in three sessions on 2026-09-30, always with fabricated values. These are the recipient’s own actions, not part of the investigation’s method, and are reported here only because they are observations the recipient chose to make and disclose.

First, a random phone number and a random PIN; the PIN was shown as wrong. [REPORTED] (evidence/raw/reporter/20260930T075302Z-https___revolut.com.3984.app__TRJ1.json) Second, at 08:19Z (10:19 CEST), the number +46 22222 only, which is not a valid Swedish subscriber number; the kit then showed its six-digit passcode screen, and nothing more was entered. [OBSERVED] (recipient’s screenshots, evidence/raw/reporter/20260930T082507Z-https___revolut.com.3984.app__TRJ1.json) Third, in the Waydroid session, +46 22222 and two fabricated passcodes; the number was accepted and both passcodes were rejected, while an alternative passkey sign-in option was also offered. [REPORTED] (evidence/raw/reporter/20260930T092422Z-https___revolut.com.3984.app__TRJ1.json)

Because +46 22222 is not a valid Swedish subscriber number, yet the kit advanced to the passcode screen in both sessions that used it, the backend does not appear to validate the phone number before proceeding. [INFERRED] This rests on two one-off observations from the recipient’s own fabricated entries; it is not a repeated test and does not establish the backend’s general behavior. [INFERRED] No claim in this report should be read as recommending or describing entering data into a live phishing kit as an investigative method; these entries are recorded as the recipient’s own choices, made outside the investigation’s rules, and reported for completeness only.

4. Infrastructure

This section lists the hosts, addresses, registrations, certificates and front-end fingerprint an abuse desk works from. All routine collection was made over Tor, with DNS resolved over HTTPS through the same proxy and registration data over RDAP; there is no whois fallback in this investigation’s tooling, so a failed lookup is recorded as a gap, not filled from another source.

4.1 The lure host and its origin

revolut.com.3984.app, and its parent 3984.app, resolve to 186.2.175.250 on nameservers ns1.ddos-guard.net and ns2.ddos-guard.net. [OBSERVED] (resolution: evidence/raw/cloak-phone/20260929T143943Z-https___revolut.com.3984.app__TRJ1.json, payload 4a093052c6681ab4498ead12d7378aeeef7d625b0cd3d93b3b66ab2d0ed183ba, and evidence/raw/dns/; nameservers: evidence/raw/dns/ dce4ea5146d7182a2ad075eea96e94a5a15b10af733e5450b68afd1baaab102e and RDAP evidence/raw/rdap/20260929T161521Z-revolut.com.3984.app.json 6eef3617ad82a823fa3d67549f983f6998b7b2dd2847a139decd6eac148afac6) 29401.app, and its subdomain revolut.com.29401.app, share that exact origin address, 186.2.175.250, and carry their own Let’s Encrypt certificate issued 2026-09-05. [OBSERVED] (evidence/raw/dns/, resolves_to hash 4f860bf9fb9d199c65aa5413085a7c7258952bd49dcf4b742a919812d0af69ff, shared by both domains; evidence/raw/certspotter/20260929T145002Z-29401.app.json)

DDoS-Guard sits in front of the origin as both the nameserver operator and the request-time filter. A 28-cell automated probe grid, seven User-Agents by two languages by two referers, run over Tor against the SMS URL, got HTTP 403 in every cell, a 1606-byte body matching DDoS-Guard’s own “restricted access from your current IP” page (sha256 9b886e1595fc07e0086aa2a0c6e2b5221f7477c38c1e87c6c6eaecd3bdf47000). [OBSERVED] (evidence/raw/cloak/, 28 files; sample .../20260929T144958*, hash f251685545430c178352147d7ea03fc685e2b13a4236eceeb07a07380d08f383) A healthy control request made in the same run, to a non-campaign URL over Tor, returned a normal 200 response, showing the Tor channel itself was working and the 403s came from DDoS-Guard’s own IP filter rather than a dead circuit. [OBSERVED] (evidence/raw/cloak-control/20260929T144954Z-https___example.com_.json) The probe’s own identity check recorded Tor exit address 66.63.170.221, but DDoS-Guard’s own __ddg9_ cookie, set in the same 403 responses, recorded a different address, 217.60.198.93. [OBSERVED] (same 28 envelopes) Because DDoS-Guard blocks the Tor exit before the kit’s own logic is ever reached, this grid result is evidence only about DDoS-Guard’s IP filter, and says nothing about what the kit’s own User-Agent check would do to a Tor exit; that second, further gate is examined separately, from an accepted (non-Tor) network address, in section 5. [INFERRED]

The kit’s origin sits behind that DDoS-Guard front, not exposed to a direct request from an ordinary investigative vantage point; every value taken from a victim (section 3) is sent to this same scam domain, and from there onward its path is hidden behind DDoS-Guard’s proxy.

4.2 The naming scheme and the confirmed siblings

Every confirmed domain in this campaign follows the same pattern: the string revolut.com as a subdomain label, followed by a short run of digits, under a cheap parent registration in .app or .com, plus the bare parent domain itself. Twenty-five such subdomain names are confirmed campaign infrastructure, each with DNS-over-HTTPS resolution recorded on 2026-09-29, for fifty domain entries in total once the bare parents are counted. [OBSERVED] (seeds.yaml, domains: section, 50 non-comment lines)

Confirmed subdomain Resolves to (2026-09-29)
revolut.com.3984.app 186.2.175.250
revolut.com.29401.app 186.2.175.250
revolut.com.03910.app 186.2.175.249
revolut.com.16508.app 186.2.175.188
revolut.com.168192.app 186.2.175.218
revolut.com.328192.app 186.2.175.243
revolut.com.328517.com 186.2.175.159
revolut.com.347591.app 186.2.175.174
revolut.com.3941.app 186.2.175.181
revolut.com.3942.app 186.2.175.209
revolut.com.395856.com 186.2.175.161
revolut.com.3985.app 186.2.175.176
revolut.com.48182.app 186.2.175.133
revolut.com.50912.app 186.2.175.149
revolut.com.53016.app 186.2.175.251
revolut.com.54810.app 186.2.175.156
revolut.com.55810.app 186.2.175.133
revolut.com.57482.app 186.2.175.210
revolut.com.58910.app 186.2.175.137
revolut.com.64412.app 186.2.175.194
revolut.com.69011.app 186.2.175.242
revolut.com.79201.app 186.2.175.167
revolut.com.85811.app 186.2.175.222
revolut.com.91481.app 186.2.175.192
revolut.com.928517.com 186.2.175.244

[OBSERVED] (seeds.yaml, ips: and domains: sections; per-domain A records in evidence/raw/dns/) Two of these twenty-five, 3984.app and 29401.app, were already known before this pivot, from the SMS itself and from the shared-origin-IP match to it; the other twenty-three were found by searching urlscan for the naming pattern and then confirmed by a fresh DNS resolution on 2026-09-29. [OBSERVED] (seeds.yaml comments) All twenty-five confirmed domains for which a nameserver was successfully recorded use ns1.ddos-guard.net and ns2.ddos-guard.net. [OBSERVED] (reports/police-dossier.md “Shared infrastructure” table, sample hashes 106f84afe47619220910b704c3d00b26a901594ff72de86f183b45e1f69f7c68, dce4ea5146d7182a2ad075eea96e94a5a15b10af733e5450b68afd1baaab102e)

A further sixteen names matching the same pattern were seen by urlscan between 2026-07-19 and 2026-09-15 but had no A record on 2026-09-29; these are unconfirmed leads, not findings, and are not counted among the twenty-five. [OBSERVED] (seeds.yaml, unconfirmed: section)

Every 2026 urlscan sighting of a confirmed domain that records an address is on AS59692, on the 186.2.175.x range listed above; the earliest is revolut.com.347591.app on 2026-08-22. [OBSERVED] (evidence/raw/urlscan/ and evidence/raw/dns/ ASN fields, e.g. 3984.app hash 07408963c32f82a1fd0e699ae6ab13fdf95ee5b638ebf04ab6f35d9838a2a54d; evidence/raw/urlscan/20260929T154155Z-page.asn__AS59692__AND_page.url__revolut.com..json) The bare 64412.app also appears in two 2024 scans on AS40065, which predate every other sighting in this case by two years and most likely reflect an earlier registration of that name. [OBSERVED] (evidence/raw/urlscan/20260929T162507Z-page.domain__64412.app.json) / [INFERRED] (earlier registration) An earlier range, 185.178.208.x on AS57724, was seen hosting names matching the same naming scheme from 2026-07-19 to at least 2026-09-08, overlapping the current range, but none of those names are in the confirmed set; they appear only among the sixteen unconfirmed leads. [OBSERVED] (evidence/raw/urlscan/20260929T154154Z-page.asn__AS57724__AND_page.url__revolut.com._.json) That this represents the same operation’s earlier hosting is an inference from the naming scheme, since no confirmed domain was observed resolving on both ranges. [INFERRED]

Seventeen of the confirmed 186.2.175.x addresses sit in the netblock IQWEB-LLC-NET, RIPE-registered, with abuse contact abuse@iqweb.io. [OBSERVED] (evidence/raw/rdap/, e.g. 186.2.175.250 hash 8f7b3b5c75eba1a211629db7d77257b585c29d6a37e0ca71ff3aac821b8c0d6d)

4.3 Registrations and certificates

RDAP returned four distinct registrars across the domains it could reach:

Registrar Abuse contact Domains
Key-Systems LLC abuse@key-systems.net 3984.app
NICENIC INTERNATIONAL GROUP CO., LIMITED abuse@nicenic.net 03910.app, 16508.app, 29401.app
CNOBIN INFORMATION TECHNOLOGY LIMITED abuse@ordertld.com 328517.com, 928517.com
Dominet (HK) Limited domainabuse@service.aliyun.com 395856.com

[OBSERVED] (evidence/raw/rdap/20260929T161551Z-3984.app.json, registrar Key-Systems LLC, abuse@key-systems.net, created 2026-09-29T03:16:29.174Z; evidence/raw/rdap/20260929T161625Z-29401.app.json, 20260929T161655Z-03910.app.json, 20260929T161943Z-16508.app.json, 20260929T162058Z-328517.com.json, 20260929T162654Z-928517.com.json, 20260929T162219Z-395856.com.json) 3984.app itself was registered 2026-09-29T03:16:29.174Z, about six hours before the SMS was sent (section 4.5’s timeline table). [OBSERVED] (same envelope)

RDAP is unrecorded for the other eighteen .app parent domains (168192.app, 328192.app, 347591.app, 3941.app, 3942.app, 3985.app, 48182.app, 50912.app, 53016.app, 54810.app, 55810.app, 57482.app, 58910.app, 64412.app, 69011.app, 79201.app, 85811.app, 91481.app), all refused with a 403 from the .app registry’s RDAP service when reached over Tor, and for six of the confirmed IP addresses (.137, .149, .167, .192, .242, .251), all refused with a 429 rate limit. [OBSERVED] (reports/police-dossier.md “Evidence gaps” table, e.g. 168192.app hash 8dd54fdc45a089c44bf8beb4038a9eb6196cd4a35574c3bb006c3600d91c964f) A retry of the eighteen domain lookups on 2026-09-30 got the identical 403 error for every one, so this gap is confirmed persistent rather than a timing artifact of when the first pass was run; the six IP lookups were not retried. [OBSERVED] (retry envelopes evidence/raw/rdap/20260930T092743Z-168192.app.json through evidence/raw/rdap/20260930T093341Z-91481.app.json, 18 files, all status: error, each with a payload identical to the original gap envelope, same payload_sha256)

revolut.com.3984.app’s TLS certificate, issued by Let’s Encrypt with CN=YR1, has not_before: 2026-09-29T04:37:38Z, about 80 minutes after the domain’s own registration. [OBSERVED] (evidence/raw/certspotter/20260929T144854Z-3984.app.json, payload_sha256 476b8fe8c27ab641c76e281481b88482f63e197444ee6eb3d7d44b979168239a)

4.4 The front-end fingerprint

DDoS-Guard’s own proxy fronts every confirmed domain: it operates the nameservers (4.1), filters requests by network origin before the kit’s own page is ever reached, and issues its own tracking cookies (__ddg1_, __ddg8_, __ddg9_, __ddg10_) independent of anything the kit itself sets. [OBSERVED] (section 4.1; evidence/raw/cloak/ and evidence/raw/cloak-phone/ Set-Cookie headers) The real origin behind that front, 186.2.175.250 and its siblings, is reachable in this evidence only through DDoS-Guard’s proxy; no direct-to-origin response was captured, and no header set from a direct connection was observed.

4.5 Timeline

Timestamp (UTC) Event
2026-07-19 to 2026-07-21 Earliest urlscan sightings of names matching the naming scheme (unconfirmed leads), on hosting range 185.178.208.x
2026-08-22T20:57:08Z Earliest confirmed-domain certificate (347591.app); urlscan first sees revolut.com.347591.app on 186.2.175.174 at 23:48:48Z
2026-09-05T01:50:59.816Z 29401.app registered, the earliest of the seven parents with a recorded RDAP registration
2026-09-29T03:16:29.174Z 3984.app registered
2026-09-29T04:37:38Z Let’s Encrypt certificate issued for 3984.app / revolut.com.3984.app
2026-09-29T07:42:57.646Z urlscan’s first recorded scan of revolut.com.3984.app
2026-09-29T09:38:31Z SMS received by the recipient
2026-09-29T14:39:43Z Kit served to the recipient’s phone, mobile User-Agent, Swedish mobile network
2026-09-29T14:49:54Z to 14:50Z Tor probe grid (403 x 28) and control request (200) run
2026-09-29T14:50:33Z Same phone, desktop User-Agent, 404 “Not Found”
2026-09-30T08:04:22Z Quad9’s filtered resolver first observed answering NXDOMAIN for the domain (section 8)

[OBSERVED] (seeds.yaml comments and evidence/raw/urlscan/20260929T154154Z-page.asn__AS57724__AND_page.url__revolut.com..json for the earliest urlscan sightings; evidence/raw/certspotter/20260929T162912Z-347591.app.json and evidence/raw/urlscan/20260929T154155Z-page.asn__AS59692__AND_page.url__revolut.com..json for 347591.app; evidence/raw/rdap/20260929T161625Z-29401.app.json for 29401.app; evidence/raw/rdap/20260929T161551Z-3984.app.json for the registration; evidence/raw/certspotter/20260929T144854Z-3984.app.json for the certificate; evidence/raw/urlscan/20260929T144910Z-page.domain_3984.app.json for the scan; the SMS envelope for receipt; evidence/raw/cloak-phone/ for the two phone visits; evidence/raw/cloak/ and evidence/raw/cloak-control/ for the probe grid) The gap between certificate issuance (04:37:38Z) and SMS receipt (09:38:31Z) is just over five hours; the gap between urlscan’s first scan (07:42:57Z) and SMS receipt is under two hours. [OBSERVED] Both timestamps are independently confirmed; which one to treat as the more meaningful lead time (registration/certificate versus scan) is a matter of phrasing, not of missing data.

The earliest evidence tied to confirmed infrastructure is 2026-08-22: a Let’s Encrypt certificate for 347591.app with not_before 2026-08-22T20:57:08Z, and urlscan’s first scan of revolut.com.347591.app at 2026-08-22T23:48:48Z. [OBSERVED] (evidence/raw/certspotter/20260929T162912Z-347591.app.json; evidence/raw/urlscan/20260929T154155Z-page.asn__AS59692__AND_page.url__revolut.com._.json) urlscan saw names matching the pattern earlier, from 2026-07-19, but those are unconfirmed leads and are not used to date the campaign. The earliest of them, revolut.com.9285.app, was scanned once, and neither it nor 9285.app returned any DNS record on 2026-09-29. [OBSERVED] (evidence/raw/urlscan/20260929T154154Z-page.asn__AS57724__AND_page.url__revolut.com._.json; evidence/raw/dns/20260929T161255Z-revolut.com.9285.app.json through evidence/raw/dns/20260929T161313Z-9285.app.json)

5. Gating and cloaking

The landing host served the phishing kit to a mobile User-Agent on a Swedish mobile connection. It answered a desktop User-Agent on the same connection with a 9-byte “Not Found” body, and DDoS-Guard answered Tor exits with a 1606-byte 403 page. [OBSERVED] (5.1) This section records the observation matrix that isolates each check, together with the control request that makes a Tor refusal interpretable at all, and closes with what is and is not established about who is allowed through.

5.1 The observation matrix

Source network Client Result Envelope
Swedish mobile carrier (LTE) mobile User-Agent 200, the kit, 458553 bytes evidence/raw/cloak-phone/20260929T143943Z-https___revolut.com.3984.app__TRJ1.json
same handset and connection, about 11 minutes later desktop User-Agent 404, 9 bytes evidence/raw/cloak-phone/20260929T145033Z-https___revolut.com.3984.app__TRJ1.json
Tor exit 66.63.170.221, 28-cell grid (7 user agents x 2 languages x 2 referers) mixed, including 3 mobile profiles 403, 1606 bytes, all 28 cells the 28 envelopes in evidence/raw/cloak/
Tor exit 66.63.170.221, control request n/a 200, 713 bytes evidence/raw/cloak-control/20260929T144954Z-https___example.com_.json
recipient’s own phone, mobile data, a private VPN and DNS service active, 2026-09-30 mobile browser DNS resolution failure (“This site can’t be reached”) evidence/raw/reporter/20260930T080816Z-https___revolut.com.3984.app__TRJ1.json
recipient’s own phone, LTE/carrier DNS, no VPN, 2026-09-30 mobile browser landing screen shown in screenshot, nothing entered evidence/raw/screenshot/20260930T082443Z-https___revolut.com.3984.app__TRJ1.json

[OBSERVED] (each envelope cited in its row, status_code, body_length and body_sha256 or equivalent field) Every request to the landing host in the table used the URL from the SMS unchanged, with its fixed ?TRJ1; no per-request token was minted. [OBSERVED] (url and target fields of the cited envelopes)

The mobile-UA capture returned the kit body, sha256 8b06133d2213db3361034c72b089b16ff432c76fef2e87dd1521f886cecbdc03, from remote address 186.2.175.250. [OBSERVED] (evidence/raw/cloak-phone/20260929T143943Z-https___revolut.com.3984.app__TRJ1.json) The desktop-UA capture, made from the same handset and connection about 11 minutes later, returned 9 bytes, sha256 0019dfc4b32d63c1392aa264aed2253c1e0c2fb09216f8e2cc269bbfb8bb49b5. [OBSERVED] (evidence/raw/cloak-phone/20260929T145033Z-https___revolut.com.3984.app__TRJ1.json) A repeat visit or token-expiry effect on the second request is not excluded, since the mobile request was not repeated afterward to control for it. [INFERRED]

The 28-cell Tor grid crossed 7 client profiles by 2 Accept-Language values by 2 Referer values, each combination run once. [OBSERVED] (the 28 envelopes in evidence/raw/cloak/, distinct cell identifiers) Every cell returned the identical result: HTTP 403, body 1606 bytes, sha256 9b886e1595fc07e0086aa2a0c6e2b5221f7477c38c1e87c6c6eaecd3bdf47000, matching the front-end proxy’s own “restricted access from your current IP” page. [OBSERVED] (programmatic comparison of status_code and body_sha256 across all 28 envelopes)

5.2 The control request

A phishing kit refusing Tor exits and a front-end content delivery network refusing Tor exits look identical from the client side, so a run of nothing but 403s proves nothing about the kit by itself. [INFERRED] The probe grid’s own circuit-identity check recorded Tor exit 66.63.170.221. [OBSERVED] (the 28 envelopes in evidence/raw/cloak/, exit_ip field) A control request to the non-campaign URL https://example.com/, made over the same run, returned HTTP 200, 713 bytes, envelope payload sha256 77dd9277a0e02dae5aac7e7316ae6be4d9e401fcb8066a6950124abe4a3f460a. [OBSERVED] (evidence/raw/cloak-control/20260929T144954Z-https___example.com_.json) The control shows the Tor channel worked during the run. The 403 body is DDoS-Guard’s own refusal page, so the refusal came from the front end, not from a failed circuit. [INFERRED]

DDoS-Guard’s __ddg9_ cookie, which on the handset captures carries the client’s own address, recorded 217.60.198.93. The probe’s identity check recorded 66.63.170.221. [OBSERVED] (the 28 envelopes in evidence/raw/cloak/, Set-Cookie header and exit_ip field; evidence/raw/cloak-phone/) The request that reached DDoS-Guard therefore probably left Tor through a different exit address from the one the identity check reported. This does not change the result, a 403 in all 28 cells. [INFERRED]

5.3 What is and is not established

What is established: because the front-end proxy in front of the landing host blocks the Tor exit before any request reaches the kit’s own gate, the all-403 grid result is evidence about that front end’s own IP filter, not about what the kit’s own User-Agent or source-network check would do to a Tor exit. [INFERRED] Separately, and on a non-Tor path, a mobile User-Agent from a Swedish mobile carrier connection was served the kit, and a desktop User-Agent from the same handset and connection, minutes later, was refused. [OBSERVED] (5.1) That single paired comparison is consistent with a User-Agent-based gate on the accepted network, but it does not exclude a repeat-visit effect, since only one desktop request was made and the order was not reversed. [INFERRED] It does not prove a source-network gate either, since no non-Tor, non-Swedish-carrier network was tested with a mobile User-Agent. [INFERRED]

What is not established: whether a non-Swedish, non-Tor network with a mobile User-Agent and the SMS URL would be served the kit. No such request was made. This report does not claim a source-network allowlist beyond “a Swedish mobile carrier connection was accepted, and the front end refuses Tor exits by IP.” [INFERRED]

The recipient’s own repeat visit on 2026-09-30, over a private VPN and DNS service on mobile data, returned a DNS resolution failure rather than a 403 or 404. [OBSERVED] (recipient’s screenshot) / [REPORTED] (network conditions) (evidence/raw/reporter/20260930T080816Z-https___revolut.com.3984.app__TRJ1.json) This is not, by itself, a gating result: it is a DNS-resolution failure on the recipient’s device and VPN path, and section 8 traces it to a Quad9 blocklist change between the two visits, not to the kit’s own access control. [INFERRED] A later direct handset visit the same day, off the VPN and on carrier DNS, loaded the landing page again with nothing entered, showing the domain was still live and still serving the kit to a mobile User-Agent on an accepted network the day after the SMS. [OBSERVED] (evidence/raw/screenshot/20260930T082443Z-https___revolut.com.3984.app__TRJ1.json)

6. The phishing kit

This section describes what the preserved kit does to a victim, what data it takes and where that data is sent, and the identifiers useful to a defender. It does not reproduce the kit’s request or response formats, field names, message bodies, or any code excerpt, and it does not explain how to build, operate, or get past the kit or its access checks.

6.1 Provenance and integrity

The kit served to a mobile User-Agent on the accepted network is a single XOR-obfuscated HTML file (key 31). Served body sha256 8b06133d2213db3361034c72b089b16ff432c76fef2e87dd1521f886cecbdc03, decoded artifact sha256 198a85ec74af744853b523b8469a480eae161131f451f1e4186632ed8a2c8a08. [OBSERVED] (xor_key field, evidence/raw/kit/20260929T152304Z-https___revolut.com.3984.app__TRJ1.json) The decoded artifact was read statically only; it was never executed. [OBSERVED] (same envelope)

Four static library files were separately fetched from the landing host: js/tf.min.js (1294424 bytes), js/tf-tflite.min.js (1219490 bytes), js/blazeface.min.js (7996 bytes) and js/tflite_web_api_cc_simd.wasm (3689633 bytes), each carrying a Last-Modified header of 2026-09-08. [OBSERVED] (evidence/raw/kit-resource/20260930T082134Z-https___revolut.com.3984.app_js_tf.min.js.json) (evidence/raw/kit-resource/20260930T082135Z-https___revolut.com.3984.app_js_tf-tflite.min.js.json) (evidence/raw/kit-resource/20260930T082136Z-https___revolut.com.3984.app_js_blazeface.min.js.json) (evidence/raw/kit-resource/20260930T082136Z-https___revolut.com.3984.app_js_tflite_web_api_cc_simd.wasm.json) The files predate the 3984.app registration by three weeks, which suggests they were prepared once and reused across domains; the date does not show when the campaign began. [INFERRED]

6.2 Libraries and the copied Revolut model

The kit uses TensorFlow.js and TFLite (the BlazeFace model) for on-device face detection, mp4-muxer and webrtc-adapter for browser-side video capture, and bodymovin/Lottie for UI animation; it calls the external services api.country.is and ipapi.co for geolocation-based language and country selection. [OBSERVED] (libraries and external-URL indicators, evidence/raw/kit/20260929T152304Z-https___revolut.com.3984.app__TRJ1.json; js/tf.min.js and js/blazeface.min.js: evidence/raw/kit-resource/20260930T082134Z-, 20260930T082136Z-)

The kit serves a face-quality TFLite model at /model/face-quality.tflite that is byte-identical to Revolut’s production model at assets.revolut.com: both are 1266248 bytes, sha256 988344b3266a6a5e7d74ae9ccda7afea8f23257fabf94578fe63efc7e468e454. [OBSERVED] (evidence/raw/kit-resource/20260930T082137Z-https___revolut.com.3984.app_model_face-quality.tflite.json and evidence/raw/kit-resource/20260930T082312Z-https___assets.revolut.com_mobile_computer_vision_models_face_quality_20251215_1.json) The decoded kit also references Revolut’s own URL for that model. [OBSERVED] (evidence/raw/kit/20260929T152304Z-https___revolut.com.3984.app__TRJ1.json, indicators.external_urls) The kit therefore reproduces Revolut’s own on-device face-quality check rather than approximating it. [INFERRED] The model’s hash is the identifier to search for on other hosts.

6.3 What the kit does and what it takes

UI strings are localized into 8 languages: de, el, en, es, et, it, pt and sv. [OBSERVED] (locale indicators, evidence/raw/kit/20260929T152304Z-https___revolut.com.3984.app__TRJ1.json) The flow presents itself as an identity-verification step and walks the victim through a phone number and passcode entry, a one-time code, and a selfie-video liveness check, before a final review step. [INFERRED] (static reading of the decoded kit, not executed) The working assumption that a human operator uses the victim’s answers in the real Revolut app while the kit polls for the outcome is a reasoned inference from the kit’s design (a review-poll step with no local pass/fail logic reachable from outside), not something directly observed of an operator. [INFERRED]

Data taken from a victim who completes the flow: a phone number, a Revolut passcode, a one-time code, and a selfie video. [INFERRED] (kit logic read statically, not executed, combined with the observed endpoint list) The video recording appears to be uploaded even when the on-screen liveness check fails, and the kit appears able to reuse a previously stored phone number or passcode without asking again. [INFERRED] (static reading of the decoded artifact, not executed) These two points rest on a single static reading of the kit’s logic and are reported as inference, not as behavior observed of a live session.

The fabricated phone number and passcodes entered in the 2026-09-30 Waydroid session were sent to the scam domain itself, revolut.com.3984.app, which sits behind the DDoS-Guard front described in section 4. [OBSERVED] (evidence/raw/har/20260930T092422Z-https___revolut.com.3984.app_auth_start.json and the other auth_* entries) That the one-time code and the video go to the same host is read from the static kit. [INFERRED] The HAR was exported filtered to the landing host and hCaptcha, so it cannot show the absence of other destinations; none is established for this data. [OBSERVED] (evidence/raw/reporter/20260930T092422Z-https___revolut.com.3984.app__TRJ1.json, command field) Whether the sibling domains serve the same kit was not tested.

6.4 hCaptcha site key

The kit embeds hCaptcha site key e1dd321d-6eb8-4505-8f09-605b005e705c. [OBSERVED] (evidence/raw/har/20260930T092422Z-https___api.hcaptcha.com_getcaptcha_e1dd321d-6eb8-4505-8f09-605b005e705c.json and the accompanying checksiteconfig capture, site key present in the request URL) hCaptcha’s own checksiteconfig response for this site key reports a custom theme and encrypted requests enabled. [OBSERVED] (evidence/raw/har/20260930T092422Z-https___api.hcaptcha.com_checksiteconfig_v_b9ca2a6602c2bf69741b771db488f3001ea35.json) If these features are tied to a paid account, hCaptcha may hold account or billing records for the operator; this report did not verify hCaptcha’s plan terms. [INFERRED] A urlscan.io search for the site key returned zero results. [OBSERVED] (evidence/raw/urlscan/20260930T082824Z-e1dd321d-6eb8-4505-8f09-605b005e705c.json) This does not show the key is unused elsewhere. [INFERRED]

6.5 A capture caveat

A separate Waydroid/mitmproxy capture of the same landing page used mitmproxy’s own TLS certificates, not the operators’; nothing about that capture’s TLS layer is evidence about the operators’ certificate, which is covered independently in section 4 from certificate transparency records (Cert Spotter). [OBSERVED] (recorded observation, evidence/raw/reporter/20260930T092422Z-https___revolut.com.3984.app__TRJ1.json)

6.6 The recipient’s own fabricated-data entries

The investigation’s own rule is GET-only, no data entry, not even fabricated data. The recipient departed from that rule in three sessions on 2026-09-30, against the investigator’s advice, and entered fabricated values. All three are labeled here as the recipient’s own action, outside the investigation’s method, and are not to be read as part of how this evidence was gathered. Section 3.4 gives the detail of each.

In the first session, the recipient entered a random phone number and a random PIN; the PIN was shown as wrong. [REPORTED] (recipient’s statement, evidence/raw/reporter/20260930T075302Z-https___revolut.com.3984.app__TRJ1.json)

In the second, at 08:19Z (10:19 CEST), the recipient entered +46 22222 only, which is not a valid Swedish subscriber number; the kit then showed its six-digit passcode screen, and nothing more was entered. [OBSERVED] (recipient’s screenshots, evidence/raw/reporter/20260930T082507Z-https___revolut.com.3984.app__TRJ1.json)

In the third, during the Waydroid/mitmproxy session, the recipient entered +46 22222 and two fabricated passcodes; the number was accepted and both passcodes were rejected, while an alternative passkey-style sign-in option was also offered. [REPORTED] (recipient’s statement, evidence/raw/reporter/20260930T092422Z-https___revolut.com.3984.app__TRJ1.json)

Because a phone number that is not a valid Swedish subscriber number was still accepted to the passcode screen in both sessions that used it, the kit does not appear to validate the phone number before proceeding. [INFERRED] These are one-off observations from the recipient’s own fabricated-data entries. They do not establish the kit’s general behavior and must not be read as part of this investigation’s method; they are reported here only because they were volunteered by the recipient and are relevant to how the kit behaves in practice.

7. The wider cluster

The lure domain does not stand alone. This section lists the sibling domains confirmed as campaign infrastructure by DNS resolution, the further names that match the naming scheme but could not be confirmed, and the two hosting ranges on which such names were seen.

7.1 Confirmed siblings

25 sibling subdomains, following the naming scheme revolut.com.<digits>.<app|com>, plus their 25 bare parent domains (50 domain entries in total), were confirmed by DNS-over-HTTPS resolution on 2026-09-29. [OBSERVED] (seeds.yaml, domains: section, 50 non-comment entries) Two of the 25, 3984.app (the SMS lure domain) and 29401.app, were already known before the pivot; the other 23 were discovered through urlscan and then confirmed by resolution. [OBSERVED] (seeds.yaml comments per entry)

Subdomain Resolves to Note
revolut.com.3984.app 186.2.175.250 SMS lure/landing domain
revolut.com.29401.app 186.2.175.250 shares origin IP with 3984.app
revolut.com.03910.app 186.2.175.249
revolut.com.16508.app 186.2.175.188
revolut.com.168192.app 186.2.175.218
revolut.com.328192.app 186.2.175.243
revolut.com.328517.com 186.2.175.159
revolut.com.347591.app 186.2.175.174
revolut.com.3941.app 186.2.175.181
revolut.com.3942.app 186.2.175.209
revolut.com.395856.com 186.2.175.161
revolut.com.3985.app 186.2.175.176
revolut.com.48182.app 186.2.175.133
revolut.com.50912.app 186.2.175.149
revolut.com.53016.app 186.2.175.251
revolut.com.54810.app 186.2.175.156
revolut.com.55810.app 186.2.175.133 shares an IP with 48182.app
revolut.com.57482.app 186.2.175.210
revolut.com.58910.app 186.2.175.137
revolut.com.64412.app 186.2.175.194
revolut.com.69011.app 186.2.175.242
revolut.com.79201.app 186.2.175.167
revolut.com.85811.app 186.2.175.222
revolut.com.91481.app 186.2.175.192
revolut.com.928517.com 186.2.175.244

[OBSERVED] (seeds.yaml ips: and domains: sections; each bare parent domain, e.g. 3984.app, is the corresponding row’s registrable domain, used for nameserver and registrar pivots, and is not listed as a separate row here)

All confirmed domains for which a nameserver lookup succeeded use ns1.ddos-guard.net and ns2.ddos-guard.net. [OBSERVED] (section 4; nameserver hashes for the sampled domains) RDAP registrars recorded for the confirmed domains: Key-Systems LLC for 3984.app; NICENIC INTERNATIONAL GROUP CO., LIMITED for 03910.app, 16508.app and 29401.app; CNOBIN INFORMATION TECHNOLOGY LIMITED for 328517.com and 928517.com; and Dominet (HK) Limited for 395856.com. [OBSERVED] (evidence/raw/rdap/20260929T161551Z-3984.app.json and the corresponding RDAP envelopes cited in section 4) RDAP is unrecorded for the other 18 .app parent domains, after a retry that reproduced the identical registry-side refusal; this gap is confirmed persistent, not a snapshot, and their registrars are unknown. [OBSERVED]

7.2 Unconfirmed leads

16 further hostnames matching the same naming scheme were seen by urlscan at various dates between 2026-07-19 and 2026-09-15 but had no DNS A record on 2026-09-29, and are listed here as unconfirmed leads, not as confirmed infrastructure. [OBSERVED] (seeds.yaml unconfirmed: section)

Name First urlscan sighting IP at sighting
revolut.com.298201.app (no sighting of 298201.app) 2026-07-24 185.178.208.134
328102.app / revolut.com.328102.app 2026-07-24 185.178.208.141
38291.app / revolut.com.38291.app 2026-09-05 186.2.175.167
39451.app / revolut.com.39451.app 2026-08-03 185.178.208.159
43981.app / revolut.com.43981.app 2026-08-13 185.178.208.155
revolut.com.48291.app (no sighting of 48291.app) 2026-08-27 186.2.175.148
revolut.com.48654.app (no sighting of 48654.app) 2026-07-21 185.178.208.137
revolut.com.49018.app (no sighting of 49018.app) 2026-08-14 185.178.208.178
revolut.com.49565.app (no sighting of 49565.app) 2026-07-21 185.178.208.135
69281.app / revolut.com.69281.app 2026-08-14 185.178.208.152
69759.app / revolut.com.69759.app 2026-09-15 186.2.175.253
75684.app / revolut.com.75684.app 2026-07-27 185.178.208.159
revolut.com.78381.app (no sighting of 78381.app) 2026-08-05 185.178.208.161
78431.app / revolut.com.78431.app 2026-08-06 185.178.208.164
revolut.com.9285.app (no sighting of 9285.app) 2026-07-19 185.178.208.155
revolut.com.93810.app (no sighting of 93810.app) 2026-09-11 186.2.175.216

[OBSERVED] (seeds.yaml unconfirmed: section, per-entry comments) None of these 16 names had a DNS A record on 2026-09-29, so none is claimed as confirmed campaign infrastructure; a rotated or already-spent domain in this naming batch can still be preserved as a lead, which is why they are kept rather than dropped. [INFERRED]

Urlscan pivot queries against IPs and ASNs shared with the confirmed hosts additionally returned several hundred further hostnames; these are co-tenants of the same hosting and CDN infrastructure, not campaign leads, are not individually named, and are not investigated further in this report. [OBSERVED] (aggregate urlscan pivot results against the confirmed hosting range)

7.3 The two hosting ranges

urlscan recorded names matching the naming scheme on 185.178.208.x, AS57724, from 2026-07-19 to at least 2026-09-08 (revolut.com.78381.app), and on the bare 69281.app on 2026-09-21. It recorded them on 186.2.175.x, AS59692, from 2026-08-22 (revolut.com.347591.app, confirmed). [OBSERVED] (evidence/raw/urlscan/20260929T154154Z-, 20260929T154155Z-; 69281.app: evidence/raw/urlscan/20260929T154043Z-page.ip__185.178.208.152_.json) The two ranges were in use at the same time from 2026-08-22 to at least 2026-09-08, or to 2026-09-21 counting the bare 69281.app. No confirmed domain was observed on both. That the operation moved new domains from the earlier range to the current one during late August 2026 is an inference from naming and timing only. [INFERRED]

The operation has been active since at least 2026-08-22 (confirmed infrastructure), and a new domain in the naming scheme appears every few days; this report does not claim a fixed registration cadence beyond that. [INFERRED] (4.5; spread of urlscan sighting dates and RDAP created_at dates across the confirmed list) Names matching the pattern were seen from 2026-07-19, but only as unconfirmed leads, which are not used to date the campaign. [OBSERVED] (7.2)

8. Blocklisting and takedown status

By 2026-09-30, one public DNS resolver had begun refusing the SMS lure domain; three others had not. No registrar, host or brand-protection desk action is recorded in this evidence; this section reports only the DNS-level change observed.

8.1 The Quad9 timeline

Time (UTC) Resolver Result for revolut.com.3984.app
2026-09-29T14:39:43Z Quad9 filtered resolver (dns.quad9.net) NOERROR, 186.2.175.250
2026-09-30T08:04:22Z Quad9 filtered resolver (dns.quad9.net) NXDOMAIN (no answer)
2026-09-30T08:17:02Z Cloudflare (plain and malware-filtering) NOERROR, 186.2.175.250
2026-09-30T08:17:02Z Google public DoH NOERROR, 186.2.175.250

[OBSERVED] (the two Quad9 rows: observations field of evidence/raw/dns-blocklist/20260930T081748Z-revolut.com.3984.app.json) (the Cloudflare and Google rows: evidence/raw/dns-blocklist/20260930T081702Z-revolut.com.3984.app.json)

Quad9’s filtered service answers NXDOMAIN specifically for names on its own threat-intelligence feeds. Because the resolver held a working answer for the domain at 14:39:43Z on 2026-09-29 and returned NXDOMAIN for the same name by 08:04:22Z on 2026-09-30, the domain was added to a Quad9 blocklist feed sometime in that roughly 17-hour window. [INFERRED] (evidence/raw/dns-blocklist/20260930T081748Z-revolut.com.3984.app.json) As of the same later check, Cloudflare’s and Google’s public resolvers still returned a working answer for the domain, so the block was not universal across public resolvers as of 2026-09-30T08:17:02Z. [OBSERVED]

8.2 Collection method, disclosed as a deviation

Direct Quad9 DoH queries routed over Tor, both the JSON API and the RFC 8484 wire-format endpoint, failed outright (a connection timeout and a malformed response respectively). [OBSERVED] (error fields of evidence/raw/dns-blocklist/20260930T081702Z-revolut.com.3984.app.json) (error fields of evidence/raw/dns-blocklist/20260930T081715Z-revolut.com.3984.app.json) The Quad9 finding in 8.1 instead came from SSH access to the recipient’s own home server and a query of its local DNS-proxy log, not from this investigation’s Tor-routed, DNS-over-HTTPS collection pipeline. [OBSERVED] (command field recording the SSH/log-query command, evidence/raw/dns-blocklist/20260930T081748Z-revolut.com.3984.app.json)

This is a genuine deviation from the project’s standing rule that DNS is resolved over HTTPS through Tor, and from the general rule that every outbound request goes through Tor: Quad9’s filtered DoH endpoints were not reachable from a Tor exit, and the alternative path used a private log on the recipient’s own infrastructure rather than a fresh query. It is disclosed here rather than folded into the finding as if it had been collected the standard way. [OBSERVED] The underlying finding, that Quad9’s filtered resolver changed from a working answer to NXDOMAIN for this domain between 2026-09-29T14:39:43Z and 2026-09-30T08:04:22Z, remains evidence-backed; only its collection method departs from the project’s routine pipeline.

8.3 What this does and does not show

Quad9 is used here as one indicator of independent threat-intelligence uptake, not as a substitute for registrar, host or brand-protection takedown. No RDAP, WHOIS or hosting-provider record in this evidence shows the domain, its parent, or any sibling domain suspended, and no correspondence with a registrar, host or CDN abuse desk is recorded. The domain remained resolvable through Cloudflare and Google as of the last check in this evidence, and the landing host itself was still serving the kit to a mobile User-Agent on an accepted network as late as 2026-09-30 (section 5). Nothing in this evidence establishes the current status of any sibling domain listed in section 7 beyond their own DNS resolution as of 2026-09-29.

9. Relation to the EasyPark campaign

A separate investigation covers an SMS phishing campaign impersonating EasyPark against Swedish mobile numbers. That case is reported on its own. Every similarity found between it and the Revolut case below is listed with the weight it carries, and no statement in this section implies that the two are run by the same people. [INFERRED]

A full attribute-by-attribute comparison was run across both evidence stores: IP addresses, autonomous systems, nameservers, registrars, abuse contacts, TLS certificate issuers, kit endpoints, kit libraries, kit external URLs and SMS sender IDs. [OBSERVED] (reports/similarity.md, every “Shared” subsection) Every one of those categories reads “None” under the shared column except one, addressed below. [OBSERVED]

Similarity EasyPark Revolut Weight
Swedish mobile numbers targeted by SMS with a Swedish-language lure parking-fine text identity-verification text generic
Spoofed alphanumeric sender ID instead of a phone number InfoSMS (as reported in the EasyPark case) Revolut generic
Brand placed as a subdomain of a throwaway domain containing digits easypank.se-45564.xyz revolut.com.3984.app generic
Landing page served only to an accepted network with a mobile User-Agent Swedish mobile network plus mobile User-Agent Swedish mobile network plus mobile User-Agent; Tor exits blocked by DDoS-Guard generic
Desktop User-Agent on an accepted network answered with the 9-byte body Not Found (sha256 0019dfc4b32d63c1392aa264aed2253c1e0c2fb09216f8e2cc269bbfb8bb49b5) yes, chunked text/plain; charset=utf-8 yes, text/plain with Content-Length: 9 generic
Let’s Encrypt TLS certificates yes yes generic
Kit code: shared text (no shared endpoint path; code compared offline, kit files not executed) Vue build, multi-country card fields and toll-tag flow hand-written JavaScript with a key-based translation table generic: only standard web API names and ordinary UI translations

[OBSERVED] (reports/police-dossier.md, “Relation to the EasyPark campaign”; reports/similarity.md)

The one value that appears in both evidence stores is the served body hash 0019dfc4b32d63c1392aa264aed2253c1e0c2fb09216f8e2cc269bbfb8bb49b5, a 9-byte “Not Found” response. [OBSERVED] (reports/similarity.md, “served_bodies”) “Not Found” is a standard HTTP reason phrase and a common default body across unrelated servers and frameworks, so a shared body hash alone is not evidence of a shared platform. [INFERRED] The two captures of that body carry different response headers: EasyPark’s copy is served chunked as text/plain; charset=utf-8, while the Revolut copy carries text/plain with a fixed Content-Length: 9, which points to different server software behind the two responses. [OBSERVED] (reports/police-dossier.md, “Relation to the EasyPark campaign” comparison table)

Naming scheme, hosting, front-end server and kit technology all differ between the two cases, as the table above shows. [OBSERVED] (reports/police-dossier.md, same table) The remaining similarities (Swedish-targeted SMS, a spoofed alphanumeric sender, the brand placed as a subdomain of a throwaway domain, User-Agent gating on an accepted network, and the use of Let’s Encrypt certificates) are common to unrelated SMS phishing operations generally. [INFERRED]

Verdict: no specific indicator, IP address, ASN, nameserver, registrar, TLS certificate, kit file, kit endpoint or SMS sender, is shared between the Revolut case and the EasyPark case. [OBSERVED] (reports/similarity.md, full comparison) Nothing in either evidence store supports treating the two campaigns as run by the same operator, and this report does not state or imply such a link. [INFERRED]

10. Unconfirmed leads and claims not made

Section 7 lists the 25 sibling subdomains (50 domain entries with their bare parents) confirmed as this campaign’s infrastructure by direct DNS-over-HTTPS resolution on 2026-09-29. [OBSERVED] (seeds.yaml, domains:) Everything in this section shares some attribute with that set, a matching naming scheme, a shared hosting range, or a shared urlscan sighting, but falls short of confirmation, for the reason stated in each item. None of it is treated as fact elsewhere in this report. [INFERRED]

10.1 Sixteen unconfirmed sibling names

Sixteen hostnames matching the naming scheme revolut.com.<digits>.<app|com> were seen by urlscan between 2026-07-19 and 2026-09-15, but had no A record on 2026-09-29 and are listed as unconfirmed leads, not findings. [OBSERVED] (seeds.yaml, unconfirmed:; reports/police-dossier.md, “Unconfirmed leads”) Eight of the sixteen bare <n>.app names return no urlscan sighting at all, only their revolut.com.<n> counterparts do, which is why they remain listed rather than dropped: 298201.app, 48291.app, 48654.app, 49018.app, 49565.app, 78381.app, 9285.app and 93810.app. [OBSERVED] (reports/police-dossier.md, “Unconfirmed leads” table)

10.2 The earlier hosting range

An earlier hosting range, 185.178.208.x on AS57724, was seen for domains matching the campaign’s naming scheme from 2026-07-19 to at least 2026-09-08, overlapping the current range, but none of those domains are in the confirmed set: they appear only as unconfirmed leads with no A record on 2026-09-29. [OBSERVED] (seeds.yaml, unconfirmed: comments; evidence/raw/urlscan/20260929T154154Z-page.asn__AS57724__AND_page.url__revolut.com._.json) That the same operation used both ranges is an inference from the naming-scheme match and the timing of the urlscan sightings; no single confirmed domain was observed resolving on both ranges. [INFERRED] (7.3)

10.3 Co-tenant hosts, not campaign leads

The urlscan pivot queries against the IPs and ASN shared with the campaign’s confirmed hosts returned 681 further hostnames, 2 of them bare <digits>.app names with no revolut.com. host. [INFERRED] (count computed by the report renderer from evidence/facts.jsonl on 2026-09-30, reports/police-dossier.md, “Unconfirmed leads”; not a stored evidence artifact) These hostnames are co-tenants of the same hosting and CDN infrastructure, not campaign leads, and are not individually named or investigated. [INFERRED]

10.4 Unrelated hits in the same searches

Some hits surfaced during urlscan and other searches share no attribute with the campaign beyond appearing in the same query results, and are not part of it: revolut-trade.com, revoluat1-5.top (dated 2024), crdpro.at, kwork.com and findonlineresults.com. [OBSERVED] (README.md, “Evidence gaps”)

10.5 Claims this report deliberately does not make

11. Methodology, chain of custody and deviations

11.1 Collection path

Routine collection, DNS, RDAP, certificate transparency and urlscan, went over Tor, with DNS resolved over HTTPS, per the project’s safety rules. [OBSERVED] (envelope command fields throughout evidence/raw/{dns,rdap,certspotter,urlscan}/, for example "GET https://rdap.org/domain/3984.app (Tor)") The handset (cloak-phone), screenshot and kit-resource captures were made directly from the recipient’s own phone on the Swedish mobile network, not over Tor; this is a deliberate, disclosed exception, since only that network and User-Agent combination can observe what the kit’s own gate does to the network that received the SMS. [OBSERVED] (evidence/raw/cloak-phone/*.json, network field: “phone mobile data (a Swedish mobile carrier, LTE)…”)

The 2026-09-30 Waydroid/mitmproxy capture (page loaded at 09:10:40Z, envelope 09:24:22Z) was also made off Tor. The investigator ran it, and it egressed through a USB-tethered phone on mobile data. The investigator loaded the page once without input. [OBSERVED] (evidence/raw/reporter/20260930T092422Z-https___revolut.com.3984.app__TRJ1.json, command and statement) In that session the served page ran in the emulated browser, unlike the decoded kit artifact in 3.1, which was only read. During the no-input load, before any entry was made, the page’s own script sent three POST requests to the landing host and hCaptcha’s configuration request, with no input from anyone. [OBSERVED] (evidence/raw/har/20260930T092422Z-*, request methods and times) The three automatic POST requests to the landing host fall outside the GET-only rule for operator hosts, and are disclosed here for that reason. [INFERRED]

The automated cloaking probe grid (7 user agents by 2 languages by 2 referers) and its control request against a non-campaign URL both ran over Tor, and the control’s healthy 200 result establishes that the channel worked before the grid’s uniform 403 result is read as a real block. [OBSERVED] (evidence/raw/cloak/, evidence/raw/cloak-control/20260929T144954Z-https___example.com_.json)

11.2 A disclosed deviation: Quad9 blocklist finding collected off-path

The finding that Quad9’s filtered resolver stopped answering for the campaign domain between 2026-09-29T14:39Z and 2026-09-30T08:04Z was not collected through the project’s Tor-routed DNS-over-HTTPS pipeline. [OBSERVED] (evidence/raw/dns-blocklist/20260930T081702Z-*.json, ...081715Z-*.json) Direct Quad9 DoH queries over Tor, both the JSON API on port 5053 and the RFC 8484 wire-format endpoint, failed outright with a connection timeout and a malformed-response error. [OBSERVED] (same envelopes, error fields) The finding instead came from SSH access to the recipient’s own home server and a query of its local DNS-proxy log, a source outside the project’s Tor-only collection path. [OBSERVED] (evidence/raw/dns-blocklist/20260930T081748Z-*.json, command field naming the SSH/journalctl query) This is a genuine deviation from the project’s DNS-over-Tor requirement, disclosed here rather than folded silently into the finding; the underlying observation is still evidence-backed, only its collection path is non-standard. [INFERRED]

11.3 A disclosed deviation: the recipient’s own data entries

On 2026-09-30, against the investigator’s advice and outside the investigation’s GET-only, no-data-entry rule, the recipient entered fabricated data into the kit in three sessions: a random number and a random PIN in the first, +46 22222 alone in the second, and +46 22222 with two fabricated passcodes in the Waydroid/mitmproxy session (3.4). [REPORTED] (evidence/raw/reporter/20260930T075302Z-*.json, evidence/raw/reporter/20260930T092422Z-*.json) / [OBSERVED] (screenshots, evidence/raw/reporter/20260930T082507Z-*.json) All three envelopes label the entries as the recipient’s own action, outside the investigation’s rules, and two of them state that every value entered was fabricated. [OBSERVED] This report treats those entries as one-off observations of how the kit responded, not as an established general behavior of its backend, and not as part of the method this investigation otherwise followed. [INFERRED]

The recipient’s Waydroid-session entries were made in the investigator’s instrumented browser, so evidence/raw/har/ also holds the kit’s responses to those fabricated entries. The records exist only because of the recipient’s own action, taken against the investigator’s advice and outside the investigation’s no-data-entry rule. The investigation itself entered no data; the only requests sent during the investigator’s own part of that session were those the page made automatically on load (11.1). [OBSERVED] (evidence/raw/har/20260930T092422Z-*auth_*.json; evidence/raw/reporter/20260930T092422Z-https___revolut.com.3984.app__TRJ1.json)

11.4 A process note: the discover confirm step

osint/discover.py’s pivot logic is designed to confirm a candidate only once it resolves onto a known IP. Against the 39 candidate sibling names this pivot produced from urlscan on 2026-09-29, running that per-candidate confirm step would have meant resolving hundreds of unrelated certificate names one at a time through Tor. The controller running the pipeline stopped that confirm step on 2026-09-29 for this reason, and the 23 newly discovered siblings that were confirmed that day were instead confirmed by direct DNS-over-HTTPS lookups against each candidate name, the same DoH-over-Tor path used for every other confirmed domain in this report. This is recorded here as a process note from the investigation log rather than as an evidence-store claim: no envelope documents the discover run starting or being stopped, only the DoH lookups that followed it, which are the same lookups behind the 25 confirmed siblings in section 7. [REPORTED] (investigation log, no envelope)

11.5 Evidence integrity and append-only handling

evidence/raw/ is append-only. A mistaken capture of the SMS’s collection time was corrected by superseding it with a later record, not by deleting or editing it: evidence/raw/sms/20260929T142900Z-Revolut.json remains on disk, superseded by evidence/raw/sms/20260929T144242Z-Revolut.json, which carries a supersedes field naming it. [OBSERVED] evidence/MANIFEST.sha256 lists every envelope and artifact file, and grew from 1450 to 1468 lines when the RDAP retry described in section 12.1 added 18 new error envelopes and their manifest entries. [OBSERVED] (git history of evidence/MANIFEST.sha256, commit 8eb855a)

11.6 What this report does not claim about testing

This report does not state that the project’s automated test suite currently passes. The project’s stated design convention is that collectors split a network-touching fetch step from a pure parse step so the suite can run fully offline, but that design convention was not re-verified by running the suite as part of this write-up, and no claim about a live pass or fail result is made here. [INFERRED] (CLAUDE.md, “Testing convention”)

12. Evidence gaps

What this investigation declined to claim matters as much as what it claims. This section lists what the evidence store does not contain, and notes where an earlier gap has since closed.

12.1 RDAP: eighteen .app parent domains, confirmed persistent

RDAP is unrecorded for 18 .app parent domains (168192.app, 328192.app, 347591.app, 3941.app, 3942.app, 3985.app, 48182.app, 50912.app, 53016.app, 54810.app, 55810.app, 57482.app, 58910.app, 64412.app, 69011.app, 79201.app, 85811.app, 91481.app), each returning a 403 from the Google-backed .app RDAP service reached over Tor. [OBSERVED] (reports/police-dossier.md, “Evidence gaps”) A retry of all 18 on 2026-09-30, between 09:27:43Z and 09:33:41Z, returned the identical 403 Forbidden error for every one, each with a payload identical to the original gap envelope (same payload_sha256). [OBSERVED] (evidence/raw/rdap/20260930T092743Z-168192.app.json through evidence/raw/rdap/20260930T093341Z-91481.app.json) This gap is therefore confirmed persistent as of 2026-09-30, not merely a snapshot of an earlier report generation. [OBSERVED]

12.2 RDAP: six hosting IPs, untouched by the retry

Six of the 186.2.175.x hosting addresses (.137, .149, .167, .192, .242, .251) also lack an RDAP record, due to a 429 rate limit. [OBSERVED] (reports/police-dossier.md, “Evidence gaps”) The 2026-09-30 retry covered only the 18 .app domain-name lookups in 12.1; these six IP lookups were not retried and remain gapped. [OBSERVED]

12.3 Certificate transparency: crt.sh unavailable

crt.sh returned a 429 rate limit for the one domain queried directly, 3984.app. [OBSERVED] (evidence/raw/ct/20260929T144908Z-_.3984.app.json) Cert Spotter was used instead; this report’s certificate data rests on Cert Spotter, not crt.sh, for that reason.

12.3a Cert Spotter: fourteen confirmed parents have no certificate record

Cert Spotter returned a 429 rate limit for 3985.app, 48182.app, 50912.app, 53016.app, 54810.app, 55810.app, 57482.app, 58910.app, 64412.app, 69011.app, 79201.app, 85811.app, 91481.app and 928517.com, and the lookups were not retried. [OBSERVED] (evidence/raw/certspotter/20260929T162917Z-* through evidence/raw/certspotter/20260929T162931Z-*)

12.4 Kit static resources: no longer a gap

An earlier note recorded the kit’s static resources, /js/tf.min.js and similar files, as not fetched. [OBSERVED] (README.md, “Evidence gaps”) That gap has since closed: on 2026-09-30 the four static libraries (js/tf.min.js, js/tf-tflite.min.js, js/blazeface.min.js, js/tflite_web_api_cc_simd.wasm) and the face-quality model, both the kit’s own copy and Revolut’s production copy for comparison, were fetched and recorded. [OBSERVED] (evidence/raw/kit-resource/20260930T082134Z-*.json through ...082312Z-*.json) This report does not repeat the earlier note as an open gap.

12.5 Handset exposure

The handset requests made directly from the recipient’s phone, needed to observe the kit’s own network and User-Agent gate, exposed the recipient’s real mobile IP address to the operators, since those requests were not routed over Tor by design. [OBSERVED] (README.md, “Evidence gaps”; evidence/raw/cloak-phone/*.json, network field) This is a disclosed operational exposure of the recipient’s own device on their own network, not a tooling defect.

12.6 The _tag cookie’s scope is not established

Whether the _tag cookie value taken from the SMS URL token (TRJ1) is unique to this recipient or a fixed value shared across a batch of SMS sends is not established: only one recipient’s SMS was observed, so there is no second value to compare it against. [INFERRED] (absence of a second data point)

12.7 The human operator behind the relay

The identity, location and infrastructure of whoever drives the real Revolut app during the account-takeover relay were not observed. The relay’s existence is inferred from the kit’s design, a review-poll step with no local pass/fail logic reachable from outside, not from any direct observation of an operator. [INFERRED]

12.8 A data-currency note, not a gap

The count of platform co-tenant hosts sharing the campaign’s IPs and ASN (section 10.3) grows between renderings under the append-only evidence model; a figure quoted at one time is not stale evidence, only a snapshot, and this report states the collection time alongside the figure for that reason. [INFERRED]

13. Indicators of compromise

Only infrastructure confirmed as this campaign’s appears in the tables below; section 10’s unconfirmed leads are not repeated here. [OBSERVED]

13.1 Lure and landing URL

Indicator Value Confidence
Landing/lure URL https://revolut.com.3984.app/?TRJ1 (URL received in the SMS) [OBSERVED]
Cookie of interest _tag (value taken from the SMS URL token, e.g. TRJ1), HttpOnly, 2-hour lifetime [OBSERVED]

[OBSERVED] (evidence/raw/sms/20260929T144242Z-Revolut.json; evidence/raw/cloak-phone/20260929T143943Z-https___revolut.com.3984.app__TRJ1.json, Set-Cookie: _tag=TRJ1; ...; Max-Age=7200; HttpOnly)

13.2 Domains

25 confirmed sibling subdomains under the naming scheme revolut.com.<digits>.<app|com>, plus their bare parent domains, 50 domain entries in total, confirmed by direct DNS-over-HTTPS resolution on 2026-09-29. [OBSERVED] (seeds.yaml, domains:) The full list is authoritative there and is not reproduced row by row here.

13.3 Hosting

Indicator Value Confidence
Current hosting range 186.2.175.0/24, AS59692, specific addresses .133, .137, .149, .156, .159, .161, .167, .174, .176, .181, .188, .192, .194, .209, .210, .218, .222, .242, .243, .244, .249, .250, .251 [OBSERVED]
Earlier hosting range, unconfirmed-lead names only 185.178.208.x, AS57724 (see section 10.2) [OBSERVED], listed separately from confirmed IOCs
Nameservers ns1.ddos-guard.net, ns2.ddos-guard.net [OBSERVED]

[OBSERVED] (evidence/raw/dns/ and evidence/raw/urlscan/ ASN fields, e.g. 3984.app announced by AS59692, hash 07408963c32f82a1fd0e699ae6ab13fdf95ee5b638ebf04ab6f35d9838a2a54d)

13.4 TLS certificates

Let’s Encrypt certificates, CN=YR1 and CN=YR2, seen across the confirmed domains. [OBSERVED] revolut.com.3984.app’s certificate carries not_before: 2026-09-29T04:37:38Z. [OBSERVED] (evidence/raw/certspotter/20260929T144854Z-3984.app.json, payload_sha256 476b8fe8c27ab641c76e281481b88482f63e197444ee6eb3d7d44b979168239a) 29401.app’s certificate was issued 2026-09-05. [OBSERVED] (evidence/raw/certspotter/20260929T145002Z-29401.app.json)

13.5 Kit body hashes

Indicator Value Confidence
Served kit HTML (XOR-obfuscated, key 31) sha256 8b06133d2213db3361034c72b089b16ff432c76fef2e87dd1521f886cecbdc03, 458553 bytes [OBSERVED]
Decoded kit HTML (statically decoded, never executed) sha256 198a85ec74af744853b523b8469a480eae161131f451f1e4186632ed8a2c8a08 [OBSERVED]
DDoS-Guard gate-refusal body sha256 9b886e1595fc07e0086aa2a0c6e2b5221f7477c38c1e87c6c6eaecd3bdf47000, 1606 bytes [OBSERVED]
404 refusal body sha256 0019dfc4b32d63c1392aa264aed2253c1e0c2fb09216f8e2cc269bbfb8bb49b5, 9 bytes [OBSERVED]
Copied Revolut face-quality model, byte-identical to production sha256 988344b3266a6a5e7d74ae9ccda7afea8f23257fabf94578fe63efc7e468e454, 1266248 bytes [OBSERVED]

[OBSERVED] (evidence/raw/kit/20260929T152304Z-https___revolut.com.3984.app__TRJ1.json; evidence/raw/cloak-phone/20260929T143943Z-*.json; evidence/raw/cloak-phone/20260929T145033Z-*.json; evidence/raw/cloak/ 28-cell grid; evidence/raw/kit-resource/20260930T082137Z-*.json, ...082312Z-*.json; all five hashes present in evidence/MANIFEST.sha256)

The body served on 2026-09-30 differed (459428 bytes, sha256 7f8ae6c78fe7fecca7ade5bf1c11915edd5b18746febf7cc35c36518f8b00c93), so the served-HTML hash identifies one capture, not the kit in general. [OBSERVED] (evidence/raw/har/20260930T092422Z-https___revolut.com.3984.app__TRJ1.json)

13.6 hCaptcha site key

Indicator Value Confidence
hCaptcha site key e1dd321d-6eb8-4505-8f09-605b005e705c [OBSERVED]

[OBSERVED] (evidence/raw/har/20260930T092422Z-https___api.hcaptcha.com_getcaptcha_e1dd321d-6eb8-4505-8f09-605b005e705c.json) A urlscan search for this site key returned zero results. [OBSERVED] (evidence/raw/urlscan/20260930T082824Z-_e1dd321d-6eb8-4505-8f09-605b005e705c_.json, total: 0) This does not show the key is unused elsewhere. [INFERRED]

13.7 SMS delivery

Indicator Value Confidence
Alphanumeric sender ID Revolut (not a phone number) [OBSERVED]
SMS receipt time 2026-09-29T09:38:31.322Z [OBSERVED]
Landing URL token pattern _tag cookie set to the SMS URL token (e.g. TRJ1) [OBSERVED]

[OBSERVED] (evidence/raw/sms/20260929T144242Z-Revolut.json, payload_sha256 ba18e44bc5454abd88332f634ce21a109a2c8590a3daede8fcb3d9e9b2e80e2d)

That the sender ID was spoofed is inferred (2.1). [INFERRED]

14. Recommendations and requested actions

Every action below traces to a fact established earlier in this report; none introduces a new claim. Abuse contacts named here are taken only from RDAP evidence in evidence/raw/rdap/.

14.1 Swedish police

The evidence supports a report of attempted identity and account fraud: a real-time, human-operated relay that collects a phone number, Revolut passcode, one-time code and a selfie video, served from infrastructure that refuses Tor exits at the DDoS-Guard front and, in one paired test, refused a desktop User-Agent. [INFERRED] The recipient’s own fabricated-data entries in three sessions on 2026-09-30, made against the investigator’s advice and outside the investigation’s method, show the kit advancing past an invalid phone number in both sessions that used +46 22222 and rejecting every fabricated passcode entered; these are one-off observations and are reported as the recipient’s own action, not as part of the method. [OBSERVED], [REPORTED] (3.4)

14.2 CERT-SE

This report and the full evidence store are intended for CERT-SE. What this report supports handing over is the confirmed sibling list (25 subdomains, 50 domain entries), the hosting range and nameservers (section 13.3), the certificate data (section 13.4), and the gating behavior (section 5 of this report), so CERT-SE can coordinate national circulation of the indicators and pass them to the registrars and network below. [INFERRED]

14.3 Registrars

Registrar Abuse contact Domains
Key-Systems LLC abuse@key-systems.net 3984.app
NICENIC INTERNATIONAL GROUP CO., LIMITED abuse@nicenic.net 03910.app, 16508.app, 29401.app
CNOBIN INFORMATION TECHNOLOGY LIMITED abuse@ordertld.com 328517.com, 928517.com
Dominet (HK) Limited domainabuse@service.aliyun.com 395856.com

[OBSERVED] (evidence/raw/rdap/20260929T161551Z-3984.app.json, 20260929T161625Z-29401.app.json, 20260929T161655Z-03910.app.json, 20260929T161943Z-16508.app.json, 20260929T162058Z-328517.com.json, 20260929T162654Z-928517.com.json, 20260929T162219Z-395856.com.json) The registrars of the other 18 .app parent domains remain unrecorded after the 2026-09-30 retry (section 12.1) and cannot be named here.

14.4 Hosting network abuse desk

The current hosting range 186.2.175.0/24 sits in netblock IQWEB-LLC-NET, with abuse contact abuse@iqweb.io. [OBSERVED] (evidence/raw/rdap/ entries for the netblock’s addresses, e.g. 186.2.175.250, payload_sha256 8f7b3b5c75eba1a211629db7d77257b585c29d6a37e0ca71ff3aac821b8c0d6d) A report can be addressed there for all confirmed addresses in that range.

14.5 The .app registry

Google Registry is a relevant abuse-report recipient for all .app-TLD confirmed domains, as the standard sponsor abuse channel for that gTLD. [INFERRED]

14.6 DDoS-Guard

DDoS-Guard operates the nameservers ns1.ddos-guard.net and ns2.ddos-guard.net in front of all confirmed domains, and its own IP-based filter is what blocks the Tor exit used by this investigation’s probe grid, ahead of the kit’s own User-Agent check. [OBSERVED] DDoS-Guard is a relevant recipient for a takedown request given that role.

14.7 Revolut’s security team

Revolut’s own security team is a relevant recipient given the brand impersonation, the byte-identical copy of Revolut’s own production face-quality model (section 13.5), and the hCaptcha site key (section 13.6), which is an operator identifier Revolut may be able to act on through hCaptcha’s own abuse process. [INFERRED] What Revolut’s team can add that this investigation cannot: confirmation of exactly where the kit’s copied flow diverges from the real one, and any internal fraud signal tied to the sessions this kit has already relayed. [INFERRED]

14.8 What remains open

Establishing who sent the SMS, or by what interconnect route, needs carrier and network records this investigation does not have access to. [INFERRED] Whether the _tag cookie is per-recipient or a fixed campaign-wide tag (section 12.6) needs a second recipient’s SMS to compare against, which this investigation also does not have. [INFERRED]

Appendix A. Evidence file index

A table row in this appendix is its own citation: the path is the anchor, so rows carry no separate evidence label beyond what is stated in the row.

A.1 Verifying the store

evidence/MANIFEST.sha256 has 1468 lines, one per envelope or artifact file under evidence/. [OBSERVED] It grew from 1450 to 1468 lines when the 2026-09-30 RDAP retry described in section 12.1 added 18 new error envelopes and their manifest entries. [OBSERVED] (git history of evidence/MANIFEST.sha256, commit 8eb855a)

A.2 Raw evidence, by source

Directory Holds
evidence/raw/sms/ The recipient’s SMS inbox record (sender, body, internal date_ms), including a superseded earlier capture kept append-only.
evidence/raw/dns/ DNS-over-HTTPS resolutions for the confirmed and candidate domains, and for the hosting addresses’ PTR records.
evidence/raw/rdap/ RDAP registration lookups for domains and IP netblocks, including the 2026-09-30 retry of the 18 gapped .app domains.
evidence/raw/certspotter/ Certificate transparency records (issuer, validity dates, DNS names) for confirmed domains.
evidence/raw/ct/ 25 crt.sh query envelopes: brand-substring and naming-pattern searches (16 status: ok, 8 errors: 429, 502, 503 or timeout) and one direct %.3984.app query that returned 429 (section 12.3).
evidence/raw/urlscan/ Scan history and pivot query results used to find sibling domains and to date the operation’s timeline.
evidence/raw/cloak/ The automated 28-cell Tor probe grid (7 user agents by 2 languages by 2 referers) against the landing URL.
evidence/raw/cloak-control/ The Tor control request against a non-campaign URL, run in the same session as the probe grid.
evidence/raw/cloak-phone/ Direct requests from the recipient’s own phone on the Swedish mobile network, off Tor by design, comparing mobile and desktop User-Agents.
evidence/raw/kit/ The kit page as served to a mobile User-Agent, and its static XOR decode, never executed.
evidence/raw/kit-resource/ Fetched kit static libraries and the face-quality model, both the kit’s own copy and Revolut’s production copy for comparison.
evidence/raw/har/ HAR of the 2026-09-30 Waydroid/mitmproxy session, made off Tor (11.1): the landing page, its static resources, hCaptcha API calls, requests the page’s own script sent to the landing host during the no-input load, and the kit’s responses to the recipient’s own fabricated entries (see 11.3).
evidence/raw/reporter/ The recipient’s own statements and screenshots, including the three fabricated-data entry sessions of 2026-09-30.
evidence/raw/screenshot/ A screenshot capture of the landing page loading on the recipient’s own handset.
evidence/raw/dns-blocklist/ Quad9, Cloudflare and Google public-resolver comparisons behind the blocklisting finding, including the off-path SSH/journalctl collection described in section 11.2.

A.3 Reports and supporting files

File Holds
docs/report-sources/facts-revolut.md Helper fact table used by the report writers; the evidence under evidence/ is authoritative.
reports/police-dossier.md The generated police/CERT dossier, including the EasyPark comparison table cited in section 9.
reports/similarity.md The full attribute-by-attribute comparison between this case’s and the EasyPark case’s confirmed infrastructure.
reports/revolut-brand-brief.md The brand-facing brief, including an abuse-contacts-on-record table.
reports/abuse-hcaptcha.md A drafted abuse report to hCaptcha’s own abuse channel for the site key in section 13.6.
seeds.yaml The authoritative list of confirmed domains (domains:) and unconfirmed leads (unconfirmed:), with per-entry discovery notes.
README.md The project’s own running notes, including the evidence-gaps and abuse-routes sections referenced throughout this report.
evidence/MANIFEST.sha256 The append-only checksum ledger for every envelope and artifact file under evidence/.

Appendix B. Screenshots

The six screenshots cited in this report, re-encoded for publication without metadata; the originals are in the evidence store under the hashes given. Screenshots taken by the SMS recipient are the recipient’s own record.

Recipient’s phone, 2026-09-30 10:07:09 CEST (08:07:09Z): Chrome shows ERR_NAME_NOT_RESOLVED for revolut.com.3984.app while public DoH still resolved it (section 8).

Recipient’s phone, 2026-09-30 10:07:09 CEST (08:07:09Z): Chrome shows ERR_NAME_NOT_RESOLVED for revolut.com.3984.app while public DoH still resolved it (section 8). [REPORTED] (sha256 90333334852c..., evidence/raw/reporter/20260930T080816Z-https___revolut.com.3984.app__TRJ1.json)

Recipient’s phone, 10:19:07 CEST: the landing screen, ‘Välkommen tillbaka’, +46 preselected.

Recipient’s phone, 10:19:07 CEST: the landing screen, ‘Välkommen tillbaka’, +46 preselected. [REPORTED] (sha256 535ceb99bc30..., evidence/raw/reporter/20260930T082507Z-https___revolut.com.3984.app__TRJ1.json)

Recipient’s phone, 10:19:16 CEST: the fabricated number 22222 typed after +46. Recipient’s own action, outside the investigation’s no-data-entry rule.

Recipient’s phone, 10:19:16 CEST: the fabricated number 22222 typed after +46. Recipient’s own action, outside the investigation’s no-data-entry rule. [REPORTED] (sha256 fd115f91506b..., evidence/raw/reporter/20260930T082507Z-https___revolut.com.3984.app__TRJ1.json)

Recipient’s phone, 10:19:24 CEST: the kit’s passcode screen, ‘Ange lösenkod’, six digits, shown after the invalid number (section 6).

Recipient’s phone, 10:19:24 CEST: the kit’s passcode screen, ‘Ange lösenkod’, six digits, shown after the invalid number (section 6). [REPORTED] (sha256 3f64f24466d3..., evidence/raw/reporter/20260930T082507Z-https___revolut.com.3984.app__TRJ1.json)

Investigator, handset on mobile data, 2026-09-30T08:24:43Z: landing screen, nothing entered.

Investigator, handset on mobile data, 2026-09-30T08:24:43Z: landing screen, nothing entered. [OBSERVED] (sha256 04bd971604db..., evidence/raw/screenshot/20260930T082443Z-https___revolut.com.3984.app__TRJ1.json)

Investigator, Waydroid browser through mitmproxy, 2026-09-30T09:11:00Z: landing screen, nothing entered (section 11 on the capture’s network path).

Investigator, Waydroid browser through mitmproxy, 2026-09-30T09:11:00Z: landing screen, nothing entered (section 11 on the capture’s network path). [OBSERVED] (sha256 2b2d4b02999b..., evidence/raw/reporter/20260930T092422Z-https___revolut.com.3984.app__TRJ1.json)