Description
Because withdraw reverted, it seems like the entire transaction should have been reverted, but recall that call does not propagate exceptions. It sends a message to another contract, and if that internal transaction
reverts, it just returns 0 to the caller.
So here's what happens:
Exploit calls withdraw.
withdraw calls Exploit's payable fallback function. [call #1]
Exploits payable fallback function calls withdraw again.
withdraw calls Exploits payable fallback function again. [call #2]
That call simply succeeds, because Exploits payable fallback function sees that it has extracted the full 2 ether and just returns.
withdraw checks the result of call #2, sees that it was successful, and then reverts.
call #1 fails, because a revert happened. This passes control back to the caller with a 0 return value indicating failure.
withdraw checks the result of call #1, sees that it failed, and does not do the revert.
The transaction completes successfully.
Vector: BVSS:1.1/B:S/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:H/CI:N/II:H/AI:L
Original source
https://etherscan.io/address/0x95d34980095380851902ccd9a1fb4c813c2cb639#code