Lack of group size check in contracts that are supposed to be called in an atomic group transaction might allow attackers to misuse the application.
Algorand supports atomic transfers, an atomic transfer is a group of transactions that are submitted and processed as a single transaction. A group can contain upto 16 transactions and the group transaction fails if any of the included transactions fails. Algorand applications make use of group transactions to realize operations that may not be possible using a single transaction model. In such cases, it is necessary to check that the group transaction in itself is valid along with the individual transactions. One of the checks whose absence could be misused is group size check.
Application only checks that transactions at particular indices are meeting the criteria and performs the operations based on that. Attackers can create the transactions at the checked indices correctly and include equivalent application call transactions at all the remaining indices. Each application call executes successfully as every execution checks the same set of transactions. This results in performing operations multiple times, once for each application call. This could be damaging if those operations include funds or assets transfers among others.
Note: This code contains several other vulnerabilities, see Rekeying, Unchecked Transaction Fees, Closing Account, Time-based Replay Attack.
def split_and_withdraw(
amount_1,
receiver_1,
amount_2,
receiver_2,
lock_expire_round,
):
return And(
Gtxn[0].type_enum() == TxnType.Payment,
Gtxn[0].receiver() == receiver_1,
Gtxn[0].amount() == amount_1,
Gtxn[1].type_enum() == TxnType.Payment,
Gtxn[1].receiver() == receiver_2,
Gtxn[1].amount() == amount_2,
Gtxn[0].first_valid == lock_expire_round,
)
-
Verify that the group size of an atomic transfer is the intended size in the contracts.
-
Use Tealer to detect this issue.