Skip to main content
Sigvex

Default Visibility Exploit Generator

Sigvex exploit generator that validates default visibility vulnerabilities in Solidity contracts where missing visibility modifiers default to public, exposing sensitive initialization and admin functions.

Default Visibility Exploit Generator

Overview

The default visibility exploit generator validates findings from the default_visibility, visibility_missing, and related detectors by classifying the type of exposed function from the finding description and producing a targeted proof of concept. This vulnerability is specific to Solidity contracts compiled with versions before 0.5.0, where omitting a visibility modifier caused functions to default to public.

Two canonical historical exploits demonstrate the impact. Rubixi (2016): A contract was renamed from DynamicPyramid to Rubixi but the constructor function name was not updated, causing the old DynamicPyramid() function to become a regular public function (Solidity 0.4.x used function-name constructors). Attackers called it to claim ownership. The Parity Wallet hack also involved an initialization function (initWallet) that was publicly accessible due to insufficient visibility controls.

Note: Exploit generation in Sigvex is for vulnerability validation purposes only.

Attack Scenario

Public initialization function:

  1. A contract has function initOwner(address _owner) without a visibility modifier.
  2. In Solidity 0.4.x, this defaults to public. Attackers can call it at any time.
  3. The attacker calls initOwner(attackerAddress) after deployment.
  4. Owner is now the attacker. All onlyOwner functions are accessible to the attacker.
  5. The attacker calls withdraw() to drain all contract ETH.

Public destructor:

  1. A contract has function kill() without visibility, defaulting to public.
  2. Any address can call kill(), triggering selfdestruct(owner).
  3. The contract is permanently destroyed and its bytecode erased.

Public admin functions:

  1. Internal helper functions (resetOwner(), updateThreshold()) lack visibility.
  2. Attackers call them directly to modify critical state variables.
  3. Combined with ownership reset and threshold manipulation, full fund extraction follows.

Exploit Mechanics

The generator classifies the vulnerability type from the finding description:

Description contains Vulnerability type Severity
“constructor”, “init”, “setup” public_initialization Critical: anyone can become owner
“owner”, “admin” public_admin_function High: unauthorized admin access
“destroy”, “kill” public_destructor Critical: anyone can destroy contract
(other) unspecified_visibility Medium: unintended exposure

Evidence collected includes: original owner address, attacker address, contract balance, and vulnerability type. Estimated gas: 40,000.

The PoC covers four vulnerability patterns using Solidity 0.4.24 syntax:

// VULNERABLE (Solidity 0.4.x): No visibility — defaults to PUBLIC
contract VulnerableInit {
    address owner;

    function initOwner(address _owner) { // Public by default!
        owner = _owner;
    }
}

// EXPLOIT
contract InitExploit {
    function attack(VulnerableInit target) public {
        target.initOwner(address(this)); // Become owner
        target.withdraw();               // Drain funds
    }
}

Remediation

The simplest fix is to upgrade to Solidity 0.5.0 or later, which requires explicit visibility on all functions at compile time:

// Solidity 0.5.0+: Compiler requires explicit visibility
// This fails to compile without a modifier:
// function initOwner(address _owner) { ... } // Error!

// Correct: Use internal for setup-only functions
function _initOwner(address _owner) internal { ... }

// Correct: Use private for helpers
function _computeReward() private returns (uint256) { ... }

// Correct: Use external for public API
function withdraw() external onlyOwner { ... }

For contracts that cannot be recompiled, deploy a new contract with corrected visibility. For upgradeable proxies, update the implementation to Solidity 0.5.0+.

Naming conventions help prevent accidental exposure: prefix _internal or __private functions accordingly, and use static analysis tools (a static-analysis tool, a linter) that flag missing visibility.

References