首页
社区
课程
招聘
[原创]2022哔哩哔哩 1024程序员节 T4 区块链详解
发表于: 2022-11-1 16:50 20672

[原创]2022哔哩哔哩 1024程序员节 T4 区块链详解

2022-11-1 16:50
20672

本次题目的地址为sepolia@0x053cd080A26CB03d5E6d2956CeBB31c56E7660CA

这一次1024程序员节中有区块链相关的题目,作为今年才开始起步区块链的小萌新,这一题也是整整看了一整个周末才做出来,不过做出来之后也是相当的具有成就感滴:),话不多说,我们现在就来看一看如何做出这一题.

先上合约源码↓↓↓↓↓↓

这里我们注意到了一个函数payforflag,很明显,我们需要调用这一个函数来获得我们的flag,那么调用这个函数的条件是什么呢?

我们需要_flag[msg.sender]的值为2

接下来要做的就是寻找函数使_flag[msg.sender]的值到2.

通过寻找,我们找到了withdraw这个函数,而这个函数的执行需要满足两个条件,分别是ethbalances[msg.sender] >= 1_ebalances[msg.sender] >= 1812.

先看第一个条件ethbalances[msg.sender] >= 1,我们可以使用deposit这个函数来令其满足

再看第二个条件_ebalances[msg.sender] >= 1812,涉及到该变量的函数有profit,borrow,buy,sale

我们看看profit这个函数,只能运行一次,获得一个_balances;而borrow这个函数,一共可以执行两次获得两个_balances.但是这两个函数都有_profited[msg.sender]这个变量进行限制,也就是说,我们最多只能通过profitborrow函数获得2个_balances.

那么_balances有什么用呢?看一看sale函数,我们可以把_balances卖掉得到_ebalances,其中tokenprice已经被定义为6了,所以_balances_ebalances之间的兑换比例为1:6.

buy这个函数,只有当_ebalances大于233时,_ebalances_balances之间的兑换比例才是1:1.

仔细看看上面两段话,稍微思考一下就可以明白,只要我的_ebalances比233要大,那么不就可以通过与_balances互刷的方式不断增加我的_ebalances从而满足条件2_ebalances[msg.sender] >= 1812?!

这里我举个简单的例子,假设我现在有_ebalances300个,那么我可以通过buy(300)获得_balances300个,随后在通过sale(300)获得_ebalances300*6=1800个,然后再重复上面的过程,那么我的_ebalances不久可以源源不断的增加的吗~~~~

所以我们现在要做的可以是:

在求解这一题的过程中,我想到了两种方法都可以来获得flag,接下来听我一一道来~~

我们知道每一个初始账号都可以固定获得2个_balances,那么我们能否通过小号为大号通过transfer方法发送_balances的方法获得足够数量的_balances呢?答案是可行的.

直接上代码!

先写一个拿两个_balances并转给大号的合约

再写一个批量创建合约的合约

通过调用第二个合约,给大号足够的_balances启动资金,就可以开始刷_ebalances拿flag咯~~

细心的同学在做这题的时候有没有发现这个函数flashloan,可以直接给你增加300的_ebalances!

不过直接编写攻击合约来调用这个函数肯定是不行滴,因为require(msg.sender == address(this), "hacker get out")这一句的限制了,咋办嘞?

再找找看叭~~于是我们找到了一个调用flashloan的函数testborrowtwice,这不就正好可以满足上面的条件了吗~

不过flashloan内还有限制条件require(_authd[scoupon.coupon.buser] == 2, "need pre auth"),就是说需要验证的意思,我们找找这两个验证函数auth1auth2

对于auth1,secret不是直接在constructor中有定义了嘛~直接看合约


NICE!一下子就找到了根本难不倒我们~

但是当你开开心心的把123456输进去的时候,结果发现居然没通过???

咋回事嘞

再找找看咯

于是你在源码中发现了这个set_secret!没想到owner还可以改secret!!

这个我们玩区块链的根本不慌滴,区块链的每一笔交易都是有记录的,我们直接去看最早的交易记录.

嘿嘿!

这不就有了嘛~

看看这笔交易的信息

嘿嘿secret就是0x154be90,转一下十进制就是22331024,还挺有寓意的嘛~

接下来就是搞auth2 的时候了

当我看到uint(keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)))这个的时候,我瞬间乐开了花,这我可太熟悉不过了~

看看这篇文章Source of Randomness简直简直就是一个模子里刻出来的哇,

uint(keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)))这玩意儿看着随机,其实是确定的!

接下来写个攻击合约就可以赚到大把大把的_balances

直接上代码!

这里简单说明一下为什么将scoupon.coupon.loankey赋值为2233通过testborrowtwice后,在flashloan函数中scoupon.coupon.loankey又变为0,这是由于solidity0.8.12编译器自身原因导致了这个bug,从而使得第一个成员变量的值清零,在solidity0.8.16后这个问题得到了修复.

贴一下bug描述

至此,这第四题区块链的解答就到此结束了

说一说感受吧,每次做区块链的题目都感觉特别有意思,其实本人过去是学习逆向工程的 ,今年才开始接触区块链,解区块链题目的过程说实话,和逆向分析真的好像哇,都是一个逆向的过程,分析需要满足的条件,然后设法编写合约来让条件得到满足,最终满足所有需要的条件之后获得flag ,好玩好玩,嘿嘿(●ˇ∀ˇ●)

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)
 
pragma solidity 0.8.12;
 
import "./IERC20.sol";
import "./IERC20Metadata.sol";
import "./Context.sol";
 
//import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
//import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
//import "@openzeppelin/contracts/utils/Context.sol";
 
 
struct Coupon {
    uint loankey;
    uint256 amount;
    address buser;
    bytes reason;
}
struct Signature {
    uint8 v;
    bytes32[2] rs;
}
struct SignCoupon {
    Coupon coupon;
    Signature signature;
}
 
 
contract MyToken is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) public _balances;
    mapping(address => uint) public _ebalances;
    mapping(address => uint) public ethbalances;
 
    mapping(address => mapping(address => uint256)) private _allowances;
 
    mapping(address => uint) public _profited;
    mapping(address => uint) public _auth_one;
    mapping(address => uint) public _authd;
    mapping(address => uint) public _loand;
    mapping(address => uint) public _flag;
    mapping(address => uint) public _depositd;
 
    uint256 private _totalSupply;
 
    string private _name;
    string private _symbol;
 
    address owner;
    address backup;
    uint secret;
    uint tokenprice;
 
    Coupon public c;
 
    address public lala;
    address public xixi;
 
 
    //mid = bilibili uid
    //b64email = base64(your email address)
    //Don't leak your bilibili uid
    //Gmail is ok. 163 and qq may have some problems.
    event sendflag(string mid, string b64email);
    event changeprice(uint secret_);
 
    constructor(string memory name_, string memory symbol_, uint secret_) {
        _name = name_;
        _symbol = symbol_;
        owner = msg.sender;
        backup = msg.sender;
        tokenprice = 6;
        secret = secret_;
        _mint(owner, 2233102400);
    }
 
    modifier onlyowner() {
        require(msg.sender == owner);
        _;
    }
 
    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }
 
 
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }
 
 
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }
 
    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }
 
    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }
 
 
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }
 
    function deposit() public {
        require(_depositd[msg.sender] == 0, "you can only deposit once");
        _depositd[msg.sender] = 1;
        ethbalances[msg.sender] += 1;
    }
 
    function getBalance() public view returns (uint) {
        return address(this).balance;                  
    }
 
 
    function setbackup() public onlyowner {
        owner = backup;
    }
 
    function ownerbackdoor() public {
        require(msg.sender == owner);
        _mint(owner, 1000);
    }
 
    function auth1(uint pass_) public {
        require(pass_ == secret, "auth fail");
        require(_authd[msg.sender] == 0, "already authd");
        _auth_one[msg.sender] += 1;
        _authd[msg.sender] += 1;
    }
 
    function auth2(uint pass_) public {
        uint pass = uint(keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)));
        require(pass == pass_, "password error, auth fail");
        require(_auth_one[msg.sender] == 1, "need pre auth");
        require(_authd[msg.sender] == 1, "already authd");
        _authd[msg.sender] += 1;
    }
 
 
 
 
    function payforflag(string memory mid, string memory b64email) public {
        require(_flag[msg.sender] == 2);
        emit sendflag(mid, b64email);
    }
 
    function flashloan(SignCoupon calldata scoupon) public {
 
 
        require(scoupon.coupon.loankey == 0, "loan key error");
 
        require(msg.sender == address(this), "hacker get out");
        Coupon memory coupon = scoupon.coupon;
        Signature memory sig = scoupon.signature;
        c=coupon;
 
        require(_authd[scoupon.coupon.buser] == 2, "need pre auth");
 
        require(_loand[scoupon.coupon.buser] == 0, "you have already loaned");
        require(scoupon.coupon.amount <= 300, "loan amount error");
 
        _loand[scoupon.coupon.buser] = 1;
 
        _ebalances[scoupon.coupon.buser] += scoupon.coupon.amount;
    }
 
 
 
    function profit() public {
        require(_profited[msg.sender] == 0);
        _profited[msg.sender] += 1;
        _transfer(owner, msg.sender, 1);
    }
 
 
    function borrow(uint amount) public {
        require(amount == 1);
        require(_profited[msg.sender] <= 1);
        _profited[msg.sender] += 1;
        _transfer(owner, msg.sender, amount);
    }
 
 
    function buy(uint amount) public {
        require(amount <= 300, "max buy count is 300");
        uint price;
        uint ethmount = _ebalances[msg.sender];
        if (ethmount < 10) {
            price = 1000000;
        } else if (ethmount >= 10 && ethmount <= 233) {
            price = 10000;
        } else {
            price = 1;
        }
        uint payment = amount * price;
        require(payment <= ethmount);
        _ebalances[msg.sender] -= payment;
        _transfer(owner, msg.sender, amount);
    }
 
 
    function sale(uint amount) public {
        require(_balances[msg.sender] >= amount, "fail to sale");
        uint earn = amount * tokenprice;
        _transfer(msg.sender, owner, amount);
        _ebalances[msg.sender] += earn;
    }
 
    function withdraw() public {
        require(ethbalances[msg.sender] >= 1);
        require(_ebalances[msg.sender] >= 1812);
        payable(msg.sender).call{value:100000000000000000 wei}("");
 
        _ebalances[msg.sender] = 0;
        _flag[msg.sender] += 1;
    }
 
 
    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }
 
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }
 
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        require(msg.sender == owner);     //不允许被owner以外调用
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }
 
 
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        require(msg.sender == owner);     //不允许被owner以外调用
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }
 
 
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        require(msg.sender == owner);     //不允许被owner以外调用
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }
 
        return true;
    }
 
 
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");
 
        _beforeTokenTransfer(from, to, amount);
 
        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }
 
        emit Transfer(from, to, amount);
 
        _afterTokenTransfer(from, to, amount);
    }
 
 
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");
 
        _beforeTokenTransfer(address(0), account, amount);
 
        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);
 
        _afterTokenTransfer(address(0), account, amount);
    }
 
 
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");
 
        _beforeTokenTransfer(account, address(0), amount);
 
        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }
 
        emit Transfer(account, address(0), amount);
 
        _afterTokenTransfer(account, address(0), amount);
    }
 
 
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");
 
        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }
 
 
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }
 
 
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
 
 
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
 
    // debug param secret
    function get_secret() public view returns (uint) {
        require(msg.sender == owner);
        return secret;
    }
 
    // debug param tokenprice
    function get_price() public view returns (uint) {
        return tokenprice;
    }
 
    // test need to be delete
    function testborrowtwice(SignCoupon calldata scoupon) public {
        require(scoupon.coupon.loankey == 2233);
        MyToken(this).flashloan(scoupon);
    }
 
    // test need to be delete
    function set_secret(uint secret_) public onlyowner {
        secret = secret_;
        emit changeprice(secret_);
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)
 
pragma solidity 0.8.12;
 
import "./IERC20.sol";
import "./IERC20Metadata.sol";
import "./Context.sol";
 
//import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
//import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
//import "@openzeppelin/contracts/utils/Context.sol";
 
 
struct Coupon {
    uint loankey;
    uint256 amount;
    address buser;
    bytes reason;
}
struct Signature {
    uint8 v;
    bytes32[2] rs;
}
struct SignCoupon {
    Coupon coupon;
    Signature signature;
}
 
 
contract MyToken is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) public _balances;
    mapping(address => uint) public _ebalances;
    mapping(address => uint) public ethbalances;
 
    mapping(address => mapping(address => uint256)) private _allowances;
 
    mapping(address => uint) public _profited;
    mapping(address => uint) public _auth_one;
    mapping(address => uint) public _authd;
    mapping(address => uint) public _loand;
    mapping(address => uint) public _flag;
    mapping(address => uint) public _depositd;
 
    uint256 private _totalSupply;
 
    string private _name;
    string private _symbol;
 
    address owner;
    address backup;
    uint secret;
    uint tokenprice;
 
    Coupon public c;
 
    address public lala;
    address public xixi;
 
 
    //mid = bilibili uid
    //b64email = base64(your email address)
    //Don't leak your bilibili uid
    //Gmail is ok. 163 and qq may have some problems.
    event sendflag(string mid, string b64email);
    event changeprice(uint secret_);
 
    constructor(string memory name_, string memory symbol_, uint secret_) {
        _name = name_;
        _symbol = symbol_;
        owner = msg.sender;
        backup = msg.sender;
        tokenprice = 6;
        secret = secret_;
        _mint(owner, 2233102400);
    }
 
    modifier onlyowner() {
        require(msg.sender == owner);
        _;
    }
 
    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }
 
 
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }
 
 
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }
 
    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }
 
    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }
 
 
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }
 
    function deposit() public {
        require(_depositd[msg.sender] == 0, "you can only deposit once");
        _depositd[msg.sender] = 1;
        ethbalances[msg.sender] += 1;
    }
 
    function getBalance() public view returns (uint) {
        return address(this).balance;                  
    }
 
 
    function setbackup() public onlyowner {
        owner = backup;
    }
 
    function ownerbackdoor() public {
        require(msg.sender == owner);
        _mint(owner, 1000);
    }
 
    function auth1(uint pass_) public {
        require(pass_ == secret, "auth fail");
        require(_authd[msg.sender] == 0, "already authd");
        _auth_one[msg.sender] += 1;
        _authd[msg.sender] += 1;
    }
 
    function auth2(uint pass_) public {
        uint pass = uint(keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)));
        require(pass == pass_, "password error, auth fail");
        require(_auth_one[msg.sender] == 1, "need pre auth");
        require(_authd[msg.sender] == 1, "already authd");
        _authd[msg.sender] += 1;
    }
 
 
 
 
    function payforflag(string memory mid, string memory b64email) public {
        require(_flag[msg.sender] == 2);
        emit sendflag(mid, b64email);
    }
 
    function flashloan(SignCoupon calldata scoupon) public {
 
 
        require(scoupon.coupon.loankey == 0, "loan key error");
 
        require(msg.sender == address(this), "hacker get out");
        Coupon memory coupon = scoupon.coupon;
        Signature memory sig = scoupon.signature;
        c=coupon;
 
        require(_authd[scoupon.coupon.buser] == 2, "need pre auth");
 
        require(_loand[scoupon.coupon.buser] == 0, "you have already loaned");
        require(scoupon.coupon.amount <= 300, "loan amount error");
 
        _loand[scoupon.coupon.buser] = 1;
 
        _ebalances[scoupon.coupon.buser] += scoupon.coupon.amount;
    }
 
 
 
    function profit() public {
        require(_profited[msg.sender] == 0);
        _profited[msg.sender] += 1;
        _transfer(owner, msg.sender, 1);
    }
 
 
    function borrow(uint amount) public {
        require(amount == 1);
        require(_profited[msg.sender] <= 1);
        _profited[msg.sender] += 1;
        _transfer(owner, msg.sender, amount);
    }
 
 
    function buy(uint amount) public {
        require(amount <= 300, "max buy count is 300");
        uint price;
        uint ethmount = _ebalances[msg.sender];
        if (ethmount < 10) {
            price = 1000000;
        } else if (ethmount >= 10 && ethmount <= 233) {
            price = 10000;
        } else {
            price = 1;
        }
        uint payment = amount * price;
        require(payment <= ethmount);
        _ebalances[msg.sender] -= payment;
        _transfer(owner, msg.sender, amount);
    }
 
 
    function sale(uint amount) public {
        require(_balances[msg.sender] >= amount, "fail to sale");
        uint earn = amount * tokenprice;
        _transfer(msg.sender, owner, amount);
        _ebalances[msg.sender] += earn;
    }
 
    function withdraw() public {
        require(ethbalances[msg.sender] >= 1);
        require(_ebalances[msg.sender] >= 1812);
        payable(msg.sender).call{value:100000000000000000 wei}("");
 
        _ebalances[msg.sender] = 0;
        _flag[msg.sender] += 1;
    }
 
 
    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }
 
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }
 
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        require(msg.sender == owner);     //不允许被owner以外调用
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }
 
 
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        require(msg.sender == owner);     //不允许被owner以外调用
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }
 
 
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        require(msg.sender == owner);     //不允许被owner以外调用
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }
 
        return true;
    }
 
 
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");
 
        _beforeTokenTransfer(from, to, amount);
 
        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2022-12-15 15:44 被oacia编辑 ,原因:
收藏
免费 6
支持
分享
最新回复 (5)
雪    币: 156
活跃值: (3806)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
没看懂,学区块链安全需要学习什么呢,除了eth的文档以及开发一个智能合约这些基础中的基础还需要做哪些事呢?
2022-11-2 17:54
0
雪    币: 3505
活跃值: (4768)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
3
如果想要学习区块链安全的话,我的建议是你可以参考这一篇文章https://learnblockchain.cn/article/3682的学习路线的进行学习,我本人目前也是在按照这篇文章的学习路线来学习区块链,希望这对你有帮助。
2022-11-2 18:47
0
雪    币: 156
活跃值: (3806)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
4
好的,感谢
2022-11-3 11:49
0
雪    币: 9
活跃值: (64)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
你好我也正在复现这个题目,第二个解法的攻击合约,我感觉mytoken.buy(302);貌似执行不了,因为buy函数有一句:
require(amount <= 300, "max buy count is 300"); 
302是不是就报错了

2022-12-1 21:30
0
雪    币: 3505
活跃值: (4768)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
6
chanra 你好我也正在复现这个题目,第二个解法的攻击合约,我感觉mytoken.buy(302);貌似执行不了,因为buy函数有一句: require(amount
真是不好意思这么久了才看到,这确实是我的疏忽所导致的问题。
写到这里的时候我想的是既然都已经转了300个了不如把最开始得到的那两个也一并转走,却忘记了还有这一条限制条件,感谢大佬的指正
下次写文章之前我也会多加注意,尽量不给看文章的小伙伴误导
再次致以歉意。
2022-12-15 15:37
0
游客
登录 | 注册 方可回帖
返回
//