In a reentrancy (RE) attack, a malicious contract A interacts with a victim bank contact B to deplete the money B stores on behalf of A and other users. In this task, you will be given a vulnerable bank contract B and be required to implement a reentrancy attack contract A (Exercise 2). You will also rewrite the bank contract to B’ such that B’ is secured against reentrancy attack A (Exercise 3). Fallback is a unique function in Solidity and is a key primitive to enable reentrancy attack. You will run a given fallback contract to understand this primitive (Exercise 1).
Tasks | Points | CS student | Finance student |
---|---|---|---|
1 | 10 | Required | Required |
2 | 40 | Required | Bonus |
3 | 30 | Required | Bonus |
4 | 20 | Bonus | Bonus |
5 | 10 | Bonus | Bonus |
pragma solidity ^0.8.15;
contract FallbackReceiver {
event Log(string func, uint gas);
fallback() external payable {
emit Log("fallback", gasleft());}
function getBalance() public view returns (uint) {
return address(this).balance;}}
contract FallbackSender {
function call(address payable _to) public payable {
(bool sent, ) = _to.call{value: msg.value}("");
require(sent, "Failed to send Ether");}}
- Deploy and run the above two smart contracts in Remix. Suppose
FallbackReceiver
is deployed at addressR
. Then runFallbackSender.call(R)
and report the execution screenshot.
pragma solidity ^0.8.15;
contract BankRE {
mapping(address => uint256) public balances;
function deposit() public payable {
balances[msg.sender] += msg.value;}
function withdraw() public {
require(balances[msg.sender] != 0);
(bool result, ) = msg.sender.call{value: balances[msg.sender]}("");
balances[msg.sender] = 0; }}
- Implement an attack smart contract,
AttackerRE
and deploy it with theBankRE
contract to mount a successful reentrancy attack to deplete the money in theBankRE
contract. The attack is successful if attackerAttackerRE
can deplete anyBankRE
's Ether, as in the following test case:- Use an EOA account
C
to deposit Ether toBankRE
, that is,[C, BankRE].deposit()
- Deploy Contract
AttackerRE
to addressA
and initialize it withBankRE
's address. - Mount the reentrancy attack by calling the
attack()
function inAttackerRE
, that is,[A, AttackerRE].attack()
- Use an EOA account
- Design one or multiple defenses against reentrancy attacks in
BankRE
contract in Exercise 2. - Implement your new defenses in a new bank contract, say
BankSafe
, so that running the reentrancy attack (as implemented by Exercise 2 in ContractAttackerRE
) againstBankSafe
would fail.
Locking the access to Ether-transfer instructions that may cause reentrance is a widely adopted defense strategy against reentrance attacks. The following smart contract implementing an Ether bank on a Pegged Token does use a lock to prevent the reentrance of the burn()
function. However, the seemingly fixed contract is still vulnerable to the reentrancy attack in a general sense. Design attack contracts to attack the EtherBankPeggedToken
smart contract so that 1) an attacker EOA can withdraw more than his account balance, and 2) an attacker EOA can deplete the Ether balance of the EtherBankPeggedToken
contract, that is, the balance of all accounts in the contract.
- The re-entered function does not have to be the same as the function it first entered.
contract EtherBankPeggedToken {
mapping(address => uint256) balance;
mapping(address => bool) lock;
mapping(address => mapping(address => uint256)) allowance;
modifier checkLock { // reentrancy locking
require(lock[msg.sender] == false); _; }
function deposit() external payable {
balance[msg.sender] += msg.value;
}
function approve(address other, uint256 amnt) external {
allowance[msg.sender][other] += amnt; }
function transferFrom(address from, uint256 amnt) external checkLock {
require(balance[from] >= amnt);
require(allowance[from][msg.sender] >= amnt);
balance[from] -= amnt;
allowance[from][msg.sender] -= amnt;
balance[msg.sender] += amnt; }
function burn() external checkLock {
// set lock
lock[msg.sender] = true;
msg.sender.call{value: balance[msg.sender]}("");
// release lock
lock[msg.sender] = false;
balance[msg.sender] = 0; }
}
- Design one or multiple defenses against reentrancy attacks to fix the bugs in the
EtherBankPeggedToken
contract in Exercise 4.
- Submit your Solidity code and the screenshot that runs the code on your computer for all exercises. The Solidity code needs to be stored in a
.sol
file in the plain-text format.