记一道区块链题目

题目地址:sepolia@0x053cd080A26CB03d5E6d2956CeBB31c56E7660CA
合约源码

// 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_);
    }
}

0x01

区块链题目一般依赖于以太坊智能合约,智能合约可以理解为在以太坊上运行的程序,而编写智能合约常用语言为solidity

一般来说,我们从payforflag()函数入手,这里有个检验条件:_flag[msg.sender]==2

    function payforflag(string memory mid, string memory b64email) public {
    
    
        require(_flag[msg.sender] == 2);
        emit sendflag(mid, b64email);
    }

只有满足这个条件之后才能执行sendflag(mid,b64email);我们的最终目的是为了调用payforflag()函数,触发事件sendflag(mid,b64email); 在这里mid输入的是你的bili uid,b64email输入你邮箱的base64编码,成功触发事件后,会自动把flag发送到你的邮箱

接下来我们要找到使_flag[msg.sender]值变化的函数,_flag[msg.sender]初始值为0

    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;  //该函数执行一次_flag[msg.sender]值加1
    }

而调用withdraw()函数的判断条件要满足:ethbalances[msg.sender] >= 1;_ebalances[msg.sender] >= 1812;而执行一次会使_ebalances[msg.sender] = 0;

0x02

因此我们的目标转到了满足ethbalances[msg.sender]和_ebalances[msg.sender]

第一个值ethbalances[msg.sender]只要满足等于1即可,而我们注意到deposit()函数可以使得ethbalances[msg.sender]值加1,故调用一次即可

    function deposit() public {
    
    
        require(_depositd[msg.sender] == 0, "you can only deposit once");
        _depositd[msg.sender] = 1;
        ethbalances[msg.sender] += 1;
    }

第二个值_ebalances[msg.sender]>= 1812,我们发现_ebalances[msg.sender]与以下4个函数相关,其中_ebalances[msg.sender]与_balances[msg.sender]的转换关系是这道题的关键所在

	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 {
    
      //使用_ebalances[]换取_balances[] 
        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 {
    
     //使用_balances[]换取_ebalances[]
        require(_balances[msg.sender] >= amount, "fail to sale");
        uint earn = amount * tokenprice;
        _transfer(msg.sender, owner, amount);
        _ebalances[msg.sender] += earn;
    }

这里的思路是,_ebalances[]的初始值为0,_balances[]的初始值也为0,但是_balances[]的值可以通过调用borrow()函数增加2个,borrow()函数有条件约束_profited[msg.sender] <= 1,故每个账户只能调用borrow()函数两次从而使得_balances[]的值增加为2,_transfer(owner, msg.sender, amount)会实现_balances[]值加1,因此我们可以创建多个子合约账户,每个子合约账户的2个代币转发到一个大账户里面去,这个大账户可以选取你的外部账户-就是你钱包的地址。

0x03

举个实例,当你的大账户的_balances[msg.sender]=39时,再调用sale()函数可以使得_ebalances[msg.value]=39*6,即_ebalances[msg.sender]=234,{sale函数_balances[]与_ebalances[]的转换率为1:6};然后再调用buy()函数,由于刚好不满足条件ethmount >= 10 && ethmount <= 233;因而price = 1;故_ebalances[]与_balances[]的转化率为1:1,故_balances[msg.sender]=234;再调用一次sale()函数即可使得_ebalances[] = 1404,循环多次,使得_ebalances[msg.sender]>= 1812。最后便可调用withdraw()函数

    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;  //该函数执行一次_flag[msg.sender]值加1
    }

但是这个withdraw()函数调用一次使得_flag[msg.sender] = 1,而我们要满足的条件是_flag[msg.sender] = 2;因此我们需要第二次执行withdraw()函数,但是这个函数把_ebalances[msg.sender] = 0; 因此我们得重新执行0x02步骤;

0x04

    function payforflag(string memory mid, string memory b64email) public {
    
    
        require(_flag[msg.sender] == 2);
        emit sendflag(mid, b64email);
    }

最终使得_flag[msg.sender] = 2,便可调用我们的payforflag()函数了,获取flag了

猜你喜欢

转载自blog.csdn.net/xuanyitwo/article/details/128080237