Fallback (Level-1) CTF
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
Now, Let’s check the “ABI(Application Binary Interface)”
await contract.abi
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
- Mohammad Hussam Alzeyyat (MHZCYBER)