Arbitrary Storage Write Exploit Generator
Sigvex exploit generator that validates arbitrary storage write vulnerabilities by simulating user-controlled SSTORE operations targeting critical storage slots including the owner address.
Arbitrary Storage Write Exploit Generator
Overview
The arbitrary storage write exploit generator validates findings from the arbitrary_storage_write, uncontrolled_storage, and related detectors by simulating the attack where a user-controlled slot parameter in an assembly SSTORE operation is used to overwrite slot 0 (the owner address). If the simulated storage write succeeds in changing the owner to the attacker’s address, complete contract takeover is confirmed.
Unchecked assembly SSTORE with user-controlled slot arguments allows attackers to write any value to any storage slot. Since slot 0 is conventionally the owner address in simple contracts, this typically results in complete ownership takeover — after which the attacker can call any onlyOwner function to drain funds or destroy the contract.
Note: Exploit generation in Sigvex is for vulnerability validation purposes only.
Attack Scenario
Direct slot overwrite:
- A contract exposes
setStorageAt(uint256 slot, uint256 value)implemented as assemblysstore(slot, value). - No access control or slot validation is present.
- The attacker calls
setStorageAt(0, uint256(attackerAddress)). - Slot 0 (the
ownervariable) is now the attacker’s address. - The attacker calls
withdraw()(protected byonlyOwner) to drain all contract ETH.
Array index overflow (indirect):
- A dynamic array is stored at slot 3. Its elements are at
keccak256(3) + index. - A function writes to
array[index]via assembly without bounds checking. - The attacker computes
offset = type(uint256).max - keccak256(3) + 1. - Writing to
array[offset]computeskeccak256(3) + offset ≡ 0 (mod 2^256), targeting slot 0. - The owner slot is overwritten.
Exploit Mechanics
The generator simulates the attack by comparing storage state before and after the exploit transaction. It configures slot 0 with a legitimate owner address, executes the attacker-controlled storage write operation, then reads slot 0 back from the post-execution state.
If the owner slot value changed between the before and after snapshots, the vulnerability is confirmed. Evidence collected:
| Field | Value |
|---|---|
vulnerable_slot | 0 |
original_owner | Legitimate owner address |
new_owner | Attacker address |
attack_type | Arbitrary storage write - owner takeover |
Estimated gas: 50,000. The six-step exploit sequence demonstrates the complete attack path from slot analysis through fund extraction.
The generated PoC shows both attack vectors:
// VULNERABLE: User controls which slot to write
contract VulnerableStorage {
address public owner; // Slot 0
function setStorageAt(uint256 slot, uint256 value) public {
assembly {
sstore(slot, value) // No validation!
}
}
}
// EXPLOIT: Overwrite owner slot directly
contract ArbitraryStorageWriteExploit {
function attack1_directSlotOverwrite() public {
// Slot 0 = owner address
target.setStorageAt(0, uint256(uint160(attacker)));
// target.owner() == attacker now
}
function attack3_drainFunds() public {
// Now we can call onlyOwner functions
target.withdraw(); // Drain all ETH
}
}
Remediation
- Detector: Arbitrary Storage Write Detector
- Remediation Guide: Arbitrary Storage Write Remediation
Eliminate user-controlled assembly storage operations. Use Solidity’s high-level storage abstractions exclusively:
// VULNERABLE: User controls storage slot
function setStorageAt(uint256 slot, uint256 value) public {
assembly { sstore(slot, value) } // Never do this
}
// SECURE: Use Solidity mappings (slot calculation is safe)
mapping(uint256 => uint256) private userData;
function setUserData(uint256 key, uint256 value) public {
userData[key] = value; // Cannot reach owner slot through mapping
}
// SECURE: Bounds-checked with access control
function setDataSafely(uint256 index, uint256 value) public onlyOwner {
require(index < 1000, "Index too large");
userData[index] = value;
}
When assembly is unavoidable (e.g., proxy implementations), implement a slot allowlist:
- Define permitted storage slots at compile time
- Reject any
sstoretarget that is not in the allowlist - Use the ERC-1967 standard slots for proxy storage to avoid collisions with implementation variables