ClimberVault: holds the asset and uses UUPS pattern
UUPS, etc. : UUPS pattern
In this level, our goal is to get the entire money in the ClimberVault
2.analyses
We can get the entire money by sweepFunds() while it can be called only by ClimberTimelock and transfers to ClimberTimelock . But we can upgrade the contract because of UUPS pattern.
ClimberTimelock contract is important since it holds a lot of authority. In this level, there is some problem with execute() which we can exploit. In normal situation, the execution is that: call schedule() first, and then call execute(). schedule() can only be called by PROPOSER_ROLE but everyone can call execute().
So we, a normal member, not PROPOSER_ROLE, can exploit execute(). In this function, it executes functionCallWithValue() first which contains call() in address.sol library and then check if operations[id].executed = true; that only can be set in schedule(). It means everyone can call execute with the proposals containing making our proposals true.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
function execute( address[] calldata targets, uint256[] calldata values, bytes[] calldata dataElements, bytes32 salt ) external payable { require(targets.length > 0, "Must provide at least one target"); require(targets.length == values.length); require(targets.length == dataElements.length);
bytes32 id = getOperationId(targets, values, dataElements, salt);
for (uint8 i = 0; i < targets.length; i++) { targets[i].functionCallWithValue(dataElements[i], values[i]); } require(getOperationState(id) == OperationState.ReadyForExecution); operations[id].executed = true; }
So our idea is that:
make delay to zero so that we can attack right now.
grant PROPOSER_ROLE to attack contract
upgrade climbervalut which contains a new sweepFunds(). and this new sweepFunds() can be call by the attack contract and send money to the attack contract.