Security issues fixed with Hive HF26

in #hf262 years ago

Prior to hardfork 26 (and since hardfork 20)[1], the implementation of resource credits in Hive had multiple security issues stemming from a logic bug in the resource credits implementation:

  • Anyone could transact using any another account's resource credits
    • This could be used to spam the chain with useless transactions
    • This could be used to prevent any user from transacting by using all their resource credits
  • An attacker who compromised an account could prevent the recovery system from being used to recover the account

Thanks to @blocktrades and the Hive developers for working to get these issues fixed! :)

This issue has not been fixed on Steem, or any other post-HF20 fork of Steem.

The rest of this post is quite technical and assumes familiarity with how Hive and resource credits work.

Cause

Both of these issues stemmed from essentially the same root cause: the way resource credits were calculated for recover_account, the operation that can be used by the rightful owner of a compromised account to recover it. The Hive recovery flow looks like:

  1. An attacker compromises an account and broadcasts a account_update to change the owner key. The compromised account pays the resource credits for this transaction.
  2. The rightful owner tells their recover partner (usually the account who created their account) about the compromise, and the public key for their new owner key.
  3. The recover partner broadcasts a request_account_recovery operation. The recovery partner pays the resource credits for this transaction.
  4. The rightful owner broadcasts a recover_account operation, signed with their old owner key and the new owner key they told their recovery partner about.

We can "attack" our own accounts and use the recovery system to exploit this issue without actually having our account compromised.

What account should pay resource credits for step 4? Here's how pre-HF25 Hive does it:

  account_name_type operator()( const recover_account_operation& op )const
  {
    for( const auto& account_weight : op.new_owner_authority.account_auths )
      return account_weight.first;
    for( const auto& account_weight : op.recent_owner_authority.account_auths )
      return account_weight.first;
    return op.account_to_recover;
  }

op.new_owner_authority is the authority that the compromised owner authority will be replaced with; op.recent_owner_authority is the previous owner authority that the owner controlled before it was changed by the person who compromised the account.

We can take advantage of the way authorities work to make a transaction such that the problematic code assigns the RC cost to a user of our choice. To understand, let's first have a digression on Hive authorities.

How authorities work

In Hive, an authority is a set of public keys and accounts. Each key/account has a weight. A transaction is considered to be signed by an authority if the sum of the weights of the keys/accounts that signed it are at least the authority's weight threshold. Hive accounts have three authorities: owner, active, and posting[2]. The owner authority can be used to completely control the account.

Abusing pointless authorities

We can create authorities that are pointless complicated. For example:

SubauthorityWeight
Key 110
@alice1
Weight threshold10

The @alice subauthority is pointless and could be removed, but the Hive software doesn't require the absence of extraneous subauthorities.

The bug

Okay, here's the bug: the first account subauthority (if it exists) specified when recovering an account pays the resource credits for the whole transaction, even if transaction wasn't signed by that account. That's what the const auto& account_weight : op.new_owner_authority.account_auths part of the code does: check for an account authority to charge the RCs to.

An attacker can recover an account they own to a new owner authority. For example, if they use the example authority above, the code that determines the RC cost of a recover_account operation will still force @alice to pay the RCs for the account recovery, even though @alice didn't sign the transaction.

Making our attack more efficient

A single Hive transaction can have multiple, potentially unrelated, operations in it. For example, if you wanted to trade HIVE for HBD with someone, you can create a transaction where you send HIVE and the counterparty sends HBD. The two-operation transaction would only be valid if it was signed by both parties.

The account charged RCs for the first operation in a transaction gets charged for all of them. So by including spam operations in the same transaction as a recover_account we can drain as much RCs as can fit into single block. By repeating this, we can drain large amounts of RCs, while only paying for relatively small request_account_recovery operations ourself. See this example transaction where I drained nearly all of the RCs from @steemit (I control @memos):

@steemit: Resource Credits at 9.94%

If we wanted to do this repeatedly to drain RCs, there are two optimizations we'd need to make:

  1. Instead of paying RCs for the request_account_recovery operation, we can tack them onto the end of the recover_account operation. We then only need to pay for the recovery request for the first iteration.
  2. An account can only be recovered once every 3603 seconds (1 hour + 1 block). To continuously exploit this, we could create 1201 accounts, and recover one each block. Normally, one has to pay of fee of 3 HIVE to create an account, but we can also use claim_account and create_claimed_account to create an account using resource credits. By using this exploit with one account a few times, we can then create as many accounts as we need.

Fix

When fixing this issue we need to take care not to:

  1. Allow an attacker to prevent an account from being recovered by draining its RCs
  2. Allow free spam transactions

I wrote a patch to fix this issue by just making transactions with a single account recovery operation not cost RCs. Since each recover_account has a matching request_account_recovery (where RCs are paid by the recovery partner), all account recoveries have RCs paid on them, which I thought would prevent spamming the chain with frivolous account recoveries.

It turns out my fix didn't fully fix the issue. You can make arbitrarily big transactions that only have a single recover_account operation, either by having your transaction signed with a lot of signatures or making your new owner authority really long. Both issues were fixed.

Conclusion

I hope you enjoyed reading about this issue as much as I enjoyed finding it! I've been looking into the security of the hived code recently, and noticed this issue while doing so. If you have any questions, I'd be happy to answer them.

The text of this document is dedicated to the public domain under the CC0 Public Domain Dedication.


  1. Actually this was fixed by a softfork in the HF26 release, so it was fixed on the Hive mainnet a bit before the actual hardfork.

  2. Accounts can also have memo and signing keys, but those are just plain public keys and not authorities.

Sort:  

🍕 PIZZA !
@smitop! The Hive.Pizza team manually upvoted your post.

Please vote for pizza.witness!

Dear @smitop,
May I ask you to review and support the new proposal (https://peakd.com/me/proposals/240) so I can continue to improve and maintain this service?
You can support the new proposal (#240) on Peakd, Ecency,

Hive.blog / https://wallet.hive.blog/proposals
or using HiveSigner.

Thank you!

Congratulations @smitop! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s):

You distributed more than 22000 upvotes.
Your next target is to reach 23000 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

Check out the last post from @hivebuzz:

CBRS Hive Infographic Contest - Get your badge and win 1000 HIVE
Our Hive Power Delegations to the October PUM Winners
Feedback from the November 1st Hive Power Up Day - New Turnout Record
Support the HiveBuzz project. Vote for our proposal!