Please note, this content is no longer actively maintained.
The content of the SWC registry has not been thoroughly updated since 2020. It is known to be incomplete and may contain errors as well as crucial omissions.
For currently maintained guidance on known Smart Contract vulnerabilities written primarily as guidance for security reviewers, please see the EEA EthTrust Security Levels specification. As well as the latest release version, an Editor's draft is available, that represents the latest work of the group developing the specification.
General guidance for developers on what to consider to ensure security, that is currently maintained, is also available through the Smart Contract Security Verification Standard (SCSVS).
Title
Write to Arbitrary Storage Location
Relationships
CWE-123: Write-what-where Condition
Description
A smart contract's data (e.g., storing the owner of the contract) is persistently stored at some storage location (i.e., a key or address) on the EVM level. The contract is responsible for ensuring that only authorized user or contract accounts may write to sensitive storage locations. If an attacker is able to write to arbitrary storage locations of a contract, the authorization checks may easily be circumvented. This can allow an attacker to corrupt the storage; for instance, by overwriting a field that stores the address of the contract owner.
Remediation
As a general advice, given that all data structures share the same storage (address) space, one should make sure that writes to one data structure cannot inadvertently overwrite entries of another data structure.
References
Samples
arbitrary_location_write_simple.sol
pragma solidity ^0.4.25;
contract Wallet {
uint[] private bonusCodes;
address private owner;
constructor() public {
bonusCodes = new uint[](0);
owner = msg.sender;
}
function () public payable {
}
function PushBonusCode(uint c) public {
bonusCodes.push(c);
}
function PopBonusCode() public {
require(0 <= bonusCodes.length);
bonusCodes.length--;
}
function UpdateBonusCodeAt(uint idx, uint c) public {
require(idx < bonusCodes.length);
bonusCodes[idx] = c;
}
function Destroy() public {
require(msg.sender == owner);
selfdestruct(msg.sender);
}
}
arbitrary_location_write_simple_fixed.sol
pragma solidity ^0.4.25;
contract Wallet {
uint[] private bonusCodes;
address private owner;
constructor() public {
bonusCodes = new uint[](0);
owner = msg.sender;
}
function () public payable {
}
function PushBonusCode(uint c) public {
bonusCodes.push(c);
}
function PopBonusCode() public {
require(0 < bonusCodes.length);
bonusCodes.length--;
}
function UpdateBonusCodeAt(uint idx, uint c) public {
require(idx < bonusCodes.length); //Since you now have to push very codes this is no longer an arbitray write.
bonusCodes[idx] = c;
}
function Destroy() public {
require(msg.sender == owner);
selfdestruct(msg.sender);
}
}
mapping_write.sol
pragma solidity ^0.4.24;
//This code is derived from the Capture the Ether https://capturetheether.com/challenges/math/mapping/
contract Map {
address public owner;
uint256[] map;
function set(uint256 key, uint256 value) public {
if (map.length <= key) {
map.length = key + 1;
}
map[key] = value;
}
function get(uint256 key) public view returns (uint256) {
return map[key];
}
function withdraw() public{
require(msg.sender == owner);
msg.sender.transfer(address(this).balance);
}
}