Fallback (Level-1) CTF

Fallback smart contract CTF (Level-1)

Introduction

In ”Level-0” challenge, We discuss about: “How to interact with a smart contract with browser’s console & ABI(Application Binary Interface)” So, if you didn’t read it read it from Here to know how to interact with the “Smart Contract”. Now, In this challenge we are going to learn how to exploit fallback function & how to send “eth” to a smart contract. We will discuss in this blog the following two parts:

  • Explaining Smart Contract Source Code
  • Exploiting the Smart Contract

Smart Contract Source Code

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import '@openzeppelin/contracts/math/SafeMath.sol';

contract Fallback {

using SafeMath for uint256;
mapping(address => uint) public contributions;
address payable public owner;

constructor() public {
owner = msg.sender;
contributions[msg.sender] = 1000 * (1 ether);
}

modifier onlyOwner {
require(
msg.sender == owner,
"caller is not the owner"
);
_;
}

function contribute() public payable {
require(msg.value < 0.001 ether);
contributions[msg.sender] += msg.value;
if(contributions[msg.sender] > contributions[owner]) {
owner = msg.sender;
}
}

function getContribution() public view returns (uint) {
return contributions[msg.sender];
}

function withdraw() public onlyOwner {
owner.transfer(address(this).balance);
}

receive() external payable {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}
}
  • First Line Is defining the solidity version, and “pragma” it’s unlocked because it’s using ^ which is a bad practices.
  • After that it’s importing “SafeMath.sol” and this can protect the arithmetics operations from some vulnerabilities like “OVerflow ”and “Underflow”.
  • Starting the contract with contract Fallback
  • Now, We are using the “SafeMath” function and basically, It’s saying attach the function (SafeMath) to the (uint256) type.
  • Then we are using mapping structure to map the addresses inside the contributions.
  • In the end, It says we have a variable called “owner” and type, “payable” and it’s type is address.
  • We have this constructor here and initialize this data when the smart contract start and this only happens once.
  • So; basically here, when the smart contract will start, the owner will be the one who deployed it. And the sender which is the owner in this case, will have 1000 * 1 ether in his contributions.
  • Here have a modifier and it’s a control check. So, It’s required that the msg.sender “Who is calling this” is the owner which is already assigned in the constructor. And if it’s not; Then, throw an error says “Caller is not the owner”.
  • At the end we got _;and it means, If the condition “If the sender is the owner” matched or it’s True then continue to the function that the modifier implemented on it and execute whatever inside it.
  • This is a public function and we can view it and it returns uint, this will get whatever it’s stored for this msg.sender in the contributions
    and the contributions stores the balance or how much got contributed by this sender. So i.e we will get the balance of this sender.
  • This is a function which the modifier implemented on it and Only can be called by the owner. So, This will transfer all the balance of the address “In this case the owner”
  • Finally, we have the “receive()” function and it’s a fallback function.
    Therefore, Anyone send “Eth” only without any data this will get this function triggered or If no then the function calls matched even though the account received data. Means the value we are sending to this contract have to be more than “0” & what we have in the contributions more than “0”, if this happened a new owner will be assigned which is the one who triggered the fallback function.

Let’s start hacking this smart contract

The fun that we waiting is here. We have to be the owner and after that will withdraw all the balance. From our code analysis we can know that we have to send token to the smart contract. After that will trigger the fallback function which is “receive()”. Then,in the end call the withdraw function.

Start interacting with the smart contract

Check the “owner” and the “player” contract addresses. Using the following commands:

await contract.owner()
player

Results

Now, Let’s check the “ABI(Application Binary Interface)”

await contract.abi

Results

Send token to the contribute function

To send token/currency to the function, you have to call it as we call other functions and Pass the value you will send. As we will pass the value in “call” function in solidity using “JSON” structure.

await contract.contribute({value: 1})

Confirm that we have balance now. And you can see the length is “1”, which is the value we have sent.

await contract.getContribution()

Trigger the fallback function

To send a transaction you have to use the web3 function, “sendTransaction({value: })”

await contract.sendTransaction({value: 1})

I will only send 1, because this is our balance.

And we can see here that the transaction is mined.

Check the owner

We can see now that, We are the owner.

Make the balance 0 by withdraw all of it

Call the withdraw function

await contract.withdraw()

Here the transaction is mined and succeed

Submit the instance

Finally we just submit the instance and we succeed.

Conclusion

That was the “Level-1”. Don’t forget to check the first part “Level-0”, And keep “Googling” for more understanding and stay tuned for the coming parts.

Follow Us (Team):

  • Zeyad Azima

LinkedinTwitterGithub

  • Mohammad Hussam Alzeyyat (MHZCYBER)

LinkedinTwitterGithub

--

--