Research
Oct 26, 2022
Superwalk xGrndStaking Loop Practices

Introduction

Superwalk is a blockchain-based Move-To-Earn service that rewards users for walking. We audited its staking feature and found a DoS bug in v.1.0. An arbitrary user can block withdrawals by generating zero amounts. The bug is resolved in v.1.1 by limiting the for-loop to 100 iterations.

A Consideration for Linear Search

Superwalk is blockchain based Move-To-Earn service provides rewards its users on their walking. We engaged in audit on the Superwalk’s new staking feature. The target contract is reward-bearing vault which implements ERC-4626 specification and we found interesting Denial of Service bug that blocks other user’s withdrawal request by generating zero amount of withdraw.

About the bug

Collecting the asset can be blocked by an arbitrary user by calling a number of zero amount withdrawals (Found — v.1.0) (Resolved — v.1.1)

Users who deposit assets can get rewards, Grnd token, every 8 hours. First, a user who wants to withdraw their assets calls the withdraw function(). The function allows the user to set a receiver who takes assets. If user1 calls the withdraw function with the user2’s address, receiver, the receiver’s _withdrawalMap which is a mapping variable that queues the receiver’s collectible amount will grow with withdrawal time and assets.

After the withdrawal time, the user2 can execute the collectGrnd function to withdraw assets which are enqueued by the withdraw function. Then, the for loop in the _collectGrnd function searches and dequeues withdraw-able assets from the front of the queue to the back of the queue.

However, this point can be exploited by DoS. The below code snippet is the PoC that executes withdraw function with 0 amount of asset for 1000 times. That means the length of the queue of user2 grows to 1000. After the 1000 times withdraw, collectGrnd() was executed. As we said before, the number of loop in the collectGrnd() function is 1000. Thus, it cause out of gas error which means, user2 can not withdraw its assets.

Solution

There are two points to fix. The first is prohibiting 0 asset withdrawals. The updated code, _withdraw(), is added 'require' statement which checks the amounts of assets. Thus, an arbitrary user can not fill a target’s queue at no cost.

The other is limiting the loop to a certain number of times. The updated point in the _collectGrnd() is running up to 100 times dequeue per call. If an arbitrary user calls the withdraw function 110 times with very few assets such as 0.001, the receiver will call the collectGrnd function twice. In the first collectGrnd call, it dequeues the first withdrawal request to the 100th withdrawal request. And it dequeues the 101st withdrawal request to the 110th withdrawal request in the second collecGrnd call.

Conclusion

In this article, we described the bug that can slip up but causes not negligible impact on users. The loop which searches items linearly in queue or others can be exploited as DoS. Thus, limiting the number of a loop in the above context is desired in queue implementation.

It is not always the best solution for limiting loop, however, when you have to use a loop on a queue considering the DoS exploitability is worth taking time.

Louis

Louis

Security Researcher

About the author.

Louis is in charge of security research at KALOS of HAECHI LABS. He focuses on the security of several DApps in the Ethereum Virtual Machine (EVM)