LEARNING

LEARNING,第1张

最近在学习《精通以太坊》的智能合约安全这一块遇到了一些问题,也有一些收获,有些一题多解的原因网上好像没有讲,我的一些想法在这里记录一下(目前未验证的都有标注出来)。

1.重入攻击

预备知识:
1.在做重入攻击的时候,可以把以太坊计算机理解为一台单线程的电脑:无并发,所有的 *** 作只能一条路走到黑
2.只有合约同时满足拥有:function()和其为payable的时候,才可以接收以太币,这就代表了在做重入攻击的时候,这个fallback函数(即function())会进行相应 *** 作

重入攻击的大概思路,在做重入攻击的时候,通过接收以太币时,攻击合约中的fallback()函数再次发动于上一次相同的 *** 作,赶在受害者合约修改其状态变量之前,实现再次提币

接下来我列举额一下臭名昭著的DAO攻击:其中有一部分情况类似:


 function CashOut(uint _am)
{
        if(_am<=balances[msg.sender])
        {
                if(msg.sender.call.value(_am)())
                //👆在这里收到币后,攻击者的fallback()启动,并再次发起提币行为
                {
                        balances[msg.sender]-=_am;
                        TransferLog.AddMessage(msg.sender,_am,"CashOut");
                }
        }
}
这个还蛮好理解的
2.构造替换攻击(蜜罐)

雷同上面的那段代码,作者在这里给出了另外一段经典代码

代码接上下图👇

(图片调整不过来,就当锻炼一下脖子好了)
这里的代码的主要问题,说实在的我半天都没有看明白
后来发现,是在构造函数这里被别人动了手脚(貌似)

function Private_bank(address _log)
{
TransferLog=Log(_log);
}

通过之前传入的变量,作者以一个掩人耳目的方式部署了一个与后面结构并不同的合约在此,并且让别人以为这个合约就是后文所定义的contract Log。可能就是在这个真正的合约中对Addmessage函数进行了重写,并且的最后加了一句

function() payable{
<owner_address>.transfer(this.balance)
}

在别人刚把钱转到这里的时候就把钱转走了(

但是上面都是我个人的理解,我还是有几点困惑:
1.通过地址创建的合约,所有者是合约而不是本人,那如何实现转账 *** 作?:
答:类似DAO事件中罗宾汉小组的工作:在提币之后,合约中的fallback()函数再次进行 *** 作,由于那个log合约是被动了手脚的,其实是与合约地址上真正的代码进行 *** 作.
2.在构造函数中,即使我想偷偷调用的合约和我为了掩人耳目而定义的不一样,也可以进行调用,是通过直接给地址进行访问吗?
答:可以的,但是函数名字啥的好像需要差不多,因为毕竟是和那个地址在交互
3.在合约中未定义一个合约类型,但是我直接用来作为类型可以吗?(如未定义contract B,但是我直接B b;
答:不行
不知道,慢慢解答

3.delegatecall和存储插槽

两个代码,一个是库一个是调用的合约

pragma solidity ^0.4.22;
contract FibonacciLib{
    uint public start;
    uint public calculatedFibNumber;
    function setStart(uint _start) public {
        start=_start;
    }
    function setFibonacci(uint n) public{
        calculatedFibNumber=fibonacci(n);

    }
    function fibonacci(uint n) internal returns (uint){
        if(n==0) return start;
        else if (n==1) return start+1;
        else return fibonacci(n-1) +fibonacci(n-2);
    }
}
pragma solidity ^0.4.22;
contract FinonacciBalance{
    address public fibonacciLibrary;
    uint public calculatedFibNumber;
    uint public start=3;
    uint public withdrawalCounter;
    bytes4 constant fibSig =bytes4(keccak256("setFibonacci(uint256)"));
    constructor(address _fibonacciLibrary) public payable{
        fibonacciLibrary=_fibonacciLibrary;
    }
    function withdraw() public{
        withdrawalCounter+=1;
        require(fibonacciLibrary.delegatecall(fibSig,withdrawalCounter));
        msg.sender.transfer(calculatedFibNumber*1 ether);

    }

    function () public {
        require(fibonacciLibrary.delegatecall(msg.data));
        
    }
}

此处主要的问题就是:在调用library的时候,library中的stotage由于不知道自己应当将数值存在那里,直接把之前FibonacciBalance中fibonacciLibrary和calculatedFibNumber两个变量的值给覆盖了。
插槽-----------合约--------------------------库
slot[0]=>fibonacciLibrary========>start
slot[1]=>calculatedFibNumber=>calculatedFibNumber
得益于此,合约中的withdraw()函数也可以因此获得修改之后的calculatedFibNumber
msg.sender.transfer(calculatedFibNumber*1 ether);
同样,如果在一个contract的function中定义数值,而不是在function外,同样也会因为找不到正确的插值位置而出现类似的情况。👇就像这样

但是在实验过程中发现,通过上面的那个withdraw()实现修改start从而修改整个合约对库函数的引用address的时候,我在对Fibonaccilib函数进行修改之后,不仅修改后的数据位置变的很奇怪,而且修改了一次之后就再也不能改了(不同的两个合约我试了两次均是这样)

truffle(development)> web3.utils.keccak256("setStart(uint256)")
'0xf6a03ebf58982b2b7429b657b28c1995ceffc3cb138ea62e6a6cf87d9f3d1c74'
truffle(development)> var kk='0x'+"f6a03ebf".padEnd(63,'0')+'1'
undefined
truffle(development)> kk
'0xf6a03ebf00000000000000000000000000000000000000000000000000000001'
truffle(development)> web3.eth.sendTransaction({from:'0x6176F30C36CEd8497b18E8910124afc76da002Fb',to:'0xc620F6a31066e45595e5b0FBe29D3429dA267d24',data:kk}).then(res=>{console.log(res.logs[0].event,res.logs[0].args)})
Uncaught TypeError: Cannot read property 'event' of undefined//其实在这里是 *** 作成功了


交易哈希:(应该是这两个之一,但是我发现我提交的时间相近的交易为什么block会顺序相反?)


位置奇怪的原因可能是因为solidity的储存模式决定的。address是20个字节,而一个slot会存储32个字节,书上说的把地址转化为uint插入那里的方法我还没有实验成功。
关于存储的两个文章:
👇solidity文档:
https://learnblockchain.cn/docs/solidity/internals/layout_in_storage.html
👇ctfwiki
https://ctf-wiki.org/blockchain/ethereum/storage/

我的问题:
1.地址修改之后再也改不动了,这是为什么?
2.setStart修改地址的位置怎么样才能比较合适地注入进去?


问题汇总

1.通过地址创建的合约,所有者是合约而不是本人,那如何实现转账 *** 作? 文中已更新
2.在构造函数中,即使我想偷偷调用的合约和我为了掩人耳目而定义的不一样,也可以进行调用,是通过直接给地址进行访问吗?
3.在合约中未定义一个合约类型,但是我直接用来作为类型可以吗?(如未定义contract B,但是我直接B b;

1.地址修改之后再也改不动了,这是为什么?
2.setStart修改地址的位置怎么样才能比较合适地注入进去?

后续懂了再更新,睡觉。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/2992310.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-23
下一篇 2022-09-23

发表评论

登录后才能评论

评论列表(0条)

保存