Skip to main content

upgradeability

Auto-loaded by access-control-auditor agent during Phase 2. Provides detection patterns for: UUPS/Transparent/Beacon proxy vulnerabilities, storage collision, initialization issues, function selector clashing, upgrade authorization. Core artifact: Proxy Architecture Map.

インストールへ移動

ソース情報

リポジトリ
BitterSecurity/Vigilo
ソースの最終更新活動
2026年2月2日 07:22
検出された SKILL.md の言語
英語
スター
66
フォーク
17

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
upgradeability
description
Auto-loaded by access-control-auditor agent during Phase 2. Provides detection patterns for: UUPS/Transparent/Beacon proxy vulnerabilities, storage collision, initialization issues, function selector clashing, upgrade authorization. Core artifact: Proxy Architecture Map.
user-invocable
false
# Upgradeability & Proxy Pattern Vulnerabilities **2025-2026 Statistics**: Proxy-related vulnerabilities caused $340M+ in losses, with initialization bugs and storage collision being the leading causes. --- ## Why Upgradeability Fails (Root Causes) ### Root Cause 1: Initialization vs Constructor Proxies can't use constructors; initializers can be called multiple times. ```solidity // VULNERABLE: No initialization protection contract ImplementationV1 { address public owner; function initialize(address _owner) external { owner = _owner; // @audit Can be called by anyone, multiple times! } } ``` **Attacker's view**: "They forgot the initializer guard. I'll take ownership." ### Root Cause 2: Storage Layout Mismatch Upgrades must maintain exact storage slot ordering. ```solidity // V1 Storage contract V1 { address public owner; // slot 0 uint256 public balance; // slot 1 } // V2 Storage - WRONG! contract V2 { uint256 public newVar; // slot 0 - COLLISION with owner! address public owner; // slot 1 uint256 public balance; // slot 2 } ``` ### Root Cause 3: delegatecall Context Implementation code runs with proxy's storage and context. ```solidity // VULNERABLE: selfdestruct in implementation contract Implementation { function destroy() external onlyOwner { selfdestruct(payable(owner)); // @audit Destroys PROXY, not implementation! } } ``` ### Root Cause 4: Unprotected Upgrade Function Anyone can upgrade to malicious implementation. ```solidity // VULNERABLE: Missing access control on upgrade contract UUPSVulnerable is UUPSUpgradeable { function _authorizeUpgrade(address) internal override { // @audit No onlyOwner check! } } ``` --- ## Proxy Patterns Overview ### Transparent Proxy Pattern ``` User → Proxy (storage + delegatecall) → Implementation (logic) ↓ Admin → ProxyAdmin (upgrade logic) ``` **Key Features**: - Admin calls go to ProxyAdmin, not implementation - Users can't accidentally call admin functions - Requires separate ProxyAdmin contract ### UUPS Pattern (EIP-1822) ``` User → Proxy (storage + delegatecall) → Implementation (logic + upgrade) ``` **Key Features**: - Upgrade logic in implementation - Smaller proxy bytecode - Risk: forgetting upgrade function in new implementation ### Beacon Pattern ``` User → Proxy → Beacon → Implementation ↑ Multiple proxies share beacon ``` **Key Features**: - Single upgrade updates all proxies - Good for factory patterns - More complex architecture --- ## The Proxy Architecture Map (Core Artifact) For each proxy system, document: | Component | Address | Type | Storage | Risk | |-----------|---------|------|---------|------| | Proxy | 0x1234... | TransparentProxy | User data | Storage collision | | Implementation | 0x5678... | Logic V1 | None | selfdestruct | | ProxyAdmin | 0x9abc... | Admin | None | Access control | | Beacon | N/A | N/A | N/A | N/A | --- ## Detection Patterns ### Pattern 1: Unprotected Initializer **Root Cause**: Initialization vs Constructor ```solidity // VULNERABLE: Can be re-initialized contract VaultV1 { address public owner; bool private _initialized; // @audit Wrong pattern function initialize(address _owner) external { require(!_initialized); // @audit Can be front-run! owner = _owner; _initialized = true; } } ``` **Attack Flow**: 1. Protocol deploys implementation 2. Protocol deploys proxy 3. Attacker front-runs initialize() call 4. Attacker becomes owner 5. Attacker drains protocol **Search Queries**: ``` Grep("function initialize|initializer", glob="**/*.sol") Grep("Initializable|_initialized|initializing", glob="**/*.sol") ``` **Secure Pattern**: ```solidity import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; contract VaultV1 is Initializable { address public owner; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); // Prevent implementation initialization } function initialize(address _owner) external initializer { owner = _owner; } } ``` ### Pattern 2: Storage Collision **Root Cause**: Storage Layout Mismatch ```solidity // V1 contract VaultV1 { address public owner; // slot 0 uint256 public balance; // slot 1 } // V2 - VULNERABLE contract VaultV2 { address public newAdmin; // slot 0 - OVERWRITES owner! address public owner; // slot 1 - READS balance as address! uint256 public balance; // slot 2 } ``` **Attack Flow**: 1. V1 deployed with owner = 0x1234... 2. Upgrade to V2 3. newAdmin (slot 0) now contains old owner value 4. owner (slot 1) now reads balance (corrupted) 5. Access control broken **Search Queries**: ``` Grep("pragma solidity|contract.*is.*Upgradeable", glob="**/*.sol") ``` **Detection**: - Use `forge inspect Contract storage-layout` - Compare layouts between versions - Check inheritance order **Secure Pattern**: ```solidity // V2 - CORRECT: Append only contract VaultV2 { address public owner; // slot 0 - SAME uint256 public balance; // slot 1 - SAME address public newAdmin; // slot 2 - NEW at end } ``` ### Pattern 3: UUPS Missing Upgrade Authorization **Root Cause**: Unprotected Upgrade Function ```solidity // VULNERABLE: No access control contract MyUUPS is UUPSUpgradeable { function _authorizeUpgrade(address newImplementation) internal override { // @audit Empty = anyone can upgrade! } } // VULNERABLE: Wrong check contract MyUUPS is UUPSUpgradeable { function _authorizeUpgrade(address newImplementation) internal override { require(msg.sender == owner); // @audit What if owner is uninitialized? } } ``` **Attack Flow**: 1. Attacker deploys malicious implementation 2. Calls upgradeTo(maliciousImpl) 3. No authorization check passes 4. Proxy now points to malicious code 5. Attacker drains all funds **Search Queries**: ``` Grep("_authorizeUpgrade|UUPSUpgradeable", glob="**/*.sol") Grep("upgradeTo|upgradeToAndCall", glob="**/*.sol") ``` **Secure Pattern**: ```solidity function _authorizeUpgrade(address newImplementation) internal override onlyOwner { // Additional checks possible require(newImplementation != address(0), "Zero address"); } ``` ### Pattern 4: UUPS Implementation Without Upgrade Function **Root Cause**: Upgrade Logic Removed ```solidity // V1 - Has upgrade contract VaultV1 is UUPSUpgradeable { function _authorizeUpgrade(address) internal override onlyOwner {} } // V2 - VULNERABLE: No longer upgradeable! contract VaultV2 { // Forgot to inherit UUPSUpgradeable! // Contract is now BRICKED - can never upgrade again } ``` **Search Queries**: ``` Grep("is UUPSUpgradeable|is.*Upgradeable", glob="**/*.sol") ``` ### Pattern 5: selfdestruct in Implementation **Root Cause**: delegatecall Context ```solidity // VULNERABLE: selfdestruct destroys proxy's storage contract Implementation { function emergencyDestroy() external onlyOwner { selfdestruct(payable(msg.sender)); // @audit This destroys the PROXY, not the implementation! } } ``` **Attack Flow**: 1. Attacker gains owner access (via other vuln) 2. Calls emergencyDestroy() 3. Proxy contract is destroyed 4. All funds in proxy lost forever **Note**: `selfdestruct` is deprecated in Solidity 0.8.20+ but may still exist in older code. **Search Queries**: ``` Grep("selfdestruct|SELFDESTRUCT", glob="**/*.sol") Grep("delegatecall.*selfdestruct", glob="**/*.sol") ``` ### Pattern 6: Function Selector Clashing **Root Cause**: Proxy and Implementation Share Function Namespace ```solidity // Proxy has admin() at selector 0xf851a440 // If implementation has function with same selector, collision occurs // DANGEROUS: Implementation function clashes with proxy admin function contract Implementation { // This selector might clash with proxy's admin() function admin_() external returns (address) { // @audit Check selector! return address(this); } } ``` **Detection**: ```bash # Check function selectors cast sig "admin()" cast sig "functionName()" ``` **Search Queries**: ``` Grep("function admin|function upgrade|function implementation", glob="**/*.sol") ``` ### Pattern 7: Transparent Proxy Admin Exposure **Root Cause**: ProxyAdmin Access Control ```solidity // VULNERABLE: ProxyAdmin owner can be changed contract MyProxyAdmin is ProxyAdmin { // If ownership transferred to attacker... // Attacker can upgrade to malicious implementation } ``` **Search Queries**: ``` Grep("ProxyAdmin|TransparentUpgradeableProxy", glob="**/*.sol") Grep("changeProxyAdmin|transferOwnership", glob="**/*.sol") ``` ### Pattern 8: Storage Collision Prevention (ERC-7201) **Root Cause**: Unnamespaced Storage in Upgradeable Contracts ERC-7201 introduces namespaced storage to prevent collisions when inheriting from multiple upgradeable contracts. Without it, storage slots can collide across inheritance chains. ```solidity // VULNERABLE: Sequential storage without namespacing contract VaultV1 { address public owner; // slot 0 uint256 public balance; // slot 1 } // When inherited by child contract, slots collide contract ChildVault is VaultV1 { address public admin; // slot 0 - COLLISION with owner! uint256 public fee; // slot 1 - COLLISION with balance! } ``` **Attack Flow**: 1. Parent contract uses slots 0-1 2. Child contract adds variables 3. Child's variables overwrite parent's storage 4. State corruption and access control bypass **Secure Pattern (ERC-7201)**: ```solidity // @custom:storage-location erc7201:myprotocol.vault contract VaultV1 is Initializable { struct VaultStorage { address owner; uint256 balance; } bytes32 private constant VAULT_STORAGE_LOCATION = keccak256(abi.encode(uint256(keccak256("myprotocol.vault")) - 1)) & ~bytes32(uint256(0xff)); function _getVaultStorage() private pure returns (VaultStorage storage $) { assembly { $.slot := VAULT_STORAGE_LOCATION } } function initialize(address _owner) external initializer { VaultStorage storage $ = _getVaultStorage(); $.owner = _owner; } } ``` **Search Queries**: ``` Grep("@custom:storage-location|erc7201|erc-7201", glob="**/*.sol") Grep("keccak256.*abi.encode.*keccak256", glob="**/*.sol") Grep("bytes32.*STORAGE_LOCATION", glob="**/*.sol") ``` **Detection**: - Check for `@custom:storage-location` NatSpec annotations - Verify storage location calculation uses ERC-7201 formula - Confirm assembly block uses correct slot offset - Validate namespace ID is unique per contract --- ## Upgradeability Audit Checklist ### Initialization - [ ] Uses OpenZeppelin Initializable pattern - [ ] Constructor calls `_disableInitializers()` - [ ] `initialize()` has `initializer` modifier - [ ] Cannot be re-initialized ### Storage - [ ] Storage layout documented - [ ] No variables reordered in upgrades - [ ] New variables only appended - [ ] Inheritance order preserved - [ ] Storage gaps for future inheritance - [ ] ERC-7201 namespaced storage used (if multiple inheritance) - [ ] `@custom:storage-location` annotations present - [ ] Storage location calculation correct (keccak256 formula) ### UUPS Specific - [ ] `_authorizeUpgrade` has proper access control - [ ] All upgrade versions maintain UUPSUpgradeable - [ ] `upgradeTo` is properly protected ### Transparent Proxy Specific - [ ] ProxyAdmin properly secured - [ ] No selector clashing - [ ] Admin functions not callable by users ### General - [ ] No `selfdestruct` in implementation - [ ] No `delegatecall` to untrusted contracts - [ ] Upgrade timelock exists - [ ] Emergency pause available --- ## Storage Gap Pattern ```solidity // Reserve storage slots for future variables in base contracts abstract contract BaseContractV1 is Initializable { address public owner; uint256[49] private __gap; // Reserve 49 slots } // When adding new variables, reduce gap abstract contract BaseContractV2 is Initializable { address public owner; address public newAdmin; // Uses one gap slot uint256[48] private __gap; // Now 48 slots } ``` --- ## Search Query Reference ``` # Find proxy patterns Grep("delegatecall|Proxy|proxy", glob="**/*.sol") Grep("ERC1967|TransparentUpgradeable|UUPSUpgradeable", glob="**/*.sol") # Find initialization Grep("initialize|initializer|Initializable", glob="**/*.sol")
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る