Reproduced Exploit
Buffer `closeAnytime` never validates the closing timestamp against the current time
Chain
Other
Category
untagged
Date
Jul 2023
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: 55636-h-04-closeanytime-timestamp-is-never-validated-against-curre. Standalone Foundry PoC and full write-up: 55636-h-04-closeanytime-timestamp-is-never-validated-against-curre_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/wrong-condition · vuln/timestamp-dependence · impact/loss-of-funds/direct-drain · novelty/variant
Reproduction: the test deploys the REAL audited Buffer v2.5
BufferRouter+BufferBinaryOptions+BufferBinaryPooland drives the realcloseAnytimepath with private keeper mode disabled. A currently out-of-the-money option is settled using a STALE, favorable price signature from the past — collecting the full locked amount from LPs.
Root cause#
In BufferRouter.closeAnytime the closing time handed to the
options contract is taken straight from the publisher (pricing) signature and is never
checked against block.timestamp:
optionsContract.unlock(
params.optionId,
params.closingPrice,
publisherSignInfo.timestamp, // @> used as the closing time — never validated vs now
params.isAbove
)
The publisher (price oracle) continuously signs (assetPair, timestamp, price) tuples. Because
closeAnytime never requires publisherSignInfo.timestamp to be anywhere near the current
time, a trader (or, per the finding, a malicious/compromised keeper) can present ANY historical
price signature the oracle ever produced. They simply pick the one most favorable to them.
Exploit walkthrough (numbers from the test)#
- An LP seeds the pool with 1,000 USDC. A trader opens an ETH/USD call, strike
1000, locking 100 USDC. Time now is far past expiry (block.timestamp = 50000,expiration = 4600). - Honest close (control): at the current fair price of
900(below strike) the option is worthless — it expires and pays 0. - Exploit: the attacker submits a stale publisher signature for price
1500(deep ITM) stamped att = 4700.closeAnytimeaccepts it despite4700 << 50000, and becauseclosingTime (4700) >= expiration (4600)the option exercises for the full 100 USDC. - The pool pays out the full 100 USDC of LP liquidity on an option that is worthless at the real current price.
The negative control in the test (an honest close at the current price paying 0) proves the payout comes purely from the unvalidated stale timestamp.
Fix#
Validate that the exercise/closing timestamp is within a small margin of block.timestamp.
Buffer's remediation (PR #4)
removes the ability to disable private keeper mode — but, as the auditor notes, a malicious or
compromised keeper can still exploit this.
Reproduction#
- Registry PoC:
_shared/run-poc/run_poc.sh 55636-h-04-closeanytime-timestamp-is-never-validated-against-curre_exp -vvvvv - Real audited source under
src/at commit84b6060b4447b2550de595202e8820c7f515988b.
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 55636-h-04-closeanytime-timestamp-is-never-validated-against-curre_exp (evm-hack-registry mirror).
- AuditVault finding: 55636-h-04-closeanytime-timestamp-is-never-validated-against-curre.
- Upstream DeFiHackLabs PoC directory: src/test.
Alerts & third-party analyses
- DeFiHackLabs incident explorer: search "Buffer
closeAnytimenever validates the closing timestamp against the current time". - Web3Sec X hacked database: search.
- Rekt leaderboard: search.
- Solodit incident search: search.
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.