BLOG |
Blink announcements
How an attacker used a flaw in our admin tools to take 6.61 BTC from 24 accounts, how every one was restored, and what we have changed.
On Saturday 19 September, at 11:39 UTC, a customer called one of our engineers on his phone to say his Blink balance was gone. Fifteen minutes later we had switched the whole custodial service off. By that evening the hole was closed. By the following Thursday every affected customer had their exact balance back, paid for by Blink's shareholders. No customer bears any loss.
To the 22 customers whose bitcoin was taken, and to the 3,817 people whose account details were looked up: we are sorry.
What happened. An attacker opened an ordinary free Blink account on 17 September. Two days later they used a flaw in how our administrative tools checked permissions to give that account admin powers. With those powers they changed the email address or phone number on 35 accounts, logged in to them as if they were the owner, raised withdrawal limits on 14 of them, and withdrew bitcoin from 24 of them between 09:51 and 11:54 UTC, partly on-chain and partly over Lightning through the flow we built to help customers move to self-custody. The attacker got about 6.61 BTC.
Whose money it was. They targeted 36 custodial accounts. One escaped by chance: their attempts to change its email address failed, and they moved on. Of the 35 they took over, they withdrew from 24. Nine had two-factor authentication and lost nothing (see below), and the attacker took over the last two accounts only minutes before we shut the service down, so nothing moved from them. 22 of the 24 drained accounts belonged to customers; two belonged to Blink group companies. Every one was restored to its exact pre-incident balance on 24 September, with the withdrawal fees refunded, and the locked accounts were reopened from that day. Blink bears the loss.
What it didn't touch. The money came out of our hot wallet — the operating balance we keep for day-to-day payments. Most customer funds sit in multi-signature cold storage that needs several keys held by separate people, and nothing in our admin tools can reach it. Non-custodial accounts were never in play: we don't hold those keys, so there was nothing for the attacker to take.
What they saw. During the attack, they also read details of 3,817 other accounts. For some, these included a phone number or an email address. They did not see names, identity documents, addresses, passwords or seed phrases. We have written to every account holder we could reach, saying what was seen.
What stopped them. Two-factor authentication. None of the 24 drained accounts had it switched on. Nine of the accounts they targeted did. The attacker logged in to all nine, but every one of their 18 attempts to use those accounts was refused. Zero loss. We point this out because it is one of the main lessons of this incident: 2FA worked, and we should have required it for large withdrawals and for accounts holding large balances.
What we've changed. The flaw was closed the same day, and a third layer of protection reached production on 21 September. The admin tools are off the public internet. The admin functions that change a customer's email or phone are switched off for everyone while we redesign them. All customer API keys were revoked. The hot wallet now holds a fraction of what it did. Full list further down.
Where the money is. Some is still sitting at the addresses they withdrew to. The rest went into their other wallets, from which about 5 BTC has gone through a cross-chain swap service and a small amount reached Binance. In El Salvador, a criminal complaint was filed with the Fiscalía General de la República, and the financial regulator was notified; in Próspera ZEDE, a criminal complaint was filed with the police, and the financial regulator was notified. We are not expecting the money back; the bounty below is for anyone who can change that.
A 50% bounty. We will pay 25% of the value of any part of the 6.61 BTC stolen on 19 September that is recovered as a direct result of information you provide — up to about 1.65 BTC if all of it were. Another 25% of anything recovered goes to Bitcoin Beach, Bitcoin Ekasi, Afribit Kibera and their joint selection of Bitcoin circular economies in the Global South. If information leads to funds being frozen, the bounty is paid once those funds are returned to a wallet we control. The offer has no end date. Write to bounty@blinkbtc.com. The full terms, which govern, are at blink.sv/bounty-terms.
What you should do. Turn on two-factor authentication (Settings → Security and Privacy → Two-factor authentication), and put it on your email account too. Add an email login if you only have a phone, because a phone number can be SIM-swapped. If you use the API, make a new key. Ignore any email or SMS about this incident that contains a link: we contacted affected users through messages in the Blink app. We will never ask for your PIN, password, seed phrase or a login code, or ask you to move funds. If we wrote to you about your account, follow the steps in that message. If you'd rather hold your own keys, non-custodial accounts have been in the app since June (Settings → Move to non-custodial).
Blink is run by about twenty people in a dozen time zones, so there is no such thing as a Saturday morning for all of us at once. At 11:39 UTC it was early evening in Hong Kong, lunchtime in Europe, before dawn in El Salvador. The customer who called had noticed his balance was zero and his email address had been changed. Within six minutes the team was on a call, the first accounts had been locked by hand from the admin panel, and the decision that mattered had been taken: shut it all down.
At 11:54 we took the whole custodial backend offline. That decision cost every Blink user nearly eight hours of service. The withdrawals stopped the moment the service did. At 12:41 we posted the first public notice: services paused, investigating. At 16:06 we posted the second: every affected account will be made whole.
By 16:17 the first fix was merged and by 17:50 the third. The remaining hot-wallet funds were moved to fresh addresses so that any payout already signed for the attacker's withdrawals would bounce. At 19:34 we switched the service back on for everyone except the 36 accounts the attacker had touched, which stayed locked until we could restore them properly. The admin functions that change contact details stayed frozen as a second lock, the fix was verified on staging that evening, and in the days after we re-ran the entire attack ourselves against a copy of the pre-fix code, to be sure we understood it — and then confirmed that each layer now stops it.
All times are UTC; El Salvador is six hours behind.
From October 2023 until 19 September 2026, anyone with a free Blink account and a web browser could give themselves the powers of our support staff: change the email address or phone number on any customer's account, then log in as that customer and raise their withdrawal limits. We are publishing exactly how, because our code is open source, other services run deployments built from it, and the mistake at its core — trusting a token's own description of what it is allowed to do — can be made in any system built the same way.
Our staff use an admin interface to help customers: unlock an account, fix a phone number, raise a limit. Access to it runs through OAuth2, the same standard that lets you log in to one website with another. Three separate things were wrong, and each of them is unremarkable on its own.
The consent screen trusted the browser. When an app asks for permissions, our consent page took the list of permissions straight from the form the browser submitted and passed it to our OAuth server as-is. It never checked that list against what the app had actually asked for. So anyone could add a hidden field to the form saying "also give me admin", and the server — correctly trusting its own consent page — would mint a perfectly valid token that said admin.
The admin API trusted any token. The gatekeeper in front of it accepted any live token from our OAuth server: no required permission, no check that the token was meant for the admin API, no check on who the client was. It copied the token's self-declared permissions into the credential the admin server uses.
The admin server trusted the permissions string. If the string said admin, you were admin. There was no check that the person behind the token was a known administrator.
Together they meant that one free account and a browser were enough. No staff account or staff credential was used, and no malware was involved. Our own identity system issued those tokens, which is exactly why no alarm went off. The attacker tried the obvious route first — adding permissions on the main authorization request — and our configuration correctly rejected it. Then they tried the consent page, and it let them through.
For anyone auditing a similar stack, the history matters. The hole was opened in October 2023 by three pull requests in a row: the first let the admin API accept OAuth tokens while a separate editor check still guarded it; the second deleted that editor check; the third gave every user access to the OAuth consent screen. A hardening change in May 2026 (PR #158) added admin-permission requirements on the admin server, but it read those permissions from the token itself, and the consent page still let anyone put them there. It was exploitable for roughly three years.
The fixes are public: the consent page now rejects any permission the app didn't ask for (PR #853); admin tokens must carry an audience issued only for the admin API, which customer tokens can't get (same PR; PR #855 let us switch this check on in production separately, which we did on 21 September); and the admin functions that change contact details can be frozen outright by configuration, for everyone (PR #861).
The code was written in 2023, inside the company where the Blink platform was built from 2019, and became ours in October 2024, when Blink went through an ownership transition and became an independent company. Two of the engineers who had built the platform came with us, but the engineers who had written the admin authorisation flow stayed behind, so nobody on the Blink team knew why an earlier permission check had been there. Through 2025 most of our engineering work went into separating our systems from that company's, which had grown together over the years: our own infrastructure, our own accounts, our own node.
In 2026 regulation changed in many of the countries we served. Google began requiring local licences for wallet apps in fifteen markets, the transition period for the EU's MiCA rules ended on 1 July, and many other jurisdictions were passing new laws or bringing into force laws passed in earlier years. We responded by holding less of our customers' money rather than applying for more licences: we built and launched a non-custodial wallet, withdrew custodial service from more than forty jurisdictions, and moved tens of thousands of users to accounts where they hold their own keys. Our non-custodial launch post in June describes that work. For a team of about twenty people, it took most of the year.
Hardening the code we had inherited waited behind both of those projects. It waited too long. How security findings are triaged and owned is the first thing we changed.
The elevated hot wallet is part of the same story. We were running it at roughly double its normal level as a buffer for the migration, the first wave of which had ended a week or two before the attack. We hadn't brought it back down yet. That's why the loss could be as large as it was. It has since been cut to a fraction of that level, and the Lightning operating balance is being reduced further.
The attacker took over 35 accounts the same way: change the email address or phone number through the admin tools, request a login code, log in. On 24 of them they then withdrew funds. On nine of them they hit a wall. Those nine had two-factor authentication switched on — an authenticator app on the owner's phone — and on a 2FA-protected account, a session you opened with only a login code cannot move money. Our logs show all 18 of the attacker's attempts on those nine accounts failing at exactly that gate. Zero loss on every one.
2FA didn't stop the takeover of our admin tools. But it did stop the theft from individual accounts. None of the 24 accounts that lost money had it on. We should have required it for large withdrawals and for accounts holding large balances.
If you take one action from this post, make it that one: Settings → Security and Privacy → Two-factor authentication. Then turn it on for your email account too, since your Blink login codes can be sent there.
By Sunday, the day after the attack, we knew what had been taken from whom, to the satoshi, reconciled against the ledger, the blockchain and the Lightning node, and the Board had decided how to pay for it. Blink's shareholders committed the full amount within 24 hours as an interest-free loan, advanced it on the Wednesday, and every shareholder has been offered the chance to fund its share on the same terms. On Thursday 24 September we restored every affected balance and refunded the fees we had charged on the fraudulent withdrawals. The affected customers heard from us directly before we said anything more in public, and so did the people whose records had been read.
The order was deliberate. Users whole first; everything else later. We briefed our shareholders on the Friday with the same facts you're reading now, and we held this post until the authorities had our reports: a criminal complaint filed with the Fiscalía General de la República of El Salvador, notices to El Salvador's financial regulator and cybersecurity agency, a criminal complaint with the Police Department of Próspera ZEDE, and a notice to Próspera's financial regulator.
Two things happened in that week that we want on the record. On the Sunday the attacker wrote to us demanding payment, with a threat to publish user data. We didn't pay and we won't; the message is evidence now of their extortion attempt. And from the Sunday to the Wednesday they kept logging in — to four of the locked accounts whose contact details still carried the attacker's email address, because we hadn't yet restored them. Every payment they tried was refused and nothing moved. It should not have been possible. Our reactivation procedure now restores the contact details first, ends every session, and only then unlocks the account.
We're not the first company to absorb a loss like this and make users whole. Exchanges and wallets have done it before us. A custodian's loss belongs to the custodian.
Blink was not starting from zero on 19 September. Most customer funds were already in multi-signature cold storage spread across continents; the service is full-reserve; two-factor authentication was available and we had been pushing users toward it; and withdrawals were limited by account level. Most of that held: the attacker never reached cold storage, never touched a non-custodial account, and failed on every account that had 2FA. What failed specifically were the authorisation checks in our inherited admin tooling, and monitoring that watched for outages rather than for an administrator doing something no administrator should. What failed more generally was our priorities: the ownership transition and then the move out of custody took most of the team, and security work on the code we had inherited waited behind them.
What we did about it, in the first days:
Further hardening is under way across the platform.
And two commitments that start today:
1.The recovery bounty. We'll pay 25% of the value of any part of the 6.61 BTC stolen on 19 September that is recovered as a direct result of information you provide — up to about 1.65 BTC if all of it were. Of anything recovered, another 25% goes to Bitcoin Beach, Bitcoin Ekasi, Afribit Kibera, and their joint selection of Bitcoin circular economies in the Global South that could use some additional backing. The offer has no end date; write to bounty@blinkbtc.com. The bounty is paid only out of funds that have been returned into a wallet that we control. Stolen funds can be returned to us on-chain at bc1q5vek5m04l6v277t6exhrurylqsmtvg2l30pt99 or over Lightning to the Lightning address bounty@blink.sv.
2.A security reporting channel, with rewards. Write to bounty@blinkbtc.com. Reports are acknowledged within 72 hours and owned by our security group until they're resolved. Good-faith research on Blink's software is welcome, and we will not pursue anyone who reports a weakness responsibly. We pay discretionary rewards of up to 0.1 BTC for critical findings. The full policy is at blink.sv/bounty-terms. This channel exists because of this incident: every report must land somewhere, be acknowledged, and stay owned until it's resolved.
The 50% is split in two: 25% goes to whoever's information leads to a recovery, and another 25% of anything recovered goes to Bitcoin Beach, Bitcoin Ekasi, Afribit Kibera and the circular economies they select. We think the funds are most likely lost for good. Stolen bitcoin is very hard to recover, so for us any amount recovered is a big win, and the more people we can encourage to help us find the attacker, the better. People like this should not get away easily, and we want to make spending the money as hard and uncomfortable for them as possible.
We added the second 25% because we wish to remind the attacker that they stole money that we and our mission-aligned shareholders would much rather have spent supporting grassroots Bitcoin adoption and empowering Bitcoin builders in debanked communities worldwide. Shame on the attacker.
We've traced the funds continuously since 19 September. As of 1 October: about 0.87 BTC was still sitting unmoved at the receiving addresses. The rest went into other wallets of theirs, which also hold bitcoin that did not come from Blink. From those wallets about 5.05 BTC has gone through a cross-chain swap service (NEAR Intents), 1 BTC on 21 September and about 4.05 BTC on 28–29 September, and after our own preservation request on 22 September went unanswered we have asked the authorities to obtain that service's records; about 0.012 BTC went into a Binance deposit, and Binance's security team is engaged and blacklisting. Because the attacker's own bitcoin is mixed in, these figures do not add up to the 6.61 BTC. The on-chain addresses are in the appendix.
Our codebase is open source and services we didn't build run deployments derived from it. The vulnerable pattern — a consent flow that trusts the browser's permission list, and an edge gatekeeper that accepts any live token without an audience check — predates Blink's current repositories and is in the archived public history. We've privately advised the operators of derived deployments known to us; one patched the same day. Our security advisory for the codebase will be published on GitHub at github.com/blinkbitcoin/blink/security/advisories, with a CVE identifier requested. If you run something built on the Blink codebase and haven't heard from us, write to bounty@blinkbtc.com; once we have confirmed that you operate a deployment, we'll share the full check procedure and help you verify it. The fixes are the three PRs linked above. Attackers now scan public code at machine speed. If you run this code, check it today.
Blink started as the everyday Bitcoin wallet for a small beach town where people needed money that worked. On 19 September, for 22 of our customers, it didn't. Every one of them had trusted us with their money, which is the only thing a custodian is for.
To our customers, for your patience; to the researchers and exchanges who helped within hours; to the shareholders who stood behind the company without hesitation; and to the team that dropped everything on a Saturday and didn't sleep — thank you. We'll earn the trust back the way it was earned in El Zonte: by showing up, and by making payments go through, every day.
Was my money at risk? If your account is open and we have not written to you, we have no record of the attacker reading your account's details, and nothing was taken from it. Until we closed it on 19 September, the flaw could have been used against any custodial account, though on an account with 2FA it would not have let anyone move money. Most customer funds sit in cold storage the admin tools cannot reach. If you have a non-custodial account, your keys were never on our servers and there was nothing for the attacker to take.
Did the attacker get my data? They read details of 3,817 accounts. We have written to every account holder we could reach, saying what was seen. If your account is open and you haven't had that message, it wasn't among them. You can always ask us what, if anything, was accessed about your account: support@blink.sv.
Why didn't your monitoring catch the attack? Our alerts were built to catch outages. They had no rule for an administrator doing things no administrator should, and the attacker was using our own tools with tokens our own system had issued.
Why was there so much in the hot wallet? We'd roughly doubled our operating balance as a buffer for the migration to non-custodial accounts, and we hadn't brought it back down after the first wave ended. It's now a fraction of that level, and we're not publishing the number.
Would 2FA have saved me? In this incident, yes. Every account that had it kept its money; every account that lost money didn't have it. It won't stop every attack, but it stopped this one. Turn it on.
Should I move to a non-custodial account? If you'd rather hold your own keys, yes — that's what they're for, and nobody at Blink, nor anyone who breaks into Blink, can move funds you hold yourself. It's not required, not every feature is there yet, and availability depends on your region. If you stay custodial, turn on 2FA.
Who paid for this? Blink's shareholders, as an interest-free loan committed within 24 hours and advanced on 23 September. Not customers, and not customer reserves. Blink remains full-reserve: every balance is backed one-for-one.
I found a security problem in Blink. What do I do? Write to bounty@blinkbtc.com. You'll hear back within 72 hours, your report will have an owner until it's resolved, and critical findings are rewarded.
Dated updates on recovery, the bounty and any correction to this post will be added here.
The following addresses received the on-chain withdrawals. Exchanges and researchers are asked to screen deposits deriving from them and to contact bounty@blinkbtc.com. (Lightning-rail proceeds went to attacker-controlled Lightning wallets and are being traced separately.)
Total on-chain: 4.62685758 BTC received at these addresses in 14 withdrawals, seven of them to the first address. Our payout system batches withdrawals, so the 14 went out in 12 on-chain transactions. The remaining 1.98399667 BTC of the 6.61085425 BTC total left over Lightning.
Start receiving and sending bitcoin now