Lucene search

K
code423n4Code4renaCODE423N4:2021-11-STREAMING-FINDINGS-ISSUES-162
HistoryDec 06, 2021 - 12:00 a.m.

Any arbitraryCall gathered airdrop can be stolen with recoverTokens

2021-12-0600:00:00
Code4rena
github.com
4

Handle

hyh

Vulnerability details

Impact

Any airdrop gathered with arbitraryCall will be immediately lost as an attacker can track arbitraryCall transactions and back run them with calls to recoverTokens, which doesn’t track any tokens besides reward, deposit and incentive tokens, and will give the airdrop away.

Proof of Concept

arbitraryCall requires that tokens to be gathered shouldn’t be reward, deposit or incentive tokens:
<https://github.com/code-423n4/2021-11-streaming/blob/main/Streaming/src/Locke.sol#L735&gt;

Also, the function doesn’t mark gathered tokens in any way. Thus, the airdrop is freely accessible for anyone to be withdrawn with recoverTokens:
<https://github.com/code-423n4/2021-11-streaming/blob/main/Streaming/src/Locke.sol#L687&gt;

Recommended Mitigation Steps

Add airdrop tokens balance mapping, record what is gathered in arbitraryCall and prohibit their free withdrawal in recoverTokens similarly to incentives[].

Now:

mapping (address =&gt; uint112) public incentives;
...
function recoverTokens(address token, address recipient) public lock {
...
		if (incentives[token] &gt; 0) {
			...
			uint256 excess = ERC20(token).balanceOf(address(this)) - incentives[token];
			...
		}

To be:

mapping (address =&gt; uint112) public incentives;
mapping (address =&gt; uint112) public airdrops;
...
function recoverTokens(address token, address recipient) public lock {
...
		if (incentives[token] &gt; 0) {
			...
			uint256 excess = ERC20(token).balanceOf(address(this)) - incentives[token];
			...
		}
		if (airdrops[token] &gt; 0) {
			...
			uint256 excess = ERC20(token).balanceOf(address(this)) - airdrops[token];
			...
		}
...
// we do know what airdrop token will be gathered
function arbitraryCall(address who, bytes memory data, address token) public lock externallyGoverned {
		...

		// get token balances
		uint256 preDepositTokenBalance = ERC20(depositToken).balanceOf(address(this));
		uint256 preRewardTokenBalance = ERC20(rewardToken).balanceOf(address(this));
		uint256 preAirdropBalance = ERC20(token).balanceOf(address(this));

		(bool success, bytes memory _ret) = who.call(data);
		require(success);
		
		uint256 postAirdropBalance = ERC20(token).balanceOf(address(this));
		require(postAirdropBalance &lt;= type(uint112).max, "air_112");
		uint112 amt = uint112(postAirdropBalance - preAirdropBalance);
		require(amt &gt; 0, "air");
		airdrops[token] += amt;

The text was updated successfully, but these errors were encountered:

All reactions