-
Notifications
You must be signed in to change notification settings - Fork 264
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unexpected Balances #63
Changes from 1 commit
02ee29a
795eb3a
bbc5bbf
3d6a14a
d6e9855
3ff0543
aa478ed
a7eae8d
4cf5b5a
0198f30
0268afe
0f89366
642aa1d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# Force Feeding | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe a better title would be "Unexpected Ether Balance"? This way it focuses more on the vulnerability rather than the attack vector. Would have to update the below paragraph to reflect this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about "Unexpected Ether Transfer"? |
||
|
||
Force-feeding is a technique where an attacker sends Ether directly to a smart contract address without invoking any of its functions. This can disrupt the contract's internal accounting mechanisms, particularly if the contract relies on balance checks for its logic. | ||
|
||
## Force Feeding Mechanics | ||
|
||
### Normal Contract Behavior | ||
|
||
In typical smart contract operation, Ether is sent to a contract via a transaction that calls a function or invokes the `receive()` or `fallback()` functions. If a contract lacks these functions, transactions sending Ether to it will normally be reverted, ensuring the contract does not inadvertently receive funds. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth also noting that ETH can be sent along with payable functions |
||
|
||
### Force-Feeding Bypasses | ||
|
||
Force-feeding bypasses this by sending Ether in a manner that doesn't require calling the contract's functions, thereby avoiding the checks and logic coded in Solidity. | ||
|
||
## Force Feeding Methods | ||
|
||
### Block Rewards and Coinbase | ||
|
||
In Proof of Stake systems like Ethereum, validators earn block rewards for successfully adding blocks to the blockchain. These rewards are sent to an address known as the **coinbase address**. Validators typically set this address to their own wallets, but an attacker-validator can set it to a target smart contract’s address. | ||
|
||
Since block reward transfers are handled at the protocol level, they bypass Solidity-level checks. As a result, the target contract receives Ether directly as part of the block reward, regardless of any Solidity-coded restrictions. | ||
|
||
## Preventing Force Feeding | ||
|
||
To safeguard against force-feeding, consider the following strategies: | ||
Comment on lines
+17
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note to self to come back to reconsider these prevention methods further |
||
|
||
### 1. Avoid Using the Contract’s Balance Directly | ||
|
||
Instead of relying on `address(this).balance` for critical logic, maintain an internal accounting system. For example: | ||
|
||
```solidity | ||
/// DO NOT USE IN PRODUCTION | ||
/// ONLY MEANT TO SERVE AS AN EXAMPLE | ||
|
||
mapping(address => uint256) internalBalances; | ||
|
||
function deposit() external payable { | ||
internalBalances[msg.sender] += msg.value; | ||
} | ||
|
||
function withdraw(uint256 amount) external { | ||
require(internalBalances[msg.sender] >= amount, "Insufficient balance"); | ||
internalBalances[msg.sender] -= amount; | ||
payable(msg.sender).transfer(amount); | ||
} | ||
|
||
function getBalance(address user) external view returns (uint256) { | ||
return internalBalances[user]; | ||
} | ||
``` | ||
|
||
### 2. Immediate Funds Transfer | ||
|
||
Instead of holding funds within the contract, immediately transfer received Ether to a secure, off-contract storage or another smart contract that handles funds securely: | ||
|
||
```solidity | ||
address payable public safeAddress = payable(0xSafeAddress); | ||
|
||
receive() external payable { | ||
safeAddress.transfer(msg.value); | ||
} | ||
``` | ||
|
||
### 3. Event-Based Balance Tracking | ||
|
||
Use events for deposit and withdrawal tracking, enabling off-chain monitoring and cross-verification of the contract’s balance: | ||
|
||
```solidity | ||
event Deposit(address indexed from, uint256 amount); | ||
event Withdrawal(address indexed to, uint256 amount); | ||
|
||
function deposit() external payable { | ||
internalBalances[msg.sender] += msg.value; | ||
emit Deposit(msg.sender, msg.value); | ||
} | ||
|
||
function withdraw(uint256 amount) external { | ||
require(internalBalances[msg.sender] >= amount, "Insufficient balance"); | ||
internalBalances[msg.sender] -= amount; | ||
payable(msg.sender).transfer(amount); | ||
emit Withdrawal(msg.sender, amount); | ||
} | ||
``` | ||
|
||
Off-chain systems can then monitor these events and compare them with the on-chain state to ensure integrity. | ||
|
||
## Sources | ||
- [Solidity coinbase address](https://docs.soliditylang.org/en/latest/units-and-global-variables.html#block-and-transaction-properties) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should update the name here, also would be good to change the filename as well