An EasyPark smishing campaign, from SMS to live card relay
EasyPark 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 /dossier/.
Prepared 2026-09-19 from evidence collected 2026-09-18 and 2026-09-19.
Intended readers: IT professionals and law enforcement handling this campaign, including Swedish police, CERT-SE, registrar and host abuse desks, and EasyPark’s security team. A separate blog rendering of this material, with additional redactions, is intended for a general journalist and public audience.
1. Summary
A single SMS impersonating EasyPark reached a Swedish mobile subscriber on
2026-09-18 at 16:46:15 CEST, that is 2026-09-18T14:46:15Z, from the
alphanumeric sender ID InfoSMS, claiming an unpaid parking fee.
[REPORTED] (2.2, 12.9) The address in it is a redirector: three 302 hops,
refusing nobody, hand the visitor to a landing host serving a live-relay
phishing kit. [OBSERVED] (2.3)
Confirmed from collected envelopes: four hostnames, easypank.se-45564.xyz,
easypank.se-9626654.pics, avcoa.click and btalning.click, all resolving
to 220.158.232.231; four NameSilo, LLC registrations on three dnsowl.com
nameservers, each carrying one Let’s Encrypt certificate; and se-45564.xyz
registered 6 h 55 min 17 s before the reported delivery. [OBSERVED]
(4.1, 4.2, 4.3, 4.7)
The landing host applies two independent gates. A Swedish mobile-carrier
connection with a mobile User-Agent was served the 140535-byte kit; the same
handset 18 seconds later with a desktop User-Agent, and all 56 requests from
a Tor exit on a run whose control request was healthy, were served nine bytes
of Not Found. [OBSERVED] (5.1, 5.2, 5.3) A reviewer opening a reported URL
from a desktop sees what looks like a campaign already taken down.
[INFERRED] (3)
One session against the operators’ live backend is preserved, 930 WebSocket
frames and 40 HTTP envelopes. Card number, expiry, CVV, cardholder name and
phone were streamed to the server as typed, over 38 input_text frames and 17
POSTs to /JisiRmktje/api/input, before any submit; each submitted card drew
a bank-identification-number lookup and a reject verdict. [OBSERVED]
(7.4, 7.5, 7.8)
Two limits must not be over-read. The session was a controlled test run with
fabricated data, not a real victim’s. [REPORTED] (7.1) Both cards were
rejected, so no verification-code or app-approval step was ever reached.
[OBSERVED] (7.1, 7.8) The second-factor relay is kit capability read from
the preserved bundle, not observed behaviour of these operators. [SOURCE]
(6.4, 7.1)
Inferred rather than observed: the token expiry of mint time plus eight hours (2.4), a division of labour across at least four parties, and the cash-out step, of which this investigation holds nothing (3).
Not established: any artifact of the SMS or its delivery route, whether
Swedish fixed broadband passes the source-network gate, an owner name for
AS38623, and any location for the operators. [INFERRED] (9.7, 11.1)
Requested actions (section 13):
- Swedish police: a report of attempted card fraud, with the infrastructure
and session evidence attached.
[INFERRED](13.1) - CERT-SE: circulate the indicators nationally and coordinate the desks below.
[INFERRED](13.2) - NameSilo: suspend the three registrations still live, and confirm the hold
on
se-9626654.picsis a deliberate abuse action.[INFERRED](13.3) - The netblock’s abuse and technical contacts: host-level action against
220.158.232.231, which carries page and console alike.[INFERRED](13.4) - EasyPark: a brand-holder takedown request, and where the kit diverges from
the real payment flow.
[INFERRED](13.5) - The recipient’s mobile operator and PTS: the lure text and its indicators,
for messaging-fraud and sender-ID abuse.
[INFERRED](13.6) - Defenders: hunt on the kit-level indicators below, never on the hosting
platform banner alone.
[INFERRED](13.7)
| Indicator | What it is | Confidence | Full set |
|---|---|---|---|
easypank.se-45564.xyz |
lure host named in the SMS; HTTP redirector | [OBSERVED] |
12.1 |
easypank.se-9626654.pics |
landing host; served the kit and the live relay | [OBSERVED] |
12.1 |
220.158.232.231 |
origin address of all four confirmed hostnames | [OBSERVED] |
12.3 |
/ESZNhaXCmd with _v=valid_<14-digit timestamp>_<32 hex> |
landing path and single-use token grammar | [OBSERVED] |
12.5 |
POST /JisiRmktje/api/input and the result_type socket event |
field exfiltration channel and the operator’s only control event | [OBSERVED] |
12.5, 12.8 |
How to read this report
Every factual sentence in this report carries one of four evidence labels:
[OBSERVED]- an envelope, artifact or capture frame is cited for the claim.[SOURCE]- read from preserved source material rather than from a captured response: the phishing kit’s own bundle, this investigation’s code, or a commit record in this repository, or a document in either repository that records the analysis, the rules it was made under, or a step of the investigation. The file and line, or the commit, is cited. A statement in one of those documents that merely records what the recipient said is[REPORTED]instead: the label follows what stands behind the claim, not which file it sits in.[REPORTED]- stated by the SMS recipient with no artifact behind it: delivery time, sender ID and the desktop-on-wifi refusal fall in this category.[INFERRED]- a conclusion reached by reasoning over other labelled claims, with that reasoning stated.
A number with no label and no anchor does not appear in this report.
Section 12, Indicators of compromise, carries confirmed campaign infrastructure, each row beside its own evidence label. Hosts of the same phishing platform that are not this campaign’s infrastructure appear there only inside a separate table marked as such. Everything not confirmed, including certificate-transparency hostnames that share the brand misspelling but do not resolve, lives in section 9, Unconfirmed leads and claims not made, and is never repeated as fact elsewhere in this report.
2. The lure
2.1 The message
The campaign reached its target as a single SMS claiming an unpaid parking fee
and threatening a fine and a damaged credit rating. [REPORTED] Two renderings
of the body are preserved, in two different repositories; neither repository
holds an artifact of the message itself. [REPORTED]
Rendering used by this repository (design spec lines 14-16;
osint/report/render.py:483-487). ASCII, diacritics dropped, no URL scheme:
SMS from InfoSMS: Din parkeringsavgift ar fortfarande obetald. Sen betalning
hog botesavgift samre kreditvardighet. Betala nu: easypank.se-45564.xyz/se
Rendering recorded in the kit repository (TAKEDOWN_REPORT.md:107-116).
Swedish diacritics present, https scheme present:
Din parkeringsavgift är fortfarande obetald. Sen betalning hög bötesavgift
sämre kreditvärdighet. Betala nu: https://easypank.se-45564.xyz/se
English gloss: “Your parking fee is still unpaid. Late payment, high fine, worse
credit rating. Pay now: …”. [REPORTED] The brand is misspelled easypank
rather than easypark. The hostname requested in the capture is
easypank.se-45564.xyz, and the registrable domain behind it is
se-45564.xyz, which RDAP returns as a domain object registered through
NameSilo, LLC. [OBSERVED]
(evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json,
payload.url; evidence/raw/rdap/20260918T162140Z-se-45564.xyz.json,
payload.raw.ldhName and payload.facts.registrar) The label to the left of
that registrable domain is easypank.se, so on a narrow phone screen the
visible left part of the hostname reads as a Swedish country tag rather than as
a subdomain of a .xyz registration. [INFERRED]
2.2 Delivery
The sender ID was the alphanumeric string InfoSMS, which carries no
originating number at recipient level. [REPORTED] (kit repository
TAKEDOWN_REPORT.md:107-116) Delivery is recorded as 2026-09-18 16:46:15 CEST,
that is 2026-09-18T14:46:15Z. [REPORTED] (same anchor) Both the sender ID and
the delivery time rest on the recipient’s statement alone: no screenshot, PDU,
message export or carrier record exists in either repository, and the receiving
ROM did not retain the service-centre address, so no route artifact was
recoverable from the handset. [REPORTED] (kit repository
TAKEDOWN_REPORT.md:238-239)
The recipient’s own first click is not recorded either, but it can be placed.
The landing URL preserved from that click carries the token
_v=valid_20260918232359_861d698a2bab901a380af4686943d595, which is the URL
replayed in both handset spent-token envelopes. [OBSERVED]
(evidence/raw/cloak-phone/20260918T161229Z-https___easypank.se-9626654.pics_ESZNhaXCmd__v_valid_20260918232359_861d698a2bab.json
and
evidence/raw/cloak-phone/20260918T161229Z-https___easypank.se-9626654.pics_ESZNhaXCmd__v_valid_20260918232359_861d698a2bab.1.json,
payload.url) The token’s timestamp component is an expiry set to mint time plus
eight hours (2.4), so the mint time was 2026-09-18T23:23:59Z minus 8 h =
2026-09-18T15:23:59Z, or 17:23:59 CEST. [INFERRED] On the reported delivery
time that puts the first click about 37 minutes after the message arrived, and
it bounds the delivery time independently: the SMS was received on or before
15:23:59Z. [INFERRED]
2.3 What the link does
The address in the SMS is a redirector, not the phishing page. A request to
http://easypank.se-45564.xyz/se is answered with three consecutive 302 Found responses before any content is served. [OBSERVED]
(evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json,
payload.headers_text)
HTTP/1.1 302 Found # hop 1, scheme upgrade
Date: Fri, 18 Sep 2026 16:10:52 GMT
Location: https://easypank.se-45564.xyz/se
Server: GoFrame HTTP Server
Via: 1.1 Caddy
HTTP/1.1 302 Found # hop 2, hand-off to the landing host
Date: Fri, 18 Sep 2026 16:10:53 GMT
Location: http://easypank.se-9626654.pics/ESZNhaXCmd?_v=valid_20260919001053_54bbe8e57f0bad4be717b02350064502
Server: GoFrame HTTP Server
Set-Cookie: gfsessionid=1is0mob1szv2acdliknx9wqy3nq954ix; Path=/; Expires=Sat, 19 Sep 2026 16:10:53 GMT
Via: 1.1 Caddy
HTTP/1.1 302 Found # hop 3, scheme upgrade again
Date: Fri, 18 Sep 2026 16:10:53 GMT
Location: https://easypank.se-9626654.pics/ESZNhaXCmd?_v=valid_20260919001053_54bbe8e57f0bad4be717b02350064502
Server: GoFrame HTTP Server
Via: 1.1 Caddy
The block above is abridged to the fields discussed here; the full response
headers of all three hops are in payload.headers_text of that envelope.
[OBSERVED] (same envelope)
Hop 2 is where the campaign hands the visitor from the lure host to the landing
host and where the gfsessionid cookie is set, with a 24-hour expiry measured
from the same second as the response Date. [OBSERVED] (same envelope) Hop 2
also targets http://, so the handover travels in cleartext for one hop before
hop 3 upgrades it again. [OBSERVED] (same envelope) The same three-hop shape,
with the same two hosts and the same ordering, is present in the redirect chains
recorded over Tor. [OBSERVED]
(evidence/raw/cloak/20260918T160754Z-http___easypank.se-45564.xyz_se.json,
payload.summary.redirect_chain)
The redirector itself applies no filter. It issued all three hops and a fresh
token to every requester observed, including Tor exits and desktop
User-Agents. [OBSERVED] (of the 56 envelopes in evidence/raw/cloak/, the 28
that start at the lure URL, each a chain of length 3, covering seven client
profiles, six User-Agent strings including Windows Chrome, Linux Firefox
and curl/8.5.0 plus requests sent with no User-Agent header at all) The
filtering happens one host further on, at the landing page, where the same envelopes
record a 404 as the final response; section 5 sets out the two gates there.
[OBSERVED] (same 28 chains, payload.summary.status_code)
2.4 The _v= token
Every minted landing URL carries _v=valid_<14-digit timestamp>_<32 hex>.
[OBSERVED] (30 distinct mints across evidence/raw/cloak/ and
evidence/raw/cloak-phone/) Three properties of that parameter are established
from the captures:
- It is an expiry, set to mint time plus eight hours, in UTC.
[INFERRED]The handset chain above was minted atDate: Fri, 18 Sep 2026 16:10:53 GMTand carriesvalid_20260919001053, exactly eight hours later. The Tor chain whose final response carriesDate: Fri, 18 Sep 2026 16:07:54 GMTreturnedvalid_20260919000752, that is a token minted two seconds before that response (evidence/raw/cloak/20260918T160754Z-http___easypank.se-45564.xyz_se.json). Across all 30 mints the difference between the token’s expiry minus eight hours and the envelope’scollected_atis 0 to 2 seconds, except in the two handset chains, which share one batchcollected_atof 2026-09-18T16:12:29Z and so come out at 79 and 96 seconds.[OBSERVED](the 30 mint envelopes inevidence/raw/cloak/andevidence/raw/cloak-phone/) - It is single use. The recipient’s own token
valid_20260918232359_861d698a2bab...was still about seven hours short of its 2026-09-18T23:23:59Z expiry when it was replayed at 16:12:29Z, and the landing host answered 404 with a 9-byte body to both a mobile and a desktop User-Agent from the accepted network.[OBSERVED](evidence/raw/cloak-phone/20260918T161229Z-https___easypank.se-9626654.pics_ESZNhaXCmd__v_valid_20260918232359_861d698a2bab.json, casespent-token-mobile-ua, and evidence/raw/cloak-phone/20260918T161229Z-https___easypank.se-9626654.pics_ESZNhaXCmd__v_valid_20260918232359_861d698a2bab.1.json, casespent-token-desktop-ua) - It is minted unconditionally. Thirty requests to the redirector produced
thirty distinct tokens, with no request refused a token.
[OBSERVED](same 30 chains)
The derivation of the 32-hex component is unknown. It is consistent with a
per-send or per-victim identifier, but nothing in the captured traffic or in
the kit bundle shows how it is generated or what the server looks up with it.
[INFERRED] A practical consequence for investigators and for abuse desks: a
404 from one of these URLs usually means a spent or expired token, not a dead
site, and captures against a live campaign URL are not repeatable. [INFERRED]
2.5 The path segment
The path segment /ESZNhaXCmd is constant. It appears in all 30 observed mints,
always on easypank.se-9626654.pics, never varying with the token. [OBSERVED]
(same 30 chains) The same segment appears on a second campaign host: a urlscan
scan of btalning.click performed at 2026-09-03T08:38:40Z recorded the URL
https://btalning.click/ESZNhaXCmd?bls=QTX0es&_v=valid_20260903163832_c2d6beb87657d474a50a691f87da7674
on the same origin address. [OBSERVED]
(evidence/raw/urlscan/20260918T175939Z-page.ip__220.158.232.231_.json,
payload.results[4], and the identical row at payload.results[0] of
evidence/raw/urlscan/20260919T105718Z-page.domain__btalning.click_.json) Those
envelopes hold the scan time and the URL and nothing else about that visit: a
scan at 2026-09-03T08:38:40Z carrying a token that expires at
2026-09-03T16:38:32Z, 7 h 59 min 52 s later. [OBSERVED] (same two envelopes)
That interval is what the mint-plus-eight-hours rule predicts for a token
minted a few seconds before the scan, so the 2026-09-03 host behaves like the
2026-09-18 one; the mint itself was not captured, and the mint time cannot be
read from these envelopes except through that rule. [INFERRED] The constant
path and the shared token grammar tie the two hosts to the same deployment
fifteen days apart, independently of the shared origin address discussed in
section 4. [INFERRED] The extra bls= parameter in the 2026-09-03 URL has no
counterpart in any 2026-09-18 capture. [OBSERVED] (the same 30 mints) Nothing
held here shows what it carries or which component reads it. [INFERRED]
3. How the operation works, step by step
This section walks the operation from domain registration to cash-out, one step per paragraph, each pointing at the section holding its detailed evidence.
The work is divided between at least four parties. [INFERRED] The bundle
served to the victim is a multi-tenant product rather than a build made for
this campaign: it carries the Swedish parking flow, an unrelated
Spanish-language toll-payment and address flow, Spanish, English and Czech
strings hardcoded alongside the Swedish copy (PATENTE CONSULTADA, Peaje base, Total Amount Due, Platba dokončena, one occurrence each in kit
repository kit_original/assets/index-d25ac0d4.js), a Danish lang attribute
over Swedish text, and no backend hostname of its own. [SOURCE]
(docs/kit-analysis.md sections 1 and 6; the bundle registers one locale,
locale:"dk" with a single Swedish string table, at
this repository’s kit_prettified/assets/index-d25ac0d4.js:24992) The same files
therefore work on any domain that serves them, which is the shape of a kit
written by one party and deployed by another. [INFERRED] Deployment,
registration and the live console are a second role; the SMS a third, since an
alphanumeric sender ID is a bulk-messaging product a handset cannot originate;
and whoever turns the authorised transaction into money a fourth, of which
this investigation holds nothing. [INFERRED]
Step 1, domain registration and host setup. [OBSERVED] The lure and
landing domains were registered through NameSilo, LLC: se-9626654.pics on
2026-09-09T06:43:13Z and se-45564.xyz on 2026-09-18T07:50:58Z. [OBSERVED]
(evidence/raw/rdap/20260918T162141Z-se-9626654.pics.json and
evidence/raw/rdap/20260918T162140Z-se-45564.xyz.json, payload.raw.events)
Both delegate to ns1.dnsowl.com, ns2.dnsowl.com and ns3.dnsowl.com.
[OBSERVED] (evidence/raw/dns/20260918T160849Z-se-45564.xyz.json and
evidence/raw/dns/20260918T160850Z-se-9626654.pics.1.json) A Let’s Encrypt
certificate for one hostname each was issued the same day as the registration
beneath it: not_before 2026-09-09T05:55:00Z for easypank.se-9626654.pics,
2026-09-18T07:27:20Z for easypank.se-45564.xyz. [OBSERVED]
(evidence/raw/certspotter/20260918T164232Z-se-9626654.pics.json and
evidence/raw/certspotter/20260918T164231Z-se-45564.xyz.json,
payload.issuances) Both hostnames resolved to 220.158.232.231.
[OBSERVED] (evidence/raw/dns/20260918T160845Z-easypank.se-45564.xyz.json and
evidence/raw/dns/20260918T160846Z-easypank.se-9626654.pics.json) RDAP returns
that address inside the range 220.158.232.0 - 220.158.232.255, name
BUCT-BD, country BD, described as BUCT COMMUNICATION, with abuse contact
yennp@viettel.com.vn; urlscan scan-time data for the same address gives
AS38623. [OBSERVED] (evidence/raw/rdap/20260918T162147Z-220.158.232.231.json
and evidence/raw/urlscan/20260918T175939Z-page.ip__220.158.232.231_.json)
Section 4 sets out the infrastructure.
Step 2, the SMS send. [REPORTED] One message reached the recipient’s
handset at 2026-09-18 16:46:15 CEST, that is 2026-09-18T14:46:15Z, with the
alphanumeric sender ID InfoSMS, claiming an unpaid parking fee, threatening
a fine and a damaged credit rating, and carrying a single link. [REPORTED]
(kit repository TAKEDOWN_REPORT.md:107-116) The sender ID and the delivery
time both rest on the recipient’s statement; no artifact of the message
exists, and the handset retained no service-centre address, so the delivery
route is visible only in carrier records. [REPORTED] (same anchor) Section 2
holds the message and the independent bound on the delivery time; section 13
the referrals that can trace the route.
Step 3, redirector and gated landing page. [OBSERVED] The address in the
SMS is a redirector: three consecutive 302 Found responses hand the visitor
to a second host, set a gfsessionid cookie on the way, and mint a fresh
single-use _v= token for every requester, refusing nobody: not desktop
browsers, not curl, not Tor exits. [OBSERVED]
(evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json,
payload.headers_text; sections 2.3 and 2.4) The filtering is at the landing
page: a source-network check and a User-Agent check, the kit served only when
both pass. A Swedish mobile-carrier address with a mobile User-Agent received
the 140535-byte page, the same address with a desktop User-Agent seconds later
a 9-byte Not Found, and every request over Tor the same 9 bytes on a run
whose control request was healthy. [OBSERVED]
(evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json
and its .1.json counterpart; section 5) The page therefore does not exist
for anyone not holding a Swedish phone. [INFERRED]
Step 4, live harvesting of what the victim types. [SOURCE] A visitor who
passes both gates is shown an EasyPark-branded page asking first for a vehicle
registration plate and then, on a payment screen, for a card number,
cardholder name, expiry, CVV and phone number; each field is streamed to the
server as it is typed, over the live socket and a parallel HTTP channel, with
no submit button needed. [SOURCE] (docs/kit-analysis.md sections 1 and 5)
The captured session shows both channels in use, with 38 streamed field
updates on the socket and 17 POSTs to the kit’s field-exfiltration endpoint
/JisiRmktje/api/input. [OBSERVED] (kit repository
tools/ws_evidence.jsonl;
evidence/raw/har/20260919T105202Z-https___easypank.se-9626654.pics_JisiRmktje_api_input_token_755a9f6f-b4bc-4ef8-9.json
and its 16 siblings) Abandoning the form therefore withdraws nothing: a
partially typed card number has already left the handset. [INFERRED]
Sections 6 and 7 carry the screens, field names and frames.
Step 5, automated card checking. [OBSERVED] When the payment form is
submitted the client routes nothing itself and waits on a loading spinner, the
next screen being the operator’s to choose. [SOURCE] (docs/kit-analysis.md
section 1) The server answers the submission with a card-network lookup of the
leading digits, returning issuing bank, country, scheme, card type and level
together with a verdict. [OBSERVED] In the captured session two card numbers
were submitted and each answered with a rejection, the first enriched to a
named bank in India, the second to a card scheme and country. [OBSERVED]
(frames 273, 275, 463, 509 and 513, kit repository tools/ws_evidence.jsonl)
This is automated validation and enrichment of stolen cards at the moment of
theft, not passive logging, sorting the cards worth working from the rest.
[INFERRED] Sections 6 and 7 carry the detail.
Step 6, session relay and second-factor capture. [SOURCE] mechanism,
[INFERRED] live use. The kit gives the operator a console-driven hold over
the victim’s screen: the victim can be moved to a code-entry screen, to one
telling them to approve a request in their bank’s app, or to one styled to
imitate the target bank’s own verification; a submitted code can be rejected
with an operator-written error and asked for again, another card can be
demanded, and the victim can be bounced to the real easypark.com at any
moment. [SOURCE] (docs/kit-analysis.md sections 1, 2 and 4) On the wire all
of this arrives as the result_type event, whose content names the action;
the correction behind that reading names one further wire event, reload,
which this capture does not contain, while my-event, otp-valid and
app-valid in the kit repository’s protocol tables are internal in-browser
signals that never cross the network. [OBSERVED] (all three inbound control
frames in the capture are result_type, and neither reload nor any of the
internal names appears in any of the 930 frames, kit repository
tools/ws_evidence.jsonl; correction at KIT_ANALYSIS.md:199-204) That is
the point of the kit: the code or approval
reaches a person who is at that moment entering the stolen card into the real
banking flow, so the second factor authorises the attacker’s transaction
rather than the victim’s, and the retry loop takes as many codes as the bank
issues. [INFERRED] Both test cards were rejected, so no verification screen
was reached and this step is a kit capability, not observed behaviour of these
operators. [OBSERVED] (no verification submission among the 930 frames)
Sections 6 and 7 carry it.
Step 7, session close. [SOURCE] On the operator’s command the victim is
shown a fake payment confirmation and, three seconds later, redirected to the
real https://www.easypark.com/sv-se. [SOURCE] (docs/kit-analysis.md
section 1) The victim is left on the genuine brand’s site with nothing
obviously wrong, which delays the moment they call their bank. [INFERRED]
Section 6 carries it.
Step 8, cash-out. [INFERRED] Nothing in this investigation shows what
happens to the money. A stolen second factor is worthless within minutes, so
the charge, wallet provisioning or transfer it authorises must happen inside
the same session, and the parking fee in the lure is cover for a transaction
of a different size. [INFERRED] Section 13 sets out the referrals.
Two properties make the scheme work. The first is that the domains are
disposable and young. se-45564.xyz was registered at 2026-09-18T07:50:58Z.
[OBSERVED] (evidence/raw/rdap/20260918T162140Z-se-45564.xyz.json) The SMS
was delivered six hours and fifty-five minutes later. [REPORTED] (kit
repository TAKEDOWN_REPORT.md:107-116) The token preserved from the
recipient’s own click puts them on the page inside eight hours of the
registration. [INFERRED] (section 2.2) A domain that young has no reputation
for a filter or blocklist to act on, and the lure and landing hosts are
separate registrations, so blocking the address in the message does not block
the page it leads to. [INFERRED] The second is that the two gates hide the
page from everyone who is not a Swedish phone: a reviewer at a registrar, host
or brand who opens the reported URL from a desktop gets nine bytes of Not Found, which looks like a campaign already taken down. [OBSERVED] (the
desktop-User-Agent row in
evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.1.json;
section 5) An abuse report about this campaign therefore has to carry the
captured evidence, because its recipient cannot reproduce the finding.
[INFERRED]
4. Infrastructure
This section holds the names, addresses, registrations, certificates and response fingerprints an abuse desk works from, each with the envelope it was read from. Every DNS answer was resolved over DNS-over-HTTPS through Tor and every registration record over RDAP; there is no whois here, so a failed lookup is a gap rather than a value from elsewhere (section 10).
4.1 Hosts and addresses
| Hostname | Role | A record | First observed | Latest observation |
|---|---|---|---|---|
easypank.se-45564.xyz |
lure host named in the SMS; HTTP redirector | 220.158.232.231 |
2026-09-18T16:08:45Z | 220.158.232.231 at 2026-09-19T10:54:51Z |
easypank.se-9626654.pics |
landing host; serves the kit | 220.158.232.231 |
2026-09-18T16:08:46Z | no A record at 2026-09-19T10:55:12Z |
avcoa.click |
sibling name on the same address | 220.158.232.231 |
2026-09-18T18:14:09Z | 220.158.232.231 at 2026-09-19T10:56:36Z |
btalning.click |
sibling name on the same address | 220.158.232.231 |
2026-09-18T18:14:15Z | 220.158.232.231 at 2026-09-19T10:56:59Z |
[OBSERVED] (evidence/raw/dns/20260918T160845Z-easypank.se-45564.xyz.json,
evidence/raw/dns/20260919T105451Z-easypank.se-45564.xyz.json,
evidence/raw/dns/20260918T160846Z-easypank.se-9626654.pics.json,
evidence/raw/dns/20260919T105512Z-easypank.se-9626654.pics.json,
evidence/raw/dns/20260918T181409Z-avcoa.click.json,
evidence/raw/dns/20260919T105636Z-avcoa.click.json,
evidence/raw/dns/20260918T181415Z-btalning.click.json,
evidence/raw/dns/20260919T105659Z-btalning.click.json)
Neither registrable parent has an A record of its own, and the two easypank.*
labels have no NS, MX, TXT, CNAME, SOA or AAAA records, so they are plain
hostnames inside their parents’ zones. [OBSERVED]
(evidence/raw/dns/20260918T160848Z-se-45564.xyz.json and
evidence/raw/dns/20260918T160849Z-se-9626654.pics.json for the parents;
evidence/raw/dns/20260918T160845Z-easypank.se-45564.xyz.1.json and
evidence/raw/dns/20260918T160846Z-easypank.se-45564.xyz.json with its .1 to
.4 siblings, and
evidence/raw/dns/20260918T160847Z-easypank.se-9626654.pics.json with its .1
and .2 siblings and
evidence/raw/dns/20260918T160848Z-easypank.se-9626654.pics.json with its .1
and .2 siblings, for the two hostnames)
4.2 Registrations
All four registrable domains are NameSilo, LLC registrations, IANA registrar
id 1479, all delegated to ns1.dnsowl.com, ns2.dnsowl.com and
ns3.dnsowl.com.
[OBSERVED] (the four envelopes below, payload.raw.entities and
payload.facts.nameservers)
| Domain | RDAP handle | Registered (UTC) | Expiry | Envelope |
|---|---|---|---|---|
se-45564.xyz |
D644515746-CNIC |
2026-09-18T07:50:58.0Z | 2027-09-18T23:59:59.0Z | evidence/raw/rdap/20260919T105537Z-se-45564.xyz.json |
se-9626654.pics |
D639688650-CNIC |
2026-09-09T06:43:13.0Z | 2027-09-09T23:59:59.0Z | evidence/raw/rdap/20260919T105554Z-se-9626654.pics.json |
avcoa.click |
DO_1dcdb4021fe3d6faa62b10e826192166-INAMING |
2026-09-05T15:55:26.273Z | 2027-09-05T15:55:26.273Z | evidence/raw/rdap/20260919T105655Z-avcoa.click.json |
btalning.click |
DO_b07f16df9e824d61205c08078811f578-INAMING |
2026-09-02T08:23:58.644Z | 2027-09-02T08:23:58.644Z | evidence/raw/rdap/20260919T105717Z-btalning.click.json |
RDAP returned the following state for each registration on 2026-09-19.
[OBSERVED] (the same four
envelopes, payload.raw.status, payload.raw.events and the abuse entity
nested under the registrar entity)
| Domain | Last changed | RDAP statuses | Abuse contact |
|---|---|---|---|
se-45564.xyz |
2026-09-18T07:51:03.0Z | server transfer prohibited, client transfer prohibited, add period | abuse@namesilo.com, tel +1.4805240066 |
se-9626654.pics |
2026-09-18T23:47:11.0Z | server hold, server transfer prohibited, client hold, client transfer prohibited | abuse@namesilo.com, tel +1.4805240066 |
avcoa.click |
2026-09-10T15:55:52.041Z | client transfer prohibited | support@namesilo.com, tel +1.6024928198 |
btalning.click |
2026-09-07T08:24:52.299Z | client transfer prohibited | support@namesilo.com, tel +1.6024928198 |
The same registrar publishes two different abuse addresses across these four
registrations. [OBSERVED] (same four envelopes, payload.facts.abuse_email)
A report covering all four registrable domains therefore has to carry both.
[INFERRED]
se-9626654.pics was not on hold when first collected: at 2026-09-18T16:21:41Z
it carried only the two transfer-prohibited statuses and a last-changed of
2026-09-17T09:24:08.0Z. [OBSERVED]
(evidence/raw/rdap/20260918T162141Z-se-9626654.pics.json) At 2026-09-19T10:55Z
it carries both hold statuses, and every record type queried in that zone
answers empty. [OBSERVED]
(evidence/raw/rdap/20260919T105554Z-se-9626654.pics.json; the seven
DNS-over-HTTPS envelopes of 2026-09-19,
evidence/raw/dns/20260919T105539Z-se-9626654.pics.json (A),
evidence/raw/dns/20260919T105541Z-se-9626654.pics.json (AAAA),
evidence/raw/dns/20260919T105543Z-se-9626654.pics.json (NS),
evidence/raw/dns/20260919T105545Z-se-9626654.pics.json (MX),
evidence/raw/dns/20260919T105547Z-se-9626654.pics.json (TXT),
evidence/raw/dns/20260919T105550Z-se-9626654.pics.json (CNAME) and
evidence/raw/dns/20260919T105552Z-se-9626654.pics.json (SOA), each
payload.values empty) The landing domain was
suspended between those two observations, which is why the host that served the
kit no longer resolves; nothing here records who requested the hold, and the
lure domain was not suspended. [INFERRED] (4.1)
Each zone’s SOA is served by dnsowl.com with a distinct serial: 1789718403
for se-45564.xyz, 1788936584 for se-9626654.pics, 1788623804 for
avcoa.click, 1788337543 for btalning.click, all with the identical
hostmaster.dnsowl.com. <serial> 7200 1800 1209600 600 tail. [OBSERVED]
(evidence/raw/dns/20260918T160849Z-se-45564.xyz.4.json,
evidence/raw/dns/20260918T160851Z-se-9626654.pics.1.json,
evidence/raw/dns/20260918T181414Z-avcoa.click.1.json,
evidence/raw/dns/20260918T181421Z-btalning.click.json)
4.3 Certificates
| Certificate covers | Issuer | not_before |
not_after |
Cert Spotter id | Envelope |
|---|---|---|---|---|---|
easypank.se-45564.xyz |
C=US, O=Let's Encrypt, CN=YE1 |
2026-09-18T07:27:20Z | 2026-12-17T07:27:19Z | 17251662632 | evidence/raw/certspotter/20260919T105728Z-se-45564.xyz.json |
easypank.se-9626654.pics |
C=US, O=Let's Encrypt, CN=YE2 |
2026-09-09T05:55:00Z | 2026-12-08T05:54:59Z | 17072948686 | evidence/raw/certspotter/20260919T105729Z-se-9626654.pics.json |
avcoa.click |
C=US, O=Let's Encrypt, CN=YE1 |
2026-09-05T15:05:21Z | 2026-12-04T15:05:20Z | 17004487410 | evidence/raw/certspotter/20260919T105724Z-avcoa.click.json |
btalning.click |
C=US, O=Let's Encrypt, CN=YE1 |
2026-09-02T07:30:50Z | 2026-12-01T07:30:49Z | 16939788574 | evidence/raw/certspotter/20260919T105725Z-btalning.click.json |
Each certificate carries one name and no wildcard, and each query used
include_subdomains=true, so these are every issuance Cert Spotter holds for
the four registrable domains. [OBSERVED] (the four envelopes,
payload.issuances[*].dns_names and payload.url) crt.sh holds neither of the
two easypank certificates: the easypank% query, which covers both of those
hostnames, returns 6 rows over two other hostnames, neither a campaign host
(section 9). [OBSERVED] (evidence/raw/ct/20260919T105739Z-easypank_.json,
payload.row_count and payload.names) No crt.sh query covering avcoa.click
or btalning.click was run: the only .click query attempted was the
structural %.se-%.click, which covers neither name and is recorded as a read
timeout after three attempts.
[OBSERVED] (evidence/raw/ct/20260919T111715Z-.se-.click.json, status and
payload.error and payload.attempts) A crt.sh miss is not evidence that no certificate exists; for
these four registrable domains Cert Spotter is the only certificate
transparency source that returned an issuance here. [INFERRED]
In all four cases the certificate’s not_before precedes the RDAP registration
event. [OBSERVED] (the four Cert Spotter envelopes above and the four RDAP
envelopes in 4.2)
| Domain | Certificate not_before |
RDAP registration | Certificate precedes by |
|---|---|---|---|
btalning.click |
2026-09-02T07:30:50Z | 2026-09-02T08:23:58.644Z | 53 min 8.644 s |
avcoa.click |
2026-09-05T15:05:21Z | 2026-09-05T15:55:26.273Z | 50 min 5.273 s |
se-9626654.pics |
2026-09-09T05:55:00Z | 2026-09-09T06:43:13.0Z | 48 min 13 s |
se-45564.xyz |
2026-09-18T07:27:20Z | 2026-09-18T07:50:58.0Z | 23 min 38 s |
A certificate cannot be validated for a domain the requester does not yet
control, so the two timestamps cannot both record when the operators took the
name, and nothing in these envelopes distinguishes a registry timestamp from a
registrar one or shows what the certificate authority put in not_before
relative to issuance. [INFERRED] Section 11 records it as unresolved; until
it is, the registration event and not the certificate is the timestamp to cite
for first control of a name. [INFERRED]
4.4 The origin address
All four hostnames resolved to the single address 220.158.232.231 when they
were collected on 2026-09-18, and the three that still resolve on 2026-09-19
still resolve to it. [OBSERVED] (the eight DNS envelopes in 4.1) Its reverse lookup is empty. [OBSERVED]
(evidence/raw/dns/20260918T160855Z-220.158.232.231.json, PTR for
231.232.158.220.in-addr.arpa) RDAP returns the following, quoted as the
envelope has it. [OBSERVED]
(evidence/raw/rdap/20260919T105723Z-220.158.232.231.json)
handle: 220.158.232.0 - 220.158.232.255
startAddress: 220.158.232.0
endAddress: 220.158.232.255
ipVersion: v4
name: BUCT-BD
type: ASSIGNED NON-PORTABLE
country: BD
remarks: description: BUCT COMMUNICATION
entity, role abuse: IRT-VIETTEL-CAMBODIA-KH
kind: group
email: yennp@viettel.com.vn
entity, roles technical and administrative: BCA3-AP
fn: BUCT Communication administrator
kind: group
tel: +880-2-8153246
email: admin@buctbd.com
adr: Suite 601-602, 5th Floor, Mahabub Plaza, 4/A, Indira Road,
Dhaka-1215, Bangladesh, Dhaka Dhaka 1215
That object holds no autonomous system number: it has no autnum field and no
occurrence of the string 38623. [OBSERVED] (same envelope) AS38623
appears in this evidence only as urlscan scan-time data on individual scans of
hosts at this address, never as a routing fact collected here, and no owner
name for that system exists in any envelope, so none is asserted. [OBSERVED]
(evidence/raw/urlscan/20260918T175939Z-page.ip__220.158.232.231_.json, the
asn field of each result; section 11)
One adjacent address appears in the evidence: 220.158.233.4, which urlscan
recorded serving easypark.se-toyota.homes at 2026-08-24T10:02:57Z and
easypark.se-toyota.lol at 2026-08-24T10:15:14Z, neither of them confirmed
infrastructure of this campaign. [OBSERVED]
(evidence/raw/urlscan/20260919T100041Z-page.server__GoFrame_HTTP_Server__AND_page.domain__easypark_.json;
sections 8 and 9)
4.5 The HTTP stack
Both campaign hosts answer with the same header set. Every 404 whose headers
were recorded, over Tor and from the handset alike, carries exactly these ten
headers. [OBSERVED] (all 56 envelopes in evidence/raw/cloak/,
payload.summary.headers; the final block of
evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.1.json,
payload.headers_text)
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: *
Access-Control-Allow-Origin: *
Alt-Svc: h3=":443"; ma=2592000
Content-Type: text/plain; charset=utf-8
Date: <response time>
Server: GoFrame HTTP Server
Trace-Id: <32 hex, distinct per response>
Transfer-Encoding: chunked
Via: 1.1 Caddy
The 302 responses, two of them from the lure host and the third from the
landing host, carry the same set with a Location added, without Alt-Svc on
the first and third hop, and the second hop adds the Set-Cookie.
[OBSERVED]
(evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json,
payload.headers_text) Trace-Id is unique per response: the 56 Tor envelopes
carry 56 distinct values; the four responses of the desktop-User-Agent handset
chain, three 302 and a 404, carry four distinct values, while the
mobile-User-Agent chain carries three, one per 302, the 200 having none.
[OBSERVED] (all 56 envelopes in evidence/raw/cloak/,
payload.summary.headers;
evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.1.json
and
evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json,
payload.headers_text) The gfsessionid cookie is set on exactly one
response, the second redirect hop, with a 24-hour expiry measured from that
response’s own Date (section 2.3). [OBSERVED] (the same handset
envelope)
The 200 that served the kit carries a different set, with no Server header
and no Trace-Id. [OBSERVED]
(evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json,
fourth header block)
Accept-Ranges: bytes
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: *
Access-Control-Allow-Origin: *
Alt-Svc: h3=":443"; ma=2592000
Cache-Control: no-store, no-cache, must-revalidate
Content-Length: 140535
Content-Type: text/html; charset=utf-8
Date: Fri, 18 Sep 2026 16:10:54 GMT
Etag: "12d5585e1626af891dc3c1cca51d90c6"
Last-Modified: Wed, 26 Aug 2026 08:00:40 GMT
Pragma: no-cache
Referrer-Policy: strict-origin-when-cross-origin
Via: 1.1 Caddy
X-Content-Type-Options: nosniff
The split is between responses the application generates and files it serves,
so a rule keyed on Server: GoFrame HTTP Server never matches the phishing
page itself, only its redirects and refusals; that banner identifies the
hosting platform rather than these operators (sections 6 and 12). [INFERRED]
No content delivery network sits in front of the origin: the control request to
https://example.com/ over the same Tor circuit returned Server: cloudflare,
CF-RAY: a3d19acd3eec0d30-AMS and cf-cache-status: HIT, while no campaign
response carries any such header. [OBSERVED]
(evidence/raw/cloak-control/20260918T160737Z-https___example.com_.json; a
case-insensitive grep for cf-ray, cf-cache, cloudflare, fastly and
akamai over evidence/raw/cloak/, evidence/raw/cloak-phone/ and
evidence/raw/kit-asset/ returns zero) Via: 1.1 Caddy shows a reverse proxy
at the origin itself; whether 220.158.232.231 runs the kit or fronts another
host is not established here. [INFERRED]
The static assets the landing page loads are not gated: both were retrieved
over a Tor exit with a mobile User-Agent at 2026-09-18T18:09:46Z, two hours
after every Tor request for the page HTML had been refused (section 5).
[OBSERVED]
(evidence/raw/kit-asset/20260918T180946Z-https___easypank.se-9626654.pics_assets_index-d25ac0d4.js.json
and evidence/raw/kit-asset/20260918T180946Z-https___easypank.se-9626654.pics_assets_index-742a24eb.css.json)
| Asset | Status | Bytes | Content-Type |
Etag |
Last-Modified |
|---|---|---|---|---|---|
/assets/index-d25ac0d4.js |
200 | 444050 | application/javascript; charset=utf-8 |
"2821cadf038408cf8d42852f3389a3bd" |
Wed, 26 Aug 2026 08:00:51 GMT |
/assets/index-742a24eb.css |
200 | 34797 | text/css; charset=utf-8 |
"e0a9d2ebc25ed7bb6efecf1a149a934b" |
Wed, 26 Aug 2026 08:00:40 GMT |
Both carry Cache-Control: public, max-age=31536000, immutable. [OBSERVED]
(same two envelopes) That gap is what made offline analysis of the kit possible
from a Tor exit, and it lets a third party confirm a report about one of these
hosts without a Swedish handset. [INFERRED] (section 6)
4.6 Naming scheme
| Element | easypank.se-45564.xyz |
easypank.se-9626654.pics |
|---|---|---|
| Brand label | easypank, one letter from easypark |
easypank |
| Parent prefix | se- |
se- |
| Digits | 45564, 5 digits |
9626654, 7 digits |
| TLD | .xyz |
.pics |
[OBSERVED] (the envelopes in 4.1 and 4.2) What the se- prefix buys the
operators is covered in section 2.1. The digit counts observed are 5 and 7, so
the two differ from each other and the 5-digit case falls outside the
se-<6-8 digits> of the design document, and the scheme also takes a word in
place of the digits, as se-toyota.homes and se-toyota.lol show.
[OBSERVED] (the envelopes in 4.1 and 4.2 for the digits, the urlscan envelope
in 4.4 for the se-toyota names; section 11 for the design document’s wording)
A hunting rule should key on the se- prefix and the brand typo, not on a
digit count. [INFERRED]
The two sibling names use a different generation: no se- prefix and no
subdomain, just a typosquat of a word a Swedish victim expects, avcoa.click
against the parking operator APCOA and btalning.click against betalning,
Swedish for “payment”. [INFERRED] (4.2) The discovery loop watches nine cheap
top-level domains. [SOURCE] (osint/discover.py:24) Three of them, .xyz,
.pics and .click, carry confirmed infrastructure here. [OBSERVED] (4.1)
4.7 Timeline of the infrastructure
Every row is [OBSERVED] except the SMS delivery, which is the recipient’s
statement (section 2.2).
| Timestamp (UTC) | Event | Anchor |
|---|---|---|
| 2026-08-24T10:02:57Z, 10:15:14Z | easypark.se-toyota.homes and .lol scanned at 220.158.233.4; naming-scheme variants, not confirmed campaign infrastructure |
evidence/raw/urlscan/20260919T100041Z-page.server__GoFrame_HTTP_Server__AND_page.domain__easypark_.json |
| 2026-08-26T08:00:40Z, 08:00:51Z | Last-Modified of the kit HTML and CSS, then the JS: the build the landing host later served |
evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json, evidence/raw/kit-asset/20260918T180946Z-https___easypank.se-9626654.pics_assets_index-742a24eb.css.json, evidence/raw/kit-asset/20260918T180946Z-https___easypank.se-9626654.pics_assets_index-d25ac0d4.js.json |
| 2026-09-02T07:30:50Z | Certificate not_before for btalning.click |
evidence/raw/certspotter/20260919T105725Z-btalning.click.json |
| 2026-09-02T08:23:58.644Z | btalning.click registered at NameSilo |
evidence/raw/rdap/20260919T105717Z-btalning.click.json |
| 2026-09-03T08:38:40Z | btalning.click scanned on the origin serving /ESZNhaXCmd with a _v= token (section 2.5) |
evidence/raw/urlscan/20260919T105718Z-page.domain__btalning.click_.json |
| 2026-09-05T15:05:21Z | Certificate not_before for avcoa.click |
evidence/raw/certspotter/20260919T105724Z-avcoa.click.json |
| 2026-09-05T15:55:26.273Z | avcoa.click registered at NameSilo |
evidence/raw/rdap/20260919T105655Z-avcoa.click.json |
| 2026-09-06T06:30:24Z, 09:30:17Z | avcoa.click scanned on the origin address |
evidence/raw/urlscan/20260919T105656Z-page.domain__avcoa.click_.json |
| 2026-09-09T05:55:00Z | Certificate not_before for easypank.se-9626654.pics |
evidence/raw/certspotter/20260919T105729Z-se-9626654.pics.json |
| 2026-09-09T06:43:13.0Z | se-9626654.pics registered at NameSilo |
evidence/raw/rdap/20260919T105554Z-se-9626654.pics.json |
| 2026-09-17T09:24:08.0Z | se-9626654.pics last changed, one day before the SMS; what changed is not recorded |
evidence/raw/rdap/20260918T162141Z-se-9626654.pics.json |
| 2026-09-18T07:27:20Z | Certificate not_before for easypank.se-45564.xyz |
evidence/raw/certspotter/20260919T105728Z-se-45564.xyz.json |
| 2026-09-18T07:50:58.0Z | se-45564.xyz registered at NameSilo; last changed 07:51:03.0Z |
evidence/raw/rdap/20260919T105537Z-se-45564.xyz.json |
| 2026-09-18T14:46:15Z | SMS delivered, 6 h 55 min 17 s after that registration [REPORTED] |
kit repository TAKEDOWN_REPORT.md:107-116 |
| 2026-09-18T16:07:37Z to 16:08:30Z | Tor probe grid, 56 cells and 2 controls | evidence/raw/cloak/, evidence/raw/cloak-control/ |
| 2026-09-18T16:10:52Z to 16:11:12Z | Four handset requests; the kit served once. The range is the Date headers of the two lure-host chains; the two spent-token requests carry no response timestamp |
evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json, evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.1.json |
| 2026-09-18T18:09:46Z | Kit JS and CSS retrieved over Tor | evidence/raw/kit-asset/20260918T180946Z-https___easypank.se-9626654.pics_assets_index-742a24eb.css.json |
| 2026-09-18T23:47:11.0Z | se-9626654.pics last changed; by the next observation it was on registry and registrar hold |
evidence/raw/rdap/20260919T105554Z-se-9626654.pics.json |
| 2026-09-19T10:55:12Z | easypank.se-9626654.pics no longer resolves |
evidence/raw/dns/20260919T105512Z-easypank.se-9626654.pics.json |
4.8 Vantage points
Three Tor exit addresses appear in the envelopes, recorded per request in an
exit_ip field. [OBSERVED] (the envelopes counted below) No geolocation of
any of them exists in this evidence, so none is asserted. [OBSERVED] (the
envelopes record the exit address and nothing else about the exit; section 11)
| Exit address | Envelopes | When |
|---|---|---|
185.220.101.150 |
56 in evidence/raw/cloak/ and 2 in evidence/raw/cloak-control/ |
2026-09-18T16:07:37Z to 16:08:30Z |
171.25.193.131 |
2 in evidence/raw/kit-asset/ |
2026-09-18T18:09Z |
192.42.116.60 |
62 in evidence/raw/tier3-verify/ |
2026-09-19T10:03:05Z to 10:06:14Z |
[OBSERVED] (the exit_ip field of each of those 122 envelopes) The one
capture not made over Tor is the handset session; section 5 has its path,
section 10 the method.
5. Gating and cloaking
The landing host serves the phishing kit only to requests that pass two
independent checks, and answers everything else with nine bytes. [INFERRED]
(5.1, 5.3) This section records what each combination of source network, client
and token was served, together with the control request that makes a refusal
interpretable at all. It
describes the operators’ access control as observed; it is not a procedure for
reaching the page, and section 10 holds the constraints the capture was made
under.
5.1 The observation matrix
Every row is 2026-09-18. “Fresh” means a token minted by the lure host in the same chain; “spent” means the recipient’s own SMS token, already consumed (section 2.4).
| Source network | User-Agent | Token | Result | Envelope |
|---|---|---|---|---|
| Swedish mobile carrier, cellular LTE | Android Chrome mobile | fresh | 200, the kit, 140535 bytes |
evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json, case lure-mobile-ua |
| the same handset and connection, 18 s later | desktop Chrome on Windows | fresh | 404, 9 bytes |
evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.1.json, case lure-desktop-ua |
| the same handset and connection | Android Chrome mobile | spent | 404, 9 bytes |
evidence/raw/cloak-phone/20260918T161229Z-https___easypank.se-9626654.pics_ESZNhaXCmd__v_valid_20260918232359_861d698a2bab.json, case spent-token-mobile-ua |
| the same handset and connection | desktop Chrome on Windows | spent | 404, 9 bytes |
evidence/raw/cloak-phone/20260918T161229Z-https___easypank.se-9626654.pics_ESZNhaXCmd__v_valid_20260918232359_861d698a2bab.1.json, case spent-token-desktop-ua |
Tor exit 185.220.101.150, 28 cells from the lure URL |
7 client profiles, including 3 mobile | fresh, a distinct one per cell | 404, 9 bytes, all 28 cells |
the 28 se-45564 envelopes in evidence/raw/cloak/ |
Tor exit 185.220.101.150, the same 28 cells against the landing URL |
the same 7 profiles | spent | 404, 9 bytes, all 28 cells |
the 28 861d698a2bab envelopes in evidence/raw/cloak/ |
[OBSERVED] (the four evidence/raw/cloak-phone/ envelopes, payload.case,
payload.status_code, payload.body_length and payload.body_sha256; all 56
evidence/raw/cloak/ envelopes, payload.summary)
The 56 Tor cells are uniform: every one a status: ok envelope with status
code 404, body_length 9, body sha256
0019dfc4b32d63c1392aa264aed2253c1e0c2fb09216f8e2cc269bbfb8bb49b5,
Content-Type: text/plain; charset=utf-8 and exit_ip 185.220.101.150,
collected between 2026-09-18T16:07:39Z and 16:08:30Z; no error cell, no other
status code. [OBSERVED] (aggregation over all 56 envelopes in
evidence/raw/cloak/) That body is preserved byte for byte: the ASCII
string Not Found, no markup. [OBSERVED]
(evidence/artifacts/0019dfc4b32d63c1392aa264aed2253c1e0c2fb09216f8e2cc269bbfb8bb49b5.html)
The single 200 carries the 140535-byte kit page, sha256
a98ddbfa1083b0c6893b60c6bc019b9ffef3dbc36528e0820dc332e8d78691d8.
[OBSERVED]
(evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json,
payload.body_sha256)
The 28 cells are 7 client profiles, six User-Agent strings (iPhone Safari,
Android Chrome, iPad Safari, desktop Chrome, desktop Firefox and curl/8.5.0)
plus requests sent with no header at all, by 2 Accept-Language values by 2
Referer values. [SOURCE] (osint/collect/cloak.py:29-61 and :64-78) Each
combination was run once against each URL. [OBSERVED] (the 28 distinct
payload.cell_id values, each appearing twice in evidence/raw/cloak/)
5.2 The control request
A phishing kit refusing Tor exits and a content delivery network refusing Tor
exits look identical from the client side, so a run of nothing but 404s
proves nothing on its own. [INFERRED] Each probe run therefore verifies the
circuit first: it calls exit_identity() against check.torproject.org,
fetches the non-campaign control URL https://example.com/ over the same
session, and only then runs the grid, the control envelope being written
before the first grid cell. [SOURCE] (osint/collect/cloak.py:99-142, the
write at :142) A failed precondition writes a cloak-precondition error
envelope and sends nothing to the operators. [SOURCE]
(osint/collect/cloak.py:130-141) No such envelope exists, so both runs passed.
[OBSERVED] (evidence/raw/ contains no cloak-precondition directory)
Both controls succeeded from the exit address that produced the 56 refusals:
at 2026-09-18T16:07:37Z and 16:07:50Z, exit 185.220.101.150, status 200,
559 bytes, body sha256 ff67a9d764d6... (the control body is not stored as an
artifact and its hash is therefore not a line of evidence/MANIFEST.sha256,
so only the prefix is cited), Server: cloudflare.
[OBSERVED]
(evidence/raw/cloak-control/20260918T160737Z-https___example.com_.json and
evidence/raw/cloak-control/20260918T160750Z-https___example.com_.json,
payload.summary) The exit address that an unrelated site served a normal
page was given nine bytes by the campaign seconds later, so the refusal is the
operators’ and not the circuit’s. [INFERRED]
5.3 The two gates, isolated
Both gates sit on the landing host; the redirector in front applies no check
of its own (section 2.3). [OBSERVED] (the 28 se-45564 envelopes in
evidence/raw/cloak/, each a 3-hop chain ending in 404)
The two handset rows isolate the client check. Same device, same cellular
connection, both chains minting their own fresh token, 18 seconds apart by the
response Date headers (Fri, 18 Sep 2026 16:10:52 GMT to 16:10:54 GMT
against 16:11:10 GMT to 16:11:12 GMT): only the User-Agent differed, and
the result flipped from the 140535-byte kit to nine bytes. [OBSERVED]
(evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json
and its .1.json counterpart, payload.headers_text and
payload.user_agent)
The Tor cells isolate the source-network check. All 28 lure-URL cells were
issued a distinct fresh token by the redirector, each expiring eight hours
after the second the cell was collected, and all 28 were still refused at the
landing page. [OBSERVED] (the 28 se-45564 envelopes in evidence/raw/cloak/:
28 distinct _v= values in payload.summary.final_url, expiry minus eight
hours falling 0 to 2 seconds from each collected_at, and
payload.summary.status_code 404 in every one) Three of those seven
client profiles are mobile, so a mobile client holding a valid token was
refused for its network alone. [OBSERVED]
(the iphone_safari, android_chrome and ipad_safari cells, 12 of those 28
envelopes) Both checks are therefore real and independent, and neither a spent
token nor the client string accounts for the Tor result. [INFERRED]
5.4 What is and is not established
The conclusion those observations support, quoted as docs/site-access.md
states it; this report does not go beyond it.
What is established: a Swedish mobile carrier connection is accepted; Tor is
refused. What is NOT established, because we did not run the clean test:
whether a Swedish home broadband connection with a mobile User-Agent would be
accepted. The operators most likely allowlist mobile carrier address space,
since the campaign is delivered by SMS to phones, but treat "Swedish broadband
will work" as unverified rather than fact.
The block above is [INFERRED] (docs/site-access.md, “Check 1: source
network”, reasoning over the matrix in 5.1).
Two further limits apply. The refusal of the recipient’s own desktop on home
wifi, which is what first made the campaign look inconsistent, rests on the
recipient’s account alone: no envelope exists for that request. [REPORTED]
And nothing in this evidence locates any Tor exit geographically, so what is
refused is the recorded exit addresses, not a named country. [OBSERVED] (the
envelopes record exit_ip and no geolocation field; sections 4.8 and 11)
5.5 Provenance of the handset requests
The four non-Tor rows were made from the handset that received the SMS, over
adb, with the reporter’s authorisation; all four envelopes carry the same
provenance block. [OBSERVED] (payload.provenance of the four envelopes in
evidence/raw/cloak-phone/)
| Field | Value |
|---|---|
device |
a Swedish mobile handset |
method |
adb shell curl on a connected Android device |
network |
a Swedish mobile carrier, on cellular data with wifi disabled |
via_tor |
false |
The handset’s device and subscriber identifiers are withheld from this published copy and are recorded in the copy held for the authorities.
The note recorded with them, verbatim:
These four requests did not go through Tor. They were made with the reporter's
explicit authorisation from the handset that received the SMS. The operators'
logs already held this address from the reporter's own click. The Tor path
could not distinguish a spent token from source-network filtering, and this
comparison does.
[OBSERVED] (same four envelopes, payload.provenance.note) Each envelope
carries its exact command, of the form
adb shell curl -sL -A '<user agent>' '<url>': a plain GET following
redirects, with one User-Agent override and no data of any kind sent.
[OBSERVED] (the four envelopes, command)
5.6 Why the finding is hard to reproduce
Three conditions must hold at once for the landing host to serve the page: the
request must leave from an accepted network, the only one confirmed accepted
being a Swedish mobile carrier on mobile data; it must present a mobile browser
User-Agent; and it must carry a fresh, unspent token, which only the lure
host mints. [INFERRED] (docs/site-access.md, “What a request must look like
to be served the kit”, over the matrix in 5.1)
An Android emulator does not by itself satisfy the first. A Waydroid container
sends a mobile Android User-Agent and so passes the client check, but it has
no modem of its own and egresses on the host machine’s default route; where
that host’s own egress is broadband, VPN or a datacentre, the case
docs/site-access.md describes, the operators see that network, the source
check fails, and changing the browser or its User-Agent inside the container
cannot move it, because the failing check is on the network the traffic leaves
from. [INFERRED] (docs/site-access.md, “Why Waydroid gets ‘Not found’”; 5.1)
The session captured under evidence/raw/har/ is the other case. A browser
presenting a WayDroid User-Agent
Mozilla/5.0 (Linux; Android 13; WayDroid x86_64 Device Build/TQ3A.230901.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/146.0.7680.153 Mobile Safari/537.36
was served 200 with the 140535-byte kit body, sha256 a98ddbfa1083....
[OBSERVED]
(evidence/raw/har/20260919T105202Z-https___easypank.se-9626654.pics_ESZNhaXCmd__v_valid_20260919031957_33bccaec191c.json,
payload.summary.status, payload.summary.body_length,
payload.summary.body_sha256 and payload.summary.request_headers) That
session must therefore have left from a network the landing host accepts.
[INFERRED] Which network that was is not recorded, and
docs/site-access.md states only that a Swedish mobile carrier is the one
egress confirmed accepted (5.4), so the path is left unstated here and in
7.2. [SOURCE] (docs/site-access.md, “What a request must look like to be
served the kit”)
Section 3 sets out what a reviewer at a registrar, host or brand-protection
desk sees on opening a reported URL. The refusal is not specific to these two
hosts either: of the 62 EasyPark-impersonation candidate names checked from a
Tor exit on 2026-09-19, none returned a phishing page, and four of the six that
answered at all returned a 9-byte 404 from addresses unrelated to this
campaign’s origin. [OBSERVED] (the 62 envelopes in evidence/raw/tier3-verify/,
payload.verdict, payload.body_length and payload.resolves_to; section 8)
The ungated static assets (section 4.5) are therefore the one part of this
finding a third party can check for itself. [INFERRED]
6. The phishing kit
This section is static analysis of the preserved bundle plus one static capture of a real operator session. It describes what the code does and what an investigator or defender can key on; it is not a guide to running the kit.
6.1 Provenance and integrity
The landing host served three files: index.html (140535 bytes), assets/index-d25ac0d4.js (444050 bytes) and assets/index-742a24eb.css (34797 bytes), with sha256 hashes a98ddbfa1083... for the HTML, c8ccfd832d7f9b2e6ed47bbf455395776aec71aa22cdde790a05a1f17dd66821 for the JS and 742a24ebaf60812317b40901165eba84d506c54938607911173959dbc4c3f045 for the CSS. [OBSERVED] (evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json
and evidence/raw/kit-asset/20260918T180946Z-https___easypank.se-9626654.pics_assets_index-d25ac0d4.js.json
and evidence/raw/kit-asset/20260918T180946Z-https___easypank.se-9626654.pics_assets_index-742a24eb.css.json,
payload.body_length and payload.body_sha256) The same three files preserved in this repository and in the kit repository’s own kit_original/ copy hash identically, confirming the preserved bundle matches what the host served. [SOURCE] (sha256sum against kit_original/index.html, kit_original/assets/index-d25ac0d4.js and kit_original/assets/index-742a24eb.css, both repositories)
The three files’ Last-Modified headers fall within eleven seconds of each other: 08:00:40Z for the HTML and CSS and 08:00:51Z for the JS, all on 2026-08-26, which is the kit build date. [OBSERVED] (evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json, payload.headers_text;
evidence/raw/kit-asset/20260918T180946Z-https___easypank.se-9626654.pics_assets_index-d25ac0d4.js.json
and evidence/raw/kit-asset/20260918T180946Z-https___easypank.se-9626654.pics_assets_index-742a24eb.css.json,
payload.headers) The operators gate the page HTML but not these two assets, so both were retrievable over Tor; the retrieval and the gating comparison are section 4.5. [OBSERVED] (same two kit-asset envelopes, status_code: 200)
6.2 Technology and libraries
The bundle is a Vue single-page application compiled with Vite, carrying vendored banner or version-constant strings for Vue 3.5.3, vue-router v4.4.3, pinia 2.2.2, vue-i18n 10.0.4, axios 1.7.7, lodash 4.17.21 and vue-scrollto 2.20.0 (axios and lodash carry theirs as a bare code constant, not a license banner), plus a socket.io/engine.io client and a second, hand-written native WebSocket wrapper (section 6.5). [SOURCE] (grep -o -F in kit_original/assets/index-d25ac0d4.js returns at least one match for each of 3.5.3, vue-router v4.4.3, 2.2.2, 10.0.4, 1.7.7, 4.17.21 and 2.20.0; docs/kit-analysis.md sections 2 and 6) The app mounts into <div id="app"> and declares the base path /we194_cz_etc_easypark/ in its HTML <base href> tag, with the same string twice more in the JS; the runtime-fetched header.html and footer.html resolve under that path, while the two bundled asset files are referenced at the site root as /assets/index-d25ac0d4.js and /assets/index-742a24eb.css. [SOURCE] (grep -o -F 'we194_cz_etc_easypark' returns 2 in the JS; <base href="/we194_cz_etc_easypark/"> at kit_original/index.html:5, the two /assets/ references at :6023-6024 and id="app" at :6029; docs/kit-analysis.md:404-407) The captured session bears that out: five of its requests are /assets/ paths at the site root, both bundle files among them, and four are under the base path, the two runtime fragments and two favicons. [OBSERVED] (the five /assets/ and four we194_cz_etc_easypark envelopes in evidence/raw/har/, payload.summary.url)
6.3 A shared, multi-tenant template
Several details are inconsistent with a page built for this campaign alone. The HTML root element declares <html lang="da", Danish, while the visible title is Swedish (Parkeringsappen som ger dig mer tid till annat | EasyPark). [SOURCE] (kit_original/index.html:2 and :9) The JS carries a Czech confirmation string, Platba dokončena, that never renders in this campaign’s flow, and toll-payment strings (PATENTE CONSULTADA and Peaje base in Spanish, Total Amount Due in English) belonging to an unreachable /pay to /address sub-flow compiled into the same bundle. [SOURCE] (grep -o -F returns 1 for each of Platba dokončena, PATENTE CONSULTADA, Peaje base and Total Amount Due in kit_original/assets/index-d25ac0d4.js; docs/kit-analysis.md sections 1 and 6) The base-path convention itself, <random-token>_<country>_etc_<brand>, reads as a per-deployment slug generator, not a hand-built value; this deployment’s brand suffix is easypark. [SOURCE] (docs/kit-analysis.md section 6) This is one shared kit template serving several brands and countries from a single build, of which only a subset of screens is live in any one deployment. [INFERRED]
6.4 The victim-facing screens
The router table wires nine paths; the observed flow and the operator-only screens are distinguished by whether a victim-facing button pushes to them, or only an operator’s command over the socket does. [SOURCE] (docs/kit-analysis.md section 1, router table at lines 20-32)
| Route | Reached by | Asks the victim for |
|---|---|---|
/home |
page load | vehicle registration plate number |
/card |
submit on /home |
card number, cardholder name, expiry, CVV, phone |
/otpValid |
operator command only | a single one-time code (verifyCode) |
/customOtpValid |
operator command only | every custom-input field present in the operator-supplied HTML, keyed by its data-verify-key attribute (can imitate a QR code or a call-to-confirm screen) |
/appValid |
operator command only | a fallback manual code (appVerifyCode), while showing a “waiting for app approval” status |
/success |
operator command (success) |
nothing; a fake confirmation, then a 3-second redirect |
/pay, /address |
unreachable from this deployment’s /home |
payment amount display; full billing address (Spanish/Chilean sub-flow) |
/temp |
operator command, transiently | nothing; a bare route used to force Vue Router to re-mount a screen the victim may already be on |
Seven of the eight rows cover eight of the router table’s nine paths (/pay and /address share a row). The ninth, root /, carries the same route name as /home but mounts a different component, a placeholder view that shows a spinner for two seconds and asks for nothing. The eighth row, /temp, is not a router path at all; it is a componentless remount-only path. [SOURCE] (docs/kit-analysis.md section 1, router table at lines 20-32, /temp at line 32; the two components are Bw, wrapping IndexView, and jT, wrapping HomeView, in kit_original/assets/index-d25ac0d4.js) A card rejection returns the victim to /card with an operator-written message rather than to any dead end, and kickOut or block end the session by redirecting to the real easypark.com. [SOURCE] (docs/kit-analysis.md sections 1 and 2)
6.5 Transport and the operator’s control channel
A session opens with an empty POST to /JisiRmktje/api. [SOURCE] (docs/kit-analysis.md section 3, lines 236-239; grep -o -F '/JisiRmktje/api' returns 2 in kit_original/assets/index-d25ac0d4.js, the standalone bootstrap path and the /input variant counted together) The response carries a server-issued relay token, a transport-mode flag, and a per-campaign text block (pay_amount, error_card_msg, deny_c_msg, deny_d_msg among its keys). [SOURCE] (docs/kit-analysis.md section 3, lines 242-255) The relay token opens wss://<host>/ws?token=<token>, present twice in the JS; depending on the mode flag this is wrapped either by the bundled socket.io client (mode 2) or by the kit’s own native WebSocket class (any other mode). [SOURCE] (grep -o -F '/ws?token=' returns 2 in the JS; docs/kit-analysis.md section 3, line 254 and section 2, lines 125-136) The two do not converge as cleanly as that shared URL suggests: in socket.io mode the library treats /ws as a namespace, while the engine.io handshake itself runs over the library’s default /socket.io path on the same host, so a network rule keyed only on /ws would miss that mode. [SOURCE] (docs/kit-analysis.md:135) The captured session used only the native-WebSocket path: all 930 frames carry host: easypank.se-9626654.pics and path: /ws?token=755a9f6f-b4bc-4ef8-9118-171fec4947f7, none /engine.io, and the HAR capture of the same session shows a single direct /ws upgrade with no /socket.io handshake request. [OBSERVED] (kit repository tools/ws_evidence.jsonl, all 930 records; evidence/raw/har/20260919T105202Z-https___easypank.se-9626654.pics_ws_token_755a9f6f-b4bc-4ef8-9118-171fec4947f7.json)
The envelope shape varies by direction and message type: of the 930 frames, 538 (all operator-side) carry {event, content, messageId}, e.g. {"event":"login","content":"success","messageId":"ederjq01000dliooq67h2ldmjq3cuvce"}; 349 (all victim-side heartbeat frames) carry only {event, content}; and 43 (the remaining victim-side frames) carry all four fields, {event, content, messageId, timestamp}. [OBSERVED] (kit repository tools/ws_evidence.jsonl, all 930 records, raw field parsed as JSON; counts by field set and dir) The victim side sends five distinct wire events: login, heartbeat, page_type (a beacon fired on every screen mount), input_text (see 6.6) and submit_card. [SOURCE] (docs/kit-analysis.md section 2, lines 173-181) The frames the capture actually contains match this, from the victim side: login 1, page_type 2, input_text 38, submit_card 2, heartbeat 349. [OBSERVED] (tools/ws_evidence.jsonl, event counts by dir: victim->c2)
The operator’s control verbs arrive on one event, result_type, whose content.type field carries the verb: the source lists otpValid, appValid, customOtpValid, success, kickOut, block, otpFail, appFail, customOtpFail, back, reject and refresh. [SOURCE] (docs/kit-analysis.md section 2, lines 183-213) The kit repository’s own correction names one further wire event alongside it, reload. [SOURCE] (kit repository KIT_ANALYSIS.md:199-204) No reload frame appears in this capture. [OBSERVED] (tools/ws_evidence.jsonl, zero occurrences of reload in any of the 930 records) The capture holds exactly three result_type frames, at records 275, 509 and 513, every one carrying type: reject. [OBSERVED] (tools/ws_evidence.jsonl, records 275, 509, 513) A companion internal, in-browser event bus (mitt-style, named Qn in the source) relays some of those verbs to the mounted Vue component under separate names, my-event, otp-valid, app-valid and custom-otp-valid; it never touches the network. [SOURCE] (docs/kit-analysis.md section 2, lines 214-223) An earlier draft of the kit repository’s own analysis listed those internal names as wire events; its correction states that “Detection/monitoring should key on WS result_type, not on otp-valid as a frame name.” [SOURCE] (kit repository KIT_ANALYSIS.md:199-204) None of the four internal names appears in any of the 930 captured frames, consistent with that correction. [OBSERVED] (tools/ws_evidence.jsonl, zero matches for my-event, otp-valid, app-valid or custom-otp-valid as an event value)
6.6 The two exfiltration paths
Everything the victim types is sent twice, on two independent channels, before any submit button is pressed, except in socket.io mode: the shared debounce helper ends o(e,t,n),_m.value!==2&&a(e,t,n), so the HTTP send (a) fires only when _m.value!==2 and is skipped when the backend’s mode flag sets it to 2. [SOURCE] (kit_original/assets/index-d25ac0d4.js, matching o(e,t,n),_m.value!==2&&a(e,t,n); docs/kit-analysis.md section 3, line 254 and section 5, lines 367-373) The captured session ran in the native-WebSocket mode that performs both sends (6.5). Over the socket, each field fires an input_text event, debounced 300 ms per field. [SOURCE] (docs/kit-analysis.md section 5, lines 367-370) In parallel, the same field fires an HTTP POST to /JisiRmktje/api/input, debounced 1000 ms per field; that path also carries the final submit_card record as a fallback whenever the socket is unreachable. [SOURCE] (docs/kit-analysis.md section 3, lines 271-283; grep -o -F '/JisiRmktje/api/input' returns 1 in kit_original/assets/index-d25ac0d4.js) The completed card record itself (cardNumber, cardName, expires, cvv, phone) is otherwise sent only once, as a single submit_card socket event, once the form is submitted. [SOURCE] (docs/kit-analysis.md section 3, lines 284-293)
The kit repository’s own takedown report states the opposite: “All stolen data leaves over WebSocket… HTTP POSTs in the bundle are only the engine.io polling fallback to /engine.io.” [SOURCE] (kit repository TAKEDOWN_REPORT.md:225-231) That is contradicted by direct observation: the captured session’s HAR log holds 17 POST envelopes to /JisiRmktje/api/input on the landing host, plus one POST to the bootstrap endpoint /JisiRmktje/api, and no /engine.io request anywhere in the capture. [OBSERVED] (evidence/raw/har/20260919T105202Z-https___easypank.se-9626654.pics_JisiRmktje_api_input_token_755a9f6f-b4bc-4ef8-9.json, one of 17 matching envelopes in evidence/raw/har/) The session’s WebSocket capture shows the same absence (6.5): none of its 930 frames carry /engine.io. [OBSERVED] (kit repository tools/ws_evidence.jsonl, all 930 records) A monitoring rule that treats /JisiRmktje/api/input as mere polling overhead would miss a genuine, continuous exfiltration channel. [INFERRED]
6.7 Session token persistence
A client-generated UUIDv4 device token is stamped as Token and X-Token request headers and a token query parameter on every HTTP request through the app’s shared axios instance, and is synced to a cookie named token (max-age=34560000, 400 days, SameSite=Lax), to localStorage, to sessionStorage, and to an IndexedDB database (TokenDB, store tokens, key userToken). [SOURCE] (docs/kit-analysis.md section 3, lines 258-268 and section 6; grep -o -F each returning 1 in kit_original/assets/index-d25ac0d4.js for headers.Token, X-Token, TokenDB, userToken and max-age=34560000) The cookie’s use is directly observed: all 17 captured field-exfiltration POSTs carry Cookie: token=755a9f6f-b4bc-4ef8-9118-171fec4947f7. [OBSERVED] (evidence/raw/har/20260919T105202Z-https___easypank.se-9626654.pics_JisiRmktje_api_input_token_755a9f6f-b4bc-4ef8-9.json, payload.summary.request_headers.cookie) This identifies the victim’s browser across reloads and across clearing that removes only one of the four storage locations; per source it is generated independently of the relay token /JisiRmktje/api issues to open the socket, though in this captured session the cookie value and the relay token are identical, an unresolved discrepancy. [INFERRED]
6.8 Cross-domain fingerprints and detection
None of the following depends on this campaign’s hostnames: a search of the bundle for easypank, se-9626654, se-45564 and .pics returns zero matches each, confirming the kit ships with no hardcoded backend host or IP. [SOURCE] (grep -o -F counts of 0 for each string in kit_original/assets/index-d25ac0d4.js; docs/kit-analysis.md section 6)
| Fingerprint | Where | Specificity |
|---|---|---|
/JisiRmktje/api and /JisiRmktje/api/input |
HTTP paths | almost certainly kit-generated, not a framework default |
<random>_<cc>_etc_<brand> base-path pattern |
<base href> and asset URLs |
naming convention; brand and country vary per deployment |
wss://<host>/ws?token= combined with Token/X-Token headers |
live-relay transport | distinctive as a combination; /ws alone is too generic |
{event, content} socket envelope (operator frames add messageId; non-heartbeat victim frames add timestamp too) with inner events page_type, submit_card, input_text |
wire protocol shape | useful only if a sibling’s socket traffic is captured |
index-d25ac0d4.js / index-742a24eb.css content hashes |
exact asset filenames | strongest but narrowest: identical only for this exact build |
[SOURCE] (docs/kit-analysis.md section 7, lines 419-427; the parenthetical in the socket-envelope row is the field-set count of the captured frames, 6.5) These are kit-code fingerprints, independent of the Server: GoFrame HTTP Server / Via: 1.1 Caddy hosting-platform signature discussed in section 4, which identifies the phishing platform rather than the kit or this campaign. [INFERRED] The two most durable of them for live monitoring are the result_type wire event and the JisiRmktje path segment, since neither is generated from the hostname and both would survive a domain and IP rotation this kit’s operators have not yet been observed to make. [INFERRED] (6.5, 6.6, this section)
7. The captured live session
One full session against the operators’ live backend is preserved, on both of
its channels: 930 WebSocket frames in the kit repository’s
tools/ws_evidence.jsonl, and the 40 HTTP requests of the same browser session
as envelopes under evidence/raw/har/. [OBSERVED] (930 records in
tools/ws_evidence.jsonl, all carrying host easypank.se-9626654.pics and
path /ws?token=755a9f6f-b4bc-4ef8-9118-171fec4947f7; 40 envelopes in
evidence/raw/har/, all status: ok and all payload.capture
kit-2026-09-18) This section reports what those two records contain.
7.1 What the session was
The session was a controlled test, run with junk data to record the backend’s
behaviour: “This is a controlled test session using junk data, but it
exercises the real C2 end to end.” [REPORTED] (kit repository
TAKEDOWN_REPORT.md:189-190) The values typed were the registration
plate boy635, card number 1111111111111111 and then 5204480010000005, cardholder name
Bosse Bildoktor, expiry 01/01 corrected to 01/30, CVV 123 and phone
07129284829. [OBSERVED] (the 38 input_text frames and the two
submit_card frames, tools/ws_evidence.jsonl; frames 273 and 463 carry the
full submitCard form) The card numbers are junk by construction:
1111111111111111 fails a Luhn check, and both were rejected by the
operators’ own validation (7.5). [INFERRED]
What the session exercised was real: every request in the record went to, and
every response came from, the campaign’s own hosts. [OBSERVED] (host and path
of all 930 frames in tools/ws_evidence.jsonl; payload.summary.url of all 40
envelopes in evidence/raw/har/, every one under easypank.se-9626654.pics or
easypank.se-45564.xyz)
No verification-code or app-approval step was ever reached: both cards were
rejected, and none of the 930 frames carries a code submission, a code-entry
page transition or any of the kit’s second-factor verbs. [OBSERVED] (event
counts over tools/ws_evidence.jsonl: heartbeat 838, ack 43, input_text
38, login 3, result_type 3, page_type 2, submit_card 2, connect 1,
and zero occurrences of verifyCode, submitValidCode, otpValid,
appValid, otp-valid, app-valid or my-event in the file) The
second-factor screens and the operator-driven navigation described in section 6
are
therefore kit capability read from the bundle, not behaviour observed of these
operators. [INFERRED]
7.2 Capture method and network path
The traffic was recorded by a proxy sitting in front of an Android browser: all
40 envelopes present an Android User-Agent, and 39 also carry
x-requested-with naming the handset’s browser package, withheld here with
the other device identifiers; the exception is the WebSocket
upgrade request, which lacks it. [OBSERVED] (the 40 envelopes in
evidence/raw/har/, payload.summary.request_headers) The envelopes’ recorded
command reads the capture back with mitmdump -r <capture> --set hardump=kit-2026-09-18.har, which is where the 40 envelopes come from,
written on 2026-09-19. [OBSERVED] (the 40 envelopes in evidence/raw/har/,
command and collected_at 2026-09-19T10:52:02Z) The kit repository states the method
as the page opened in Waydroid with traffic through mitmproxy, decoded with
tools/ws_capture.py; no artifact of the capture host exists in either
repository. [REPORTED] (kit repository TAKEDOWN_REPORT.md:104-105 and
README.md:51)
The capture’s network path is not recorded in either repository: the kit
repository gives the method but no egress, and the envelopes carry no
provenance block of the kind the handset requests carry (5.5). [SOURCE]
(kit repository TAKEDOWN_REPORT.md:104-105 and README.md:51; the 40
envelopes in evidence/raw/har/, whose payload keys are capture,
har_file and summary only) docs/site-access.md states only that a
Swedish mobile carrier is the one egress confirmed accepted, so no network
follows from the fact that the kit was served. [SOURCE]
(docs/site-access.md, “What a request must look like to be served the kit”;
5.4) Section 5.6 records the same gap for the 200 that served the kit to
this session’s WayDroid User-Agent.
7.3 Reading the timestamps
The per-record time field is not the capture time. It is identical in all 930
records, 2026-09-18T22:13:19. [OBSERVED] (one distinct time value across
all 930 records of tools/ws_evidence.jsonl) That is the wall clock of the
offline decode run, since the decoder stamps each line as it writes it.
[INFERRED] (kit repository tools/ws_capture.py:54) The real
times are inside the frames. Every victim-to-server frame other than a
heartbeat carries an epoch-ms timestamp, 43 of them, spanning 1789759200876
to 1789759512954, that is 2026-09-18T19:20:00.876Z to 19:25:12.954Z,
21:20:00.876 to 21:25:12.954 CEST. [OBSERVED] (parse of the raw field of
all 930 records: the 43 frames carrying a timestamp are exactly the 43
non-heartbeat victim-to-server frames) Server-to-victim
frames carry no timestamp, which is why the operator rows below have no time
of their own. [OBSERVED] (same parse) The server’s own heartbeats carry
content.time in epoch seconds, 349 of them from 1789759202 to 1789759899,
that is 19:20:02Z to 19:31:39Z, 21:20:02 to 21:31:39 CEST, so the socket stayed
open for six minutes and twenty-six seconds after the last victim frame
carrying a timestamp, frame 463 at 19:25:12.954Z. [OBSERVED] (the 349
server-to-victim heartbeat records of tools/ws_evidence.jsonl that carry a
content.time field, against frame 463)
7.4 The session timeline
Frame numbers are 1-based lines of the kit repository’s
tools/ws_evidence.jsonl; times are each frame’s own embedded timestamp.
[OBSERVED] (the parse above; the same rows appear in the kit repository’s
tools/APPENDIX_session.txt:1-18)
| Frames | CEST | UTC | Direction | Event |
|---|---|---|---|---|
| 1 | – | – | server to victim | login ack success |
| 3 | 21:20:00.876 | 19:20:00.876Z | victim to server | login, tag user, token 755a9f6f-b4bc-4ef8-9118-171fec4947f7, isFirst false |
| 6 | – | – | server to victim | login ack success |
| 7 | 21:20:01.705 | 19:20:01.705Z | victim to server | page_type home |
| 42-49 | 21:20:29.517 | 19:20:29.517Z | victim to server | input_text plate, 3 frames, final boy635 |
| 56 | 21:20:36.624 | 19:20:36.624Z | victim to server | page_type card |
| 72-183 | 21:20:47.896 | 19:20:47.896Z | victim to server | input_text cardNumber, 11 frames, final 1111111111111111 |
| 187-192 | 21:22:05.729 | 19:22:05.729Z | victim to server | input_text expires, 3 frames, final 01/01 |
| 196 | 21:22:07.958 | 19:22:07.958Z | victim to server | input_text cvv, 1 frame, 123 |
| 205-218 | 21:22:14.137 | 19:22:14.137Z | victim to server | input_text cardName, 5 frames, final Bosse Bildoktor |
| 220-246 | 21:22:17.920 | 19:22:17.920Z | victim to server | input_text phone, 9 frames, final 07129284829 |
| 259-267 | 21:22:35.230 | 19:22:35.230Z | victim to server | input_text expires, 3 frames, final 01/30 |
| 273 | 21:22:42.099 | 19:22:42.099Z | victim to server | submit_card, first card |
| 275 | – | – | server to victim | result_type reject, errorCard |
| 449-455 | 21:25:06.283 | 19:25:06.283Z | victim to server | input_text cardNumber, 3 frames, final 5204480010000005 |
| 463 | 21:25:12.954 | 19:25:12.954Z | victim to server | submit_card, second card |
| 509 | – | – | server to victim | result_type reject |
| 513 | – | – | server to victim | result_type reject |
[OBSERVED] (frame indices, events, directions, embedded timestamps and
content of every row above, read from tools/ws_evidence.jsonl)
The burst end times are absent from the appendix and they matter. [OBSERVED]
(kit repository tools/APPENDIX_session.txt:1-18, which gives each burst’s
start time and frame count but not its last frame’s time) The card-number
burst began at 21:20:47.896 CEST, and its last frame, frame 183, at
21:22:03.665 CEST (19:22:03.665Z), already carried the complete sixteen
digits. [OBSERVED] (frames 72 to 183, tools/ws_evidence.jsonl) That is
38.4 seconds before the submit at frame 273: the full number was on the
server well before anything was submitted, so closing the page then would
have withdrawn nothing. [INFERRED]
The plate field is streamed under a different shape from the card fields: the
plate frames carry type Registreringsnummer while every card field carries
type input_card. [OBSERVED] (frame 42 against frames 72 to 267,
tools/ws_evidence.jsonl)
7.5 The operator side
Three frames in the whole session travel from the server as control messages,
all of them result_type, the single wire event that carries every operator
command (section 6); each carries a verdict and a bank-identification-number
lookup of the leading digits. [OBSERVED] (frames 275, 509 and 513,
tools/ws_evidence.jsonl) Frame 275, answering 1111111111111111, carries
type reject, an inner value.type errorCard, and
cardBIN {"bin": 111111, "bank": "CENTRAL BANK OF INDIA", "country": "INDIA", "schema": "LOCAL BRAND", "type": "DEBIT", "level": "CLASSIC"}.
[OBSERVED] (frame 275) Frames 509 and 513, answering 5204480010000005,
carry type reject with no inner value.type, cardBIN {"bin": 520448, "bank": "", "country": "UNITED STATES", "schema": "MASTERCARD", "type": "DEBIT", "level": ""}, and a Swedish error string for the victim,
message2 Betalningen misslyckades. [OBSERVED] (frames 509 and 513)
Frame 275 falls between heartbeats at 19:22:41Z (frame 272) and 19:22:43Z
(frame 277), about a second after the submit at frame 273, 19:22:42.099Z: the
first verdict came back essentially as the card arrived. [OBSERVED] (frames
272, 275 and 277 against frame 273, tools/ws_evidence.jsonl) That immediacy
does not hold for the second card: frames 509 and 513, both answering frame
463 (19:25:12.954Z), fall between heartbeats bracketing them at
19:25:48Z-19:25:50Z and 19:25:50Z-19:25:53Z, roughly 35 and 37 seconds later.
[OBSERVED] (frames 508, 509, 512, 513 and 515 against frame 463,
tools/ws_evidence.jsonl) A delay that long fits a human operator
choosing the next step, as section 6.5 describes, better than grading on
arrival. [INFERRED] (section 6.5)
Frames 509 and 513 also return a server-built cardHistory array with a
submitTime per card, 2026-09-19 03:25:13 for frame 463’s card and
03:22:42 for frame 273’s, each eight hours ahead of that frame’s own UTC
timestamp to the nearest second (19:25:12.954Z and 19:22:42.099Z, the first
rounded up by 46 ms and the second truncated). [OBSERVED] (frames 509 and
513, value.data.cardHistory, against frames 273 and 463) The backend’s
application clock is therefore set to UTC+8, six hours ahead of the victim’s
own CEST display, which says nothing about where the operator sits.
[INFERRED]
Nothing else was sent from the operator side: the remaining server-to-victim
frames are 489 heartbeats, 43 acks, two login acknowledgements and one
connect carrying {"ping_interval": 5, "ping_timeout": 30}. [OBSERVED]
(event and direction counts over tools/ws_evidence.jsonl; frame 2 for the
connect content)
7.6 The session token
One token identifies the victim throughout: 755a9f6f-b4bc-4ef8-9118-171fec4947f7.
It is the path query parameter of all 930 WebSocket frames, the login payload
of frame 3, the cookie and the token and x-token request headers of the
session-bootstrap POST, the Token in that POST’s response, and the query
string of all 17 field-exfiltration POSTs. [OBSERVED]
(tools/ws_evidence.jsonl path field and frame 3;
evidence/raw/har/20260919T105202Z-https___easypank.se-9626654.pics_JisiRmktje_api_token_755a9f6f-b4bc-4ef8-9118-17.json,
payload.summary.request_headers and the stored response body
evidence/artifacts/e3eaf9aa94f833e92c3e09df408c03dd98ba0675f7f6a878bbe1692b566d1e66.bin)
The cookie already carries the token on that first bootstrap request, and no
Set-Cookie for it appears anywhere in the 40 envelopes; the only
Set-Cookie in the capture is the gfsessionid on the second redirect hop.
[OBSERVED] (the bootstrap envelope above, payload.summary.request_headers;
evidence/raw/har/20260919T105202Z-https___easypank.se-45564.xyz_se.json, the
only envelope with a set-cookie response header) That is what a first load
looks like, not evidence of an earlier visit: the token is generated
client-side, stamped into the cookie (and localStorage, sessionStorage and
IndexedDB), and attached to every outgoing request by an interceptor,
bootstrap included. [INFERRED] (section 6.7)
That bootstrap response is also where the backend configures the page for the
victim: it returns "country":"SE", "mode":1, "isFirst":true and a
custom block of operator-editable message strings, one of them the Swedish
Det här kortet stöds inte. Var god byt ut det. [OBSERVED]
(evidence/artifacts/e3eaf9aa94f833e92c3e09df408c03dd98ba0675f7f6a878bbe1692b566d1e66.bin,
418 bytes) That isFirst disagrees with the relay login frame, which carries
isFirst false (7.4, frame 3); section 11 covers the discrepancy.
[OBSERVED] (frame 3, tools/ws_evidence.jsonl, against the bootstrap
response above)
7.7 The HTTP requests of the same session
The 40 har envelopes cover the same browser session, 2026-09-18T19:14:36Z
to 19:25:08Z, and store response bodies only: the envelope schema has no
request-body field, so what the browser POSTed is recoverable only from the
WebSocket frames. [OBSERVED] (payload.summary.started_at of the 40
envelopes; payload.summary keys are body_length, body_sha256, host,
method, mime_type, request_headers, response_headers, started_at,
status, url)
| Time (UTC) | Request | Status | Bytes | Body sha256 |
|---|---|---|---|---|
| 19:14:36.256 | GET landing /ESZNhaXCmd?_v=valid_20260918232359_861d698a2bab... |
404 | 9 | 0019dfc4b32d... |
| 19:14:36.577 | GET landing /favicon.ico |
404 | 9 | 0019dfc4b32d... |
| 19:19:54.024 | GET http://easypank.se-45564.xyz/se |
no response recorded | 0 | – |
| 19:19:54.222 | GET http://easypank.se-45564.xyz/se |
302 | 5 | b0ee315f4ac6... |
| 19:19:55.283 | GET https://easypank.se-45564.xyz/se |
302 | 5 | b0ee315f4ac6... |
| 19:19:55.572 | GET landing /ESZNhaXCmd?_v=valid_20260919031955_b48ac3f511fe2... over HTTP |
302 | 5 | b0ee315f4ac6... |
| 19:19:56.136 | GET the same landing URL over HTTPS |
no response recorded | 0 | – |
| 19:19:56.214 | GET http://easypank.se-45564.xyz/se |
no response recorded | 0 | – |
| 19:19:56.398 | GET http://easypank.se-45564.xyz/se |
302 | 5 | b0ee315f4ac6... |
| 19:19:56.945 | GET https://easypank.se-45564.xyz/se |
302 | 5 | b0ee315f4ac6... |
| 19:19:57.197 | GET landing /ESZNhaXCmd?_v=valid_20260919031957_33bccaec191cb... over HTTP |
302 | 5 | b0ee315f4ac6... |
| 19:19:57.453 | GET the same landing URL over HTTPS |
200 | 140535 | a98ddbfa1083... |
| 19:19:58.602 | GET /assets/index-742a24eb.css |
200 | 34797 | 742a24ebaf60... |
| 19:19:58.605 | GET /assets/index-d25ac0d4.js |
200 | 444050 | c8ccfd832d7f... |
| 19:19:59.436 | GET /we194_cz_etc_easypark/favicon-16x16.png |
200 | 493 | 0a33c850d00d... |
| 19:19:59.439 | GET /we194_cz_etc_easypark/favicon-32x32.png |
200 | 906 | 4182ef039afa... |
| 19:19:59.803 | POST /JisiRmktje/api?token=755a9f6f-... |
200 | 418 | e3eaf9aa94f8... |
| 19:20:00.631 | GET /ws?token=755a9f6f-..., WebSocket upgrade |
101 | 0 | e3b0c44298fc... |
| 19:20:00.883 | GET /we194_cz_etc_easypark/header.html |
200 | 383237 | 60ef3826e47c... |
| 19:20:01.416 | GET /we194_cz_etc_easypark/footer.html |
200 | 53375 | 03049f61761e... |
| 19:20:36.626 | GET /assets/default-d2edf42c.svg |
200 | 630 | d2edf42ccc57... |
| 19:22:42.111 | GET /assets/80066acd3fcfa-80066acd.svg |
200 | 1312 | 80066acd2096... |
| 19:25:07.692 | GET /assets/d9f501073fcfa-d9f50107.svg |
200 | 9499 | d9f50107aa84... |
| 19:20:30 to 19:25:08, 17 requests | POST /JisiRmktje/api/input?token=755a9f6f-... |
200 each | 33 each | 973a77295b63... each |
[OBSERVED] (the 40 envelopes in evidence/raw/har/, payload.summary.method,
url, status, body_length, body_sha256 and started_at; “landing” is
easypank.se-9626654.pics) Full hashes: 0019dfc4b32d63c1392aa264aed2253c1e0c2fb09216f8e2cc269bbfb8bb49b5,
b0ee315f4ac6af09d05f9e6f23ffb606f3b4fec1ba897bd4315592d2a2979876 (the body
Found), 973a77295b63aeb2bc2d960f68ff5698bf5d09746aa8f3f2418e4144c133715f
(the body {"code":0,"message":"","data":{}}),
e3eaf9aa94f833e92c3e09df408c03dd98ba0675f7f6a878bbe1692b566d1e66,
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,
60ef3826e47cb004d36b418c4f9a372dc719077aa96130a4f43091c1db3db8b2,
03049f61761eca5519531b5069b52775a55b06a1a02b445bbd0dea3c0667d6c7,
0a33c850d00dc1f91d4634eaf02dd2d020d6a86731f67daed548fb3c9c50ad42,
4182ef039afa0be9d9a6e3537921f8ed913b0768e76a1233395c800536ba4161,
d2edf42ccc57441bebe758887091cef7b5c94ada72b2f9b2b991a25a5755ec42,
80066acd2096893762dab64b37b03c9de576e948372ac516f05d25f2b1cc988f,
d9f50107aa842d19b7f4bac799d3e6199c2fdbc8c3197f4305b292bb0db143b6; the page
body is a98ddbfa1083b0c6893b60c6bc019b9ffef3dbc36528e0820dc332e8d78691d8 and
the two assets are as in section 6.1. [OBSERVED] (the same envelopes; every
one of these hashes is listed in evidence/MANIFEST.sha256)
Four things in that list are worth naming. The capture opens at 19:14:36Z with a
request carrying the recipient’s own, already spent SMS token, answered with the
same nine bytes of Not Found as every other refused request, five minutes
before the working chain. [OBSERVED]
(evidence/raw/har/20260919T105202Z-https___easypank.se-9626654.pics_ESZNhaXCmd__v_valid_20260918232359_861d698a2bab.json;
section 2.4) The working chain, the three 302 hops of section 2.3, runs
twice: the first attempt’s landing request has no response recorded, and the
second, with a freshly minted _v=valid_20260919031957_33bccaec191cb..., is
answered 200 with the 140535-byte kit page. [OBSERVED] (the ten envelopes
between 19:19:54 and 19:19:57 listed above, payload.summary.status) The
WebSocket upgrade at 19:20:00.631Z is answered 101 with Server: Caddy, and
the first relay frame carries a timestamp 245 milliseconds after it, tying the
two records to one session. [OBSERVED]
(evidence/raw/har/20260919T105202Z-https___easypank.se-9626654.pics_ws_token_755a9f6f-b4bc-4ef8-9118-171fec4947f7.json
against frame 3 of tools/ws_evidence.jsonl) And the 17 POSTs to
/JisiRmktje/api/input fall between 19:20:30 and 19:25:08, interleaved with
the input_text typing bursts of that window (7.4), each answered with the
same 33-byte acknowledgement. [OBSERVED] (the 17 envelopes) The bundle
identifies that path as a per-field HTTP exfiltration channel alongside the
socket. [SOURCE]
(docs/kit-analysis.md:271, “/JisiRmktje/api/input - HTTP exfiltration channel”)
Against this capture, the kit repository’s statement that the bundle’s only
HTTP POSTs are an engine.io polling fallback does not hold; section 6.6
covers that contradiction. [INFERRED] (section 6.6)
7.8 What the capture proves, and what it does not
Proved by the frames and envelopes above: card number, expiry, CVV,
cardholder name and phone are streamed to the operators’ server as typed,
before and independently of any submit [OBSERVED] (the 38 input_text
frames of tools/ws_evidence.jsonl, with the 17 field POSTs of
evidence/raw/har/ running in parallel); the full card record is sent again
on submit [OBSERVED] (frames 273 and 463); the server runs a
bank-identification-number lookup and returns an enriched verdict, promptly
for the first card and after a longer delay for the second (7.5) [OBSERVED]
(frames 275, 509 and 513); and one per-victim token binds the whole session
together, giving the operator a single live row to work [OBSERVED] (section
7.6).
Not proved by it: anything about second-factor relay in practice, since no
verification screen was reached and no code or approval was ever submitted, so
section 6’s account of those screens rests on the bundle alone [OBSERVED]
(the event counts over tools/ws_evidence.jsonl in 7.1); nor what the
operators do with a card they accept, or where they are, since the only
location-shaped datum in the session is a backend clock set to UTC+8.
[INFERRED] (7.5)
8. The wider platform cluster
Sections 4 and 5 establish the four confirmed hosts and the two gates that
protect them. This section looks outward, at other domains served by the same
phishing platform and impersonating the same brand. None of it is offered as
this campaign’s infrastructure. [INFERRED] It is preserved because it shows
the scale and disposability of the platform the campaign runs on, and because
a future capture from a live cluster member, made the same way section 5’s
handset capture was made, is the only thing that could turn a member of this
set into a confirmed sibling.
8.1 The query is never the fingerprint alone
The backend fingerprint common to every campaign host, Server: GoFrame HTTP Server behind Via: 1.1 Caddy (4.5), also answers on a very large number of
unrelated domains, so a search on that header by itself is not a lead: it
returns the platform, not an operator. [SOURCE] (osint/discover.py:8-10:
“The fingerprint alone is useless as a lead … It is only ever combined with
another term.”) The codebase’s own infrastructure-pivot queries enforce that
rule by construction, always pairing the header with an AS term
(osint/discover.py:28-32). The two urlscan queries behind this section were
run by hand, not by that function, but follow the same rule: each pairs the
platform header with a brand or AS term, never the header alone. [INFERRED]
(evidence/raw/urlscan/20260919T100041Z-page.server__GoFrame_HTTP_Server__AND_page.domain__easypark_.json
and 20260919T100044Z-page.server__GoFrame_HTTP_Server__AND_page.asn__AS132203__AND_page.domain__park_.json,
target; the envelopes record the query string, not who issued it)
8.2 The 62-domain result and its hosting
On 2026-09-19T10:00:41Z a query for page.server:"GoFrame HTTP Server" AND page.domain:*easypark* returned 65 results. Counted directly from the
envelope, those 65 results name 62 distinct domains, scanned between
2026-07-06T09:34:14Z and 2026-09-18T14:43:21Z. [OBSERVED]
(evidence/raw/urlscan/20260919T100041Z-page.server__GoFrame_HTTP_Server__AND_page.domain__easypark_.json,
payload.total, payload.results) The commit that captured this envelope
describes “63 distinct EasyPark-impersonation domains”; the envelope itself
has 62, and this report uses the verified figure. [SOURCE] (commit 1019840)
The scan-time ASN field on each result gives the hosting spread: AS132203 for
60 of the 65 results, AS38623 for 4, and AS45102 for 1. [OBSERVED] (same
envelope, payload.results[*].asn) The four AS38623 results are the
campaign’s own se- names: easypark.se-36586.online twice, both on the
campaign’s origin address (9.3), and easypark.se-toyota.homes and
easypark.se-toyota.lol once each, on the adjacent address (9.4). [OBSERVED]
(same envelope, payload.results[*].domain, payload.results[*].ip) As with
the origin address in section 4, this report states the AS number as recorded
by urlscan at scan time and does not assert an AS owner name, since no RDAP
record for any of these addresses was collected. [INFERRED]
8.3 Verifying each name
Each of the 62 domains was checked over Tor on 2026-09-19: resolved over DNS
over HTTPS, then its root fetched with a mobile User-Agent, exit
192.42.116.60, collected between 2026-09-19T10:03:05Z and 10:06:14Z.
[OBSERVED] (62 envelopes in evidence/raw/tier3-verify/; docs/tier3-verification.md)
Three verdicts were possible: dead (no DNS answer, or no reachable server),
platform (live, the GoFrame header present, no kit markers at the root), and
kit (the phishing template itself served at the root). Counted directly from
the 62 envelopes: [OBSERVED]
| Verdict | Count |
|---|---|
| dead | 56 |
| platform | 6 |
| kit | 0 |
The six live names, with the address and root response each returned:
[OBSERVED] (evidence/raw/tier3-verify/, one envelope per domain,
payload.resolves_to, payload.status_code, payload.body_length)
| Domain | Address | Root response |
|---|---|---|
aseeasypark.cfd |
43.162.111.113 |
404, 9 bytes |
easypark.work |
43.165.1.140 |
404, 9 bytes |
easypark88.com |
43.129.85.86 |
200, 8988 bytes |
easypark9.com |
43.129.85.86 |
200, 6650 bytes |
easyparkss.cfd |
43.157.24.72 |
404, 9 bytes |
easyparkss.sbs |
43.157.24.72 |
404, 9 bytes |
No kit verdict occurred anywhere in the set. [OBSERVED]
8.4 Reading the result
None of the 62 could be positively confirmed as serving the EasyPark kit from
a Tor vantage, for the same two structural reasons documented for the primary
campaign in section 5: the kit is served only at a tokened path, never at the
root, and the source-network gate that refuses Tor for the confirmed campaign
hosts applies here as well, so a Tor request cannot see the kit even on a live
cluster domain. [SOURCE] (docs/tier3-verification.md:17-21) The verdict counts
read as the same phishing platform impersonating the same brand, with most of
that platform already rotated out of use, consistent with the confirmed
campaign’s own pattern of short-lived domains (section 4). [INFERRED] The
finding stays classified as context: the commit that captured it states
“These are the same platform impersonating the same brand as the Swedish
campaign;
the link to this specific campaign is weaker than the AS38623 origin-IP
hosts, so they normalise to unconfirmed leads, not confirmed infrastructure.”
[SOURCE] (commit 1019840)
8.5 A broader, unverified parking-brand set
A second query, page.server:"GoFrame HTTP Server" AND page.asn:"AS132203" AND page.domain:*park*, reports a total of 1312 matches but returns only the
first 100 (the search API’s page size), spanning 93 distinct domains, all on
AS132203, scanned between 2026-08-08 and 2026-09-18. [OBSERVED]
(evidence/raw/urlscan/20260919T100044Z-page.server__GoFrame_HTTP_Server__AND_page.asn__AS132203__AND_page.domain__park_.json,
payload.total, payload.results) It surfaces the platform’s use against
other parking-brand names on the same infrastructure. None of these 93 has a
tier3 envelope: they exist in the evidence store only as urlscan search
results and the facts derived from them, not as anything independently
verified, and this report does not list them individually or treat any one of
them as a lead. [INFERRED]
8.6 Naming-scheme generations
The naming pattern behind the confirmed hosts is a brand misspelling followed
by a se- prefix and a digit or word segment (section 4). The wider cluster
also shows other naming generations built on the same brand: single-letter
host prefixes, a run of suffix letters appended directly to the brand string,
and two-letter country tags. [OBSERVED] (the 62-name list in
evidence/raw/urlscan/20260919T100041Z-page.server__GoFrame_HTTP_Server__AND_page.domain__easypark_.json,
payload.related_domains) Three of the 62, not the rest, carry the
campaign’s se- marker: easypark.se-36586.online, easypark.se-toyota.homes
and easypark.se-toyota.lol (9.3, 9.4). [OBSERVED] (same envelope) None of
the generations was checked against a Swedish source network, and nothing in
the evidence dates one generation relative to another, so this report treats
the remaining naming similarity as platform-level context, not as a link to
the confirmed campaign beyond the three names already addressed in 9.3 and
9.4. [INFERRED]
8.7 What confirming a cluster domain would require
The Tor vantage used for this section cannot distinguish a live cluster
domain that would serve the kit from one that would not, because both
present the same nine-byte refusal to a source network the gate does not
accept. Confirming any individual domain in this cluster needs the same
method used for the confirmed campaign: a capture from a Swedish mobile
network, with a mobile User-Agent, against a freshly minted token.
[SOURCE] (docs/tier3-verification.md:26-28: “Confirming any individual domain
would need a capture from a Swedish mobile network with a fresh token, the
same method used for the primary campaign.”) No such capture exists for any
cluster member, and this report makes no claim beyond what is written here.
[INFERRED]
9. Unconfirmed leads and claims not made
Section 4 lists the four hostnames and the origin address confirmed as this
campaign’s infrastructure. Everything below shares some attribute with that
set (a brand misspelling, the origin address, the phishing platform) but
falls short of confirmation, for a reason stated in each subsection. None of
it is repeated as fact elsewhere in this report. [INFERRED]
9.1 Two certificate-transparency hostnames
A crt.sh query for the pattern easypank% returned exactly two hostnames,
one beginning with the label easypank. and the other with the label
easypanknofakturen., each under a different long-established corporate
parent domain that this report does not name; the full hostnames are
recorded in the cited certificate-transparency envelope, not withheld from
the evidence store. [OBSERVED] (evidence/raw/ct/20260918T164240Z-easypank_.json,
payload.names, row_count 6) Neither resolves: every DNS record type
queried for either hostname, on both 2026-09-18 and again on 2026-09-19,
returns an empty value. [OBSERVED] (DNS envelopes for both hostnames,
timestamped 20260918T160851Z-53Z and 20260918T160853Z-55Z and, for the full
seven-record-type re-run, 20260919T105557Z-08Z and 20260919T105616Z-30Z, all
in evidence/raw/dns/, payload.values []) RDAP for both resolves to the same kind of corporate
registrant, with registration events dated 2007-01-03T17:23:31Z and
2007-01-03T17:20:11Z respectively, on nameservers belonging to a major cloud
platform, provisioned two decades before this campaign existed. [OBSERVED]
(RDAP envelopes for both hostnames, timestamped
20260918T162831Z and 20260918T162834Z, in evidence/raw/rdap/,
payload.raw.events, payload.facts.nameservers)
A brand-typo match in certificate transparency, on names that do not
resolve, under a registration unconnected to anything else here, is not
evidence of a shared operator. An earlier draft treated
these hostnames as a second infrastructure track before withdrawing that
claim once RDAP returned this registration data. [SOURCE] (commit 32a8cbf)
CLAUDE.md
states the resulting rule: “Never describe them as campaign infrastructure,
and never name the parent domains in a report.” [SOURCE] (CLAUDE.md,
“Domain context”) This report follows that rule: the parent domains are not
named here, and the full hostnames are not spelled out either, since that
would reveal the parent domain too.
9.2 liuguan168.lol, a suspected co-tenant
liuguan168.lol resolves to the confirmed campaign’s own origin address,
220.158.232.231, and was seen on that address by urlscan on
2026-09-07T06:22:10Z. [OBSERVED] (evidence/raw/dns/20260918T181422Z-liuguan168.lol.json,
payload.values; evidence/raw/urlscan/20260918T175939Z-page.ip__220.158.232.231_.json,
results[0].scanned_at) It was auto-promoted to the confirmed set purely on
that shared address, then reclassified once reviewed. The reclassifying
commit states the reasoning: “It was auto-promoted purely by resolving to
the shared origin 220.158.232.231, a multi-tenant malicious host. It has no
brand typosquat, no current kit (the origin serves it no valid certificate),
and a name unlike the Swedish campaign, so it is a suspected co-tenant, not
confirmed campaign infrastructure.” [SOURCE] (commit 62199d7) The same
commit adds that naming it to police would risk the same kind of false
accusation that 9.1 already guards against. [SOURCE] (commit 62199d7) This
report treats liuguan168.lol as a suspected co-tenant only, not confirmed
campaign infrastructure.
9.3 easypark.se-36586.online
urlscan recorded this hostname on the campaign’s origin address on
2026-09-02T08:18:38Z and 2026-09-03T11:54:20Z. [OBSERVED]
(evidence/raw/urlscan/20260919T100041Z-page.server__GoFrame_HTTP_Server__AND_page.domain__easypark_.json)
A discovery run attempted to confirm it and could not: “easypark.se-36586.online
was a candidate but did not confirm: it no longer resolves (empty
A/AAAA/CNAME), consistent with the campaign abandoning domains within days.”
[SOURCE] (commit 20ffaf6) Re-checked on 2026-09-19, it still returns no
address and no reachable server. [OBSERVED]
(evidence/raw/tier3-verify/20260919T100351Z-easypark.se-36586.online.json,
payload.resolves_to [], payload.verdict “dead”) A name that matches the
naming scheme but cannot be reached by any means available to this
investigation is recorded as a lead, not a finding.
9.4 easypark.se-toyota.homes and easypark.se-toyota.lol
These two are recorded in sections 4.4 and 4.7 as scanned at 220.158.233.4
on 2026-08-24, adjacent to the campaign’s origin. [OBSERVED]
(evidence/raw/urlscan/20260919T100041Z-page.server__GoFrame_HTTP_Server__AND_page.domain__easypark_.json)
CLAUDE.md’s “Domain context” section describes them, with se-36586.online,
as “Confirmed variants include se-36586.online, se-toyota.homes and
se-toyota.lol.” [SOURCE] (CLAUDE.md, “Domain context”) That sentence
confirms only that the names fit the naming scheme; both verified dead on
2026-09-19, with no address and no reachable server. [OBSERVED]
(evidence/raw/tier3-verify/20260919T100351Z-easypark.se-toyota.homes.json and
evidence/raw/tier3-verify/20260919T100351Z-easypark.se-toyota.lol.json,
payload.resolves_to [], payload.verdict “dead”) This report treats the
naming-scheme match as a lead only: neither name is stated as confirmed
campaign infrastructure.
9.5 Two four-year-old tenants of the origin address
matrix-client.dev-ctalk.us and packages.glivedev.xyz also appear in the
urlscan history of the origin address, scanned on 2022-12-19T02:59:37Z and
2022-12-14T14:01:01Z. [OBSERVED]
(evidence/raw/urlscan/20260918T175939Z-page.ip__220.158.232.231_.json) Both
sightings predate the kit build recorded in this evidence, 2026-08-26
(4.7), by roughly four years, and neither name carries any brand relation to
EasyPark or to parking. [INFERRED] A shared address four years apart, with
no other shared attribute, does not connect these names to the campaign;
this report treats them only as other tenants of the origin address.
9.6 A claim with no evidence behind it at all
The design document that preceded this evidence store names two further
hostnames on a netblock adjacent to the origin address, said to have
impersonated government sites in two other countries since 2025. [SOURCE]
(design spec lines 170-171, 177) No envelope in this evidence store records
either name or that address: a search of evidence/ for both returns no
match. [OBSERVED] This report does not name them or restate the claim as a
finding, and records only that it appears in an earlier planning document
and could not be verified.
9.7 Claims this report deliberately does not make
- The two certificate-transparency hostnames (9.1) are campaign infrastructure. A brand-typo match with no resolution and an unrelated, decades-old registration is not evidence of a shared operator.
- The parent domains behind those two hostnames, named anywhere in this report. They belong to an uninvolved third party; naming them risks a false accusation.
liuguan168.lol(9.2) is confirmed campaign infrastructure. A shared multi-tenant address is not evidence of a shared operator; it is a suspected co-tenant only.- The
Server: GoFrame HTTP Server/Via: 1.1 Caddyfingerprint (4.5, 8.1) is evidence about these operators on its own. It matches many unrelated hosts and identifies the phishing platform, not this campaign; every use of it here is paired with another fact. - A figure for how many hosts that fingerprint matches. No urlscan envelope exists for the fingerprint queried alone, so no count appears here.
- An owner name for AS38623 or AS132203. Both are recorded only as urlscan scan-time data; no RDAP record for either was collected, so no owner name is asserted.
- A location for any Tor exit used in this investigation. No envelope
records a geolocation for
185.220.101.150,171.25.193.131or192.42.116.60(4.8). - The wider platform cluster’s domain count as 63 (8.2). One commit message states 63; the envelope has 62 distinct domains, and this report uses the envelope’s count.
- The two unnamed government-impersonation hostnames (9.6) as a finding. Nothing in the evidence store supports the claim.
10. Methodology, chain of custody, and deviations
10.1 Transport: one path for every request
osint/transport.py is the only module in this codebase that opens a
network connection (module docstring, transport.py:1-9). [SOURCE]
session() refuses to build a requests.Session unless a SOCKS proxy is
reachable at an explicit host and port: check_proxy_reachable() parses
OSINT_SOCKS (default socks5h://127.0.0.1:9050), rejects a value with no
explicit host or port, and attempts a raw TCP connect first
(transport.py:39-56); session() then sets proxies and trust_env = False
so an ambient HTTP_PROXY cannot bypass Tor (transport.py:59-65).
[SOURCE] grep -rn "import requests" osint/ returns exactly one line,
osint/transport.py:17: no collector opens its own connection. [SOURCE]
A narrower check exists only for the cloaking probe: exit_identity() GETs
https://check.torproject.org/api/ip over the session and raises
TorUnavailable unless the response’s IsTor field is true
(transport.py:68-79). [SOURCE] grep -rn "exit_identity" osint/ returns
three lines: the definition (transport.py:68), the call gating probe_url()
before the control request and any grid cell (cloak.py:132), and a string
literal, "exit_identity(sess)", used as the error envelope’s command
value when that call fails (cloak.py:138) - only the second line is a real
call site. [SOURCE] DNS, RDAP, certificate and urlscan never call it: they
don’t touch an operator host and rely on the proxy-reachability check above.
[SOURCE]
DNS is resolved over HTTPS through the same session: resolve() calls
https://cloudflare-dns.com/dns-query with accept: application/dns-json
(transport.py:23, 91-98), and osint/collect/dns.py calls only
transport.resolve. [SOURCE] grep -rn "getaddrinfo(\|subprocess\." osint/
returns no hits, so no collector can fall back to the system resolver (a
looser pattern without the parenthesis matches only that same docstring
line, osint/collect/dns.py:3). [SOURCE]
Registration data comes from RDAP, not whois: the module docstring states
whois is TCP/43 and cannot traverse the SOCKS proxy (rdap.py:4-6), and
_fetch(), built on transport.session().get(), is the only path to a
registry (rdap.py:61-66), with no subprocess call or whois parser anywhere
in osint/. [SOURCE] This was not always true: commit 76de89d (“fix:
remove the whois Tor-bypass from rdap.py”, 2026-09-18T20:48:07+02:00) deleted
an OSINT_ALLOW_WHOIS-gated subprocess.run(["whois", target]) path that
had opened TCP/43 directly, outside Tor, whenever set. [SOURCE] An RDAP
envelope from before that commit still carries the old wording:
evidence/raw/rdap/20260918T162138Z-easypank.se-9626654.pics.json records
"command": "GET https://rdap.org/domain/easypank.se-9626654.pics (Tor); whois fallback enabled=False", and a whois_skipped field repeating the
same TCP/43 reasoning. [OBSERVED] That lookup failed with a 404,
superseded by a later run against the registrable parent the same evening
(evidence/raw/rdap/20260918T162807Z-se-9626654.pics.json, status ok); the
wording is a fossil of a removed code path, not evidence that whois ran.
[OBSERVED]
10.2 Request discipline: GET only, budgeted where it touches the operators
grep -rn "\.post(" osint/ returns no matches; every collector calls
sess.get or transport.session().get(). [SOURCE] The per-domain request
budget (CLAUDE.md rule 3) is config.REQUEST_BUDGET = 40 (config.py:15),
and only the cloaking probe enforces it: probe_url() raises
BudgetExceeded before sending anything if the grid (7 user agents by 2
languages by 2 referers, 28 cells) plus one control request, 29 of 40,
exceeds it (cloak.py:29-78, 99-128, 119-128). [SOURCE]
grep -rln "REQUEST_BUDGET" osint/ returns only config.py and cloak.py:
the DNS, RDAP, certificate and urlscan collectors issue a handful of
requests per domain and never check the budget, never expected to approach
it. [SOURCE]
10.3 The envelope, quoted from the store
Every collector call produces one envelope through evidence.envelope():
collected_at (UTC, %Y-%m-%dT%H:%M:%SZ), source, target, command,
status, payload_sha256 (the sha256 of the payload’s canonical JSON,
sorted keys, compact separators, ensure_ascii=False), and payload
(evidence.py:42-51). [SOURCE] The stored file is compact JSON with sorted
keys and no line breaks, not the pretty-printed form below (evidence.py:76).
[SOURCE] One, reformatted for readability:
{
"collected_at": "2026-09-18T16:08:45Z",
"source": "dns",
"target": "easypank.se-45564.xyz",
"command": "DoH A easypank.se-45564.xyz via https://cloudflare-dns.com/dns-query?name={name}&type={rtype}",
"status": "ok",
"payload_sha256": "a6f0ec975caa...",
"payload": {"record": "A", "values": ["220.158.232.231"]}
}
(evidence/raw/dns/20260918T160845Z-easypank.se-45564.xyz.json) [OBSERVED]
payload_sha256 is what facts.jsonl cites as evidence (facts.py:34, 43),
but it hashes only the payload, so identical payloads collide. [SOURCE]
This hash is shared by 8 envelopes under evidence/raw/dns/, five hostnames
with the same A answer across two collection runs, so the hash alone does
not identify an envelope - the file path does
(grep -rl a6f0ec975caa evidence/raw/ | wc -l -> 8). [OBSERVED] The manifest instead records the hash of the whole
envelope blob, 0bb5626b9333291ecca90138de7ef5ba8b8a4e369c41d4b819f47e04b46b9665,
so a tampered file or forged payload hash is caught by two independent
checks. [OBSERVED] (evidence/MANIFEST.sha256)
10.4 Artifacts, the manifest, and append-only
Page bodies and other blobs are stored once, by content: store_artifact()
writes to evidence/artifacts/<sha256>.<ext> only if that path does not
already exist (evidence.py:82-91). [SOURCE] write_envelope() never
overwrites either: it appends a .1, .2 collision suffix rather than
replace a file at the same timestamp and slug (evidence.py:63-79).
[SOURCE] Both call append_manifest(), which appends a
<sha256> <relpath> line and skips it if already present
(evidence.py:54-60) - a mechanism, not an enforced lock: nothing stops a
later manual edit, so the manifest is an independent check.
[SOURCE]
Verified now:
$ (cd evidence && sha256sum -c MANIFEST.sha256 | grep -vc OK)
0
$ wc -l evidence/MANIFEST.sha256
433 evidence/MANIFEST.sha256
433 files, all checksums current, zero mismatches. [OBSERVED]
10.5 Failures are recorded, never dropped
Collectors do not raise into the pipeline: probe_url() catches a failed
exit_identity() before any campaign request and writes a
cloak-precondition error envelope instead of sending anything
(cloak.py:130-141); a failed grid cell is caught individually and written
with status: "error" and no body_artifact (cloak.py:163-171). [SOURCE]
The same pattern - status: "error" on the exception branch, envelope
written regardless - is standard across the other collectors, e.g.
rdap.py:94-104. [SOURCE] facts.rebuild() applies the same
discipline to the read side: a raw file that fails to parse is recorded in
evidence/facts-skipped.jsonl rather than silently dropped (facts.py:159-191).
[SOURCE] That file is currently 0 lines, so no envelope has failed to
parse (wc -l evidence/facts-skipped.jsonl -> 0). [OBSERVED]
10.6 Facts and the promotion gate
osint/facts.py normalizes only status: "ok" envelopes into
{subject, predicate, object, observed_at, evidence} facts (facts.py:39-40);
every fact carries a payload_sha256 from its envelope. [SOURCE]
evidence/facts.jsonl holds 1563 lines, each with a non-empty evidence
field (verified by loading every record; wc -l evidence/facts.jsonl ->
1563). [OBSERVED] Analysis and reports read only this file
(osint/analyze/pivot.py, osint/report/render.py), never the network.
[SOURCE]
A candidate domain is promoted from pivot.score_candidates() only if its
score against confirmed infrastructure is at least CONFIDENCE_THRESHOLD = 2
(pivot.py:42) and one shared relationship is in
INFRASTRUCTURE_FAMILIES = {"resolves_to", "hosting"} (pivot.py:45, 129,
135). [SOURCE] A shared kit body (served_body) or registrar/DNS zone
(registrar, nameserver_zone) never promotes alone, since those families
are excluded from INFRASTRUCTURE_FAMILIES (pivot.py:46-51). [SOURCE]
osint/discover.py’s confirm() adds a second gate: a candidate must
resolve onto a known IP before being confirmed (discover.py:61-72).
[SOURCE] osint/seeds.py keeps the two parent domains behind section 9’s
unconfirmed certificate-transparency hostnames out of known, the set
anchoring the pivot: discover.run() builds it by filtering the seed list
through is_excluded() (EXCLUDED_PARENT_DOMAINS, seeds.py:13-30;
discover.py:94). [SOURCE] seen, used only to suppress already-known
candidates, deliberately re-includes both parent domains (discover.py:100),
so a match against either is never returned as a new candidate. [SOURCE]
10.7 Deviations from the rules
Not every request behind this report followed the rules above, listed in full, in the order they happened:
Pre-pipeline lookups, outside Tor. The “Confirmed facts as of
2026-09-18T15:35Z” table in the design document was “collected with dig,
whois and crt.sh before this design was written” (design spec, line 34).
dig and whois go directly over UDP/53 and TCP/43, not through Tor,
disclosing the reporter’s real address to the campaign’s nameservers and the
queried registries - exactly what CLAUDE.md rules 4 and 5 forbid.
git log --diff-filter=A -- CLAUDE.md shows those rules were added
in commit f3688ef at 2026-09-18T17:36:30+02:00, one minute after that
collection (17:35 CEST for the 15:35Z table). The lookups predate the rules,
were never repeated, and every value was independently re-collected over
Tor afterward (section 4).
Four handset requests, outside Tor, with the reporter’s authorisation.
The cloak-phone source holds exactly four envelopes. [OBSERVED]
(ls evidence/raw/cloak-phone/ | wc -l -> 4) Each carries a
payload.provenance block: device “a Swedish mobile handset”,
method “adb shell curl on a connected Android device”, network “a Swedish
mobile carrier, on cellular data with wifi disabled”, via_tor: false, and
the note: “These four requests did not go through Tor. They were made with
the reporter’s explicit authorisation from the handset that received the SMS.
The operators’ logs already held this address from the reporter’s own click.
The Tor path could not distinguish a spent token from source-network
filtering, and this comparison does.” (evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json,
payload.provenance) [OBSERVED] These isolate the client-type gate from
the source-network gate in section 5, a comparison Tor exits cannot make:
they fail the source-network check regardless of User-Agent.
Kit assets and cluster verification, outside the CLI. grep -rln "kit-asset\|tier3-verify" osint/ finds no matching source file: neither
collector exists here. The static-asset fetch holds 2 envelopes, both
via_tor: true, exit 171.25.193.131 (evidence/raw/kit-asset/); the
wider-cluster root checks are a separate source of 62 envelopes
(ls evidence/raw/tier3-verify/ | wc -l -> 62). [OBSERVED] Neither was
populated by python -m osint probe, the only command CLAUDE.md names as
touching operator hosts: both used ad hoc scripts, through Tor and GET only,
but outside the CLI path enforcing REQUEST_BUDGET and cloak.py’s
envelope format.
Page-initiated POSTs during the Waydroid session. The har source holds
40 envelopes from the captured browser session, ingested with osint ingest-har, a legitimate CLI command (cli.py:67-72)
(ls evidence/raw/har/*.json | wc -l -> 40); 18 of them are POST requests -
17 to /JisiRmktje/api/input?token=... and 1 to /JisiRmktje/api?token=...,
all status: 200, issued automatically by the kit’s own JavaScript as the
session progressed
(evidence/raw/har/20260919T105202Z-https___easypank.se-9626654.pics_JisiRmktje_api_input_token_755a9f6f-b4bc-4ef8-9.json
and 17 siblings, payload.summary.method: "POST"). [OBSERVED] Not issued
by any collector here; the ingest step only imported an existing mitmproxy
capture, filtered to the campaign host (har.py:61-75).
Junk form data, against rule 1, entered anyway. CLAUDE.md rule 1 is
explicit: “never enter credentials or card data, not even fabricated data.”
The captured session did exactly that: a fabricated plate, two fabricated
card numbers (one deliberately Luhn-invalid), an expiry, CVV, name and phone
number - why the POSTs above and the session’s WebSocket frames exist
(section 7 has the full record). Done in the kit repository, not through any
collector here, and its own account states why: “This is a controlled test
session using junk data, but it exercises the real C2 end to end.”
[REPORTED] (kit repository TAKEDOWN_REPORT.md:189-190) That let the
operators’ live relay - server-side BIN check and card verdict - be
documented from a genuine round trip, not asserted from static analysis
alone. No real payment or identity data was entered at any point.
A stale method statement. All four reports carry the same
METHOD_STATEMENT (render.py:17-24), stating observations used
“HTTP GET requests, DNS lookups, WHOIS/RDAP queries, and queries to public
certificate transparency and URL scanning services.” grep -l "WHOIS/RDAP" reports/{police-dossier,abuse-reports,brand-brief,press-narrative}.md matches
all four. [OBSERVED] Per section 10.1, the code has had no whois path since
commit 76de89d; the “WHOIS/RDAP” wording
predates that removal and was never updated.
11. Evidence gaps and inconsistencies
What an investigation declines to claim matters as much as what it claims. This section lists what the evidence store does not contain, and every place where a document of this investigation, in either repository, says something the evidence does not bear out. Every row was re-verified against its anchor. Gaps the 2026-09-19 collection run or the browser-session ingest have closed are not listed as open.
11.1 Gaps
| Gap | Why it is open | What would close it |
|---|---|---|
No artifact of the SMS. Delivery time 2026-09-18 16:46:15 CEST and sender ID InfoSMS are [REPORTED] only (kit repo TAKEDOWN_REPORT.md:107-116) |
No screenshot or export was captured; evidence/ has no match for InfoSMS [OBSERVED] |
A handset export, or the carrier’s delivery record |
| The route the SMS took | The ROM does not retain the SMSC address (kit repo TAKEDOWN_REPORT.md:238-239); the grey-route account at :246-259 is analysis, not observation [SOURCE] |
A carrier or PTS ingress trace |
| Whether Swedish fixed broadband passes the source-network gate | The clean test was never run; docs/site-access.md:54-59 calls the allowlist hypothesis unverified [SOURCE] |
One GET from such an address, mobile User-Agent, fresh token |
| The reporter’s desktop-on-wifi refusal that started the investigation | [REPORTED], never reproduced: the four non-Tor handset envelopes are the only ones from the reporter’s own connections [OBSERVED] (evidence/raw/cloak-phone/) |
One authorised captured request from that address, fresh token |
| Certificate transparency is only partly covered | 14 of 51 crt.sh envelopes are status: error (timeouts, 502, 404), and eight Cert Spotter envelopes, four each for the two parents of 9.1, are 403 [OBSERVED] (evidence/raw/ct/, evidence/raw/certspotter/) |
A retry from another vantage, or an API key |
Reverse DNS and routing origin for 220.158.232.231 |
PTR is empty in both runs; the IP’s RDAP object carries no AS number [OBSERVED] (evidence/raw/dns/20260918T160855Z-220.158.232.231.json; evidence/raw/rdap/20260918T162147Z-220.158.232.231.json, zero matches for 38623) |
A routing-registry or BGP lookup, stored as an envelope |
| Embedded signed certificate timestamps | Cert Spotter records issuer, dates, dns_names and id only; no SCT field exists [OBSERVED] (evidence/raw/certspotter/20260918T164231Z-se-45564.xyz.json) |
Fetching and parsing the certificates |
| The bodies of the two substantive live platform hosts | Their envelopes record 200 at 8988 and 6650 bytes, but carry no body_sha256 field and no artifact was stored [OBSERVED] (evidence/raw/tier3-verify/) |
Re-fetching both roots over Tor, storing the bodies |
| The parking-brand platform set is truncated | The broader urlscan query reports total 1312 and returned 100 rows [OBSERVED] (evidence/raw/urlscan/20260919T100044Z-page.server__GoFrame_HTTP_Server__AND_page.asn__AS132203__AND_page.domain__park_.json) |
Paginating it, which needs a higher urlscan quota |
| Second-factor relay in practice | No verification screen was reached: the 930 frames hold zero OTP, app-approval or custom-OTP submissions [OBSERVED] (kit repo tools/ws_evidence.jsonl) |
Nothing this investigation may safely do; 6.4 rests on the bundle alone |
| Whether the origin serves directly or sits behind a proxy | Responses carry Via: 1.1 Caddy ahead of Server: GoFrame HTTP Server; no envelope separates proxy from origin [OBSERVED] (evidence/raw/cloak/) |
Host records, held only by the abuse desks |
The certificate state of liuguan168.lol |
seeds.yaml:23 calls it uncertificated; no TLS or Cert Spotter envelope exists [OBSERVED] (evidence/raw/certspotter/) |
A Cert Spotter query for it. See 9.2 |
The host count behind the GoFrame platform header |
The design document’s “more than 10000” figure has no envelope: the store holds no bare page.server query, only two that AND it with a brand term [OBSERVED] (evidence/raw/urlscan/) |
A bare page.server query, which 8.1 declines to run |
| No independent replay of the kit | The kit repo’s mock harness was never exercised: its output tools/evidence.jsonl is 0 bytes in that repository’s working tree. Unlike every other kit-repository citation here it is untracked, so it cannot be read back with git show phishing-investigation:<path> [SOURCE] (git -C <kit repo> status --short tools/evidence.jsonl returns ?? tools/evidence.jsonl) |
Running it offline against the preserved bundle, and committing the output |
Five gaps the source material lists are closed, and so are absent above.
avcoa.click and btalning.click lacked RDAP, Cert Spotter and per-domain
urlscan collection and now have all three [OBSERVED]
(evidence/raw/rdap/20260919T105655Z-avcoa.click.json and siblings, 2026-09-19T10:56Z).
The kit’s bootstrap response, never observed before, is preserved whole and
carries "mode":1, "country":"SE" and the operator text block [OBSERVED]
(evidence/artifacts/e3eaf9aa94f833e92c3e09df408c03dd98ba0675f7f6a878bbe1692b566d1e66.bin).
No HAR had been ingested; 40 now exist [OBSERVED] (evidence/raw/har/). The
dossier’s manifest count was stale; all four generated reports were regenerated
2026-09-19T11:18:04Z against 433 verifying manifest lines [OBSERVED]
(reports/police-dossier.md:3-4). And no abuse report covered the two .click
domains, which now have their own registrar section [OBSERVED]
(reports/abuse-reports.md:39-47).
11.2 Inconsistencies
Where a document and the evidence disagree, this report follows the evidence. Where two documents disagree, it follows the capture file and the preserved bundle.
| Document statement | What the evidence shows | This report |
|---|---|---|
| Certificates were “issued minutes after each domain was registered” (design spec, line 183) | Both precede registration: not_before 07:27:20Z against 07:50:58.0Z, and 05:55:00Z against 06:43:13.0Z [OBSERVED] (evidence/raw/certspotter/20260918T164231Z-se-45564.xyz.json; evidence/raw/rdap/20260918T162140Z-se-45564.xyz.json) |
The envelopes; the ordering is left unexplained (4.3) |
se-45564.xyz was registered “~9 hours before the lure” (kit repo SCAMMER_WORKFLOW.md:47-48, TAKEDOWN_REPORT.md:137) |
07:50:58Z to the reported delivery at 14:46:15Z is 6 h 55 min 17 s; the nine-hour figure reads 16:46 CEST as if it were UTC [OBSERVED] (evidence/raw/rdap/20260918T162140Z-se-45564.xyz.json) |
6 h 55 min 17 s |
| The grid’s uniform “Not found” follows from the SMS token “having already been used” (reports/police-dossier.md:29) | 28 of the 56 cells requested the lure URL, and the redirector minted each a fresh token; all still returned the same nine bytes [OBSERVED] (evidence/raw/cloak/, final_url carrying _v=valid_20260919000752_...) |
A spent token explains the landing-URL cells only; the source-network gate the rest (5.2, 5.3) |
| The operators serve the page “only to mobile User-Agent strings” (reports/abuse-reports.md:17-18; reports/police-dossier.md:13 makes the same claim in different words, “only to mobile browsers”) | Two gates must both pass; no generated report mentions the source-network one [OBSERVED] (evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json and .1.json, and the 56 envelopes under evidence/raw/cloak/; see 5.3. No generated report names the source-network gate: grep -il "source network" reports/{police-dossier,abuse-reports,brand-brief,press-narrative}.md returns nothing) |
Both gates (5.3) |
| Observations used “WHOIS/RDAP queries” (all four generated reports) | No whois path exists in the codebase after commit 76de89d [SOURCE] (osint/collect/rdap.py:4-5, “There is no whois fallback: whois is TCP/43 and cannot traverse the SOCKS proxy”) |
RDAP only |
The origin belongs to a named Cambodian operator (design spec, line 41), against “BUCT-BD, Bangladesh, via Viettel” (kit repo README.md:41) |
RDAP gives netname BUCT-BD, country BD, abuse yennp@viettel.com.vn, no AS number; AS38623 appears only as urlscan scan-time data [OBSERVED] (evidence/raw/rdap/20260918T162147Z-220.158.232.231.json) |
The RDAP wording plus “AS38623 per urlscan scan-time data”; no owner asserted (4.4) |
The origin address “came back 404 from the relevant RDAP registries” (commit 50a87b7) |
The RDAP envelope for 220.158.232.231 is a successful lookup carrying netname BUCT-BD, country BD and abuse contact yennp@viettel.com.vn [OBSERVED] (evidence/raw/rdap/20260918T162147Z-220.158.232.231.json) |
The commit message was wrong when written: the successful lookup it calls a 404 was collected at 16:21:47Z, 91 seconds before the commit at 16:23:18Z, by the same run. Section 4.4 cites the later 2026-09-19 envelope |
A Tor exit was in a named country (docs/site-access.md:32-33) |
No envelope carries a country or geolocation field for any exit address [OBSERVED] (evidence/raw/cloak/, evidence/raw/cloak-control/) |
The country is omitted; closing this needs each recorded exit resolved against a consensus source |
| The AS-constrained urlscan search “returned 11 hosts” (design spec, line 160), above a table of 8 rows | That envelope has 8 results over 6 domains, and no page.asn:"AS38623" envelope exists [OBSERVED] (evidence/raw/urlscan/20260918T175939Z-page.ip__220.158.232.231_.json) |
The 6 domains it names (4.1, 9.2, 9.5) |
“63 distinct EasyPark-impersonation domains” (commit 1019840), the 62 checked “excluding” one name belonging to the corporate registrar behind the 9.1 parents (docs/tier3-verification.md:3-4) |
The envelope holds 62 related domains, none of them such a name [OBSERVED] (evidence/raw/urlscan/20260919T100041Z-page.server__GoFrame_HTTP_Server__AND_page.domain__easypark_.json) |
62, no exclusion (8.2) |
easypark.se-36586.online first seen 2026-09-03 (design spec, line 164) |
Scans at 2026-09-02T08:18:38.730Z and 2026-09-03T11:54:20.273Z [OBSERVED] (evidence/raw/urlscan/20260918T175939Z-page.ip__220.158.232.231_.json) |
2026-09-02 (9.3) |
The scheme is se-<6-8 digits> (design spec, line 52) |
Observed: 45564, 36586, 9626654, that is five, five and seven digits [OBSERVED] (evidence/raw/rdap/20260919T105537Z-se-45564.xyz.json, evidence/raw/rdap/20260919T105554Z-se-9626654.pics.json, evidence/raw/urlscan/20260918T175939Z-page.ip__220.158.232.231_.json for easypark.se-36586.online) |
se-<digits or word>, lengths given |
The capture used “wss://TAKEDOWN_REPORT.md:98, 228, SCAMMER_WORKFLOW.md:69), the flag “_m defaults to 1 => wss://KIT_ANALYSIS.md:33) |
All 930 frames carry path: /ws?token=755a9f6f-..., none /engine.io; the bootstrap returned "mode":1, and the bundle routes both modes to that same /ws URL [OBSERVED] (kit repo tools/ws_evidence.jsonl; evidence/artifacts/e3eaf9aa94f833e92c3e09df408c03dd98ba0675f7f6a878bbe1692b566d1e66.bin; docs/kit-analysis.md section 2) |
/ws?token=, both modes (6.5) |
“HTTP POSTs in the bundle are only the engine.io polling fallback” (kit repo TAKEDOWN_REPORT.md:230) |
The session made 17 POSTs to /JisiRmktje/api/input, each answered 200 with 33 bytes, plus the bootstrap POST [OBSERVED] (18 POST envelopes, evidence/raw/har/) |
A second, parallel exfiltration channel (6.6, 7.7) |
index.html hashes to c8ccfd83... and its title is Danish (kit repo SCAMMER_WORKFLOW.md:211, TAKEDOWN_REPORT.md:10) |
c8ccfd832d7f... is the JS bundle; index.html is a98ddbfa1083.... Only lang="da" at line 2 is Danish; the title at line 9 is Swedish [SOURCE] (sha256sum, sed, against kit_original/) |
The computed hashes, and the attribute-versus-title split as a multi-tenant tell (6.1, 6.3) |
The plate streams as input_text { type:"input_card", key:"plate" } (kit repo KIT_ANALYSIS.md:130) |
Record 42 carries {"type":"Registreringsnummer","key":"plate","text":"boy"} [OBSERVED] (kit repo tools/ws_evidence.jsonl) |
The captured value (6.6) |
The operator table lists my-event, otp-valid, app-valid, custom-otp-valid, success as wire events (kit repo TAKEDOWN_REPORT.md:54-65, SCAMMER_WORKFLOW.md:96-110) |
That repo’s own correction calls them internal bus signals and names result_type (KIT_ANALYSIS.md:199-204); none of the five appears in the 930 frames [OBSERVED] (kit repo tools/ws_evidence.jsonl) |
result_type (6.5) |
The frame file is “timestamped” (kit repo README.md:28) |
The time field is 2026-09-18T22:13:19 on all 930 lines: the wall clock of the offline decode run [OBSERVED] (kit repo tools/ws_capture.py:54) |
Only the frames’ embedded timestamp values (7.3) |
The kit writes 13 localStorage keys (kit repo TAKEDOWN_REPORT.md:14-15), against 16 in the same repo’s KIT_ANALYSIS.md:55-58 |
The lists differ by price, countdownDuration and countdownStartTime [SOURCE] |
Neither list is cited as complete; 6.7 anchors to the bundle |
Phase 0 setup, Phase 1 delivery and the hosting jurisdiction are labelled [OBSERVED] in the kit repo (SCAMMER_WORKFLOW.md:114-119, 178) |
That repo defines [OBSERVED] as “confirmed from capture” (README.md:17) and holds no RDAP, DNS or registration artifact [SOURCE] |
Labels registration and hosting [OBSERVED] only because this repository holds the envelopes; delivery stays [REPORTED] |
The kit “also ships toll-road and generic bank skins” (kit repo TAKEDOWN_REPORT.md:5), against “also toll” (README.md:38) |
The bundle carries toll strings; nothing in either repository substantiates a bank skin [SOURCE] (6.3) |
Toll and multi-language only |
Six routes “were seen live in the collector’s crawl” (docs/kit-analysis.md:32) |
No crawl exists, and rule 2 forbids path enumeration: none of the 412 envelope targets ends in /home, /card, /address, /pay, /success or /temp [OBSERVED] (evidence/raw/) |
The route table as [SOURCE], read from the router (6.4) |
The page is “a copy of EasyPark’s Next.js site with the framework’s attributes intact” (design spec, lines 146-149), against a Vite-built Vue 3 app (docs/kit-analysis.md:10) |
Both hold in part: index.html carries data-next-head twice and __next once; the application is Vue [SOURCE] (grep -o -F, kit_original/) |
Copied markup inside a Vue kit (6.2, 6.3) |
“56 grid cells plus 2 control requests” (design spec, lines 72-73) against “29 envelopes, being 28 grid cells plus one control request” (docs/vps-runbook.md:139) |
Both are right at different scopes: 56 cloak envelopes, 28 per URL over two URLs, and 2 cloak-control envelopes [OBSERVED] (evidence/raw/cloak/, evidence/raw/cloak-control/) |
Totals, with the per-URL split stated (5.1) |
The submitted phone number passes without comment (kit repo TAKEDOWN_REPORT.md:213-220) |
07129284829 is eleven digits, one more than a Swedish mobile number, in both submissions [OBSERVED] (kit repo tools/ws_evidence.jsonl, records 273 and 463) |
States plainly that the session used fabricated data throughout (7.1) |
facts-scam.md carries file:line anchors into this repository’s code (facts-kit.md carries none) |
Several are stale: config.py:203-206 is cited for REQUEST_BUDGET in a 54-line file, and evidence.py:118-149 for envelope(), which starts at line 42 [OBSERVED] (grep -n, wc -l) |
Every anchor was re-opened; the lines cited here are the lines actually read |
None of the above changes a finding in sections 2 through 8. Each changes how a finding may be worded, which is why they are listed rather than reconciled in silence.
12. Indicators of compromise
This section is the blocklist and hunting-query extract of the report. Every
row was re-read from the envelope or artifact named beside it. The confidence
column uses the report’s four labels. Only infrastructure
confirmed as this campaign’s appears in 12.1 to 12.9; section 9’s unconfirmed
leads appear nowhere here, and 12.10 is a separate table that is not this
campaign’s infrastructure. [INFERRED] Status values are those of the
2026-09-19 collection run. [OBSERVED]
12.1 Hostnames
| Indicator | Role | A record 2026-09-18 | Status 2026-09-19 | Confidence | Anchor |
|---|---|---|---|---|---|
easypank.se-45564.xyz |
lure host named in the SMS; HTTP redirector | 220.158.232.231 |
resolving at 10:54:51Z | [OBSERVED] |
evidence/raw/dns/20260918T160845Z-easypank.se-45564.xyz.json, evidence/raw/dns/20260919T105451Z-easypank.se-45564.xyz.json |
easypank.se-9626654.pics |
landing host; served the kit and the live relay | 220.158.232.231 |
no A record at 10:55:12Z | [OBSERVED] |
evidence/raw/dns/20260918T160846Z-easypank.se-9626654.pics.json, evidence/raw/dns/20260919T105512Z-easypank.se-9626654.pics.json |
avcoa.click |
sibling name on the origin address; APCOA typosquat | 220.158.232.231 |
resolving at 10:56:36Z | [OBSERVED] |
evidence/raw/dns/20260918T181409Z-avcoa.click.json, evidence/raw/dns/20260919T105636Z-avcoa.click.json |
btalning.click |
sibling name on the origin address; betalning typosquat, seen serving the campaign’s own path |
220.158.232.231 |
resolving at 10:56:59Z | [OBSERVED] |
evidence/raw/dns/20260918T181415Z-btalning.click.json, evidence/raw/dns/20260919T105659Z-btalning.click.json |
12.2 Registrable domains and registration data
All four are NameSilo, LLC registrations, IANA registrar id 1479, all
delegated to ns1.dnsowl.com, ns2.dnsowl.com and ns3.dnsowl.com.
[OBSERVED] (the four envelopes below, payload.facts.registrar,
payload.facts.nameservers and the registrar entity’s publicIds)
| Domain | Handle | Registered (UTC) | Last changed | RDAP status 2026-09-19 | Abuse contact | Confidence | Anchor |
|---|---|---|---|---|---|---|---|
se-45564.xyz |
D644515746-CNIC |
2026-09-18T07:50:58.0Z | 2026-09-18T07:51:03.0Z | server transfer prohibited, client transfer prohibited, add period | abuse@namesilo.com, tel +1.4805240066 |
[OBSERVED] |
evidence/raw/rdap/20260919T105537Z-se-45564.xyz.json |
se-9626654.pics |
D639688650-CNIC |
2026-09-09T06:43:13.0Z | 2026-09-18T23:47:11.0Z | server hold, server transfer prohibited, client hold, client transfer prohibited | abuse@namesilo.com, tel +1.4805240066 |
[OBSERVED] |
evidence/raw/rdap/20260919T105554Z-se-9626654.pics.json |
avcoa.click |
DO_1dcdb4021fe3d6faa62b10e826192166-INAMING |
2026-09-05T15:55:26.273Z | 2026-09-10T15:55:52.041Z | client transfer prohibited | support@namesilo.com, tel +1.6024928198 |
[OBSERVED] |
evidence/raw/rdap/20260919T105655Z-avcoa.click.json |
btalning.click |
DO_b07f16df9e824d61205c08078811f578-INAMING |
2026-09-02T08:23:58.644Z | 2026-09-07T08:24:52.299Z | client transfer prohibited | support@namesilo.com, tel +1.6024928198 |
[OBSERVED] |
evidence/raw/rdap/20260919T105717Z-btalning.click.json |
se-9626654.pics is the one domain carrying both a registry hold and a
registrar hold; the other three carry no hold and remain live registrations.
[OBSERVED] (the same four envelopes, payload.raw.status) One registrar
publishes two different abuse addresses across these four domains, so a
report covering all four has to be sent to both. [INFERRED] (4.2)
12.3 Origin address
| Field | Value as the registration data has it | Confidence |
|---|---|---|
| Address | 220.158.232.231 |
[OBSERVED] |
| Netblock handle | 220.158.232.0 - 220.158.232.255 |
[OBSERVED] |
name |
BUCT-BD |
[OBSERVED] |
type |
ASSIGNED NON-PORTABLE |
[OBSERVED] |
country |
BD |
[OBSERVED] |
remarks description |
BUCT COMMUNICATION |
[OBSERVED] |
| Abuse entity | IRT-VIETTEL-CAMBODIA-KH, email yennp@viettel.com.vn |
[OBSERVED] |
| Technical and administrative entity | BCA3-AP, BUCT Communication administrator, tel +880-2-8153246, email admin@buctbd.com |
[OBSERVED] |
| PTR | empty | [OBSERVED] |
| Autonomous system | AS38623, per urlscan scan-time data only |
[OBSERVED] |
[OBSERVED] (evidence/raw/rdap/20260919T105723Z-220.158.232.231.json for
every field but the last two;
evidence/raw/dns/20260918T160855Z-220.158.232.231.json for the PTR;
evidence/raw/urlscan/20260918T175939Z-page.ip__220.158.232.231_.json,
the asn field of each result, for AS38623) The RDAP object holds no
autonomous system number and no owner name for AS38623 exists in any
envelope collected here, so none is asserted. [OBSERVED] (the same RDAP
envelope contains no autnum field and no occurrence of 38623)
12.4 Certificates
| Covers | Issuer | not_before |
not_after |
Cert Spotter id | Confidence | Anchor |
|---|---|---|---|---|---|---|
easypank.se-45564.xyz |
C=US, O=Let's Encrypt, CN=YE1 |
2026-09-18T07:27:20Z | 2026-12-17T07:27:19Z | 17251662632 | [OBSERVED] |
evidence/raw/certspotter/20260919T105728Z-se-45564.xyz.json |
easypank.se-9626654.pics |
C=US, O=Let's Encrypt, CN=YE2 |
2026-09-09T05:55:00Z | 2026-12-08T05:54:59Z | 17072948686 | [OBSERVED] |
evidence/raw/certspotter/20260919T105729Z-se-9626654.pics.json |
avcoa.click |
C=US, O=Let's Encrypt, CN=YE1 |
2026-09-05T15:05:21Z | 2026-12-04T15:05:20Z | 17004487410 | [OBSERVED] |
evidence/raw/certspotter/20260919T105724Z-avcoa.click.json |
btalning.click |
C=US, O=Let's Encrypt, CN=YE1 |
2026-09-02T07:30:50Z | 2026-12-01T07:30:49Z | 16939788574 | [OBSERVED] |
evidence/raw/certspotter/20260919T105725Z-btalning.click.json |
Each issuance carries exactly one name and no wildcard. [OBSERVED] (the
four envelopes, payload.issuances[*].dns_names)
12.5 URL and path patterns
| Pattern | Where observed | Confidence | Anchor |
|---|---|---|---|
easypank.se-45564.xyz/se |
the lure URL as given in the SMS. The two preserved renderings differ on the scheme, https:// in one and none in the other (2.1); the hostname and path are common to both |
[REPORTED] |
kit repo TAKEDOWN_REPORT.md:107-116; design spec lines 14-16 |
http://easypank.se-45564.xyz/se |
the probe target; answers with three 302 hops. Requested from the handset over a Swedish mobile carrier, not over Tor (5.5), and also from 28 Tor cells |
[OBSERVED] |
evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json and the 28 lure-URL envelopes under evidence/raw/cloak/ |
_v=valid_<14-digit UTC timestamp>_<32 hex> |
every landing URL; the timestamp is mint time plus eight hours, and the token is single use (2.4) | [OBSERVED] |
31 distinct values across the 60 envelopes in evidence/raw/cloak/ and evidence/raw/cloak-phone/, none deviating from the grammar: the 30 mints of 2.4 plus the spent token that arrived in the SMS |
bls=<6 chars> |
one additional query parameter, seen once, on the 2026-09-03 btalning.click scan |
[OBSERVED] |
evidence/raw/urlscan/20260919T105718Z-page.domain__btalning.click_.json |
path segment /ESZNhaXCmd |
every landing URL on easypank.se-9626654.pics, and on btalning.click fifteen days earlier |
[OBSERVED] |
all 60 envelopes of evidence/raw/cloak/ and evidence/raw/cloak-phone/ carry it; evidence/raw/urlscan/20260919T105718Z-page.domain__btalning.click_.json |
base path /we194_cz_etc_easypark/, pattern <random>_<cc>_etc_<brand> |
<base href> in the kit HTML, twice more in the JS |
[SOURCE] |
kit_original/index.html:5, <base href="/we194_cz_etc_easypark/">, and grep -o -F returning 2 in kit_original/assets/index-d25ac0d4.js |
base path /we194_cz_etc_easypark/ |
on four requests of the captured session | [OBSERVED] |
the four we194_cz_etc_easypark envelopes in evidence/raw/har/ |
POST /JisiRmktje/api |
session bootstrap; returns the relay token and the operator’s message strings | [OBSERVED] |
evidence/raw/har/20260919T105202Z-https___easypank.se-9626654.pics_JisiRmktje_api_token_755a9f6f-b4bc-4ef8-9118-17.json |
POST /JisiRmktje/api/input |
per-field HTTP exfiltration, parallel to the socket; 17 requests in the captured session | [OBSERVED] |
the 17 JisiRmktje_api_input envelopes in evidence/raw/har/ |
/assets/index-d25ac0d4.js, /assets/index-742a24eb.css |
ungated static assets of this exact build | [OBSERVED] |
evidence/raw/kit-asset/20260918T180946Z-https___easypank.se-9626654.pics_assets_index-d25ac0d4.js.json, evidence/raw/kit-asset/20260918T180946Z-https___easypank.se-9626654.pics_assets_index-742a24eb.css.json |
12.6 Response fingerprints
| Indicator | Value | Confidence | Anchor |
|---|---|---|---|
| Refusal body | 9 bytes, the ASCII string Not Found, sha256 0019dfc4b32d63c1392aa264aed2253c1e0c2fb09216f8e2cc269bbfb8bb49b5 |
[OBSERVED] |
evidence/artifacts/0019dfc4b32d63c1392aa264aed2253c1e0c2fb09216f8e2cc269bbfb8bb49b5.html; all 56 envelopes in evidence/raw/cloak/ |
404 header set |
exactly these ten: Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin, Alt-Svc, Content-Type, Date, Server, Trace-Id, Transfer-Encoding, Via |
[OBSERVED] |
the same 56 envelopes, payload.summary.headers, one identical header set |
| Session cookie | gfsessionid=<32 lowercase alphanumeric>; Path=/; Expires=<Date plus 24 h>, set on the second redirect hop only |
[OBSERVED] |
evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json, payload.headers_text |
| Device token | UUIDv4 sent as cookie token and as the token and x-token request headers on every application request |
[OBSERVED] |
the bootstrap envelope in 12.5 and all 17 JisiRmktje_api_input envelopes, payload.summary.request_headers |
| Kit page response | 200, 140535 bytes, Etag: "12d5585e1626af891dc3c1cca51d90c6", Last-Modified: Wed, 26 Aug 2026 08:00:40 GMT, no Server header |
[OBSERVED] |
evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json, fourth header block |
| Platform banner | Server: GoFrame HTTP Server behind Via: 1.1 Caddy, with a per-response Trace-Id of 32 hex |
[OBSERVED] |
the same 56 envelopes |
The platform banner identifies the hosting platform, not these operators, and
is never a lead on its own: it is used here only paired with another
indicator from this section. [SOURCE] (osint/discover.py:8-10)
12.7 Kit files
| File | Bytes | sha256 | Confidence |
|---|---|---|---|
index.html |
140535 | a98ddbfa1083b0c6893b60c6bc019b9ffef3dbc36528e0820dc332e8d78691d8 |
[OBSERVED] |
assets/index-d25ac0d4.js |
444050 | c8ccfd832d7f9b2e6ed47bbf455395776aec71aa22cdde790a05a1f17dd66821 |
[OBSERVED] |
assets/index-742a24eb.css |
34797 | 742a24ebaf60812317b40901165eba84d506c54938607911173959dbc4c3f045 |
[OBSERVED] |
[OBSERVED] (sha256sum and stat -c '%s' against kit_original/ in this
repository; the HTML hash matches the captured payload.body_sha256 of
evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json,
and the JS and CSS hashes match the bodies retrieved live in the two
evidence/raw/kit-asset/ envelopes) These three hashes are the narrowest
indicator here: they match this build only, and a rebuild defeats them.
[INFERRED] (6.8)
12.8 Live relay channel
| Indicator | Value | Confidence | Anchor |
|---|---|---|---|
| Relay path pattern | wss://<host>/ws?token=<UUIDv4>, same origin as the page, answered 101 |
[OBSERVED] |
evidence/raw/har/20260919T105202Z-https___easypank.se-9626654.pics_ws_token_755a9f6f-b4bc-4ef8-9118-171fec4947f7.json |
| Captured instance | /ws?token=755a9f6f-b4bc-4ef8-9118-171fec4947f7 on easypank.se-9626654.pics |
[OBSERVED] |
all 930 records of the kit repository’s tools/ws_evidence.jsonl |
| Operator wire event | result_type, the single inbound event carrying every operator command in its content.type |
[OBSERVED] |
the 3 result_type records at frames 275, 509 and 513, tools/ws_evidence.jsonl |
| Victim wire events | login, heartbeat, page_type, input_text, submit_card |
[OBSERVED] |
event counts over the same 930 records |
| Frame envelope | one JSON object per frame, event and content in all 930, messageId in 581 and timestamp in 43 |
[OBSERVED] |
key counts over the same 930 records |
A detection rule keys on result_type, not on the in-browser names
my-event, otp-valid, app-valid or custom-otp-valid: none of those
four appears in any of the 930 captured frames. [OBSERVED] (the same file;
6.5)
12.9 SMS delivery
| Indicator | Value | Confidence |
|---|---|---|
| Alphanumeric sender ID | InfoSMS |
[REPORTED] |
| Delivery time | 2026-09-18 16:46:15 CEST, 2026-09-18T14:46:15Z | [REPORTED] |
[REPORTED] (kit repository TAKEDOWN_REPORT.md:107-116) Both rest on the
recipient’s statement, with no message artifact or carrier record behind
them, so these two rows are leads for a carrier or police query rather than
values a defender can match on. [INFERRED] (2.2)
12.10 Live platform hosts, platform and not campaign
The six names below are live hosts of the same phishing platform impersonating
the same brand, verified over Tor on 2026-09-19 (section 8). [OBSERVED] None
is confirmed as this campaign’s infrastructure: no capture from an accepted
network exists for any of them, and none served the kit. [OBSERVED] (the 62
envelopes in evidence/raw/tier3-verify/, verdict kit zero times) They are
listed as platform context and must not be reported as this campaign’s hosts.
[INFERRED]
| Host | Address | Root response over Tor | AS at scan time | Confidence |
|---|---|---|---|---|
aseeasypark.cfd |
43.162.111.113 |
404, 9 bytes |
AS132203 | [OBSERVED] |
easypark.work |
43.165.1.140 |
404, 9 bytes |
AS132203 | [OBSERVED] |
easypark88.com |
43.129.85.86 |
200, 8988 bytes |
AS132203 | [OBSERVED] |
easypark9.com |
43.129.85.86 |
200, 6650 bytes |
AS132203 | [OBSERVED] |
easyparkss.cfd |
43.157.24.72 |
404, 9 bytes |
AS132203 | [OBSERVED] |
easyparkss.sbs |
43.157.24.72 |
404, 9 bytes |
AS132203 | [OBSERVED] |
[OBSERVED] (one envelope per domain in evidence/raw/tier3-verify/,
payload.resolves_to, payload.status_code, payload.body_length and
payload.server;
evidence/raw/urlscan/20260919T100041Z-page.server__GoFrame_HTTP_Server__AND_page.domain__easypark_.json,
payload.results[*].asn, for the AS number) The four 404 rows return a
9-byte body, the same length as the campaign hosts’ refusal, but the tier3
envelopes record no body hash, so byte equality is not established and none
of these refusals is evidence that these hosts are gated as the campaign’s
are. [OBSERVED] (the same envelopes have no body_sha256 field; 8.4) No
owner name is asserted for AS132203, since no registration record for it was
collected. [INFERRED] (section 9.7)
13. Recommendations and requested actions
Every action below traces to a fact established earlier in this report; none
of it introduces a new claim. Each subsection states what the report can
support asking for, and what would need cooperation the reporter does not
have. The four generated reports (reports/police-dossier.md,
reports/abuse-reports.md, reports/brand-brief.md,
reports/press-narrative.md) and the kit repository’s own
TAKEDOWN_REPORT.md and README.md are the baseline this section preserves
and re-checks against the current evidence.
13.1 Swedish police
The evidence supports a report of attempted card fraud: a live, human-operated
backend that streams typed card data before submission, validates it with a
bank-identification-number lookup and returns a verdict, reject in each of
the three observed cases (section 7.5, 7.8), served from infrastructure that
gates on network and client type specifically to delay detection (section 5). [INFERRED] The
captured session used fabricated data by deliberate design, so it demonstrates
the mechanism without exposing any real victim’s card (section 7.1, section
10.7). [INFERRED] What the evidence cannot support is anything about the SMS
itself: the sender ID InfoSMS and the delivery time rest on the recipient’s
own account, with no PDU, screenshot or carrier record in either repository
(section 2.2). [REPORTED] A filing in the bedrägeri / dataintrång track, as
the kit repository frames it (kit repository TAKEDOWN_REPORT.md:275,
README.md:59), can attach the infrastructure and session evidence directly.
[SOURCE] Establishing who sent the message or by what route requires the
carrier and network records addressed in 13.6. [INFERRED]
13.2 CERT-SE
CERT-SE takedown and abuse notification is a preserved ask from both source
documents (kit repository README.md:56, SCAMMER_WORKFLOW.md:219).
[SOURCE] What this report supports handing over is the confirmed
infrastructure of section 4 (four hostnames, one origin address, two
registrar abuse contacts), the gating behaviour of section 5, and the
detection material of 13.7, so CERT-SE can coordinate with the registrar and
host abuse desks below and circulate the indicators nationally. [INFERRED]
What needs cooperation this investigation does not have: visibility into
whether other Swedish recipients report the same sender ID or campaign, which
only a national coordination point can aggregate. [INFERRED]
13.3 The registrar, NameSilo
The registrar publishes two different abuse addresses across the four
registrations: abuse@namesilo.com for se-45564.xyz and se-9626654.pics,
and support@namesilo.com for avcoa.click and btalning.click (section
4.2). [OBSERVED] (section 4.2) Both are needed for a report covering all
four. [INFERRED]
The ask has changed for one of them since the abuse reports were drafted.
[INFERRED] (section 4.2) se-9626654.pics, and the hostname
easypank.se-9626654.pics under it, moved from two transfer-prohibited
statuses to server hold and client hold between 2026-09-18T16:21:41Z and
2026-09-19T10:55Z, and the landing host no longer resolves (section 4.2).
[OBSERVED] That domain is already off the air by registry or registrar
action. [OBSERVED] (section 4.2) What is still worth asking for is
confirmation that the hold is a deliberate abuse action and not a transient
state, since nothing in this evidence records who requested it (section 4.2).
[INFERRED] The other three registrations, se-45564.xyz, avcoa.click and
btalning.click, carry only transfer-prohibited statuses and still resolve
to the origin address as of the same collection (section 4.1, 4.2).
[OBSERVED] The suspension request already drafted for all four
(reports/abuse-reports.md) remains the live ask for these three
specifically: no record in this evidence shows that request was actually
sent to the registrar, only that the four reports were generated
(2026-09-19T11:18:04Z, section 11.1). [INFERRED]
13.4 The hosting network’s abuse contact
The origin address 220.158.232.231 is where the takedown lever with the
widest reach sits: the kit repository’s own reasoning is that the phishing
page and the live operator console are the same host, so a host-level action
stops both at once (kit repository TAKEDOWN_REPORT.md:75-76). [SOURCE]
Section 7.5 shows a response delay consistent with a human operator choosing
the next step rather than with automatic grading; 7.8 states what the capture
does not prove. [INFERRED] The RDAP object for the containing netblock names an abuse-role
contact, yennp@viettel.com.vn, and
a separate technical/administrative contact, admin@buctbd.com (section
4.4). [OBSERVED] A report can be addressed to both roles recorded on the
block, as reports/abuse-reports.md already does for the first. [INFERRED]
This report does not assert an autonomous-system owner name for that address:
the RDAP object carries no AS number, and AS38623 appears here only as
urlscan scan-time data (section 4.4). [OBSERVED] A report should therefore
cite the RDAP netblock and its named contacts rather than an AS owner.
[INFERRED] Confirming the abuse desk’s actual response track record needs
cooperation this investigation does not have. [INFERRED]
13.5 EasyPark
EasyPark has brand-holder standing for a takedown request that an individual
reporter does not (reports/brand-brief.md:46-48). [SOURCE] The kit
presents itself under EasyPark’s own Swedish payment-page branding, down to
the visible title Parkeringsappen som ger dig mer tid till annat | EasyPark
(section 6.3), and harvests vehicle registration plate, full card number,
cardholder name, expiry, CVV and phone number, streamed as typed and again on
submit (section 6.4, 7.8). [SOURCE] The current live set, as of the
2026-09-19 collection, is easypank.se-45564.xyz (redirector) still
resolving, easypank.se-9626654.pics (landing host) no longer resolving
following the holds of 13.3, and avcoa.click and btalning.click still
resolving on the same address (section 4.1). [OBSERVED] What EasyPark’s own
security team can add that this investigation cannot: confirmation of exactly
where the kit’s copy diverges from the real payment flow, which would
sharpen the brand brief beyond what external observation alone established.
[INFERRED]
13.6 The recipient’s mobile operator and PTS
This report can support forwarding the lure text and its known indicators
(the redirector hostname, the origin address) to the recipient’s mobile
operator’s messaging-fraud channel and to PTS (Post- och telestyrelsen) for
sender-ID abuse. [INFERRED] Both asks are already present in the kit
repository (TAKEDOWN_REPORT.md:272-274, README.md:57). [SOURCE] What it
cannot support is any claim about how the message reached the handset.
[INFERRED] The sender ID InfoSMS carries no originating number at the
recipient’s end (section 2.2). [REPORTED] The interconnect partner or
paying SMPP account behind it lives only in carrier signalling records, CDRs
and SS7 data that this investigation never had access to and that only the
operator or a law-enforcement request can read (kit repository
TAKEDOWN_REPORT.md, “Attribution limits”). [SOURCE] Reported
carrier-level countermeasures against this kind of delivery, SMS firewalls,
SMS Home Routing and sender-ID registries (kit repository
TAKEDOWN_REPORT.md:268-271), are policy context this report repeats but did
not test. [SOURCE]
13.7 Defenders, generally
The kit-level and platform-level fingerprints established in sections 6 and 8
are durable across a domain and IP rotation this kit’s operators have not yet
been observed to make (section 6.8). [INFERRED]
| Signal | Where established | Note | Confidence |
|---|---|---|---|
result_type WebSocket event, content.type verb |
section 6.5 | the operator’s only control channel [SOURCE]; internal names such as my-event appear in none of the 930 captured frames [OBSERVED] |
[SOURCE] and [OBSERVED] as marked |
/JisiRmktje/api and /JisiRmktje/api/input HTTP paths |
section 6.5, 6.6 | a second, parallel exfiltration channel, not polling overhead | [OBSERVED] |
<random>_<cc>_etc_<brand> base-path convention |
section 6.3 | per-deployment slug; this build’s suffix is easypark |
[SOURCE] |
Asset hash c8ccfd832d7f9b2e6ed47bbf455395776aec71aa22cdde790a05a1f17dd66821 |
section 6.1 | strongest signal, narrowest scope: this exact build only | [OBSERVED] |
page.server:"GoFrame HTTP Server" combined with a brand or AS term |
section 8.1, 8.2 | never the header alone | [SOURCE] |
A hunting query on the platform header by itself is not a lead: it answers on
a very large number of unrelated hosts and identifies the phishing platform,
not this campaign’s operators (osint/discover.py:8-10; sections 4.5, 6.8, 8.1). [SOURCE] The kit
repository’s own ask to pivot on the naming pattern by passive DNS or
certificate transparency (kit repository README.md:58) does not hold for
this campaign: crt.sh does not hold this campaign’s certificates, although
the pattern does return other hostnames that have to be excluded on other
grounds (4.3, 9.1), and Cert Spotter cannot take a pattern at all. Nothing in
this evidence tests the passive-DNS half of that ask, so this repository’s own discovery code instead
pivots outward from confirmed infrastructure, the origin address, the hosting
AS and the kit’s own server fingerprint (osint/discover.py:3-6). [SOURCE]
Confirming any candidate this pivots to as a sibling, rather than as
unrelated platform noise, needs the same method used for the confirmed
campaign: a capture from a Swedish mobile network, with a mobile User-Agent,
against a freshly minted token, since a Tor vantage cannot distinguish a live
cluster member that would serve the kit from one that would not (section
8.7). [INFERRED] The carrier-level countermeasures of 13.6 apply here too,
as the durable defence against this delivery mechanism rather than against
any one domain. [INFERRED]
Appendix A. Evidence index
A table row in this appendix is its own citation: the path is the anchor and the sha256 beside it is the value being verified, so rows carry no separate evidence label. Prose sentences outside the tables carry a label as elsewhere in this report.
A.1 Verifying the store
evidence/MANIFEST.sha256 has 433 lines, one per envelope or artifact file
under evidence/; (cd evidence && sha256sum -c MANIFEST.sha256 | grep -vc OK) returns 0, so all 433 are present with a matching hash. [OBSERVED]
(wc -l evidence/MANIFEST.sha256; same sha256sum -c command, run
2026-09-19)
grep -oE 'evidence/(raw|artifacts)/[A-Za-z0-9._/-]+' reports/easypark-smishing-investigation.md | sort -u returns 84 matches; 11
end in a slash, citing a directory as a set rather than a single file (for
example “the 56 envelopes in evidence/raw/cloak/”), leaving 73 distinct
cited files. All 73 exist on disk and each sha256 matches a manifest line;
none is missing and none is an unlisted hash. [OBSERVED] (that command
against this report; ls of each path; cross-reference against
evidence/MANIFEST.sha256, 2026-09-19)
Three of those directory-set citations check out exactly against the
manifest: evidence/raw/cloak/ holds 56 files, all cited as a set (5.3,
12.6); evidence/raw/cloak/ and evidence/raw/cloak-phone/ together hold 60
files, cited as a set (12.5); of the 40 files in evidence/raw/har/, 17
match JisiRmktje_api_input, cited as that set (6.6). [OBSERVED] (grep -c
of the manifest against each of raw/cloak/, raw/cloak(-phone)?/ and
raw/har/.*JisiRmktje_api_input, 2026-09-19) These sets are covered by the
whole-store verification above, not listed file by file below.
A.2 Envelopes and artifacts cited by name
Grouped by the directory under evidence/ that holds them. The path shown
is relative to that directory; the full path is evidence/<directory>/<path shown>.
evidence/raw/certspotter/ (6 of 18 files in this directory cited by name)
| file | sha256 |
|---|---|
20260918T164231Z-se-45564.xyz.json |
013b6e520e6c385067cf19bd06ba028cc018a187ca3fef7ff0e1ff132ac9dde8 |
20260918T164232Z-se-9626654.pics.json |
9b50854cce23a1d8b979f4180b5d8f7c01cb50acc4acdbdc039bd8db27b9f572 |
20260919T105724Z-avcoa.click.json |
1f4446d9291797e7089eb092cf0fd862f5bc85803b6bddb85f9d5587e87462b7 |
20260919T105725Z-btalning.click.json |
33c9779803e9056b59295e6d2965476e90bf11263122ec3785bafbeb7ab6b872 |
20260919T105728Z-se-45564.xyz.json |
b6be22df03d10cb8333b33ca472ba146994ce183638f17366cf909afd9897c63 |
20260919T105729Z-se-9626654.pics.json |
ca9044a83eae928e08e592c0c2597922724693515d952182061a077f017811cf |
evidence/raw/cloak/ (1 of 56 files cited by name; the remaining 55 are covered by the set citations in A.1)
| file | sha256 |
|---|---|
20260918T160754Z-http___easypank.se-45564.xyz_se.json |
597c66efc17d1bb18aa64dec8ed51605c443181fd18bc6fa257364049e2567e7 |
evidence/raw/cloak-control/ (2 of 2 files in this directory)
| file | sha256 |
|---|---|
20260918T160737Z-https___example.com_.json |
57599e78d8b2d726f811fb1d946bb32e8cd7e8dd8fe3207f1cec88eac758b491 |
20260918T160750Z-https___example.com_.json |
64371bda05a8e0192c8cb11e90ae8c611e8bd26b9ae7fbcf29f32ba5ea220ceb |
evidence/raw/cloak-phone/ (4 of 4 files in this directory)
| file | sha256 |
|---|---|
20260918T161229Z-http___easypank.se-45564.xyz_se.1.json |
3fd51a30591763b87fc9129e934ea49d589e4b7e054dd822183a90f8ceda3d7e |
20260918T161229Z-http___easypank.se-45564.xyz_se.json |
f5a19e578e5a7a5308bab75a1bae01f4efbfa8b955ccff1d6d0f19b3d528bb2c |
20260918T161229Z-https___easypank.se-9626654.pics_ESZNhaXCmd__v_valid_20260918232359_861d698a2bab.1.json |
d1882f881dae67cd1151d83934f4088f5381b51b12192aa367983912fd8b955f |
20260918T161229Z-https___easypank.se-9626654.pics_ESZNhaXCmd__v_valid_20260918232359_861d698a2bab.json |
48c10b2efeb76aed6dd611b364e14154b371c6d16830c41ea89e88426f8902e1 |
evidence/raw/ct/ (3 of 51 files in this directory cited by name)
| file | sha256 |
|---|---|
20260918T164240Z-easypank_.json |
ae6092531a88f20231dad877a4c647c888113b88d35c68850cde0c04d05fb258 |
20260919T105739Z-easypank_.json |
0853d869c292f507fc849c268cd0137ea7d31428621829252f721b1de48fc1ee |
20260919T111715Z-_.se-_.click.json |
4e2897cd0a76653dbd3b7b45468f0862145ae8220fc508c60534606c40d38d01 |
evidence/raw/dns/ (29 of 121 files in this directory cited by name)
| file | sha256 |
|---|---|
20260918T160845Z-easypank.se-45564.xyz.1.json |
8a69e1267f79b89a8b69988aca579e4760076f5bb6e24e5ccb64189f75dcd985 |
20260918T160845Z-easypank.se-45564.xyz.json |
0bb5626b9333291ecca90138de7ef5ba8b8a4e369c41d4b819f47e04b46b9665 |
20260918T160846Z-easypank.se-45564.xyz.json |
c54e6ae160f51c4254b5d26f3e2f6294bc16cf1a39ec5d12ecadd0cec97b1e45 |
20260918T160846Z-easypank.se-9626654.pics.json |
4f94b863f818c9f2262217ad3e20f7b9ebbe322780af8639f457ed41d0f4c19a |
20260918T160847Z-easypank.se-9626654.pics.json |
33eed65dc92121bc19447f5038a4cd93dbf5f5b670dabba0ba83cfaaa9091503 |
20260918T160848Z-easypank.se-9626654.pics.json |
dd81a443f3fa09d2dc391c48824bd334960e91307e0f1a4665c91ad4e0d2f58b |
20260918T160848Z-se-45564.xyz.json |
0754c1b0ef80e09cd386a5fa5882ba96337187c11947442ef02792746950a7d3 |
20260918T160849Z-se-45564.xyz.4.json |
eedd493a9ff3c0aecb94bf2c12ff8f033de94ebec66349d93ed1bcd845f0bd79 |
20260918T160849Z-se-45564.xyz.json |
c581353f6632dd23d83d3f0c997bf36e809e0d094119e10084035cf22f790628 |
20260918T160849Z-se-9626654.pics.json |
684337140053f10e20851cf93242a364ec54d608c1ca4fb5f4ce6376a462f637 |
20260918T160850Z-se-9626654.pics.1.json |
11389cdef56b013d85a1ac43915639fc7653656c80ba0fad098772e9577d8da2 |
20260918T160851Z-se-9626654.pics.1.json |
9bb365de80654d794386dc9c71c0264c0615f101ccd7f73a2850bbeaddcddd72 |
20260918T160855Z-220.158.232.231.json |
40f47d9958a0d23bf8560f477e7be8d396943c07df6f16e28280934211e1106f |
20260918T181409Z-avcoa.click.json |
2ed297fa5e645ea34330176d9b6c067403c4470302ceef54f4359808601e7b88 |
20260918T181414Z-avcoa.click.1.json |
708f78e00d2e07c1d32ce39955fc091aaea57981cdb429f1007dc02e403abb31 |
20260918T181415Z-btalning.click.json |
da733f524003095bb10b46c1c31560fac064bb5d68e90d0707f449aedbbdcdc7 |
20260918T181421Z-btalning.click.json |
085e429edb98ca269f265822325a4aafdbf7541f01b940e10d20ac2f89af5001 |
20260918T181422Z-liuguan168.lol.json |
d70a79c111be619c295db2f8de14b9e885ba3b114dc9da37f85f96203aedd022 |
20260919T105451Z-easypank.se-45564.xyz.json |
46d41b603d8e4d041e1d1660e5c0f5e7461aafe81862c5bbe2ae6964b4670cf7 |
20260919T105512Z-easypank.se-9626654.pics.json |
4494a01157e5ade72641bb775d46368b77bba25e0ff67f28e39929071c1ff7fc |
20260919T105539Z-se-9626654.pics.json |
337d4660d8c53e997f283013296f229baba20129398eb8c70f7411b2afd2908d |
20260919T105541Z-se-9626654.pics.json |
650bf12c6c995ecbb15ea15349f18fbcbc70e9c119485d7608c23026fdf62783 |
20260919T105543Z-se-9626654.pics.json |
ab436b4dcc99069a42a4b6b50b986ce7e442480ac5304dbe7f027fdd1ecddb0f |
20260919T105545Z-se-9626654.pics.json |
368b9d9c7b1da4150002448e35c0880ab1e141cab68e09ed7818f986a7086947 |
20260919T105547Z-se-9626654.pics.json |
8c030b0c3cd42bd7dac49d4dd56f260fbe9ebea83042c39eb19c96803007ed65 |
20260919T105550Z-se-9626654.pics.json |
b910ccc5469d4c5b3c68c3d0d2bd26ae47bb34a398ca46b3befd6712a7d13d58 |
20260919T105552Z-se-9626654.pics.json |
5ea71ccd915f616ec563086dae3551cdccb408441b71fa59ee227368d8ed31a6 |
20260919T105636Z-avcoa.click.json |
d859c50355e1a8ad110a2a41252fbf0ce7fafbc497a2ee1c25d4331d49928fb0 |
20260919T105659Z-btalning.click.json |
4c2d0e768d14a867e214af47536212c12587f7bc91bb242055e8fe1f030b3f0e |
evidence/raw/har/ (6 of 40 files in this directory cited by name; 17 of the 40 are also cited as the JisiRmktje_api_input matching set, A.1)
| file | sha256 |
|---|---|
20260919T105202Z-https___easypank.se-45564.xyz_se.json |
e1a75ac6266e4b6dcb57335a9c35cbfcdd62b04adaf622ea879d3c607775bff6 |
20260919T105202Z-https___easypank.se-9626654.pics_ESZNhaXCmd__v_valid_20260918232359_861d698a2bab.json |
cc4df2ee6ac7a007e1fa8386d69cddb92a7d5f668e92f492ada01f96d2505364 |
20260919T105202Z-https___easypank.se-9626654.pics_ESZNhaXCmd__v_valid_20260919031957_33bccaec191c.json |
17ccbf3ad92486ffd15ecea49811bf905c59e2da7b49772e9b562e885ce37baa |
20260919T105202Z-https___easypank.se-9626654.pics_JisiRmktje_api_input_token_755a9f6f-b4bc-4ef8-9.json |
d29b6943da6582255b2684326533239663db41b094d9911f0520c7c6c0e4a40f |
20260919T105202Z-https___easypank.se-9626654.pics_JisiRmktje_api_token_755a9f6f-b4bc-4ef8-9118-17.json |
7e8e0a838ffd586d2c3c99b31b99b650788f94a84ef6fc1171f906d6bb63b108 |
20260919T105202Z-https___easypank.se-9626654.pics_ws_token_755a9f6f-b4bc-4ef8-9118-171fec4947f7.json |
ff4646883cfd2ee2125265528d8aa768b8322668acf2dfed3a717d13e77c75b4 |
evidence/raw/kit-asset/ (2 of 2 files in this directory)
| file | sha256 |
|---|---|
20260918T180946Z-https___easypank.se-9626654.pics_assets_index-742a24eb.css.json |
0ca3005fa582326412490cc6890319610ba58ba76975bb5625ad514c43af57f5 |
20260918T180946Z-https___easypank.se-9626654.pics_assets_index-d25ac0d4.js.json |
3d88055da9da2258c8a63fbb53c33d809d52485b64016313994d9dda6bbe3bd3 |
evidence/raw/rdap/ (10 of 23 files in this directory cited by name)
| file | sha256 |
|---|---|
20260918T162138Z-easypank.se-9626654.pics.json |
4d13601b4c460ef2ab4f9d8a300f004877ff97f9f27e9d6d993c100abd8a5ceb |
20260918T162140Z-se-45564.xyz.json |
cf8b05b6c4a8e3ae51488a957b30d37ee0c76a6da4e78c3827dfe0477a7db5d9 |
20260918T162141Z-se-9626654.pics.json |
7c85602d60e656fe0d57b484f98fd732b8f2013f261e31fed6c25467d63e48b1 |
20260918T162147Z-220.158.232.231.json |
6900fa75ea004e8ef788bc0a3179110f9924ddb356b6c5faaaddd75ebb09a3d3 |
20260918T162807Z-se-9626654.pics.json |
5e335891ca57f5170081d61afbfa764904ea204b0c43b767cbbe3d12bd074922 |
20260919T105537Z-se-45564.xyz.json |
5c64a05afbd59b24736eac8a0b870735ed43bd3d29126a6298f5975b815cb606 |
20260919T105554Z-se-9626654.pics.json |
9cba0f0d5bc9caa17abe1a4e3cfebe1384c844ba73e7cdb3c403b45caf4dfa67 |
20260919T105655Z-avcoa.click.json |
cf0f3f0b629474c7c17d480d963914c6f6f647907c7816b396b8c56ed90ea7f6 |
20260919T105717Z-btalning.click.json |
97bd545edcd47c631935a25247010a358d89dd383dd3124eb17c934982bbb2cf |
20260919T105723Z-220.158.232.231.json |
d1b81979138d7b893c79f07b8505ecd3459c3d6e8e9ba96d6ea5515bf79518da |
evidence/raw/tier3-verify/ (3 of 62 files in this directory cited by name)
| file | sha256 |
|---|---|
20260919T100351Z-easypark.se-36586.online.json |
3156ddcd298a89d02ec560460f80cf460602c773db74a8b243547b75b8490678 |
20260919T100351Z-easypark.se-toyota.homes.json |
5876d3dc42d6f2fdc8a505624789b2f1e4e984a5fa0b208a540d60b43233aa79 |
20260919T100351Z-easypark.se-toyota.lol.json |
0fdcc0f2eba000bb4802522779244567a1aac0ec058f8603249872314887ec4b |
evidence/raw/urlscan/ (5 of 33 files in this directory cited by name)
| file | sha256 |
|---|---|
20260918T175939Z-page.ip__220.158.232.231_.json |
75984a12303cfd7c2ba17b9e3d276b204bc66ff55bfa0acf9f963742856c1aaa |
20260919T100041Z-page.server__GoFrame_HTTP_Server__AND_page.domain__easypark_.json |
c42d1cf9be6738d53ffa0ca30104fbd61e14a05de7bfa6b829def34296f4530f |
20260919T100044Z-page.server__GoFrame_HTTP_Server__AND_page.asn__AS132203__AND_page.domain__park_.json |
9209e4839565714a8d6d042a75329524fe359956329f2c6c40e80d32817f361f |
20260919T105656Z-page.domain__avcoa.click_.json |
769949429c48a978e90ef49244dc8ec956ff36502f2cecb860e751f7b71bb4cc |
20260919T105718Z-page.domain__btalning.click_.json |
fe7a7de639c52e1d72d34222aeed2da0950e3dc1ed1bc78636b0d8868387d6c4 |
evidence/artifacts/ (2 of 21 files in this directory cited by name; all are content-addressed, so the filename stem is the hash)
| file | sha256 |
|---|---|
0019dfc4b32d63c1392aa264aed2253c1e0c2fb09216f8e2cc269bbfb8bb49b5.html |
0019dfc4b32d63c1392aa264aed2253c1e0c2fb09216f8e2cc269bbfb8bb49b5 |
e3eaf9aa94f833e92c3e09df408c03dd98ba0675f7f6a878bbe1692b566d1e66.bin |
e3eaf9aa94f833e92c3e09df408c03dd98ba0675f7f6a878bbe1692b566d1e66 |
A.3 Kit repository files cited
The kit repository (/home/tugg/misc/dev/projects/kit, branch
phishing-investigation) is a separate working tree from this repository
and its files carry no line in evidence/MANIFEST.sha256; the hashes below
were computed directly from the files as read from that repository, not from
this repository’s evidence store. [OBSERVED] (git -C /home/tugg/misc/dev/projects/kit show phishing-investigation:<path> | sha256sum, run against each of the four files below, 2026-09-19)
| file | size (bytes) | sha256 |
|---|---|---|
kit_original/index.html |
140535 | a98ddbfa1083b0c6893b60c6bc019b9ffef3dbc36528e0820dc332e8d78691d8 |
kit_original/assets/index-d25ac0d4.js |
444050 | c8ccfd832d7f9b2e6ed47bbf455395776aec71aa22cdde790a05a1f17dd66821 |
kit_original/assets/index-742a24eb.css |
34797 | 742a24ebaf60812317b40901165eba84d506c54938607911173959dbc4c3f045 |
These three sha256 values match the ones the landing host served, which is
what establishes that the preserved kit source and the captured live build
are the same code (section 6.1). The HTML was served to the authorised
handset request over a Swedish mobile carrier, outside Tor (5.5, 10.7); the
JS and the CSS were served to two Tor requests from exit 171.25.193.131.
[OBSERVED] (evidence/raw/cloak-phone/20260918T161229Z-http___easypank.se-45564.xyz_se.json
and evidence/raw/kit-asset/20260918T180946Z-https___easypank.se-9626654.pics_assets_index-d25ac0d4.js.json
and evidence/raw/kit-asset/20260918T180946Z-https___easypank.se-9626654.pics_assets_index-742a24eb.css.json)
The captured frame file, tools/ws_evidence.jsonl, has 930 lines (one JSON
record per WebSocket frame) and sha256
f65a299f6969907be98c00a6cb5d0160e10f65b2501f0ba81ad7c5ed891664a9 as read
from the kit repository. [OBSERVED] (git -C /home/tugg/misc/dev/projects/kit show phishing-investigation:tools/ws_evidence.jsonl | wc -l returns 930; piped
to sha256sum returns the hash above, 2026-09-19) Like the three files
above, it is tracked in the kit repository’s own git history and is not
covered by this repository’s manifest; sections 6 and 7 cite it as
tools/ws_evidence.jsonl or “the 930 captured frames”, never as an
evidence/ path, because it was never collected into this repository’s
evidence store. [OBSERVED] (kit repository, git -C /home/tugg/misc/dev/projects/kit log --oneline -- tools/ws_evidence.jsonl)
Appendix B. Glossary
Plain-language definitions for the terms this report relies on, written for a reader who is a police officer or a journalist rather than a network engineer. A definition below is background, not a factual claim about this campaign, so it carries no evidence label; where the report demonstrates the term, the relevant section is named.
Smishing. SMS phishing: a text impersonating a trusted sender to lure the recipient into opening a link and entering personal or payment details on a fake page, as in the EasyPark-impersonating message this report examines (section 2).
User-Agent. A short string a phone or browser sends with every web request, naming the browser and device. This campaign’s landing page reads it and serves the phishing page only to a mobile-looking User-Agent, refusing a desktop one making the same request (section 5).
Cloaking. Serving different content to different visitors depending on who, or what, appears to be asking, so an investigator sees something harmless while the intended victim sees the attack. Section 5 sets out this campaign’s two independent checks, source network and User-Agent.
Certificate transparency. Public, append-only logs recording every HTTPS certificate a certificate authority issues, searchable by hostname, letting a new domain be found within minutes of going live. Section 4.3 reads this campaign’s certificates from it; section 9.1 explains why the same brand-typo search also surfaces two hostnames this report treats as unconfirmed.
RDAP (Registration Data Access Protocol). The structured, machine-readable successor to WHOIS, returning who registered a domain, when, through which registrar, and its current status. Section 4.2 reads this campaign’s registration data from it; RDAP is used instead of WHOIS because it runs over the same Tor circuit as every other lookup (CLAUDE.md).
Registrar. The company a domain owner pays to hold a registration, and that can suspend or lock it on an abuse report. All four of this campaign’s registrable domains share one registrar, NameSilo, LLC (section 4.2), addressed directly in section 13.3.
Nameserver. The server that answers, for a given domain, which address it resolves to. A shared, unusual nameserver set across otherwise unrelated-looking names is itself a clustering signal (sections 4.1, 4.6).
Tor exit. The last computer in a Tor circuit, the one that contacts the destination and whose address the server sees instead of the real requester’s. Every request to the operators’ own hosts left from a Tor exit; section 5.2 notes that phishing kits, including this one, sometimes block Tor exits outright, which is why every probe also sends a control request over the same circuit to a non-campaign site.
DNS over HTTPS (DoH). Resolving a hostname to an address inside an ordinary encrypted HTTPS request rather than the plaintext lookup an operating system normally sends, so resolution travels over the same Tor circuit as everything else instead of leaking the investigator’s real path to the campaign’s own nameservers (section 4, section 10.1).
Bank identification number (BIN). The first several digits of a payment card number, identifying the issuing bank and scheme without identifying the cardholder. The captured session’s live relay looks this number up against a card the operator is shown in real time, before accepting or rejecting it (section 7.5).
One-time code relay. A phishing technique that takes whatever the
victim enters, including a one-time code from the victim’s own bank, and
relays it live into the real bank’s login flow to complete a fraudulent
transaction. The kit’s /otpValid and /customOtpValid operator commands
describe this capability (section 6.4), though this captured session never
reached that stage (section 7.8).
WebSocket. A persistent, two-way connection that, unlike an ordinary web request, stays open so either side can send messages at any moment. This kit uses one to give the operator a live view of the victim’s session and a channel to send commands back (section 6.5), identified by a per-session token (section 7.6).
Alphanumeric sender identifier. An SMS sender name made of letters
rather than a phone number, such as InfoSMS in this report’s subject
message (section 2.2). A handset cannot originate one itself; it is a
feature of the bulk-messaging product used to send it, so the identifier
alone does not reveal who paid to send the message (section 3, 12.9).
Grey route. An SMS delivery path reaching a handset through an interconnect or reseller outside the sending brand’s own direct agreement with the recipient’s mobile operator, often chosen for lower cost or weaker sender-identity checks. Whether such a route carried this report’s subject message is not established: that needs carrier interconnect records, CDRs or SS7 data this investigation never had access to (section 13.6).
SMPP (Short Message Peer-to-Peer protocol). The protocol bulk SMS senders and aggregators use to submit messages into the mobile network for delivery. Which SMPP account or interconnect partner submitted this campaign’s message is a question only the recipient’s mobile operator or a law-enforcement request can answer (section 13.6).