Update on the August 21st 2018 Security Update to the `eosio` System Contract (dubbed v1.1.0-sec.patch-3)
We want to take a moment to give an update on a mitigation for exploits discovered on the EOS Mainnet, deployed on August 22nd 2018, at 13:02 UTC with transaction 628293a6b5398747e14b2c0416b303b4d71a2ff287b02d6c63f236ddb4abda7e.
Everybody recognizes that no software is perfect. However, as with previous hot patches, what happened here is a great example of the reactive governance model and upgradability of EOS.IO software-based blockchains.
We want to thank all Block Producers that participated in the analysis, review, and testing of the patch that was applied in that transaction. Thanks to Block.one’s team for doing the heavy work of fixing the issues. The source code of the patch will be released in the `eosio.contracts` GitHub repository.
Timeline of Events
- A few days ago, some contracts were seen growing in RAM usage, which triggered a deeper investigation.
- As a the EOS.IO software provider, Block.one was made aware of the bug through their bug bounty program and promptly produced a patch.
- On August 20th, at 13:51 UTC the active Block Producers were made aware of the risks, mitigations as well as the patch through secure communication channels.
- Numerous Block Producer teams independently reviewed and locally applied the patch and validated the new behavior on their testnets.
- 3 iterations of the patch were created (ending with patch3) and the Block Producers were satisfied with the final test results.
- On August 21st, at 22:48 UTC Igor (from EOS Rio) sent an `eosio.msig` proposal for the 21 active Block Producers to approve the transaction c06eaf4d99368079b3c61a5e61257d520045f1a0926febc8a3e327660c6ea04a
- See the transaction breakdown here: https://abourget.keybase.pub/p3analysis.txt
Issue 1 - RAM consumption
Context
During the boot of the EOS Mainnet chain, some system accounts were initialized with no limits on RAM, CPU and Network bandwidth (values are -1, cleos shows as `unlimited`).
The semantics of EOS allows a contract that is notified by another contract, to use the authorization of the original transaction, and therefore to consume its RAM.
Risks: Severe
- If a contract issues an inline `transfer` action (like with `refund`) from an unlimited RAM account, that account is authorized to pay for RAM in a notified contracts action.
- Some transactions on the Mainnet prove that an unknown bad actor has found a vulnerability in a system contract caused by an unsafe use of this design pattern which has consumed several megabytes of RAM.
Mitigations in patch3
- Added authorization for the inline `transfer` actions (`vpay`, `bpay`, `unstake`, and `bidrefund` which was in `bidname` before) so that recipients may be billed RAM for new token balance rows.
- Added `setalimits` to set limits on those unlimited accounts (especially RAM), and stop the bleeding. To make sure it impacts as few users as possible, the patch also adds an `[owner]@active` authorization for `stake`, `producer bpay` and `producer vpay` which could potentially be exploited in the same way. A similar fix was introduced in the `eosio.token` version 1.1.0 which was released to the Mainnet previously.
- Once the patch is deployed, `setalimits` needs to be called on unlimited accounts to prevent them from issuing a `transfer`.
- Decouple success of `bidname` by making it a deferred transaction rather than an INLINE_ACTION. This way you can block the deferred transaction, but it won’t block the `bidname`.
- Also added the `bidrefund` action to the system contract that can manually refund you if the deferred transaction failed for some reason (similar to `refund` alongside `undelegatebw`).
Issue 2 - Blocking Name Bids
Context
- In the name bid auction, a new highest bid automatically sends a refund to the previous (displaced) bidder.
Risks: Inconvenient / Exploitable
- A contract deployed on the receiving end of that refund could block the whole transaction by failing an assertion, making it impossible to be outbid.
Mitigations in patch3:
- Decouple success of `bidname` by making it a deferred transaction rather than an INLINE_ACTION. This way you can block the deferred transaction, but it won’t block the `bidname`.
- Also added the `bidrefund` action to the system contract that can manually refund you if the deferred transaction failed for some reason (similar to `refund` alongside `undelegatebw`).