UniAuth
Back to blog
Security30 min read

The Binance Verification-Code SMS That Uses a Real Binance Link

A user reported two SMS messages carrying a Binance verification code they never asked for, and a link on a genuinely authentic Binance domain. The link is real, the shortener is Binance's own, and the page at the end asks you for nothing at all. We decode the full chain and explain why the fake code is the cleverest part of the attack.

UniAuth Threat IntelligenceAugust 20, 2026
Update · 22 August 2026 — three waves
Three campaigns in 48 hours from one operator. The second reused the same Binance Mini Program identifier as the first, so that component was never disabled. The third abandoned Binance’s infrastructure entirely for a lookalike domain of the attacker’s own, and is cloaked against automated analysis — which is why a scanner can call a live phishing site clean. There is also a confirmed case in which the payload’s conversion stage executed on a funded account. See What an empty address actually hides and Wave three below.

A user forwarded us two text messages that arrived a couple of hours apart. Both looked like a Binance security notice. Both carried a link on a domain that is genuinely, verifiably Binance's own.

We pulled the chain apart. What is at the end of it is not a fake login page. It is a script that, if it reaches you inside the Binance app while you are signed in, redeems your savings, converts every balance you hold into Bitcoin, and files a withdrawal to an address the attacker hardcoded. It never asks you for a password, a code, or a seed phrase, because it does not need any of them.

This post documents the whole chain, because the usual advice — "check the domain" — does not save you here. The domain is real.

The messages

SMS · wave one2 of 4
Your verification code is: 780641. If you did not initiate this request, please visit hxxps://s.binance[.]com/spe​ZiskQ
你嘅驗證碼係:366812。如果你冇提出呢個請求,請瀏覽 hxxps://s.binance[.]com/spe​ZiskQ
Every hostile address on this page is written in defanged form and is not a working link. Do not reconstruct it.

These are the first two. Two more arrived thirty-six hours later, from the same Binance Mini Program, after everything below had been written — they are in It came back, and nothing had changed.

Two things are already worth noticing.

The codes differ — 780641 and 366812 — but the link is identical. A real one-time code is bound to a real request. These are decoration, generated per send so the message reads like something you have received before.

The second message is written in colloquial Cantonese, not standard written Chinese. Somebody localised this deliberately for Hong Kong readers. We will come back to that, because the payload itself is localised too, and it tells us who the campaign is aimed at.

Why "check the domain" fails

The person who reported this is experienced with DNS and domain infrastructure, and very nearly clicked. That is the part worth taking seriously, because they did the right checks and every one of them passed:

  • The host is s.binance.com. That is a subdomain of binance.com, the authentic registrable domain.
  • It is not an internationalised-domain homograph. Every character is plain ASCII. Compared letter by letter, it is the real string.
  • WHOIS on binance.com returns Binance's own registration. There is no lookalike registrar, no recent creation date, nothing anomalous.
  • TLS terminates on a valid certificate. The padlock is real.

All of that is true, and none of it helps, because the attacker never needed to control a Binance domain. s.binance.com is Binance's own link shortener. The attacker put their payload somewhere else and used Binance's infrastructure to point at it.

Compare it with the version you can spot

Here is the same pretext in its ordinary form — another message reported to us, and the shape most of these campaigns still take:

SMS · the catchable generation
[BN] Your withdrawal code is: 854248. Do not share this code. If you did not request this withdrawal, cancel it immediately: hxxps://binance[.]com@0691[.]app/193

That one is catchable. The string binance.com is right there in the link — but it sits before an at-sign, and in a URL everything between the scheme and the @ is credentials, not a hostname. A browser reads it as: connect to the host 0691[.]app, offering the username "binance.com". The real destination is a four-digit throwaway domain. The brand is a decoy parked in the username field.

Anyone who knows that rule spots it in a second. Many messaging and mail clients now flag or rewrite the pattern outright. It is obfuscation, and obfuscation can be taught, detected and filtered. It also fails against exactly the person who reported both of these.

The s.binance.com message is a different category of problem, and it deserves to be treated as one. Nothing about it is disguised:

  • No at-sign trick. The host is the host.
  • No homograph, no punycode, no Cyrillic lookalikes.
  • No misspelling anywhere in the visible string.
  • No suspicious registrar, no young domain, no self-signed certificate.
  • Nothing a link filter can key on, because the link is one a legitimate Binance user might genuinely send you.

It does not imitate Binance's credibility. It borrows the real thing — the domain, the certificate, the app hand-off, and the interface that renders the words "Protect Your Account". The deception does not begin until you are already inside the app, at which point there is no address bar left to inspect.

That is why this one is worth a write-up and the first one is not. The at-sign message is defeated by knowledge. This one is not, because every check you know how to run returns "authentic" — and it is authentic, right up to the moment a trusted app hands its request channel to a domain that was registered that morning.

The chain, decoded

The short link answers with a plain HTTP redirect:

HTTP/2 302
location: https://app.binance.com/en/mp-cms/app/3cb3235
          ?_dp=<base64>
          &description=Protect+Your+Account
          &title=Binance
          &utm_campaign=app_mini_program_share_link
          &utm_source=mini_program

Still Binance. Still authentic. The interesting parts are the campaign tags: app_mini_program_share_link and mini_program. This is a Binance Mini Program share link — the artefact you get when you share a mini app from inside the Binance app. Nothing was hacked. The attacker published or abused a mini program and used the share feature the way it was designed to work.

Note also description=Protect Your Account. The social-engineering line travels inside the URL, so it is Binance's own interface that renders the reassuring words.

The _dp parameter is base64. Decoded:

bnc://app.binance.com/mp/app
    ?appId=xoqXxUSMRccLCrZNRebmzj
    &startPagePath=L3BhZ2VzL2Jyb3dzZXIvaW5kZXg
    &startPageQuery=<base64>
    &sceneValue=1300

bnc:// is the Binance app's custom URL scheme. On a phone with the app installed, this hands off to the app rather than opening a browser tab.

startPagePath is base64 again, and decodes to /pages/browser/index — the mini program's in-app browser page. So the deep link is telling the Binance app: open this mini program, and go to its browser screen. The address it should load is base64 a third time, in startPageQuery:

url=https%3A%2F%2Faccounts.authenticated.binancc.cdn378129.com%2Fauth8%2F

There it is. Read the host carefully:

accounts . authenticated . binancc . cdn378129 . com
└──────────── decoration ────────────┘ └── real ──┘

The registrable domain is cdn378129[.]com. Everything to the left of it is free text the attacker chose — including binancc, with two c's, which is not a typo of the brand so much as a deliberate near-miss that survives a glance. On a phone, in a webview, most of that string is off-screen anyway.

So the malicious domain is never visible in the message. It is base64, inside a query parameter, inside another base64 payload, inside an authentic Binance URL, behind an authentic Binance shortener.

The infrastructure is hours old

Public records, checked while writing this:

  • cdn378129[.]com was registered at 13:31 UTC on 20 August 2026, through OwnRegistrar, Inc., with DNS delegated to DNSPod.
  • Its Let's Encrypt certificate carries a notBefore of 12:35 UTC the same day. Let's Encrypt backdates that field by about an hour, so issuance was roughly four minutes after registration. The certificate covers that one hostname and nothing else.
  • The host resolves to 107.189.17.50, in a range assigned to RouterHosting LLC within FranTech Solutions.
  • The domain is unsigned — no DNSSEC.

A registrable domain less than a day old, a certificate minted minutes after it, and a single-purpose hostname. None of that is visible from the SMS, and none of it is visible from s.binance.com either — which is precisely the point of routing through the shortener.

What the page actually does

We retrieved the endpoint in an isolated sandbox, without executing it. The first surprise is what is not there:

  • No form. Not one <form> element.
  • No input fields. Not one <input>.
  • No password prompt, no one-time-code prompt, no seed phrase prompt, no wallet-connect button.

Every instinct people have been trained to rely on — do not type your password into a strange page, never enter your seed phrase, watch what you are signing — is inert here, because the page never asks you for anything.

What it contains instead is an obfuscated script whose configuration sits in the clear at the top:

var WITHDRAW_COIN     = "BTC";
var ATTACKER_ADDRESS  = "bc1qnwhg6za5m0adlny6t4xx6qa2heyrntsvk8pw4f";
var WITHDRAW_NETWORK  = "BTC";
var FEE_RESERVE       = 0.00007;
var MIN_COIN_AMOUNT   = 0.01;

And the error strings the obfuscator failed to hide name the mechanism outright:

"bridge interface not found (window.bn.miniProgram missing)"
"bridge did not appear within "
"bridge request timed out after 10s"

window.bn.miniProgram is the JavaScript bridge the Binance app injects into mini program webviews. The script waits for it to appear, then issues its requests through it. That is the entire trick: it does not need your credentials, and it does not need to defeat the same-origin policy, because the app hands a trusted request channel to whatever page the mini program browser is showing — and the mini program browser was told to show the attacker's page.

The withdrawal pipeline

Recovering the string table gives the private Binance endpoints the payload calls, in the order the logic uses them:

  1. /bapi/accounts/v1/private/account/get-user-base-info — identify the victim. The page greets you by name: "Welcome Back, …".
  2. /bapi/kyc/v2/private/certificate/user-kyc/get-current-kyc-status-lite — check verification status, because withdrawals depend on it.
  3. /bapi/asset/v3/private/asset-service/asset/get-wallet-asset — enumerate every balance in the account.
  4. /bapi/earn/v1/private/lending/daily/redeemredeem flexible savings, so money that is earning yield becomes spendable.
  5. /bapi/margin/v1/private/new-otc/get-quote and …/execute-quoteconvert every other asset into Bitcoin, skipping balances below the minimum and reserving a little for fees.
  6. /bapi/capital/v4/private/capital/withdraw/applyfile the withdrawal to the hardcoded address.

While that runs, the screen shows a short sequence of reassuring states, lifted from the page's own markup:

Loading your account · We are securely gathering your details · Welcome Back, … · Checking for authorization… · Logging back into Binance

"Logging back into Binance" is doing a lot of work. It explains a pause, it explains why the app is busy, and it primes you to expect an authentication step.

The clever part is the fake code

Here is what took us a moment to appreciate. The fake verification code in the SMS is not the bait. The link is the bait. The code is inoculation.

A withdrawal from a real exchange account normally requires a real confirmation — an email link, an authenticator code, a push approval. That confirmation is the last thing standing between the victim and the loss, and it is supposed to be alarming.

But the victim has already received a Binance code they did not request, and has already been told by the message itself that this is what an unrequested code looks like. They are now inside a flow captioned "Checking for authorization" on a screen that greeted them by name. When the genuine confirmation arrives, it does not read as an alarm. It reads as the next step of the security check they believe they are performing.

The fake code teaches you to be calm about the real one.
That is the whole design, and it is why the first message is harmless on its own and still essential to the attack.

Who it is aimed at

The payload ships a translation table with exactly two non-English locales: zh and ko. It reads navigator.language and localises itself. Combined with a lure written in Hong Kong Cantonese, this is a campaign built for Chinese- and Korean-speaking users, not a broadcast in English that happened to reach one.

The destination address had no transactions at all when we checked it against two independent block explorers — nothing confirmed, and nothing waiting in the mempool either. So far as the chain shows, this address has not yet received one victim's funds.

We originally read that as encouraging. It was the wrong reading, and we were told so by someone it happened to. An empty payout address does not mean nobody was caught. It means the money did not reach the last step. Everything before that step can still have run — and on at least one funded account, it did. See What an empty address actually hides below.

What it does mean is that there is time, which is the reason to publish quickly rather than carefully.

What actually protects you

Domain inspection does not work against this. These do:

  • Set an anti-phishing code with your exchange, and check for it. Binance lets you define an identifier that appears in its genuine messages, including SMS. The messages above do not carry one. Absence is the tell, and it is the single most reliable one available to a normal person.
  • Treat "if this was not you, tap here" as the attack, not the remedy. Legitimate security notices tell you to open the app yourself. The urgency and the link are the payload.
  • Never navigate to your exchange from a message. Open the app from your home screen. That single habit defeats this entire chain, because nothing in it survives you not clicking.
  • Set a withdrawal address whitelist. With whitelisting on and a delay for new addresses, a script that files a withdrawal to a fresh address has nowhere to send it.
  • Read what a confirmation actually says. A genuine withdrawal confirmation names a coin, an amount and a destination address. If a screen told you it was "logging you back in", that text is a withdrawal.
  • Be suspicious of a link that opens an app. A link that jumps straight into an app has skipped the browser, and with it the address bar and every safe-browsing warning you would otherwise have got.

If you tapped it

  1. Open the exchange app directly and check withdrawal history and pending withdrawals first. Cancel anything you did not start.
  2. Check whether flexible savings positions were redeemed, and whether balances were converted into Bitcoin. Both happen before the withdrawal, so both are earlier warnings.
  3. Do not rely on your login history — it will look clean. The payload runs inside the session you already opened, so it creates no new sign-in event. An account that was drained this way shows nothing unusual in sign-in activity at the time it happened.
  4. Check your conversion history specifically, not your trade history. A Convert does not appear under spot orders. Someone looking for an unexplained trade will find an empty list and conclude nothing happened.
  5. Revoke active sessions and devices, then change your password and rotate two-factor.
  6. Turn on withdrawal address whitelisting.
  7. Report it. Binance takes scam reports through its support centre, and a report that includes the short link, the mini program identifier and the destination address is far more actionable than a screenshot.

Indicators

Defanged, and published so defenders can block and correlate them — not so anyone can visit them.

WAVE 2 (2026-08-22)
Shorteners     hxxps://s.binance[.]com/DwOKKciE
               hxxps://s.binance[.]com/hOD42AgF
Mini program   appId xoqXxUSMRccLCrZNRebmzj  (mp-cms e3efa5b, scene 1300)
Final host     accounts.authentication.binance.com73184[.]com  /auth-1334/
Registrable    com73184[.]com   registered 2026-08-21 21:45:28 UTC
SOL address    H2RMUS1nhiqtwToUfLzCdUB94rmHrFDzyrnJaNKiMGr2

WAVE 1 (2026-08-20)
Shortener      hxxps://s.binance[.]com/speZiskQ
Mini program   appId xoqXxUSMRccLCrZNRebmzj  (sceneValue 1300)
Deep link      bnc://app.binance.com/mp/app?...startPagePath=/pages/browser/index
Final host     accounts.authenticated.binancc[.]cdn378129[.]com
Path           /auth8/
Registrable    cdn378129[.]com   registered 2026-08-20 13:31 UTC
Registrar      OwnRegistrar, Inc.        DNS: DNSPod
IP             107.189.17.50   (RouterHosting LLC / FranTech Solutions)
TLS            Let's Encrypt, single-SAN, issued same day
BTC address    bc1qnwhg6za5m0adlny6t4xx6qa2heyrntsvk8pw4f

The evidence expired, and nobody caught it

Roughly a day after the domain was registered, we re-checked every link in the chain. All of it now 404s — the short link, and the payload at the end of it.

The instinct is relief. That instinct is wrong, and understanding why is the most useful thing in this article.

Nothing was taken down

Look at what is still standing:

Domain status     ok            (not clientHold, not serverHold)
WHOIS updated     unchanged since the moment of registration
DNS               still resolves to 107.189.17.50
Web server        nginx still running, still answering
Certificate       still valid, unrevoked, good until November
Payload           gone
BTC address       still zero transactions, mempool included

A registrar suspension changes the domain status and stops the name resolving. A host suspension stops the server answering. Neither happened. What we are looking at is a server that still runs, on a name that still resolves, holding a certificate somebody is still paying for — with the files removed by hand.

That is not a takedown. That is an operator tidying up. And the delivery link going dead at the same time could be the platform revoking it or the same operator retiring it; from outside, those are indistinguishable, which is itself the problem.

No party has acknowledged any of it

There is no case number. No advisory. No published note that a mini program was disabled, no registrar action, no blocklist entry, no statement from anyone. We have found no public acknowledgement from any party that this occurred at all.

So the campaign has not been stopped. It has been concluded, by the person running it, on their schedule, with their infrastructure intact and their reputation with every registrar and host in the world completely unblemished.

Why this makes reporting almost impossible

Every reporting channel available to a member of the public assumes live content:

  • A blocklist classifier fetches the URL to decide. It finds a stock nginx 404 and declines to list it.
  • A registrar abuse desk receiving "this domain served phishing" for a URL that now returns nothing closes the ticket as content-removed, no action required.
  • A hosting abuse desk does the same.

The window in which any of this was actionable closed while the analysis was still being written. That is not a criticism of abuse desks — verifying a live attack is tractable and adjudicating a dead one is not. It is a description of a structural gap: the reporting system runs slower than the attack it is meant to catch.

The proof is volatile, and that is the whole trick

Here is the part worth sitting with. A phishing operation that lives twenty-four hours produces evidence with a twenty-four-hour shelf life. The attacker does not need to destroy anything, cover any tracks, or outwit anyone. They need only to outlast the reporting latency — and a domain that costs ten dollars, weaponised four minutes after registration, does that comfortably.

After that, the position is symmetrical and unhelpful for everyone. We cannot prove it to an abuse desk, because the desk's verification step is a fetch. They cannot verify it, because there is nothing to fetch. The recipients cannot report it, because the link in their message now goes nowhere and looks like a mistake. And the operator can do it all again next week with a new domain and a new mini program identity, having paid no price and left no record.

Which is why the technique matters and the domain never did. Not one thing about the mechanism has changed. A mini program browser page still accepts a destination as a parameter. The bridge is still reachable by whatever that page loads. The share feature still mints links on an authentic domain with a valid certificate. A new identifier and a hostname nobody has heard of reproduces every word of this article in an afternoon.

If one of these reaches you

The practical consequence: capture first, report second. What exists in the first hour is the only thing that will ever exist.

  • Screenshot the message before anything else, including the sender.
  • If you are able to, record the redirect chain without following it — request the headers only, from a machine that does not have the app installed. The location header of the first hop is the single most valuable artefact in the entire chain, and it is the first thing to disappear.
  • Do not open it on a device with the app installed. That is not a test, it is the attack.
  • Note the times. The gap between a domain's registration and the first message is evidence in its own right, and it survives after the content does not.

Everything we captured is in this article for exactly that reason: the request chain, the decoded deep link, the endpoints the payload called, and the address it was going to send to. None of it can be fetched any more. This is now the record, and a write-up that had waited another day for tidier confirmation would have had nothing in it.

What an empty address actually hides

An account affected by the first wave has since been described to us. Its records show the conversion stage of this payload having actually run. They asked to stay anonymous, and every figure that could identify them — balances, holdings, addresses, order numbers — is deliberately absent below. None of it is needed. The proof is in the shape of what happened, not the size of it.

What follows is their account, cross-checked against measurements we had already taken independently. We are not describing who they are, where they are, or what they held, and we are not going to.

The part we got wrong

We reported that the payout address had no transactions and read it as the campaign not having succeeded. That inference does not hold. On this account the payload's conversion stage completed: the entire balance, across two separate wallets, was converted to a single asset in one action the account holder did not perform and had no memory of. The withdrawal is what failed — the funds stayed in the account.

So an empty payout address does not mean nobody was caught. It means the exchange's withdrawal controls held at the final step, after everything before it had already run. Those are very different facts, and we published the more comfortable one.

Three details that match the payload exactly

We pulled MIN_COIN_AMOUNT = 0.01 out of the obfuscated script by static analysis, two days after this account was hit. On the account:

  • A balance of exactly 0.01 in the second wallet was converted — precisely at the threshold.
  • Two other holdings, both below 0.01, were left untouched.
  • Both wallets were drained in the same action, matching the enumeration step that reads every balance rather than just the main one.

A constant recovered from the code, and a live account whose leftovers sit exactly on either side of it. That is the confirmation that was missing when we wrote this.

Why it cannot have been done by hand

The two wallet legs converted at an identical locked rate, roughly a second apart. A locked rate is quoted for one instant, so an identical rate across both legs means they were quoted together, in one batch.

Doing that manually requires selecting the pair, previewing, and confirming within a countdown timer — separately, for each wallet. Two complete manual conversions a second apart, across two different wallets, at one quoted rate, is not something a person can physically do. The timing is machine execution, and it is visible in the account's own records.

The finding that changes the advice

This is the one worth carrying away, and it invalidates guidance we and everyone else routinely give.

The attack leaves no sign-in event. There is no login at the moment the conversion ran. There is a perfectly ordinary sign-in several minutes earlier, from the account holder's own device and their own address, because that is exactly what it was. The payload then executed inside that already-authenticated session, through the app's bridge.

So the standard instruction — check your login history for sessions you do not recognise — returns a clean result for an account this has happened to. There is nothing to recognise. The session was theirs.

The second trap is where a victim looks next. The conversion does not appear in trade history, because a Convert is not a trade on the order book. Someone searching their spot orders for the transaction that emptied their account finds an empty list, and reasonably concludes they imagined it. It is recorded, but only under conversion history — a different screen, which nobody thinks to open.

Between those two, an account can be substantially interfered with and every place a careful person would look shows normal. That is not a gap in the victim's diligence. It is a gap in what the account is able to tell them, and it is the same gap this article opened with: the platform-side record that would settle it — which mini program originated the order — exists, but is not something the account holder can see.

What we have changed above

The recovery steps earlier in this article now say to check conversion history rather than trade history, and say plainly that sign-in activity will look clean. Both were wrong in a way that would have sent someone away reassured.

It came back, and nothing had changed

Everything above was written on the assumption that the campaign was over. Roughly thirty-six hours later, two more messages arrived. Same pretext, same shortener, different codes:

SMS · wave two4 of 4
Your verification code is: 239812. If you did not initiate this request, please visit hxxps://s.binance[.]com/DwOK​KciE
Your verification code is: 717844. If this was not you, please visit hxxps://s.binance[.]com/hOD4​2AgF

We captured the whole chain again inside the first hour. One line of it matters more than everything else in this article:

Wave 1  2026-08-20   appId xoqXxUSMRccLCrZNRebmzj
Wave 2  2026-08-22   appId xoqXxUSMRccLCrZNRebmzj   <-- identical

The Mini Program was never disabled. The short link from wave one stopped resolving, and that was the only thing that changed on the platform side. The identifier behind it was still live, still reachable, still willing to open its browser page and hand the bridge to whatever URL it was given. The operator minted two fresh short links against it and pointed them somewhere new.

The rest of the fingerprint is unchanged too, which rules out a copycat: the same mini program page (/pages/browser/index), the same sceneValue, the same description=Protect Your Account, the same server at 107.189.17.50, the same registrar, the same DNS provider. Only three things moved — the short links, the domain, and the coin.

This is the answer to a question the earlier sections could only speculate about. Cutting the delivery link does not stop the operator, because the delivery link is the cheapest component they own. Cutting the mini program does, and nobody did.

Registered at night, sending by morning

21:45:28Z   domain registered
21:49:06Z   TLS certificate issued        +3m 38s
23:34:00Z   first message                 +1h 48m
00:22:00Z   second message                +2h 36m
00:43:52Z   captured, payload live        +2h 58m

Under four minutes from registration to a valid certificate, and under two hours to messages landing on real phones. The same shape as wave one, to the minute. Nobody is doing this by hand.

The new domain is a better lie

accounts . authentication . binance . com73184 . com
└─────────────── decoration ──────────────┘ └─ real ─┘

The registrable domain is com73184[.]com. Read the full string at speed and it says …binance.com73184.com — the eye stops at binance.com and treats the digits as a cache node or a shard. It is a straight improvement on wave one's binancc, which at least looked misspelled.

The payload was worked on in between

It grew from 24,640 to 34,335 bytes. The seven private endpoints are identical — same enumeration, same savings redemption, same conversion, same withdrawal — but four things were added:

  • The payout moved from Bitcoin to Solana. Faster, cheaper, and harder to unwind.
  • Per-asset conversion caps, consistent with keeping individual conversions under whatever threshold draws attention.
  • Rate-limit evasion. One surviving string reads Retried just under limit: — they are tuning against the platform's throttles, deliberately, and measuring it.
  • Portuguese, added to the Chinese and Korean already there.

And it now counterfeits the approval screen

This is the change worth stopping on. The page renders a fake version of a Binance security prompt, in four languages:

New login detected · Is this you? · Device · Location · IP address · [Approve] [Deny] · auto-block in 15s

Earlier in this article, describing what makes a push approval trustworthy, we wrote that the prompt has to show the application, the location and the IP address — that a prompt without context is one you cannot be right about. Those are precisely the fields reproduced here. The attacker has taken the design that makes a genuine security prompt safe and rebuilt it as furniture.

It also completes the loop that the SMS opens. The message says if this was not you, visit this link. The visitor arrives worried, and finds exactly the screen a worried person hopes to find: a login they do not recognise, the details laid out, and a button marked Deny. Tapping Deny feels like taking control. Both buttons belong to the attacker, and the fifteen-second countdown is there to stop anyone thinking about it for sixteen.

What we did this time

Everything was captured inside the first hour and preserved with checksums, both payloads kept side by side so the diff between waves is demonstrable rather than remembered. Reports went to Binance's published security address, to the registrar, to the hosting provider and to two anti-phishing clearing houses, while the site was still answering.

The registrar opened a ticket. At the time of writing, both destination addresses — the Bitcoin one from wave one and the Solana one from wave two — have never received a transaction.

The uncomfortable part is that none of the reasoning in the previous section was wrong. The evidence from wave one did expire, no party did acknowledge it, and the technique did survive untouched. What we did not anticipate was how quickly that would be demonstrated.

Wave three: they stopped needing Binance at all

Two days after the first messages, a third arrived. Same pretext, different plumbing:

SMS · wave three
You have initiated an update to your preferences. If this was not you, visit hxxps://bnbshort[.]com/7PSB​6w0F/

Read that host again. It is not a Binance domain. There is no s.binance.com, no redirect through app.binance.com, no bnc:// deep link and no Mini Program identifier anywhere in the chain. The operator registered their own domain, named it to be mistaken for a Binance short-link service, and serves the payload from it directly.

Same hands

Everything that identifies the operator is unchanged from the earlier waves: the same server, the same registrar, the same DNS provider, the same web server build. Only the delivery route is new.

registered      08:14:03 UTC
certificate     ~08:21 UTC     +7 minutes
first message   ~08:48 UTC     +34 minutes
captured        08:57 UTC      +43 minutes

Under seven minutes from registration to a valid certificate, and under thirty-five to messages on real phones — the same shape as both earlier waves, to the minute.

Why they gave up a perfectly good Binance link

Between wave two and wave three, exactly one thing changed on the platform side: one of the two wave-two short links stopped resolving. The other still worked, and the Mini Program behind both was still live.

That is a small, partial intervention, and the response to it was to build a replacement delivery channel from scratch inside two days. It is worth sitting with what that implies. The operator did not treat the Binance-hosted route as disposable. They treated it as valuable enough to reconstruct the moment it became unreliable — which is a clearer statement about that route's worth than anything we could argue.

The part that should worry a defender

We fetched the wave-three page the same way we fetched the others. What came back is 94 KB of obfuscated script — nearly three times the size of the previous payload — containing no form, no input element, and no network calls at all. Two requests seconds apart returned different bytes, because the page is generated per request with a fresh key.

It also contains three named checks that the obfuscator failed to hide:

isWebDriverPresent
isPhantomOverflow
isPhantomETSL

Those detect browser automation — the exact tooling a security scanner uses. What we retrieved is not the attack. It is a gate that decides whether you are a person.

We can be more precise than that, because of something the file does not contain. Searched end to end, those 94 KB hold no URL of any kind — no http, no ://, not a single dot-com, not even the name of the site serving it. There is also no network call: no fetch, no XMLHttpRequest, no WebSocket. What it does have is a routine for writing a cookie, and a key regenerated for every request.

So the sequence is: the page measures the browser, writes what it concluded into a cookie, and reloads. The server decides what to send next. The attack is not hidden inside the file — it is not in the file at all, and cannot be. Whoever downloads that page and takes it apart, however carefully, is examining a doorman rather than the room.

That is worth separating from ordinary obfuscation. Waves one and two surrendered their entire payload to a single command-line fetch: the endpoints, the payout address, the conversion logic, all sitting in the response for anyone who asked. Wave three surrenders nothing, and no amount of patience with a disassembler changes that, because the thing being looked for was never sent.

We can say what that means in practice, because it happened to us. A submission of this campaign to an automated analysis service came back no threats found on a site that was live and serving. Part of that was our own error, and we have described it above. But a cloaked page defeats a scanner honestly and by design: the scanner reports exactly what it was shown.

So the guidance to draw from this is uncomfortable and worth stating plainly. A clean result from an automated checker is not evidence that a link is safe. It is evidence that whatever the checker was served looked safe. Those are the same sentence only when nobody is trying.

What changed, and what did not

The delivery moved off Binance's infrastructure. The brand did not — the domain exists solely to be mistaken for theirs, and the message still impersonates an account notification. The lure, the operator, the server and the timing are all identical.

And the Mini Program identifier that carried waves one and two is, at the time of writing, still live. It has now outlasted three campaigns.

Why there are no links in this article

Every hostile address above is written in defanged form and none of them is a hyperlink. That is deliberate. Publishing a live phishing link as a working anchor turns the article into a redirect, lends the campaign a fragment of our reputation, and asks a search engine to associate the two. Describing an attack should not extend its reach.

We are not affiliated with Binance. This is published because the technique generalises: an official shortener, a deep link into a trusted app, and a webview bridge that hands site-level trust to an arbitrary URL is a pattern any large consumer app can be made to exhibit. If your product ships a link shortener, a custom URL scheme, or an in-app browser that takes a destination as a parameter, this is the chain to go and check for yourself.

Was this article helpful?

30 min read5,950 wordsSecurityUniAuth Threat IntelligencePublished August 20, 2026
Share:Twitter / XLinkedIn