Smart Contract Security: An example of Reentrancy Attack
date
Feb 15, 2023
slug
smart-contract-security-an-example-of-reentrancy-attack
status
Published
tags
solidity
smart-contract
blockchain-security
summary
Performing a simulation to exploit a reentrancy vulnerability found within a Bank's smart contract to withdraw the entire available balance of the bank. This simulation is purely for educational or research purposes and does not serve as a recommendation or encouragement to exploit such vulnerabilities in real-world scenarios
type
Post
The Bank’s smart contract incorporates a TokenAsset that represents its digitized currency
TokenAsset is simply a ERC20 token smart contract
Attack vector
Exploit the Reentrancy Vulnerability in withdraw function of the UnsecuredBank smart contract. This function makes an external function call to the transfer function of TokenAsset and only modifies its local storage variable after the call.
In the UnsecuredBank contract, the tokenFallback(address, uint256, bytes) function allows customers of UnsecuredBank to deposit their TokenAsset tokens by making a direct transfer on TokenAsset's smart contract, with the recipient being the address of UnsecuredBank.
In the TokenAsset contract, the transfer(address,uint256, bytes) only calls the tokenFallback function if the "to" value is a smart contract address. An attacker could build an untrusted contract that implements the tokenFallback method to do something harmful. The withdrawal (uint256) in the UnsecuredBank contract calls transfer(address,uint256, bytes) of TokenAsset to proceed with the withdrawal request from the customer of UnsecuredBank, so if the requestor is a smart contract, the tokenFallback will get called!
Create AttackContract
Create AttackContract that:
- Inherits ITokenInstance and implement tokenFallback(address, uint256, bytes) function
- Contains a withdraw() function that invokes UnsecuredBank’s withdraw(uint256) function
- Contains depositToBank() function to deposit all of its funds to UnsecuredBank, pretend to be Bank’s customer
- Contains triggerAttack() to make a withdrawal request that sent from AttackContract to Bank
- Inside tokenFallback(address, uint256, bytes), AttackContract makes another withdrawal request to Bank, with same amount (smaller than its Bank balance and not greater than Bank’s Vault balance)
Start the attack
- The attacker withdraws all of his funds from Bank by calling UnsecuredBank’s withdraw(…) function
- The attacker transfers all of his TokenAsset to AttackContract by calling the transfer function of TokenAsset as a common user
By using AttackContract:
- Attacker calls depositToBank() to deposit all of AttackContract’s TokenAsset to Bank, then AttackContract becomes a Bank’s customer
- The attacker calls triggerAttack() to start the heist
- triggerAttack() calls UnsecuredBank’s withdraw(uint256). Look at the withdraw(uint256) function, the codes are executed on the external call from UnsecuredBank to TokenAsset, while the msg.sender’s balance has not been reduced, the tokenFallback(address, uint256, bytes) in AttackContract makes another withdrawal request, and cause a loop of withdrawal. The AttackContract can transfer TokenAsset from Bank to its wallet even though it already received the funds of origin withdrawal
- After AttackContract receives all funds from Bank, Attacker withdraws funds to his wallet
You can download full source code from here