Uninitialized Storage Pointer Exploit Generator
Sigvex exploit generator that validates uninitialized storage pointer vulnerabilities by checking whether uninitialized struct or array pointers overwrite critical storage slots including owner and balance variables.
Uninitialized Storage Pointer Exploit Generator
Overview
The uninitialized storage pointer exploit generator validates findings from the uninitialized-storage and storage-collision detectors by executing three scenarios and checking whether critical storage slots — particularly slot 0 (typically the owner address) — are modified by attacker-controlled input. If an uninitialized storage pointer or unbounded array write overwrites the owner slot, complete contract takeover is confirmed.
This vulnerability class was prevalent in contracts compiled with Solidity versions before 0.5.0. In Solidity 0.4.x, declaring a struct or array variable with storage keyword without assigning it to an existing state variable caused the pointer to default to storage slot 0 — colliding with the first declared state variable (often the owner address). Multiple honeypot contracts deliberately used this behavior, and some early DeFi protocols suffered accidental storage corruption.
Note: Exploit generation in Sigvex is for vulnerability validation purposes only.
Attack Scenario
Uninitialized struct pointer:
- A contract declares
UserData storage data;inside a function without assigning it to a mapping or array element. - The pointer defaults to slot 0 (where the
owneraddress is stored). - Assigning
data.id = uint256(attackerAddress)executesSSTOREat slot 0, overwriting the owner. - The attacker is now the owner and can call privileged functions to drain the contract.
The attack exploits the struct field layout directly:
struct UserData {
uint256 id; // +0 offset → Slot 0 (overwrites owner!)
address user; // +1 offset → Slot 1 (overwrites balance!)
uint256 amount; // +2 offset → Slot 2
}
Array index overflow:
- A contract stores an array in slot 3. Elements are stored at
keccak256(3) + index. - A function writes
array[index] = valueusing assembly without bounds checking. - The attacker calculates an index
offset = type(uint256).max - keccak256(3) + 1such thatkeccak256(3) + offset = 0 (mod 2^256), hitting slot 0. - Owner slot is overwritten with the attacker’s address.
Exploit Mechanics
The generator constructs calldata targeting updateData(uint256,address,uint256) (selector 0xa2f02d1e) with attacker-controlled parameters. A second scenario uses setItem(uint256,uint256) (selector 0xb2f13d2f) with a zero index targeting slot 0 directly.
Initial storage state for all scenarios:
- Slot 0: a legitimate owner address
- Slot 1:
1000(a balance or counter value) - Slot 2:
500(a secondary state value)
After each execution, the generator reads slot 0 back from the world state:
Verdict:
- Scenario 2 does not revert AND slot 0 value changed → critical (confidence 0.95): owner slot overwritten. If the new value matches the attacker’s address, contract takeover is confirmed.
- Scenario 3 (array manipulation) does not revert AND slot 0 changed → critical (confidence 0.90): array write reaches owner slot.
- Non-critical slots modified unexpectedly → storage corruption warning (confidence 0.75).
// VULNERABLE (Solidity 0.4.x)
contract VulnerableStorage {
address public owner; // Slot 0
function updateData(uint256 id, address user, uint256 amount) public {
UserData storage data; // Uninitialized! Defaults to slot 0
data.id = id; // SSTORE at slot 0 → overwrites owner!
data.user = user; // SSTORE at slot 1 → overwrites balance!
}
}
contract StorageExploiter {
function exploit(VulnerableStorage v) external {
// data.id = attackerAddress overwrites owner slot
v.updateData(uint256(address(this)), address(0), 0);
// v.owner() is now address(this)
}
}
Remediation
- Detector: Uninitialized Storage Detector
- Remediation Guide: Uninitialized Storage Remediation
The primary fix is to upgrade to Solidity 0.5.0 or later, which requires an explicit data location keyword for all reference types. For contracts that must remain on older compiler versions, always initialize storage pointers:
// VULNERABLE (Solidity 0.4.x): Uninitialized storage pointer
UserData storage data; // Points to slot 0!
// SAFE (Solidity 0.4.x): Initialize to mapping slot
mapping(uint256 => UserData) public userData;
UserData storage data = userData[id]; // Points to keccak256(id, mapping_slot)
// SAFE (Solidity 0.5.0+): Use memory for temporary data
UserData memory data; // Never writes to storage
// PREFERRED: Use mappings instead of unkeyed storage structs
function updateData(uint256 id, address user, uint256 amount) public {
userData[id] = UserData(id, user, amount); // Safe mapping assignment
}