Reproduced Exploit

Shido WSHIDO — transferFrom moves tokens when allowance is zero

WSHIDO is a WETH fork. transferFrom deducts allowance only when src != msg.sender and the allowance is not zero. After that if, it always debits src and credits dst.

May 2024•Other•logic•3 min read

Chain

Other

Category

logic

Date

May 2024

Source

AuditVault

EVM Playground

Source-level debugger — step opcodes and Solidity in sync

evm-hack-analyzer

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.

Loading fork state…

Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 35628-insufficient-src-validation-allows-malicious-users-to-steal. Standalone Foundry PoC and full write-up: 35628-insufficient-src-validation-allows-malicious-users-to-steal_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/logic/missing-check · vuln/access-control/broken-logic

Reproduction: a self-contained Foundry PoC that compiles and runs offline with only forge-std — no fork and no RPC. Trace: output.txt. PoC: test/35628-insufficient-src-validation-allows-malicious-users-to-steal_exp.sol.


Key info#

ImpactHIGH — 1,000 WSHIDO moves from a victim whose allowance is 0
ProtocolShido WSHIDO.transferFrom
Vulnerable codeThe allowance deduction is skipped when the allowance is zero, and the balance update is unconditional
Bug classMissing allowance check on transferFrom
FindingZokyo — Shido, May 2024 · id 35628
ReportZokyo Shido report
SourceAuditVault finding
StatusResolved in the audit. Reproduced here as a local synthetic of the described check.
Compilersolc 0.8.27, cancun, via IR

TL;DR#

WSHIDO is a WETH fork. transferFrom deducts allowance only when src != msg.sender and the allowance is not zero. After that if, it always debits src and credits dst.

A victim with 1,000 WSHIDO and allowance 0 loses the full balance to transferFrom. The fixed function reverts on that call and the victim keeps 1,000.

run() does not warp.

The vulnerable code#

SOLIDITY
if (src != msg.sender && allowance[src][msg.sender] != 0) {
    allowance[src][msg.sender] -= wad;
}
// @> VULN: zero allowance skips the branch and src is still debited
balanceOf[src] -= wad;
balanceOf[dst] += wad;

WETH9 requires allowance >= wad whenever the allowance is not uint.max. This fork replaced that require with a non-zero test, so the zero allowance is the path that skips the check.

Root cause#

The allowance update is not a guard. It is an optional side effect. The balance move is outside the condition, and both src and dst are arguments.

Preconditions#

  • The victim's WSHIDO balance is 1,000 (18 decimals).
  • allowance[victim][attacker] is 0.
  • No timestamp change happens inside run().

Attack walkthrough#

  1. Read the victim balance: 1,000. Read the allowance: 0.
  2. transferFrom(victim, attacker, 1000 ether).
  3. The allowance branch is skipped. balanceOf[victim] becomes 0. balanceOf[attacker] becomes 1,000.

Control: the same call on the fixed token reverts with allowance. The victim balance stays 1,000 and the attacker balance stays 0.

Diagrams#

flowchart TD A["Victim holds 1000 WSHIDO"] --> B["Allowance to the attacker is 0"] B --> C["transferFrom skips the allowance branch"] C --> D["Victim balance is debited by 1000"] D --> E["Attacker balance is 1000"] E --> F["Fixed transferFrom reverts and the victim keeps 1000"]

Impact#

Any holder who has not approved the attacker can be debited for their full balance. Here that is 1,000 WSHIDO.

Remediation#

Require allowance[src][msg.sender] >= wad when src != msg.sender, and deduct inside that branch, matching WETH9. Do not move balances when the require fails.

Sources#

Taxonomy#

  • severity/high
  • vuln/logic/missing-check
  • vuln/access-control/broken-logic

Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

  • 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.