Reproduced Exploit
FairSide — anyone can inflate FSDVesting and claim the balance
updateVestedTokens(address _user, uint256 _amount) checks _user == beneficiary. _user is chosen by the caller, and the caller is not required to be the FSD token. A stranger passes the beneficiary and 1900.
Chain
Ethereum
Category
access-control
Date
Nov 2021
Source
AuditVault
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.
Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 42324-h-01-anyone-can-arbitrarily-call-fsdvestingupdatevestedtoken. Standalone Foundry PoC and full write-up: 42324-h-01-anyone-can-arbitrarily-call-fsdvestingupdatevestedtoken_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/access-control/missing-auth · vuln/logic/reward-calculation
Key info#
| Protocol | FairSide |
| Finding | Code4rena, November 2021, AuditVault 42324 |
| Vulnerable code | FSDVesting.updateVestedTokens |
| Harm | The cliff pays 100 instead of 5. The vesting contract is emptied. |
| Chain | local synthetic (audited Ethereum contracts) |
TL;DR#
updateVestedTokens(address _user, uint256 _amount) checks _user == beneficiary. _user is chosen by the caller, and the caller is not required to be the FSD token. A stranger passes the beneficiary and 1900.
The schedule unlocks 5 percent at the cliff and the rest linearly over 18 months. On an amount of 100 the cliff pays 5. After the amount is 2000, 5 percent is 100, which is every token the contract holds. The claim transfers 100. The fair claim left 95 locked.
The vulnerable code#
require(_user == beneficiary, "FSDVesting::updateVestedTokens: Only beneficiary can update the vested amount");
require(_amount != 0, "FSDVesting::updateVestedTokens: Amount cannot be zero");
// @> VULN: no onlyFsd, and _user is an argument, so any caller can inflate amount.
amount = amount + _amount;
The fix is the onlyFsd gate. A call from anyone else reverts and does not change amount.
Root cause#
The beneficiary check does not bind msg.sender. Each user has their own vesting contract, and the intended writer is FSD. Without that restriction the stored amount can be raised above the tokens actually deposited, and the cliff percentage then covers the whole balance.
Preconditions#
The contract was created with amount 100 and holds 100 tokens. The cliff has passed and the 18 month linear period has not meaningfully started, so the linear term is 0. The caller is not FSD. One transaction.
Attack walkthrough#
calculateVestingClaimis 5.- The transaction sender is not the beneficiary.
- A stranger calls
updateVestedTokens(beneficiary, 1900).amountbecomes 2000. claimVestedTokenspays 100, the token balance.- The beneficiary holds 100. The vesting contract holds 0.
updateGuardedfrom that same non-FSD caller reverts.
Control, with no update, claims 5 and leaves 95 in the contract.
Diagrams#
Impact#
95 tokens that should still be vesting are withdrawn at the cliff. The contract that was supposed to hold them is empty.
Remediation#
Restrict updateVestedTokens to the FSD token, and stop taking the beneficiary as a caller-supplied argument.
How to reproduce#
cd 42324-h-01-anyone-can-arbitrarily-call-fsdvestingupdatevestedtoken_exp
forge test -vv
Sources#
- AuditVault finding: https://github.com/Auditware/AuditVault/blob/main/findings/42324-h-01-anyone-can-arbitrarily-call-fsdvestingupdatevestedtoken.md
- Code4rena report: https://code4rena.com/reports/2021-11-fairside
FSDVesting.updateVestedTokensas quoted fromcode-423n4/2021-11-fairsidecontracts/token/FSDVesting.sol
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 42324-h-01-anyone-can-arbitrarily-call-fsdvestingupdatevestedtoken_exp (evm-hack-registry mirror).
- AuditVault finding: 42324-h-01-anyone-can-arbitrarily-call-fsdvestingupdatevestedtoken.
Alerts & third-party analyses
These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.