57610 sc medium venues can steal from customers by replaying payments via belongcheckin paytovenue

Submitted on Oct 27th 2025 at 15:39:22 UTC by @blackgrease for Audit Comp | Belongarrow-up-right

  • Report ID: #57610

  • Report Type: Smart Contract

  • Report severity: Medium

  • Target: https://github.com/immunefi-team/audit-comp-belong/blob/main/contracts/v2/platform/BelongCheckIn.sol

  • Impacts:

    • Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield

Description

Affected Files: BelongCheckIn.sol

The BelongCheckIn contract coordinates venue deposits, customer check-ins and promoter settlements. The BelongCheckIn::payToVenue function processes a customer's payment to a venue.

The payment logic is gated: a Signer address must sign message input parameters before a payment proceeds — specifically the fields in the CustomerInfo struct: paymentInUSDC, visitBountyAmount, spendBountyPercentage, customer, venueToPayFor, promoter, amount, block.chainid.

However, once signed, payToVenue does not enforce logic to:

  • prevent repeating a signed payment to the same venue,

  • control who can call/execute payToVenue.

As a result, if a user has approved the BelongCheckIn contract for USDC or LONG, a venue can replay the signed message and drain more funds than the intended single payment.

Below are two exploitation scenarios (USDC used as the payment method).

Scenario 1:

1

A victim has a balance of 5000 USDC and has 3 venue payments to make.

2

Venue_A requires 500 USDC, Venue_B requires 2500 USDC, Venue_C requires 2000 USDC. All inputs are signed by the Signer.

3

The victim approves the BelongCheckIn contract for 5000 USDC.

4

The victim makes the initial payment to Venue_A.

5

Because payToVenue does not:

  • check the caller, and

  • enforce a nonce preventing replay, Venue_A can call/frontrun the victim's following payToVenue calls repeatedly until the user's balance is depleted.

6

Venue exits with the user's funds causing user loss.

Scenario 2:

1

A victim has 500 USDC and grants infinite approval to the BelongCheckIn contract for convenience.

2

Venue_A requires a payment of 500 USDC.

3

The victim approves the BelongCheckIn for the required USDC amount and completes the payment to Venue_A.

4

At that moment the victim's balance is 0, so immediate replays would fail.

5

The victim later receives 10,000 USDC to their address for other uses.

6

Because payToVenue does not:

  • check the caller, and

  • enforce a nonce preventing replay, Venue_A can call payToVenue again for the previously-signed payment (500 USDC) and drain the newly deposited funds.

7

Venue exits with the user's funds causing user loss.

The Problematic code

The vulnerable code path allows this behavior because there is no nonce, deadline, nor caller restriction on execution:

Impact

A user’s USDC/LONG funds can be stolen. Venues are not controlled by the protocol and must be treated as untrusted actors. Even if venues are initially vetted, they could turn malicious later and exploit this replay behavior.

Mitigation

Two possible mitigations are proposed:

circle-info
  1. Add a nonce (and optionally a deadline) to the signed CustomerInfo payload and store used nonces to prevent replays.

  2. Restrict execution of payToVenue so that only the customer (or an authorized executor) can call it. Note: restricting only to the customer may reduce flexibility (e.g., paying from another address), so the nonce + deadline approach is recommended.

https://gist.github.com/blackgrease/cb90d9d6706dfc83b27ed97507d87aaa

Proof of Concept

A runnable Foundry PoC is provided in the gist above. The PoC demonstrates Scenario 1.

Run with:

Test Console logs (from the PoC):

Explaining the test setup:

1

The PoC forks mainnet and uses on-chain Uniswap Router/Factory/Quoter addresses to create a USDC/LONG pool so venue deposits and payments function.

2

Deploys necessary contracts and creates the Uniswap Pool so the Venue Deposit and customer payment flows can be exercised.

3

Because the original LONG contract did not fit the flow in testing, a Mock LONG is used in the PoC (the issue demonstrated is unchanged).

Due to multiple interactions, a stack trace was not provided in the report; the PoC and test logs above show the exploit behavior.

Foundry Setup

1

Clone the repo:

2

Install Foundry dependencies:

3

Update remappings in foundry.toml (replace previous commented remappings with the following):

Was this helpful?