Impacts: Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)
Description
Brief/Intro
The venueDeposit(..) and payToVenue(...) functions of BelongCheckIn.sol use USDC.safeTransferFrom(from, address(this), amount) to pull USDC from users.
The from address is user-supplied as a parameter to venueDeposit(..) and payToVenue(...).
This allows anyone to call venueDeposit(..) and payToVenue(...) with the same message/signature values previously used by a user and drain the token allowance that user has granted to BelongCheckIn.sol.
Vulnerability Details
There are two causes of this issue:
The venueDeposit(..) and payToVenue(...) functions use a user-supplied address as the from parameter in safeTransferFrom(...).
The signed messages used with venueDeposit(..) and payToVenue(...) do not include a nonce to prevent replay attacks and do not include the verifying contract in the signed message to prevent cross-contract signature replay attacks.
As a result, users who have granted allowance to the contract are vulnerable: previously used message+signature pairs can be replayed multiple times to drain a user's USDC allowance.
Example relevant code snippet (from BelongCheckIn.sol):
Impact Details
For users that have a non-zero allowance granted to BelongCheckIn.sol, and who have previously called venueDeposit(..) (or payToVenue(..)) with signed data, an attacker can reuse the same values and signature multiple times to drain USDC from the user's allowance up to the existing allowance amount.
Recommendation
Use msg.sender as the from address in calls to safeTransferFrom(...) instead of accepting an externally supplied from address.
Ensure signatures follow an EIP-712-style scheme including at least: nonce, deadline, and verifying contract (domain separator). With a msg.sender-specific increasing nonce, signatures can be executed only once.
Proof of Concept
1
PoC: Steps to reproduce (high-level)
Add the provided test to the test suite.
Run the test suite (yarn test).
The test demonstrates: a victim approves max USDC allowance and calls venueDeposit(..) / payToVenue(..). An attacker then calls the same function with the same parameters and signature and can pull USDC from the victim up to their allowance.
2
PoC: Test code to reproduce (insert into test suite)
Copy and paste the following test into belong-check-in-bsc-fork.test.ts in the describe('Customer flow usdc payment', () => { suite, then run yarn test.