TL;DR
A user of Fomo, the social trading app, lost about $48,000 after following a "human verification" step on a phishing site, according to SlowMist's analysis published on October 8, 2026. The user did not connect a wallet, sign an approval, or type a seed phrase. The phishing page had them drag an icon into the bookmarks bar and click it three times. That bookmark was JavaScript, and it ran inside the Fomo tab where they were already logged in through Google with no second factor. It read the Privy login tokens and wallet data out of browser storage and shipped them to a Cloudflare Workers endpoint. SlowMist's MistTrack trace follows the money as three USDC transfers into a Solana address that had been running the same USDC to SOL swaps since September 17. SlowMist's Yu Xian flagged the campaign as still underway on October 8. We found no public statement from Fomo as of October 9. On an embedded wallet with no second factor, the login session is the key, and anything that runs inside the logged-in page holds it.
What Happened to the Fomo User?
The user was browsing the MOONLET token page on Fomo's web app. Token pages carry a project website link, and the user clicked it. That link went to voltage.family, a page dressed up as a trading product. SlowMist does not say who set the link or when. On launchpad-style token pages the website field is usually supplied by whoever created the token, which would make the phishing link part of the token listing itself; that is our inference, not a finding in the report. Its /app path showed nine icons and asked the user to find "the four-legged animal," drag it to the bookmarks bar, and then click the new bookmark three times in a row.
After that, roughly $48,000 left the account. SlowMist's write-up is specific about what the user did not do: no wallet connection, no on-chain authorization, no seed phrase or private key entered anywhere. They logged in to Fomo with Google, and two-factor authentication was off.
Privy's case study on Fomo explains why none of those familiar warning signs were needed. Every Fomo user gets a self-custodial embedded wallet "the moment they sign up," and logs in with Apple, Google, or email. There is no extension, seed phrase, or connect step to get through.
How Does a Malicious Bookmark Steal a Wallet Session?
A bookmarklet is a bookmark whose address is code: it starts with javascript: rather than a web address. Click it, and the browser runs that code inside whatever page is open in the current tab, with that page's origin and that page's access. Click it on a logged-in Fomo tab, and the code can read everything a Fomo script can read.
MDN describes javascript: URLs as "fake navigation targets that execute JavaScript when the browser attempts to navigate," and warns they can lead to "execution of arbitrary code, similar to the ramifications of using eval()." The Content Security Policy Level 3 specification says a page's policy "SHOULD NOT interfere with the operation of user-agent features like addons, extensions, or bookmarklets." A CSP that would stop an injected script does not stop the user's own bookmark.
So the attacker never needed a cross-site scripting bug in Fomo. They needed the user to install the payload and click it on the right tab. The fake human check did exactly that.
The bookmark the user ended up with was named "Triple click to verify." SlowMist found that a script called fomo-track.js set every one of the nine icons to the same bookmark code, and re-applied it on DOM changes, timers, mouse presses, and the start of each drag, so whichever icon the user picked, they got the payload.
What Did the Bookmarklet Take?
The payload is more than 8,000 characters compressed. Its first check is location.hostname.includes("fomo.family"), so it only does anything on a page whose hostname contains Fomo's domain. SlowMist points out this is a substring match rather than a strict domain check. The script was built to run on the Fomo tab.
It collects in four passes:
- Browser storage. It walks
localStorageandsessionStorageand keeps any entry whose key or first 300 characters matchprivy,wallet,share,secret,seed,mnemon,turnkey,dynamic,fomo, ormfa. That keyword list covers three embedded-wallet providers by name, Privy, Turnkey and Dynamic, which suggests a kit written to be pointed at more than one app. - IndexedDB. It enumerates every database the page can reach and dumps every object store with
getAll(), under a 4-second timeout per step. - Login tokens. It looks for
privy:tokenandprivy:refresh_tokenfirst, then widens to any token-shaped field, and finally falls back to anything starting witheyJ, the base64 prefix of a JSON Web Token. - MFA state. With the access token in hand it calls
auth.privy.io/api/v1/users/meto read the account'smfa_methods, retrying with the refresh token on a 401.
Storing the access token where a script can read it is Privy's default. Its cookie configuration guide says "By default, Privy will store the user's access token in local storage," and offers an HttpOnly cookie on the app's base domain as the alternative for production apps. Per Privy's token documentation, the access token lives one hour by default and the refresh token 30 days, rotated on use. A stolen refresh token is a month of renewable access.
Everything is base64url-encoded and sent to noisy-heart-5856.zmfkc29.workers.dev twice, once by fetch and once by redirecting the tab with the tokens and data in the URL. If the payload runs long, the script sheds IndexedDB first and keeps privy:token, privy:refresh_token, and mfa-session-storage to the last. SlowMist's probing found the endpoint answers almost every request with a 302 back to voltage.family, so the victim lands on the fake trading terminal and sees nothing odd.
What Was the TOTP Branch For?
It was written for accounts that already have a second factor.
If /users/me shows the account already has a second factor, the script opens a 4x4 pixel iframe at 0.01 opacity, loads Privy's embedded-wallet frame, and talks to it over postMessage. It asks to start a TOTP enrollment, grabs the returned secret, computes a valid six-digit code locally (30-second steps, HMAC-SHA1, dynamic truncation), and submits it. The aim is an authenticator the attacker controls, bound to the victim's wallet.
Two caveats from SlowMist matter here. The branch only fires when mfa_methods is non-empty, and this user had no second factor, so the branch likely never ran. And a started enrollment may never complete. Privy's MFA verification overview lists enrolling or unenrolling an MFA method among the actions that themselves require MFA, alongside signing transactions, exporting the private key, and recovering the wallet on a new device. Whether the attacker's enrollment succeeds against a properly enforced flow is exactly what SlowMist says still needs server-side logs to confirm.
So accounts without MFA get drained on the token alone, and accounts with MFA get a second attempt aimed at the factor itself.
The Attack, Step by Step
| Step | What happened |
|---|---|
| 1 | User opens the MOONLET token page on Fomo and clicks the project website link |
| 2 | Link resolves to voltage.family/app, a fake trading product with a "human verification" puzzle |
| 3 | fomo-track.js rewrites all nine icons to the same javascript: bookmark, "Triple click to verify" |
| 4 | User drags an icon to the bookmarks bar and clicks it on the logged-in Fomo tab |
| 5 | Payload confirms the hostname, then dumps localStorage, sessionStorage, and IndexedDB |
| 6 | Payload extracts privy:token and privy:refresh_token, queries /users/me for MFA methods |
| 7 | No MFA on the account, so the TOTP enrollment branch likely does not fire |
| 8 | Data goes to noisy-heart-5856.zmfkc29.workers.dev, the tab bounces back to the phishing site |
| 9 | Three USDC transfers leave the account for D783Jq...hZ6 |
| 10 | Funds are swapped to SOL via Jupiter, bridged via Relay, and pushed to Privacy Cash and gambling deposits |
SlowMist is careful about the gap between steps 8 and 9: the script collects and exfiltrates, and signs nothing itself. Whether the transfer was made by replaying the session, by exporting the key, or by some other path "still needs to be further verified."
What Do the Numbers Say?
The headline number is the victim's: "approximately $48,000" in assets, as reported to SlowMist. It is a self-reported total. SlowMist's fund-tracing section, built from transaction details the victim supplied, itemizes the USDC side as three withdrawals to the attacker address and does not break out whatever else made up the total. Neither SlowMist nor Fomo has published a reconciled figure.
The receiving address, D783JqupQ2FYhkxUfGEd1gaFEoAJDXRX1NEb4aVH2hZ6, is a Solana wallet. SlowMist dates its first activity to September 17 and notes "relatively frequent USDC ↔ SOL transactions" before this victim's funds arrived, "suggesting that there may have been other victims." Our own pull of its signature history (getSignaturesForAddress against a public Solana RPC node, which counts every transaction that touched the address, failed ones included) returns 478 signatures, the oldest on September 17 and the newest on October 5. SlowMist does not give the time of the click or of the three transfers, so whether the session was replayed within minutes or days later is not public.
The exit was ordinary, and SlowMist's own summary of the trace calls it a complex flow of repeated USDC/SOL swaps, multi-address splitting, cross-chain transfers, and deposits to Privacy Cash and gambling platforms. USDC was swapped to SOL mostly through Jupiter, split across intermediary addresses, and bridged out through Relay into ETH and BTC in sub-SOL slices. The main outlet was Privacy Cash, which received 1,568.33 SOL from the address. Some SOL went to Stake and some to Winna deposit addresses, and the address also received 7,199.81 USDC back from a Winna hot wallet.
Nobody has published a victim count for this address, or a dollar total for what passed through it.
Haven't We Seen This Before?
The trick is at least four years old.
In 2022, SlowMist documented how a scammer used a malicious bookmark to gain access to the Discords of NFT projects: a bookmarklet run on discord.com that stole the admin's session token. A year later Krebs on Security reported the same pattern at scale. Attackers posing as crypto journalists asked Discord admins to "verify" by dragging a button to the bookmarks bar, took their session tokens, and posted fake mint announcements from the hijacked accounts while the real admins slept.
The verification costume is borrowed from ClickFix, the fake-CAPTCHA technique that Microsoft wrote up in Think before you Click(Fix), where a "human check" walks the victim into running a command themselves. A decade of CAPTCHAs has trained users to do odd little tasks to prove they are human, and this kit leans on that habit.
The wallet provider has been targeted this way before. During the friend.tech boom, SlowMist's Yu Xian warned of a bookmark script aimed at friend.tech users that tried to steal passwords and the tokens tied to Privy, the embedded wallet friend.tech used.
What changed since the Discord wave is what the session is worth. The Discord attackers still had to talk members into a fake mint to get paid. Here, on an account with no second factor, the stolen session sat directly in front of the balance.
Why Does This Matter Beyond Fomo?
Embedded wallets moved the security boundary.
The rule most users carry is: if you never connect, never sign, and never paste a seed phrase, a website cannot take your funds. That rule was written for extension wallets, where the key sits in a separate process and every signature is a popup. With an embedded wallet, the app's own logged-in page is where the wallet lives. Anything that executes in that page acts with the user's session.
No audit of Fomo's contracts, or of any token on it, would have found this.
The keyword list suggests the kit was written to travel. If so, any trading app that ships an embedded wallet, keeps tokens in web storage, and renders third-party links on token pages could be the same target with a different hostname string.
What Should Operators and Users Do Now?
- Move session tokens out of script reach. Privy supports an HttpOnly cookie on the app's base domain instead of local storage. That does not stop a bookmarklet from acting inside the page while it runs, but it stops the token from being read, exfiltrated, and replayed from the attacker's machine for 30 days.
- Step up on sensitive actions, not only on login. Require fresh verification for MFA changes, key export, wallet recovery on a new device, and large or first-time withdrawals. SlowMist's own recommendation for project teams is the same: never change security settings "based solely on an existing session."
- Push users into a second factor. Privy's MFA gate sits in front of signing, export and recovery, but only once a method is enrolled. This victim had none. Nudge at signup, and nudge again before the first large balance.
- Treat token-page links as untrusted content. The website link on a token page is a link to a stranger. Show an interstitial with the full destination domain, and keep a blocklist of reported phishing domains such as
voltage.family. - Watch for session reuse and drain-shaped withdrawals. Here the pattern was a session that sent three USDC transfers to an address first active September 17 that was already swapping other inflows into SOL. A refresh token used from a new device and network, followed by a withdrawal of that shape, is something a server can flag and hold.
- For users: no real CAPTCHA asks you to drag anything into your bookmarks bar. If you clicked one on a logged-in wallet page, revoke sessions from a trusted device first, then check for unfamiliar authenticators, as SlowMist advises. A password change alone may not kill an existing refresh token.
What Would an App Review Have Found?
No review stops a user from clicking a bookmark, and there was no contract to audit or on-chain alarm before the first transfer. What a review can change is how much that click is worth. Several things the attacker relied on were visible in the web app before anyone was phished: a session token readable by page script, an account allowed to hold a balance with no second factor, and an external website link on a token page with nothing between the click and the destination. SlowMist's recommendations to project teams cover the rest: independent verification before MFA changes and fund transfers, session visibility and revocation, and less long-lived credential exposure on the client. A full-stack dApp audit covers that layer: frontend security, backend and API authentication, and wallet integration testing against phishing and spoofing. We test what a stolen session can move, and how far one click on a token page can carry a user, before an attacker runs the same test.
Frequently Asked Questions
Was Fomo hacked?
No. Nothing in SlowMist's analysis points to a breach of Fomo's servers or contracts. The attacker got the user to run their own code on the Fomo page through a malicious bookmark, then took the login session from the browser. The user reached the phishing site through the project website link on a Fomo token page; who set that link is not public.
Can a website drain an embedded wallet without a signature?
Yes, if it can run code inside the logged-in app page. A bookmarklet clicked on that page runs with the page's access, and in this case read the Privy access and refresh tokens. The victim never saw a signature prompt. On an account without MFA, the session was enough for the attacker to empty it.
What would two-factor authentication have changed?
It would have put a second step in the attacker's way. Privy requires MFA for signing, key export, recovery on a new device, and MFA changes once a method is enrolled. The kit had a branch built for MFA accounts that tries to enroll an attacker-controlled TOTP authenticator, and SlowMist says it is unconfirmed whether that enrollment can succeed. Without MFA, there was nothing to get past.
Sources / References
- SlowMist: Threat Intelligence | Analysis of a Malicious Bookmark Phishing Attack Targeting Fomo Users
- KuCoin (relaying SlowMist): In a recent investigation, SlowMist analyzed a malicious bookmark phishing attack targeting Fomo users
- KuCoin Flash (BlockBeats): SlowMist Warns of Bookmark Phishing Attacks Targeting the FOMO Web Platform
- Privy Blog: Turning trading into a social experience with fomo
- MDN: javascript: URLs
- W3C: Content Security Policy Level 3
- Privy Docs: Configure cookies
- Privy Docs: Tokens
- Privy Docs, MFA verification: Overview
- Solscan: Attacker address D783JqupQ2FYhkxUfGEd1gaFEoAJDXRX1NEb4aVH2hZ6
- SlowMist: How Scammer Used Malicious Bookmark to Gain Access to Discords of NFT projects
- BlockBeats: 慢雾余弦:针对friend.tech的恶意书签脚本或可窃取用户密码和资金
- Krebs on Security: Discord Admins Hacked by Malicious Bookmarks
- Microsoft Security Blog: Think before you Click(Fix): Analyzing the ClickFix social engineering technique



