全国大学生信息平安比赛—区块链问题剖析 | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

全国大学生信息平安比赛—区块链问题剖析

申博_安全预警 申博 95次浏览 未收录 0个评论

申博网络安全巴士站

申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

————————————-

一、媒介

头几天全国大学生信息平安竞赛预赛准期举行,在这次竞赛中也看到了区块链问题的身影。以是我将问题拿来举行剖析,并为后续的竞赛赛题供应一些剖析思绪。

因为本次竞赛我并没有列入,以是我并没有Flag等相干信息,然则我拿到了竞赛中的相干文件和合约地点并在此基础上举行的详细剖析,愿望能资助到举行研讨的同砚。

二、问题剖析

拿到问题后,我们只获得了两个内容,一个是合约的地点,一个是broken.so

pragma solidity ^0.4.24;

contract DaysBank {
    mapping(address => uint) public balanceOf;
    mapping(address => uint) public gift;
    address owner;

    constructor()public{
        owner = msg.sender;
    }

    event SendFlag(uint256 flagnum, string b64email);
    function payforflag(string b64email) public {
        require(balanceOf[msg.sender] >= 10000);
        emit SendFlag(1,b64email);
    }

起首我们看这个合约文件。合约最先界说了两个mapping变量——balanceOf 与gift,以后为组织函数,和发送flag的事宜。当我们挪用payforflag函数并传入运用base64加密的邮件地点以后,须要知足以后账户的余额比10000多。

由这第一手信息我们可以或许举行一些简朴的料想。这道问题须方法本身的余额大于10000,只需如许才能购置flag。这也是很罕见的问题范例。而这个问题异常设想的照样异常奇妙的,我们接着向下看。

依据上面的合约代码,我们并不能获得更多的有效信息。但是此时我们就须要应用合约地点来进一步剖析。

此处合约地点为:0x455541c3e9179a6cd8C418142855d894e11A288c

我们接见公链信息看看是不是可以或许接见到有价值的信息:

全国大学生信息平安比赛—区块链问题剖析

发明出题人并没有公然源代码,只需ABI码,此时我们只能依据此来举行合约逆一直寻觅更有效的解题思绪。

https://ethervm.io/decompile#func_profit

在此网站中举行逆向剖析后,我们获得以下代码:

全国大学生信息平安比赛—区块链问题剖析

contract Contract {
    function main() {
        memory[0x40:0x60] = 0x80;

        if (msg.data.length < 0x04) { revert(memory[0x00:0x00]); }

        var var0 = msg.data[0x00:0x20] / 0x0100000000000000000000000000000000000000000000000000000000 & 0xffffffff;

        if (var0 == 0x652e9d91) {
            // Dispatch table entry for 0x652e9d91 (unknown)
            var var1 = msg.value;

            if (var1) { revert(memory[0x00:0x00]); }

            var1 = 0x009c;
            func_01DC();
            stop();
        } else if (var0 == 0x66d16cc3) {
            // Dispatch table entry for profit()
            var1 = msg.value;

            if (var1) { revert(memory[0x00:0x00]); }

            var1 = 0x009c;
            profit();
            stop();
        } else if (var0 == 0x6bc344bc) {
            // Dispatch table entry for 0x6bc344bc (unknown)
            var1 = msg.value;

            if (var1) { revert(memory[0x00:0x00]); }

            var temp0 = memory[0x40:0x60];
            var temp1 = msg.data[0x04:0x24];
            var temp2 = msg.data[temp1 + 0x04:temp1 + 0x04 + 0x20];
            memory[0x40:0x60] = temp0 + (temp2 + 0x1f) / 0x20 * 0x20 + 0x20;
            memory[temp0:temp0 + 0x20] = temp2;
            var1 = 0x009c;
            memory[temp0 + 0x20:temp0 + 0x20 + temp2] = msg.data[temp1 + 0x24:temp1 + 0x24 + temp2];
            var var2 = temp0;
            func_0278(var2);
            stop();
        } else if (var0 == 0x70a08231) {
            // Dispatch table entry for balanceOf(address)
            var1 = msg.value;

            if (var1) { revert(memory[0x00:0x00]); }

            var1 = 0x013a;
            var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
            var2 = balanceOf(var2);

        label_013A:
            var temp3 = memory[0x40:0x60];
            memory[temp3:temp3 + 0x20] = var2;
            var temp4 = memory[0x40:0x60];
            return memory[temp4:temp4 + temp3 - temp4 + 0x20];
        } else if (var0 == 0x7ce7c990) {
            // Dispatch table entry for transfer2(address,uint256)
            var1 = msg.value;

            if (var1) { revert(memory[0x00:0x00]); }

            var1 = 0x009c;
            var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
            var var3 = msg.data[0x24:0x44];
            transfer2(var2, var3);
            stop();
        } else if (var0 == 0xa9059cbb) {
            // Dispatch table entry for transfer(address,uint256)
            var1 = msg.value;

            if (var1) { revert(memory[0x00:0x00]); }

            var1 = 0x009c;
            var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
            var3 = msg.data[0x24:0x44];
            transfer(var2, var3);
            stop();
        } else if (var0 == 0xcbfc4bce) {
            // Dispatch table entry for 0xcbfc4bce (unknown)
            var1 = msg.value;

            if (var1) { revert(memory[0x00:0x00]); }

            var1 = 0x013a;
            var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
            var2 = func_0417(var2);
            goto label_013A;
        } else { revert(memory[0x00:0x00]); }
    }
    //0x66d16cc3函数   空投函数??
    function func_01DC() {
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x01;
    // 若是gift已存在,revert
        if (storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }

        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
        var temp0 = keccak256(memory[0x00:0x40]);
        storage[temp0] = storage[temp0] + 0x01;
        memory[0x20:0x40] = 0x01;
        storage[keccak256(memory[0x00:0x40])] = 0x01;
    }


    // 利润函数: 
    function profit() {
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;

        if (storage[keccak256(memory[0x00:0x40])] != 0x01) { revert(memory[0x00:0x00]); }

        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x01;

        if (storage[keccak256(memory[0x00:0x40])] != 0x01) { revert(memory[0x00:0x00]); }

        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
        var temp0 = keccak256(memory[0x00:0x40]);
        storage[temp0] = storage[temp0] + 0x01;
        memory[0x20:0x40] = 0x01;
        storage[keccak256(memory[0x00:0x40])] = 0x02;
    }

    function func_0278(var arg0) {
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;

        if (0x2710 > storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }

        var var0 = 0xb1bc9a9c599feac73a94c3ba415fa0b75cbe44496bfda818a9b4a689efb7adba;
        var var1 = 0x01;
        var temp0 = arg0;
        var var2 = temp0;
        var temp1 = memory[0x40:0x60];
        var var3 = temp1;
        memory[var3:var3 + 0x20] = var1;
        var temp2 = var3 + 0x20;
        var var4 = temp2;
        var temp3 = var4 + 0x20;
        memory[var4:var4 + 0x20] = temp3 - var3;
        memory[temp3:temp3 + 0x20] = memory[var2:var2 + 0x20];
        var var5 = temp3 + 0x20;
        var var7 = memory[var2:var2 + 0x20];
        var var6 = var2 + 0x20;
        var var8 = var7;
        var var9 = var5;
        var var10 = var6;
        var var11 = 0x00;

        if (var11 >= var8) {
        label_02FD:
            var temp4 = var7;
            var5 = temp4 + var5;
            var6 = temp4 & 0x1f;

            if (!var6) {
                var temp5 = memory[0x40:0x60];
                log(memory[temp5:temp5 + var5 - temp5], [stack[-7]]);
                return;
            } else {
                var temp6 = var6;
                var temp7 = var5 - temp6;
                memory[temp7:temp7 + 0x20] = ~(0x0100 ** (0x20 - temp6) - 0x01) & memory[temp7:temp7 + 0x20];
                var temp8 = memory[0x40:0x60];
                log(memory[temp8:temp8 + (temp7 + 0x20) - temp8], [stack[-7]]);
                return;
            }
        } else {
        label_02EE:
            var temp9 = var11;
            memory[temp9 + var9:temp9 + var9 + 0x20] = memory[temp9 + var10:temp9 + var10 + 0x20];
            var11 = temp9 + 0x20;

            if (var11 >= var8) { goto label_02FD; }
            else { goto label_02EE; }
        }
    }

    function balanceOf(var arg0) returns (var arg0) {
        memory[0x20:0x40] = 0x00;
        memory[0x00:0x20] = arg0;
        return storage[keccak256(memory[0x00:0x40])];
    }

    function transfer2(var arg0, var arg1) {
        if (arg1 <= 0x02) { revert(memory[0x00:0x00]); }

        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;

        if (0x02 >= storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }

        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;

        if (storage[keccak256(memory[0x00:0x40])] - arg1 <= 0x00) { revert(memory[0x00:0x00]); }

        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
        var temp0 = keccak256(memory[0x00:0x40]);
        var temp1 = arg1;
        storage[temp0] = storage[temp0] - temp1;
        memory[0x00:0x20] = arg0 & 0xffffffffffffffffffffffffffffffffffffffff;
        var temp2 = keccak256(memory[0x00:0x40]);
        storage[temp2] = temp1 + storage[temp2];
    }

    function transfer(var arg0, var arg1) {
        if (arg1 <= 0x01) { revert(memory[0x00:0x00]); }

        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;

        if (0x01 >= storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }

        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
    // 若是arg1大于余额,revert
        if (arg1 > storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }

        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
        var temp0 = keccak256(memory[0x00:0x40]);
        var temp1 = arg1;
        storage[temp0] = storage[temp0] - temp1;
    // 地点arg0的余额增添arg1的个数
        memory[0x00:0x20] = arg0 & 0xffffffffffffffffffffffffffffffffffffffff;
        var temp2 = keccak256(memory[0x00:0x40]);
        storage[temp2] = temp1 + storage[temp2];
    }

    function func_0417(var arg0) returns (var arg0) {
        memory[0x20:0x40] = 0x01;
        memory[0x00:0x20] = arg0;
        return storage[keccak256(memory[0x00:0x40])];
    }
}

以后我们针对此逆向后的代码举行剖析。

我们经由剖析发明了以下的public函数:

全国大学生信息平安比赛—区块链问题剖析

很明显这是代币合约,而且可以或许举行转账。而此代码中具有两个转账函数。而且可以或许检察余额。

我们详细依据代码对函数详细剖析:

全国大学生信息平安比赛—区块链问题剖析

起首我们剖析编号为0x652e9d91func_01DC()函数。

起首合约将内存切换到0x01地位,此处为:mapping(address => uint) public gift;

memory[0x00:0x20] = msg.sender;
memory[0x20:0x40] = 0x01;

即合约起首要推断该用户的gift是不是为0,若不为0则revert(也就是说这个函数要包管只能领取一次)。

以后内存切换到mapping(address => uint) public balanceOf;

对此变量举行操纵,也就是将用户的余额值+1。并将gift值加一。

profit()函数的剖析以下:

全国大学生信息平安比赛—区块链问题剖析

依据函数的称号我们也晓得,此函数为利润函数,其目标也很明显,依据我们的代币配景学问,我们预测这个函数是用来赠予代币的。

函数请求balanceOf与gift必需==1,否则就会revert。当挪用此函数时,当知足上述前提后就会给用户的余额+1,令用户余额为2 。

balanceOf()函数

这个函数很简朴,就是返回用户的余额状况。

下面我们来看两个症结的转账函数:

transfer()

全国大学生信息平安比赛—区块链问题剖析

浅析图形验证码安全

0x01 前言 验证码的定义: 验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。可以防止:恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,实际上用验证码是现在很多网站通行的方式,我们利用比较简易的方式实现了这个功能。这个问题可以由计算机生成并评判,但是必须只有人类才能解答。由于计算机无法解答CAPTCHA的问题,

函数一样对照简朴。

起首须要推断用户的余额是不是小于1 。以后推断转账的金额(arg1)是不是大于余额,若是用户余额不足以举行转账,那末就会revert。

以后将以后用户的账面上减掉arg1代币数目,将收款方arg0的账户上增添arg1代币数目。

我们可以或许恰当复原此函数:

function transfer(var arg0, var arg1){

   if(arg1<=1) revert();
   if(balance(msg.sender)<=1) revert();
   if(balance(msg.sender)<arg1) revert();
   balance(msg.sender) = balance(msg.sender) - arg1;
   balance(arg0) = balance(arg0) + arg1; 

}

此时我们看transfer2()函数。

全国大学生信息平安比赛—区块链问题剖析

在看到这个函数前我就疑问为什么一个代币中有两个转账函数?厥后在剖析了源码后我相识到第二个转账函数中就存在破绽。详细以下:

最先时函数推断arg1须要>2,即转账数目要大于2. 。

以后推断用户余额须要大于即是2.

知足前提后须要令(余额 – arg1)大于零。即其本意是要用户余额大于转账金额。

以后举行转账后的余额更新。

我们剖析该代码后将合约详细代码举行复原:

function transfer2(var arg0, var arg1){
        require(arg1>2);
        require(balance(msg.sender) >= 2);
        require(balance(msg.sender) - arg1 >= 0);
        balance(msg.sender) = balance(msg.sender) - arg1;
        balance(arg0) = balance(arg0) + arg1;

    }

不知用户是不是发明,我们就看到了破绽点了,这是一个典范的溢出破绽。

全国大学生信息平安比赛—区块链问题剖析

依据作者给出的代码,我们发明其详细余额是运用uint界说的,因为uint的位数是有限的,而且其不支持负数。以是当其负数溢出时就会酿成一个很大的正数。

而依据我们的transfer2函数内容,我们晓得:require(balance(msg.sender) - arg1 >= 0);。此句举行推断的时刻是将用户余额减去一个arg1来推断是不是大于0的。而若是arg1设置一个对照大的数,那末balance(msg.sender) - arg1就会溢出为一个异常大的数,此时就胜利绕过了检测而且转账大批的代币。

以是我们可以或许应用此处的整数溢出来举行问题求解,但是在剖析的过程当中我又发明了另一个解法。

若是做题人没有发明此处的破绽点,我们可以或许应用通例做法来举行求解。

全国大学生信息平安比赛—区块链问题剖析

依据给出的flag函数我们晓得,我们只须要余额>10000便可,那末我们可以或许发明,我们的profit函数可以或许给我们赓续的新增钱。

依据我们的剖析,我们须要令合约余额==1而且gitf==1,此时便可挪用profit()来将余额++,挪用后余额为2,gift为1 。这时刻将余额转给第二个账户,余额就又酿成1了,就又可以或许挪用profit()函数。如许赓续给第二个用户转账,转账10000次便可。(这里肯定是要用剧本去写,手动转账对照傻emmmm)

三、破绽应用技能

此处我们引见破绽应用的技能。

起首我们须要具有两个钱包地点(Addr1 Addr2)

  • 此时我们令Addr1挪用func_01DC()函数领取1个代币和1个gift。

  • 以后我们挪用profit领取一个代币。此时余额为2,gift为1 。

因为transfer2须要余额大于2才能挪用,以是我们起首令Addr2一样实行上面的两步。此时两个钱包均有余额为2 。

  • 这时刻Adde1挪用transfer给Addr2转账两个代币,此时Addr余额为0,Addr2为4 。

以后Addr2就可以或许挪用transfer2给Adde1转账一个异常大的金额。到达溢出结果。此时Addr1与Addr2均具有了大批的代币(Addr2为溢出获得,Addr1为转账获得)。恣意地点均可以或许挪用flag函数。

详细的生意业务日记以下:

全国大学生信息平安比赛—区块链问题剖析

全国大学生信息平安比赛—区块链问题剖析

全国大学生信息平安比赛—区块链问题剖析

全国大学生信息平安比赛—区块链问题剖析

此时flag就被挪用发送到用户账户上了。

四、总结

本次问题异常奇妙,若是背面的同砚想直接检察生意业务日记是异常难经由过程一个账户来举行跟踪的。而且本问题没有宣布合约,以是磨练逆向才能。然则只需逆出来后就是一道对照简朴的问题,没有完整逆出来的同砚也可以或许运用通例做法举行赓续转账来使余额知足请求。愿望本文对人人以后的研讨有所资助。迎接议论。


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明全国大学生信息平安比赛—区块链问题剖析
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址