On 6th October 2026, a German-language phishing page was submitted to Sandfish. The initial capture recorded two requests and a screenshot of what looked like a Cloudflare security check. It never reached the payment page behind it.
The Cloudflare check was fake. Behind it, a payment-phishing application combined card collection with a command channel designed to let an operator choose the visitor's next verification prompt. Reading that client code explains why the apparent €3,99 health-card payment can become a much longer interaction.
The screen in front of it is a static page with one unlabelled button. It is enough to stop the initial Sandfish capture. The investigation's public-scan corpus contained 2,076 submissions between 11th May and 6th October 2026, with no page behind the gate recorded. That measures coverage in this corpus, not every scanner's ability to pass it.
This article covers attacker infrastructure and public scan records. The session used to reach the kit was generated on a researcher's own machine. No victim data was collected, and no phishing form was submitted with real values.
A check that never talks to Cloudflare
The gate page is 22,882 bytes. It copies the Cloudflare wordmark, prints
"Privacy · Help" where the real Turnstile widget puts its footer, and selects a
prompt from navigator.languages. The table holds 62 locale keys that resolve to
54 distinct prompt strings. A German visitor reads "Bitte bestätigen Sie, dass
Sie ein Mensch sind".

The gate as a German visitor sees it. Rendered offline from the preserved page, with all network requests blocked. The box on the left is the only control, and it carries no visible label.
It makes no request to a Cloudflare challenge endpoint. The complete capture of this page is two requests: the HTML and a favicon.
The only control that advances the page is this:
<button type="button" class="checkbox" id="verifyTrigger" aria-label="点击验证">The accessible name is Chinese for "click to verify". The button carries no visible text, only a drawn square box.
Clicking it posts to the same URL:
POST /E8VuD7i
__verify_submit=1&verify_code=<16 hex characters>
{"code":200,"message":"ok"}The page then reloads, and the server returns the phishing kit instead of the
gate. A wrong or expired token returns {"code":400,"message":"verify failed"}
along with a fresh value to try next.
Why scanners stop here
The initial Sandfish capture missed the gate for two related reasons. There was no form to fill, and the icon-only control lacked the visible text used to select buttons. The successful recapture required recognizing the control's verification role. The public scans show the same stopping point, but do not expose each scanner's internal decision.
Across the 237 hostnames selected by the public query, the investigation found no capture of a later page. The query's selection limits matter: hosts without the expected title and server banner fall outside it.
What the gate hides
Once the gate opens, the server serves a Vue application under
/apps/DE_PAY_01/. It fetches its configuration from /getApp?app_id=DE_PAY_01,
which returns three encrypted fields.
The encryption adds work for an investigator without keeping the configuration secret from a visitor. The client bundle contains every
step: AES-decrypt the third field using the first as a passphrase to recover an
RSA private key, use that key to unwrap the AES passphrase, then decrypt the
configuration. The AES mode is CBC with the key derived as SHA256(passphrase)
and the IV taken from the first 16 bytes of the encrypted payload.
The configuration recovered from the researcher's session describes the German lure:
sn DE_PAY_01
title Elektronische Gesundheitskarte (eGK) | BMG
pay_amount € 3,99
redirect_uri https://www.bundesgesundheitsministerium.de
wss_server wss://bmg-xgesundheitsversorgung.ink/wssThe lure is a €3,99 fee for a German electronic health card. The application registers six top-level routes:
/ landing and fee notice
/select option or date selection
/card card entry
/notice interstitial for "verification required" steps
/fail failure or retry
/finish completionThe client state holds 40 fields. Those that matter are number, date, cvv,
and pin, alongside username, password, birthday, and credit. Counted by
reference, the bundle is a card funnel: 202 mentions of cvv, 131 of
card_number, 63 of pay_amount.
One message string reveals a card-selection preference. reject_bin appears once in each of
the 55 language packs, and it tells the victim that debit cards are unavailable
and to pay by credit card instead. The client can steer users toward a particular card type. It does not tell us whether credit limits, resale value, or another constraint explains that preference.
Commands for steering the visitor in real time
The client code opens a WebSocket to /wss?uuid=<session>, defines a 30-second heartbeat, and encrypts messages with a per-session key from the configuration. It reports current_page and current_action and handles 25 server command types.
No WebSocket payloads were recovered. The following behavior comes from the client handlers, not an observed operator session or access to an operator panel. The handlers divide into three groups.
Move the victim.
| Command | Effect |
|---|---|
goto, action_jump | Push the victim to a named route |
action_back, action_next | Move through history |
refresh | Reload the page |
clear | Wipe local state and restart |
block | Eject the visitor to the real government site |
Demand another factor. Eight commands each open a prompt and set a paired waiting state. The internal names are Chinese:
sms 短信验证页 SMS code -> 等待短信验证码
email 邮箱验证页 email code -> 等待邮箱验证码
pin PIN验证页 card PIN -> 等待PIN码
app App验证页 banking app -> 等待App验证
balance 余额验证页 account balance -> 等待余额验证
mobile 手机号码验证页 phone number -> 等待手机号码验证
diy 临时验证页 temporary -> 等待临时验证
custom 自定义验证页 custom -> 自定义错误Reject and retry. These commands could let an operator adapt the page to an authorisation result obtained elsewhere:
| Command | Internal status | Meaning |
|---|---|---|
rejectBin | 拒绝卡头 | "reject card prefix" — wrong card type |
cardReject | 拒绝卡号 | the card failed; ask again |
duplicateCard | 拒绝重复卡号 | this card was already used |
smsReject, emailReject | 拒绝短信验证码 | the code was wrong; ask again |
pass | 放行 | "release" — send the victim to the completion page |
Separate commands for a card prefix, a duplicate card, and an incorrect code let the controller choose a specific retry instead of restarting the whole journey. This supports an interactive fraud workflow. It does not establish a live connection to a bank or a completed authorisation in the investigated session.
Opening developer tools sends you to the real ministry
The bundle includes disable-devtool, a public JavaScript library, with seven
detectors enabled. It is wired to the destination supplied by the server:
Dg({url: t.redirect_uri}) // https://www.bundesgesundheitsministerium.deA visitor who grows suspicious and opens developer tools is navigated to the genuine German Federal Ministry of Health website. We observed this directly: the detector fired, and the browser left for the real ministry 727 milliseconds later.
The redirect removes the suspicious page just as the visitor begins inspecting it. It also leaves a genuine government page on screen. We observed the navigation, not how a victim interpreted it.
Campaign scale
The preserved gate has a response hash useful for exact-copy searches. The broader investigation used a different pivot: its page title combined with the workerman server banner. That banner names a PHP socket framework. The query selects candidate related hosts; it does not itself test their response hashes:
page.title:"Verify you are human" AND page.server:"workerman"That query returns 2,076 scans across 237 hostnames, from 11th May to 6th October 2026.
The themes below are inferred from hostnames. Because those scans stopped at the gate, they do not confirm the branding or payment flow on each hidden page.
| Hostname theme | Hostnames | Example |
|---|---|---|
| Australia Post | 177 | auspost.addqwi.com |
| myGov and Medicare | 22 | my.gov.medicarecarq.com |
| Irish HSE | 10 | hse-renewals.com |
| Australian Taxation Office | 2 | ato.gov.individual-tax.com |
| Parking | 2 | uniparkpay.shop |
| Qantas | 1 | qantas.addqwf.com |
| Unlabelled | 23 | coreacori.click |
Hostnames are mass-produced from a few registrable stems. The addq* family
carries most of the corpus, with the brand word pushed into the subdomain, as in
auspost.addqwi.com and auspost.addqwe.com. A second family, coerr* and
core*, carries no brand word at all. The sequence addqwe.com, addqwg.com, addqwh.com, addqwp.com, addqwr.com, addqws.com, and addqwt.com is consistent with related provisioning. Similar names alone cannot distinguish one operator from several customers using a shared supplier.
Public scan volume fell in midsummer and rose again in September. Submission counts also depend on discovery and scanner activity, so this is not a measure of victim traffic:
| Month | Scans |
|---|---|
| May | 691 |
| June | 670 |
| July | 66 |
| August | 111 |
| September | 522 |
| October (to the 6th) | 16 |
The records include Alibaba Cloud and Tencent Cloud addresses. One Alibaba address was associated with 206 hostnames from May to September; Tencent addresses also appeared, with overlapping observation windows. This supports infrastructure diversity, but does not by itself prove a coordinated migration.
| Address | Provider | Hostnames | Window |
|---|---|---|---|
47.79.39.190 | Alibaba Cloud | 206 | 11th May - 30th Sep |
43.165.7.184 | Tencent Cloud | 11 | 29th Aug - 29th Sep |
43.165.68.243 | Tencent Cloud | 10 | 28th Sep - 5th Oct |
43.166.217.161 | Tencent Cloud | 2 | 2nd Jul - 28th Jul |
43.157.40.45 | Tencent Cloud | 2 | 4th Sep - 26th Sep |
The German host, bmg-xgesundheitsversorgung.ink, sits behind Cloudflare and does not expose the same banner. It shows why a banner-based search misses some related deployments. One such deployment does not establish a sequence of platform hardening.
One build, many lures
The German health-ministry deployment uses a multilingual build. It ships 55 complete language packs in a single file, from Albanian to Traditional Chinese, including Tajik, Sinhala, and Swahili.
It also ships lures the German lure never uses. Alongside the health-card text are complete traffic-fine and toll templates, with ready-made strings for speeding, for using a phone while driving, for an unpaid toll, and for the licence suspension that follows non-payment. There are parcel-delivery and loyalty-points templates too.
The government branding is interchangeable dressing over one payment flow. The brand names used in the corpus hostnames include Australia Post, myGov, Medicare, the Australian Taxation Office, the Irish HSE, and Qantas: high-trust services that contact large numbers of ordinary people, where an unexpected small fee is plausible. Their broad consumer use fits a reusable small-fee lure, although we have no delivery data establishing who received it.
A €3,99 pretext brings the visitor into a flow capable of collecting reusable card data and requesting additional codes. The amount shown on the page does not bound the potential misuse of that data.
Shared software, unresolved ownership
Two details suggest reusable deployment infrastructure, but neither proves that it was rented to customers.
The application uses configurable identifiers. The German case ran DE_PAY_01. A
Dutch tax-authority typosquat, belastingdlienst.icu, ran NL_PAY_05 through
the same getApp endpoint. Neither host appears in the 237 above, because both
sit behind Cloudflare and return no workerman banner. The query therefore undercounts known deployments. Application identifiers are not customer or operator counts.
That Dutch host shared an address, 43.156.109.100, with ddbsso.com, which
urlscan recorded 111 times serving a page titled
RuoYi-Vue-Plus多租户管理系统, "RuoYi-Vue-Plus multi-tenant management system",
with admin., api., demo., and staging. subdomains. Multi-tenancy fits a
platform serving several customers. RuoYi is a widely used open-source framework
with hundreds of unrelated instances in public scan data, so the framework alone
proves nothing. The shared address is a lead worth recording, but shared hosting is also a plausible explanation. No recovered request or configuration links the phishing client to that management system. The panels were offline when checked.
How this compares
Darcula provides a useful comparison for what has and has not been observed. Its documented operator-side features go beyond the victim-side bundle recovered here.
Netcraft first described Darcula in March 2024 across more than 20,000 domains and 11,000 IP addresses, targeting over 100 brands in 100-plus countries. Those figures describe Netcraft's March 2024 dataset, not a directly comparable measure of this investigation's reach.
| Darcula (reported) | This platform (observed) | |
|---|---|---|
| Infrastructure counted | 20,000+ domains reported | 237 hostnames in a narrower query; not a like-for-like total |
| Operator control | Admin functionality documented | Client command handlers recovered; no operator panel inspected |
| Delivery vector | SMS, RCS, iMessage | not observed |
| Card-to-wallet provisioning | yes | no markers found |
The clearest difference is what the investigations could inspect. Netcraft documented Darcula's admin panel generating an image of the stolen card so it can be loaded into a digital wallet, after which the loaded phones are sold on. That is a fencing operation: the card becomes a reusable asset.
We found no wallet-provisioning markers in the recovered client. Its pass and rejection handlers support an interactive approval-and-retry flow, but that does not reveal the eventual cash-out method. Wallet provisioning could happen in a separate operator system. The absence of that code on the victim's page cannot rule it out.
The shared traits with that ecosystem are a Chinese operator interface, Alibaba and Tencent
hosting, workerman, delivery and toll lures, and client support for real-time control.
Those traits are common across unrelated tools and cannot attribute this software to Darcula. The investigation found no shared code or infrastructure establishing such a link.
What it took to see any of this
Sandfish's first capture of the German page stopped at the gate, like the public scans reviewed for this investigation. The fix was to teach the capture to press the control when three conditions hold together: the page demands verification in a single phrase, the page is short enough to be an interstitial rather than real content, and it offers a control that states a verification role through its markup, its accessible name, or a checkbox shape. At the time of this investigation, that imitation-gate handler excluded pages carrying a real challenge-provider frame. This describes the capture change tested then, rather than the current handling of every challenge type.
The historical before-and-after capture comparison:
| Before | After | |
|---|---|---|
| Interaction actions | 0 | 1 |
| Requests | 2 | 12 |
| JavaScript functions | 8 | 4,073 |
| Technologies | 4 | 8 |
The same press was accepted on two further domains, tigocaqci.help and coreacori.click, each returning {"code":200,"message":"ok"}. Both then reached a Cloudflare Error 300 origin timeout. That confirmed gate acceptance, not a successful capture of their payment flows.
Indicators
Gate fingerprint
page size 22882 bytes
sha256 2b10e03cc23363ac9ebcdeb211cf98b0d1fbf5824836b17b0306209d1e108cc0
control <button class="checkbox" id="verifyTrigger" aria-label="点击验证">
post body __verify_submit=1&verify_code=<16 hex>
success {"code":200,"message":"ok"}
server banner workermanPlatform endpoints
/getApp?app_id=<APP_SN> encrypted application configuration
/wss?uuid=<session> AES-encrypted operator channel
/apps/<APP_SN>/assets/js/ application bundle
https://ipapi.co/json/ visitor geolocationObserved application identifiers: DE_PAY_01, NL_PAY_05.
Hosts
bmg-xgesundheitsversorgung[.]ink
belastingdlienst[.]icu
tigocaqci[.]help
coreacori[.]click
corepeimci[.]click
hseaz[.]top
hse-renewals[.]com
healthcard-hse[.]com
services-hse[.]com
auspost[.]addqwi[.]com
qantas[.]addqwf[.]com
my[.]gov[.]medicarecarq[.]com
ato[.]gov[.]individual-tax[.]com
uniparkpay[.]shop47[.]79[.]39[.]190 Alibaba Cloud
43[.]165[.]7[.]184 Tencent Cloud
43[.]165[.]68[.]243 Tencent Cloud
43[.]166[.]217[.]161 Tencent Cloud
43[.]157[.]40[.]45 Tencent Cloud
43[.]162[.]122[.]44 Tencent Cloud
43[.]156[.]109[.]100 Tencent CloudPivot
page.title:"Verify you are human" AND page.server:"workerman"ddbsso[.]com is an unresolved co-hosting lead discussed above, not a confirmed phishing host. Shared provider addresses also require a date and hostname before they can support a campaign link.
Method and limits
Updated on 9th October 2026: we narrowed earlier claims about a rental model, operator counts, infrastructure migration, and cash-out. The recovered client supports the capabilities described here; it does not establish those business or attribution conclusions. We also clarified that two additional gate acceptances ended in origin timeouts.
The initial capture and recapture document the change in capture coverage. Configuration and command-handler findings came from the researcher's preserved browser session. Claims about customer counts, a rental business, and a specific cash-out method are not established by those artifacts.
Sandfish preserved the captures, request records, screenshots, and WACZ archives for the German case and the two further domains. The session that reached the kit's configuration was produced in a normal browser on a researcher's machine. The gate image above was rendered from that preserved page with network access blocked. Campaign scope comes from public urlscan records.
Several limits apply to the conclusions.
We recovered no WebSocket frames. The command set is read from the client bundle, so the platform's capability is established, but its use in any specific case is not. Firefox does not retain WebSocket payloads in HAR exports.
The pivot undercounts. It finds hosts that expose a workerman banner, and both
confirmed application identifiers sat behind Cloudflare instead. The 2,076 figure
also counts urlscan submissions, not victims. It measures the captures selected by this query, not total campaign reach or universal scanner resistance.
Session keys, UUIDs, and verification tokens are per-session. They identify nothing beyond the session that produced them.
The Chinese interface strings, the workerman banner, and the hosting choices do not establish where the software was written. The Chinese strings suggest a Chinese-speaking development context. None of them identifies
an operator, and hosting location does not establish nationality. The recovered payment flow supports a card-fraud interpretation. Government branding alone does not establish an espionage target or motive, and a client bundle cannot establish the limits of an operator's wider activity.
During the investigation the platform began refusing our analysis host and one research address. Repeated probing likely caused that, so later negative results from those addresses should not be read as the campaign ending. At the time of writing, the German lure still served its gate.
One earlier conclusion in this investigation was wrong. We briefly assessed that the kit had been withdrawn, on the basis of replaying an expired verification token, which can only fail. A full browser session corrected it. We record the error because the same mistake is easy to repeat against any kit that rotates a token.