The Target
A digital asset custody platform that manages cryptocurrency wallets for institutional clients — banks, funds, and enterprises. Each enterprise has its own isolated tenant with separate wallets, keys, and transaction history. The platform uses a JSON-RPC API for all wallet operations.
The Discovery
While exploring the API documentation, I noticed that the platform exposed two related methods for querying wallet information:
Method A: wallet.getAddress → requires wallet_id
Method B: wallet.getAddresses → requires account_id
Both methods returned wallet addresses, but there was a subtle difference. Method A (singular) properly validated that the requested wallet_id belonged to the authenticated enterprise. Method B (plural) only validated the account_id parameter — and account IDs followed a predictable sequential pattern.
The Exploit
Observe the Pattern: I checked my own enterprise's account ID and noticed it was a simple incrementing integer. My account was something like 10042. This suggested there were at least 10,000+ accounts on the platform.
Test Cross-Tenant Access: I called wallet.getAddresses with a different account ID — one that didn't belong to my enterprise. The API returned a full list of wallet addresses for that foreign account, including blockchain addresses across multiple chains (BTC, ETH, etc.).
Confirm the Scope: I tested with several different account IDs. Every single one returned wallet data. The authorization check was completely missing on this specific method — any authenticated user from any enterprise could enumerate wallet addresses belonging to any other enterprise on the platform.
Root Cause: The singular method (getAddress) had proper tenant isolation because it resolved ownership through the wallet object. The plural method (getAddresses) was likely added later and the developer forgot to add the enterprise-level authorization check, only validating that the account ID existed — not that it belonged to the caller's tenant.
Impact
- Full disclosure of wallet addresses for every enterprise client on the platform
- Blockchain transaction tracking — knowing wallet addresses allows monitoring all transactions
- Holdings enumeration — balances of institutional wallets become visible on-chain
- Targeted attacks — leaked addresses enable phishing, dusting attacks, or social engineering
- Regulatory and compliance violations — custody platforms must guarantee data isolation
Timeline
Reported immediately. The platform's security team added the missing enterprise-level authorization check within 48 hours and conducted an audit of all similar API methods.
Takeaway
When an API has two methods that do similar things, test both. Developers often implement proper authorization on the first method but forget it on the variant. Look for singular vs. plural endpoints, v1 vs. v2, and "get" vs. "list" — these inconsistencies are where IDOR vulnerabilities hide. In multi-tenant systems, always test if you can access resources belonging to other tenants by manipulating IDs.