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:
- The sender ID “Revolut” is an alphanumeric SMS sender, not a phone number.
Alphanumeric sender IDs are trivially spoofable at the bulk-SMS gateway and
carry no verification of the party enroute; the field being “Revolut” is
not proof that Revolut, or anyone acting for Revolut, sent the message.
[INFERRED](same envelope as the message body) - The link in the message,
https://revolut.com.3984.app/?TRJ1, does not point at Revolut’s own domain.revolut.comsits to the left of the registrable domain3984.app, which is a look of a Revolut subdomain but is in fact a leaf label inside an unrelated.appregistration. Section 4 gives the registration and hosting detail.
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:
- 2026-09-29T14:39:43Z, from the recipient’s own phone on the Swedish mobile
network with a mobile User-Agent (investigation capture,
curlon the handset via adb): HTTP 200, the kit served.[OBSERVED] - 2026-09-29T14:50:33Z, same phone, same network, a desktop User-Agent about
eleven minutes later (investigation capture,
curlon the handset via adb): HTTP 404, a 9-byte “Not Found” body.[OBSERVED] - 2026-09-30T08:07:09Z (10:07:09 CEST by the phone clock), the recipient’s own
phone on mobile data with a private VPN and DNS service active:
ERR_NAME_NOT_RESOLVED.[OBSERVED](recipient’s screenshot, evidence/raw/reporter/20260930T080816Z-https___revolut.com.3984.app__TRJ1.json) - 2026-09-30T08:24:42Z (10:24:42 CEST), a direct handset visit without VPN,
on LTE with carrier DNS: the landing page loaded (“Välkommen tillbaka”),
with nothing entered.
[OBSERVED](evidence/raw/screenshot/20260930T082443Z-https___revolut.com.3984.app__TRJ1.json)
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:
- The selfie-video recording is uploaded even when the on-screen liveness
check reports failure.
[INFERRED](decoded artifact, static reading) - The backend can reuse a phone number and passcode already stored from an
earlier visit without asking the victim to enter them again.
[INFERRED](decoded artifact, static reading)
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
- The 16 unconfirmed sibling names (10.1) are confirmed campaign infrastructure. A naming-scheme match with no current A record is a lead, not a finding.
- The 185.178.208.x hosting range (10.2) is directly linked to the confirmed campaign by continuous observation. It was in use from 2026-07-19 to at least 2026-09-08, overlapping the current range, but no confirmed domain was observed on both hosting ranges; the link rests on naming and timing only.
- A fixed count for platform co-tenants (10.3). The append-only evidence model means this number grows between renderings; this report states the figure current at the time it was re-run, not a final total.
- The operator’s identity, location, or any individual. No claim is
made about who runs this infrastructure; the deliverable is
infrastructure evidence only.
[INFERRED](CLAUDE.md, safety rule 7) - A fixed cadence for new domain registration. The operation has been
active since at least 2026-08-22 (confirmed infrastructure), with a new
domain appearing every few days, but no fixed schedule is asserted beyond
that spread.
[INFERRED] - A campaign start date from unconfirmed leads. Names matching the pattern were seen from 2026-07-19, but only as unconfirmed leads, and they are not used to date the campaign (4.5).
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). [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. [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. [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). [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. [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). [OBSERVED] (sha256 2b2d4b02999b..., evidence/raw/reporter/20260930T092422Z-https___revolut.com.3984.app__TRJ1.json)