Denial-of-Service(DoS) Attacks
DOS class of bugs are underrated, yet they are highly damaging and capable of halting an entire system.
Denial-of-Service (DoS) attacks in Solidity smart contracts refer to malicious activities aimed at disrupting the normal functioning of a contract, rendering it unresponsive or unusable. These attacks exploit vulnerabilities in the contract's design or implementation to exhaust resources, consume excessive gas, or cause infinite loops, ultimately leading to a denial of service for legitimate users.
Most of the time, DoS-type attacks don't directly cause the theft of user funds. However, attackers still engage in these attacks to block operations for their own profit and gain a competitive advantage, such as permanently freezing funds.
Seven common bugs for Understanding DoS attacks
Unbounded gas consumption by returning a lot of data
Unbounded loop
DoS due to Underflow
Assumption: Decimals, symbol, and name are mandatory for ERC20 tokens.
Assumption: Every address can accept ether
Strict equalities
DoS due to blacklisting
Unbounded gas consumption from returning a lot of data
Most of the time developers don’t consider that low-level calls can also cause DoS if the target address returns a lot of data.
It is from the previous version of Openzeppelin-contracts (It was corrected in v4.7.2), you can find the contract here.
On line number 3 we are using staticcall
to check whether the interface is supported or not, the problem is that it can return a lot of data which will take a lot of gas because it will be copied into memory, and in this way, it can cause DoS due to exceeding the gas limit.
MITIGATION: It can be mitigated by utilizing inline assembly to prevent the automatic copying of data in memory. You can refer to the correct version of this function here.
Unbounded loop
The unbounded loop can easily become a problem because it can cause DoS if the size increases too much because of the gas limit.
It is from Hubble exchange contest on code4rena, you can find the code here.
Attackers exploit the withdraw
function by executing it with a zero amount, enabling them to add to the array. However, when the processWithdrawals
function is executed, the length of the withdrawals array grows excessively. Consequently, this transaction fails as it exceeds the gas limit.
MITIGATION: It can be easily mitigated by disallowing zero amount
.
DoS due to Underflow
Subtraction can cause a Revert if the user tries to subtract a value greater than the value needed. This can seem obvious but in certain conditions, attackers can create these conditions artificially that will cause DoS.
It is from the Timeswap exchange contest on code4rena, you can find the code here.
The user can repay their debt using the pay
function. However, the problem arises on line number 21 because an attacker can front-run to repay a single token for the user's debt. As a result, when the user tries to pay off all their debt, their transaction fails. This failure occurs because '(due.debt - 1)' becomes less than 'assetsIn[i]', leading to revert and a potential DoS situation.
MITIGATION: It can be mitigated by setting 'assetsIn[i]' to 'due.debt' when 'assetsIn[i]' is higher than 'due.debt'.
Assumption: Decimals, symbol, and name are mandatory for ERC20 tokens
According to the ERC20 standard, the functions name
, symbol
, and decimals
are optional, and no one should expect these functions to be present in a contract. However, many developers mistakenly think they are mandatory.
It's from my audits repo. You can find it here.
The createCanonicalERC20Wrapper
function of the SuperTokenFactory
contract calls name
, symbol
, and decimals
methods to obtain the name, symbol, and decimals of the ERC20 token. If we pass a correct ERC20 token that does not implement these functions, the call will fail, and the user will be unable to create a canonical ERC20 wrapper for that token. This limitation can potentially lead to a denial-of-service (DoS) scenario.
For example, we have a DAI ERC20 token that is widely used, and it returns a name and symbol as bytes32. Due to this, no one can create a canonical ERC20 wrapper of DAI because our function expects a name and symbol in a string.
MITIGATION: It can be mitigated by utilizing this.
Assumption: Every address can accept ether
Sometimes developers assume that every address can accept ether, but an attacker can easily take advantage of this assumption by breaking it.
It's from DefiVuln repositry. You can find the code here.
On line number 4, the low-level call is used to transfer the balance amount. However, if King is a contract and lacks a payable fallback or receive function, it will revert, causing a DoS situation. Consequently, calling claimThrone
will become inaccessible for anyone.
MITIGATION: The Pull-over-Push pattern can be used to mitigate this risk. You can learn more about it here.
Strict equalities
Using strict equalities with tokens or Ether in smart contracts can potentially lead to a DoS attack.
It's from the first challenge of DAMN VULNERABLE DEFI. You can find the code here.
On line number 8, the flashLoan function uses an assertion with a strict equality condition, stating that the poolBalance should be equal to the balanceBefore. However, an attacker can easily stop or perform a DoS attack by simply sending only 1 wei of damnValuableToken to the contract.
MITIGATION: Consider using '>=' or '<=' instead. The above example can be fixed by using 'poolBalance <= balanceBefore' in the assertion.
DoS due to blacklisting
Many developers are not aware that addresses can be blacklisted by Circle. As blacklisted addresses can no longer receive USDC, and all USDC controlled by those addresses is blocked, preventing on-chain transfers. Due to this, any interaction with these addresses can cause a DoS. You can view the blacklisted addresses here.
MITIGATION: It can be mitigated by skipping any interaction with the address if it is blacklisted.
I hope you enjoyed it, Thanks for the read!