Learn how to prevent denial of service (DoS) attacks in smart contracts by ensuring queues are secure, handling low decimal tokens, and securely managing external calls.
Welcome back to the Solodit Checklist Resolution series. We will continue to explore Solodit smart contract audit checklists, focusing on practical strategies for writing secure and resilient smart contracts .
This checklist helps you identify potential vulnerabilities and implement proactive security measures. In the previous section " Solodit Checklist Resolution (1) ", we examined three checklists ( extraction pattern, minimum transaction amount, and blacklist ) related to a denial of service (DoS) attack and provided solutions for developers. If you haven't read it yet, I recommend checking out the article before continuing.
In this article, we will discuss three other items in the checklist, focusing again on DoS attacks. We will dive into the queue handling vulnerabilities , the challenges posed by low decimal tokens , and the importance of safely handling external contract calls .
For the best experience, open a Solodit checklist tag for reference.
Here is what we will discuss today:
SOL-AM-DOSA-4: Blocking Queue Handling : This problem occurs when an attacker manipulates the queue to pause or interrupt its operations, resulting in DoS. We will look at how attackers can destroy processing queues.
SOL-AM-DOSA-5: Low Decimal Tokens : Problems that occur when dealing with tokens with low decimal places. Calculation, especially division, can be truncated to zero, resulting in unexpected and harmful behavior. These low-precision tokens can cause problems with integer division, thus interfering with key functions.
SOL-AM-DOSA-6: Unsafe External Calls : We will explore how to rely on external contracts without proper error handling to create vulnerabilities. When the failure of an external call is not handled correctly, it may cause the entire contract to roll back .
Real-world attacks prove the potential damage these vulnerabilities can cause. The project suffered huge losses due to seemingly minor negligence.
Solodit checklists are based on audit findings, vulnerability bounty reports, and real-world events. By studying these checklists, you can learn from past mistakes and improve the security of your code .
Now, let's look at each check item one by one through the sample code. These examples have been simplified to highlight core vulnerabilities.
Problem : If your smart contract depends on a queue for task processing, an attacker may manipulate a specific state in the queue to prevent correct processing.
Checklist question : "Can an attacker block or prevent queue processing to cause service outage?"
Solution : Your queue handling mechanism requires powerful error handling and fallback mechanisms to ensure that problems can continue to be processed even if they occur.
Example :
Consider an example of extracting a queue:
The user requests the extraction, and some flags indicate that the request is active.
The attacker can use the resetUserStatus
function after the extraction is enqueued to prevent the same operation of other users.
// 易受攻击的函数:可以被利用function resetUserStatus() external { // 任何人在仍然处于队列中的情况下都可以重置他们的状态withdrawalRequested[msg.sender] = false; // 注意:用户没有被移出队列! } // 处理队列中的下一次提取function processNextWithdrawal() external { require(withdrawalQueue.length > currentIndex, "没有可处理的提取"); // 获取下一次提取Withdrawal memory withdrawal = withdrawalQueue[currentIndex]; // 可被攻击者利用:此检查可能会被重置状态所攻击require(withdrawalRequested[withdrawal.user], "提取请求不再有效"); // 处理提取uint256 amount = withdrawal.amount; require(balances[withdrawal.user] >= amount, "余额不足"); // 更新余额balances[withdrawal.user] -= amount; // 重置提取请求withdrawalRequested[withdrawal.user] = false; // 发送资金(bool success, ) = payable(withdrawal.user).call{value:amount}(""); require(success, "资金发送失败"); // 移动到队列中的下一个currentIndex++; }
To take advantage of this, an attacker can do the following:
resetUserStatus
function.processNextWithdrawal
function will roll back, causing a continuous DoS attack.Remedies :
withdrawalRequested
status to the administrator.Problem : Tokens with low decimal places can cause integer division problems, thus rounding to zero .
Imagine a token streaming contract allocating tokens over a period of time. If tokensPerSecond
rounds to zero due to integer division with low decimal tokens, the allocation function will be blocked. Checklist question : "Does low decimal tokens lead to DoS?" Solution : Implement logic to handle low decimal points to prevent interruption of trading processes due to rounding errors. Example :
Consider a TokenStream
contract that regularly streams a certain number of tokens to users, where:
total_tokens
need to be transferred to the contracttoken_per_second
may be rounded to zero because we are using tokens with a decimal point of 1distributeTokens
function will roll back contract TokenStream { IERC20 public token; uint256 public streamDuration; uint256 public tokensPerSecond; constructor(IERC20 _token, uint256 _streamDuration, uint256 _tokensPerSecond) { token = _token; streamDuration = _streamDuration; tokensPerSecond = _tokensPerSecond; } function distributeTokens(address recipient) external { uint256 balance = token.balanceOf(address(this)); uint256 amount = tokensPerSecond * streamDuration; uint256 tokensToSend = amount > balance ? balance : amount; require(tokensToSend > 0, "没有足够的代币进行流送"); token.transfer(recipient, tokensToSend); } } contract LowDecimalToken is ERC20 { constructor() ERC20("LowDecimalToken", "LDT") { _mint(msg.sender, 100000 * (10 ** decimals())); } function decimals() public view virtual override returns (uint8) { return 1; // 模拟小数点为低的代币} }
In this case, the testDOSWithLowDecimalTokens
test in TokenStreamTest
will be rolled back.
Remedy : Ensure that the contract handles low decimal tokens correctly, alleviating the problems caused by rounding integers during the calculation process by scaling mathematical formulas .
Problem : Many smart contracts rely on external contracts to interact. Unexpected behavior of external contracts can cause the entire system to roll back . Failure to handle these external errors can lead to DoS vulnerabilities.
Checklist question : "Does the protocol handle external contract interactions safely?"
Solution : Ensure strong error handling for external contract interactions to protect protocol integrity regardless of the performance of external contracts.
Example : Consider a contract that interacts with an external Chainlink price data source. If you do not use try/catch for proper error handling, any rollbacks from external data sources will propagate to the superior and cause the contract to rollback.
getPrice
function retrieves price data.calculateSomethingImportant
function depends on getPrice
and will also roll back. contract PriceDependentContract { AggregatorV3Interface public priceFeed; constructor(address _priceFeed) { priceFeed = AggregatorV3Interface(_priceFeed); } // 易受攻击的函数,在没有处理可能的Chainlink 回滚的情况下获取价格function getPrice() public view returns (uint256) { (, int256 price, , , ) = priceFeed.latestRoundData(); // 易受攻击的行:没有错误处理require(price > 0, "价格必须为正"); return uint256(price); } function calculateSomethingImportant() public view returns (uint256) { uint256 price = getPrice(); // ... 使用价格的一些重要计算return price * 2; }
Remedy : Wrap external contract calls in a try/catch block to handle rollback errors and implement rollback or cache values.
Note : There is an edge case where the external contract intentionally consumes gas , which can also fail the catch block! We'll discuss this later.
We explore three key checks designed to enhance your smart contract’s ability to resist DoS attacks: queue handling vulnerabilities , challenges associated with low decimal tokens , and the importance of handling external contract interactions securely .
Remember that the examples provided are intended to illustrate core vulnerabilities. It is very important to understand the basic principles and adapt these concepts to your specific use cases.
By implementing these suggestions, you can significantly improve the security and resilience of your smart contracts.
Stay tuned for the next part of the Solodit Checklist Resolution series.
- Original link: cyfrin.io/blog/solodit-c...
- Denglian Community AI Assistant translates excellent English articles for everyone. If there are any areas where the translation is not understandable, please forgive me~