UPDATE: We got our ETH and BOOTY back! Original post below.
At 6pm PST Saturday, an unknown attacker drained 165.38 ETH (~$38,000) from our payment channel smart contract which also resulted in $4,000 worth of BOOTY on the contract becoming immobilized. Of the stolen/immobilized ETH/BOOTY, 34.99 ETH (~$8,000) and 1271.88 BOOTY belongs to users (~$9,300 total), and the rest belonged to SpankChain.
Unfortunately, as we were in the middle of investigating other smart contract bugs, we didn’t realize the hack had taken place until 7:00pm PST Sunday, at which point we took Spank.Live offline to prevent any additional funds from being deposited into the payment channels smart contract.
Our immediate priority has been to provide complete reimbursements to all users who lost funds. We are preparing an ETH airdrop to cover all $9,300 worth of ETH and BOOTY that belonged to users. Funds will be sent directly to users’ SpankPay accounts, and will be available as soon as we reboot Spank.Live.
Our plan is to keep the camsite down over the next 2–3 days (but possibly more) while we:
- Redeploy the payment channel smart contract with a patch to prevent any future hacks
- Update Spank.Live to use the new payment channel contract
- Airdrop ETH equivalent to 100% of the ETH+BOOTY lost by each user to their SpankPay accounts
- Fix the bugs we were originally working around the BOOTY upgrade
By the time we reboot Spank.Live, all viewers and performers will have 100% of the total value in BOOTY+ETH they had in their SpankPay airdropped to their current SpankPay addresses, so users don’t need to do anything.
The site will continue to function exactly as it was before with a single exception — because of the 4,000 BOOTY currently immobilized, we will temporarily reduce the BOOTY limit for each viewer to 10 BOOTY. This means viewers will only be able to tip 10 BOOTY at a time, and upon spending all 10 BOOTY they will automatically recharge their 10 BOOTY with any extra ETH they have deposited, until they completely deplete their ETH balance.
Over the next few days our team is preparing an in-depth investigation of the attack.
In short, the attack capitalized on a “reentrancy” bug, much like the one exploited in The DAO. The attacker created a malicious contract masquerading as an ERC20 token, where the “transfer” function called back into the payment channel contract multiple times, draining some ETH each time.
The malicious contract first called createChannel to set up the channel, then called LCOpenTimeout repeatedly via reentrancy. The LCOpenTimeout is there to allow users to quickly exit payment channels which have not yet been joined by the counter-party.
The LCOpenTimeout transfers the user their initial ETH deposit balance and their token deposit balance, both initially set in the createChannel function. Critically, the LCOpenTimeout function only deletes the on-chain channel data (which zeroes out channel balances) after the token transfer function. This allows the malicious contract’s transfer function to call LCOpenTimeout in a loop, each time sending the attacker ETH equivalent to their channel balance.
Payment Channel contract: https://etherscan.io/address/0xf91546835f756da0c10cfa0cda95b15577b84aa7#code
Attacker address: https://etherscan.io/address/0xcf267ea3f1ebae3c29fea0a3253f94f3122c2199
Internal txs (reentrancy) from the attacker’s account: https://etherscan.io/address/0xcf267ea3f1ebae3c29fea0a3253f94f3122c2199#internaltx
Attacker’s malicious contract: https://etherscan.io/address/0xc5918a927c4fb83fe99e30d6f66707f4b396900e
Internal txs (reentrancy) from the attacker’s malicious contract: https://etherscan.io/address/0xc5918a927c4fb83fe99e30d6f66707f4b396900e#internaltx
It was our decision to forego a security audit for the payment channel contract. We actually had Zeppelin conduct an audit which cost $17,000 for the previous unidirectional payment channels contract (which was far simpler by comparison). We considered that quite expensive, given that the most funds ever held by that contract only ever reached $17,000 in total.
For this far more sophisticated non-custodial payment channel contract we were quoted $30,000 — $50,000 for security audits, but taking into account both the perception value and opportunity cost of the time spent reacting to the hack, it would have been worth it. This is due to our agile development process, the site being in beta, and how quickly we’ve been iterating and redeploying the contracts (of which we’re pushing every 2–3 weeks). This identified an issue in our development and deployment process, and we’ll make the appropriate changes to help ensure it doesn’t happen again — while still innovating at the pace you all are familiar with.
As we move forward and grow, we will be stepping up our security practices, and making sure to get multiple internal audits for any smart contract code we publish, as well as at least one professional external audit.
For more information about the SpankChain project: